Skip to content

Commit f7f17ca

Browse files
committed
Faster day 6
1 parent 36fcf53 commit f7f17ca

File tree

3 files changed

+97
-12
lines changed

3 files changed

+97
-12
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
1616
| [Day 3](./src/bin/03.rs) | `222.9µs` | `246.5µs` |
1717
| [Day 4](./src/bin/04.rs) | `145.9µs` | `33.2µs` |
1818
| [Day 5](./src/bin/05.rs) | `66.0µs` | `188.7µs` |
19-
| [Day 6](./src/bin/06.rs) | `44.8µs` | `11.1ms` |
19+
| [Day 6](./src/bin/06.rs) | `48.5µs` | `4.2ms` |
2020
| [Day 7](./src/bin/07.rs) | `316.2µs` | `319.6µs` |
2121

22-
**Total: 12.87ms**
22+
**Total: 5.98ms**
2323
<!--- benchmarking table --->
2424

2525
---

mygrid/src/point.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,29 @@ impl Point {
4848
pub fn min(&self, other: &Point) -> Self {
4949
Point::new(self.line.min(other.line), self.column.min(other.column))
5050
}
51+
52+
#[inline]
53+
pub fn is_aligned(&self, other: &Point) -> bool {
54+
self.line == other.line || self.column == other.column
55+
}
56+
57+
#[inline]
58+
pub fn is_between_inclusive(&self, a: &Point, b: &Point) -> bool {
59+
if !self.is_aligned(a) || !self.is_aligned(b) || !a.is_aligned(b) {
60+
return false;
61+
}
62+
63+
let min_line = a.line.min(b.line);
64+
let max_line = a.line.max(b.line);
65+
let min_column = a.column.min(b.column);
66+
let max_column = a.column.max(b.column);
67+
68+
let is_between_lines = (min_line..=max_line).contains(&self.line);
69+
let is_between_columns = (min_column..=max_column).contains(&self.column);
70+
71+
(a.line == b.line && a.line == self.line && is_between_columns)
72+
|| (a.column == b.column && a.column == self.column && is_between_lines)
73+
}
5174
}
5275

5376
impl Hash for Point {
@@ -100,6 +123,49 @@ mod tests {
100123
assert_eq!(min.column, 1);
101124
}
102125

126+
#[test]
127+
pub fn test_point_is_aligned() {
128+
let point = Point::new(1, 2);
129+
let other = Point::new(1, 3);
130+
assert!(point.is_aligned(&other));
131+
132+
let point = Point::new(1, 2);
133+
let other = Point::new(3, 1);
134+
assert!(!point.is_aligned(&other));
135+
136+
let point = Point::new(1, 2);
137+
let other = Point::new(1, 2);
138+
assert!(point.is_aligned(&other));
139+
}
140+
141+
#[test]
142+
pub fn test_point_is_between_inclusive() {
143+
let point = Point::new(1, 2);
144+
let a = Point::new(0, 2);
145+
let b = Point::new(2, 2);
146+
assert!(point.is_between_inclusive(&a, &b));
147+
148+
let point = Point::new(1, 2);
149+
let a = Point::new(1, 0);
150+
let b = Point::new(1, 4);
151+
assert!(point.is_between_inclusive(&a, &b));
152+
153+
let point = Point::new(1, 2);
154+
let a = Point::new(0, 1);
155+
let b = Point::new(2, 3);
156+
assert!(!point.is_between_inclusive(&a, &b));
157+
158+
let point = Point::new(1, 2);
159+
let a = Point::new(1, 2);
160+
let b = Point::new(2, 3);
161+
assert!(!point.is_between_inclusive(&a, &b));
162+
163+
let point = Point::new(1, 2);
164+
let a = Point::new(2, 1);
165+
let b = Point::new(3, 2);
166+
assert!(!point.is_between_inclusive(&a, &b));
167+
}
168+
103169
// #[test]
104170
// pub fn test_infinite_grid_to_real_grid() {
105171
// let point = Point::new(45, 20);

src/bin/06.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use mygrid::direction::{Direction, UP};
33
use mygrid::grid::Grid;
44
use mygrid::point::Point;
55
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
6-
use rustc_hash::FxHashSet;
6+
use rustc_hash::{FxHashMap, FxHashSet};
7+
8+
type JumpTable = FxHashMap<Guard, Guard>;
79

810
#[derive(Default, Clone, PartialEq, Eq, Hash, Debug)]
911
struct Guard {
@@ -46,55 +48,72 @@ fn parse_grid_and_start_pos(input: &str) -> (Grid<char>, Point) {
4648
fn get_guard_path_positions_assuming_no_loops(
4749
grid: &Grid<char>,
4850
start_pos: Point,
49-
) -> FxHashSet<Point> {
51+
) -> (FxHashSet<Point>, JumpTable) {
5052
let mut guard = Guard {
5153
position: start_pos,
5254
direction: UP,
5355
};
5456
let mut visited = FxHashSet::with_capacity_and_hasher(10_000, Default::default());
55-
visited.insert(guard.position);
57+
let mut jump_table = JumpTable::default();
58+
let mut old_guard = guard.clone();
5659

5760
while let Some(new_guard) = guard.turn(grid) {
61+
if new_guard.direction != guard.direction {
62+
jump_table.insert(old_guard, guard.clone());
63+
old_guard = new_guard.clone();
64+
}
5865
guard = new_guard;
5966
visited.insert(guard.position);
6067
}
61-
visited
68+
(visited, jump_table)
6269
}
6370

6471
#[inline]
65-
fn does_start_pos_loop(grid: &Grid<char>, start_pos: Point) -> bool {
72+
fn has_loops(grid: &Grid<char>, start_pos: Point, jump_table: &JumpTable) -> bool {
73+
// remove entries from jump_table that contain pos between key and value
6674
let mut guard = Guard {
6775
position: start_pos,
6876
direction: UP,
6977
};
78+
7079
let mut visited = FxHashSet::with_capacity_and_hasher(10_000, Default::default());
7180
visited.insert(guard.clone());
7281

73-
while let Some(new_guard) = guard.turn(grid) {
82+
while let Some(new_guard) = guard.turn(&grid) {
7483
guard = new_guard;
7584
if !visited.insert(guard.clone()) {
7685
return true;
7786
}
87+
if let Some(next_guard) = jump_table.get(&guard) {
88+
guard = next_guard.clone();
89+
}
7890
}
7991
false
8092
}
8193

8294
pub fn part_one(input: &str) -> Option<u32> {
8395
let (grid, start_pos) = parse_grid_and_start_pos(input);
84-
let visited = get_guard_path_positions_assuming_no_loops(&grid, start_pos);
96+
let (visited, _) = get_guard_path_positions_assuming_no_loops(&grid, start_pos);
8597
Some(visited.len() as u32)
8698
}
8799

88100
pub fn part_two(input: &str) -> Option<u32> {
89101
let (grid, start_pos) = parse_grid_and_start_pos(input);
90-
let positions_to_check = get_guard_path_positions_assuming_no_loops(&grid, start_pos);
102+
let (visited, jump_table) = get_guard_path_positions_assuming_no_loops(&grid, start_pos);
91103

92-
let count = positions_to_check
104+
let count = visited
93105
.par_iter()
94106
.filter(|&&pos| {
95107
let mut grid = grid.clone();
108+
let jump_table: JumpTable = JumpTable::from_iter(
109+
jump_table
110+
.iter()
111+
.filter(|(key, val)| !pos.is_between_inclusive(&key.position, &val.position))
112+
.map(|(key, val)| (key.clone(), val.clone())),
113+
);
96114
grid[pos] = 'O';
97-
does_start_pos_loop(&grid, start_pos)
115+
116+
has_loops(&grid, pos, &jump_table)
98117
})
99118
.count();
100119

0 commit comments

Comments
 (0)