Skip to content

Commit 3655131

Browse files
committed
Day 6
1 parent 57fec3a commit 3655131

File tree

7 files changed

+199
-27
lines changed

7 files changed

+199
-27
lines changed

.vscode/launch.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
{
88
"type": "lldb",
99
"request": "launch",
10-
"name": "Debug unit tests for a solution",
10+
"name": "Debug unit tests for solution 06",
1111
"cargo": {
1212
"args": [
1313
"test",
1414
"--no-run",
1515
// replace `01` here with the solution you like to debug.
16-
"--bin=01",
16+
"--bin=06",
1717
"--package=advent_of_code"
1818
],
1919
},

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
1616
| [Day 3](./src/bin/03.rs) | `717.8µs` | `830.1µs` |
1717
| [Day 4](./src/bin/04.rs) | `810.2µs` | `298.7µs` |
1818
| [Day 5](./src/bin/05.rs) | `67.7µs` | `184.5µs` |
19+
| [Day 6](./src/bin/06.rs) | `71.4µs` | `167.3ms` |
1920

20-
**Total: 3.41ms**
21+
**Total: 170.78ms**
2122
<!--- benchmarking table --->
2223

2324
---

data/examples/06.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
....#.....
2+
.........#
3+
..........
4+
..#.......
5+
.......#..
6+
..........
7+
.#..^.....
8+
........#.
9+
#.........
10+
......#...

mygrid/src/direction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::ops::{Add, Mul};
33

44
use crate::point::Point;
55

6-
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
6+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
77
pub struct Direction {
88
pub vertical: isize,
99
pub horizontal: isize,

mygrid/src/grid.rs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ impl<T> Grid<T> {
2222
content: vec![default; width * height],
2323
}
2424
}
25-
2625
#[inline]
2726
pub fn new_from_str<F>(input: &str, map_char: F) -> Self
2827
where
@@ -48,7 +47,7 @@ impl<T> Grid<T> {
4847
pub fn new_from_str_capture_start(
4948
input: &str,
5049
map_char: &dyn Fn(char) -> T,
51-
is_start: &dyn Fn(T) -> bool,
50+
is_start: &dyn Fn(char) -> bool,
5251
) -> (Self, Point)
5352
where
5453
T: From<char> + Copy,
@@ -59,7 +58,7 @@ impl<T> Grid<T> {
5958
let mut start = None;
6059
for (i, c) in input.chars().filter(|&c| c != '\n').enumerate() {
6160
let t = map_char(c);
62-
if is_start(t) {
61+
if is_start(c) {
6362
start = Some(Point::new_usize(i / width, i % width));
6463
}
6564
content.push(t);
@@ -160,6 +159,13 @@ impl<T> Grid<T> {
160159
}
161160
}
162161

162+
impl Grid<char> {
163+
#[inline]
164+
pub fn new_char_grid_from_str(input: &str) -> Self {
165+
Self::new_from_str(input, |c| c)
166+
}
167+
}
168+
163169
impl<T> Grid<T> {
164170
#[inline]
165171
pub fn is_in_bounds(&self, point: Point) -> bool {
@@ -254,6 +260,22 @@ impl<T: std::fmt::Display> std::fmt::Display for Grid<T> {
254260
}
255261
}
256262

263+
impl<T: std::fmt::Display> std::fmt::Debug for Grid<T> {
264+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265+
write!(f, "{}", self)
266+
}
267+
}
268+
269+
impl<T: PartialEq> Grid<T> {
270+
#[inline]
271+
pub fn find_position_of(&self, item: &T) -> Option<Point> {
272+
self.content
273+
.iter()
274+
.position(|t| *t == *item)
275+
.map(|i| Point::new_usize(i / self.width, i % self.width))
276+
}
277+
}
278+
257279
#[cfg(test)]
258280
mod tests {
259281
use super::*;
@@ -517,4 +539,27 @@ mod tests {
517539
assert_eq!(grid.content[2], '8');
518540
assert_eq!(grid.content[3], '9');
519541
}
542+
543+
#[test]
544+
pub fn test_grid_get_item() {
545+
let grid = Grid::new_from_str("123\n456\n789", &|c| c);
546+
assert_eq!(grid.get_item(Point::new(0, 0)), Some(&'1'));
547+
assert_eq!(grid.get_item(Point::new(0, 1)), Some(&'2'));
548+
assert_eq!(grid.get_item(Point::new(0, 2)), Some(&'3'));
549+
}
550+
551+
#[test]
552+
pub fn test_grid_get_item_out_of_bounds() {
553+
let grid = Grid::new_from_str("123\n456\n789", &|c| c);
554+
assert_eq!(grid.get_item(Point::new(3, 0)), None);
555+
assert_eq!(grid.get_item(Point::new(0, 3)), None);
556+
}
557+
558+
#[test]
559+
pub fn test_grid_find_position_of() {
560+
let grid = Grid::new_from_str("123\n456\n789", &|c| c);
561+
assert_eq!(grid.find_position_of(&'1'), Some(Point::new(0, 0)));
562+
assert_eq!(grid.find_position_of(&'2'), Some(Point::new(0, 1)));
563+
assert_eq!(grid.find_position_of(&'3'), Some(Point::new(0, 2)));
564+
}
520565
}

mygrid/src/point.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::hash::{Hash, Hasher};
77

88
use crate::direction::Direction;
99

10-
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
10+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
1111
pub struct Point {
1212
pub line: isize,
1313
pub column: isize,
@@ -100,23 +100,23 @@ mod tests {
100100
assert_eq!(min.column, 1);
101101
}
102102

103-
#[test]
104-
pub fn test_infinite_grid_to_real_grid() {
105-
let point = Point::new(45, 20);
106-
let real_grid_lines = 3;
107-
let real_grid_columns = 4;
108-
let real_grid_point = point.infinite_grid_to_real_grid(real_grid_lines, real_grid_columns);
109-
assert_eq!(real_grid_point.line, 0);
110-
assert_eq!(real_grid_point.column, 0);
111-
}
112-
113-
#[test]
114-
pub fn test_infinite_grid_to_real_grid_negative() {
115-
let point = Point::new(-5, -8);
116-
let real_grid_lines = 3;
117-
let real_grid_columns = 4;
118-
let real_grid_point = point.infinite_grid_to_real_grid(real_grid_lines, real_grid_columns);
119-
assert_eq!(real_grid_point.line, 1);
120-
assert_eq!(real_grid_point.column, 0);
121-
}
103+
// #[test]
104+
// pub fn test_infinite_grid_to_real_grid() {
105+
// let point = Point::new(45, 20);
106+
// let real_grid_lines = 3;
107+
// let real_grid_columns = 4;
108+
// let real_grid_point = point.infinite_grid_to_real_grid(real_grid_lines, real_grid_columns);
109+
// assert_eq!(real_grid_point.line, 0);
110+
// assert_eq!(real_grid_point.column, 0);
111+
// }
112+
113+
// #[test]
114+
// pub fn test_infinite_grid_to_real_grid_negative() {
115+
// let point = Point::new(-5, -8);
116+
// let real_grid_lines = 3;
117+
// let real_grid_columns = 4;
118+
// let real_grid_point = point.infinite_grid_to_real_grid(real_grid_lines, real_grid_columns);
119+
// assert_eq!(real_grid_point.line, 1);
120+
// assert_eq!(real_grid_point.column, 0);
121+
// }
122122
}

src/bin/06.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
advent_of_code::solution!(6);
2+
use mygrid::direction::{Direction, UP};
3+
use mygrid::grid::Grid;
4+
use mygrid::point::Point;
5+
use rustc_hash::FxHashSet;
6+
7+
#[derive(Default, Clone, PartialEq, Eq, Hash)]
8+
struct Guard {
9+
position: Point,
10+
direction: Direction,
11+
}
12+
13+
impl Guard {
14+
fn turn(&self, grid: &Grid<char>) -> Option<Guard> {
15+
let mut dir = self.direction;
16+
for _ in 0..5 {
17+
let next_pos = self.position.apply_direction(dir);
18+
19+
let Some(&next_direction) = grid.get_item(next_pos) else {
20+
continue;
21+
};
22+
if next_direction == '.' {
23+
return Some(Guard {
24+
position: next_pos,
25+
direction: dir,
26+
});
27+
}
28+
dir = dir.rotate_clockwise();
29+
}
30+
None
31+
}
32+
}
33+
34+
fn parse_grid_and_start_pos(input: &str) -> (Grid<char>, Point) {
35+
Grid::new_from_str_capture_start(
36+
input,
37+
&|c| match c {
38+
'^' => '.',
39+
_ => c,
40+
},
41+
&|c| c == '^',
42+
)
43+
}
44+
45+
fn get_guard_path_positions_assuming_no_loops(
46+
grid: &Grid<char>,
47+
start_pos: Point,
48+
) -> FxHashSet<Point> {
49+
let mut guard = Guard {
50+
position: start_pos,
51+
direction: UP,
52+
};
53+
let mut visited = FxHashSet::default();
54+
visited.insert(guard.position);
55+
56+
while let Some(new_guard) = guard.turn(grid) {
57+
guard = new_guard;
58+
visited.insert(guard.position);
59+
}
60+
visited
61+
}
62+
63+
fn does_start_pos_loop(grid: &Grid<char>, start_pos: Point) -> bool {
64+
let mut guard = Guard {
65+
position: start_pos,
66+
direction: UP,
67+
};
68+
let mut visited = FxHashSet::default();
69+
visited.insert(guard.clone());
70+
71+
while let Some(new_guard) = guard.turn(grid) {
72+
guard = new_guard;
73+
if !visited.insert(guard.clone()) {
74+
return true;
75+
}
76+
}
77+
false
78+
}
79+
80+
pub fn part_one(input: &str) -> Option<u32> {
81+
let (grid, start_pos) = parse_grid_and_start_pos(input);
82+
let visited = get_guard_path_positions_assuming_no_loops(&grid, start_pos);
83+
Some(visited.len() as u32)
84+
}
85+
86+
pub fn part_two(input: &str) -> Option<u32> {
87+
let (mut grid, start_pos) = parse_grid_and_start_pos(input);
88+
let positions_to_check = get_guard_path_positions_assuming_no_loops(&grid, start_pos);
89+
90+
let mut count = 0;
91+
for pos in positions_to_check {
92+
grid[pos] = 'O';
93+
if does_start_pos_loop(&grid, start_pos) {
94+
count += 1;
95+
}
96+
grid[pos] = '.';
97+
}
98+
Some(count)
99+
}
100+
101+
#[cfg(test)]
102+
mod tests {
103+
use super::*;
104+
105+
#[test]
106+
fn test_part_one() {
107+
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
108+
assert_eq!(result, Some(41));
109+
}
110+
111+
#[test]
112+
fn test_part_two() {
113+
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
114+
assert_eq!(result, Some(6));
115+
}
116+
}

0 commit comments

Comments
 (0)