Skip to content

Commit f2d8986

Browse files
committed
Faster approach for part two
1 parent de1e3d4 commit f2d8986

File tree

2 files changed

+98
-116
lines changed

2 files changed

+98
-116
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ pie
109109
| 20 | [Pulse Propagation](https://adventofcode.com/2023/day/20) | [Source](src/year2023/day20.rs) | 6 |
110110
| 21 | [Step Counter](https://adventofcode.com/2023/day/21) | [Source](src/year2023/day21.rs) | 180 |
111111
| 22 | [Sand Slabs](https://adventofcode.com/2023/day/22) | [Source](src/year2023/day22.rs) | 486 |
112-
| 23 | [A Long Walk](https://adventofcode.com/2023/day/23) | [Source](src/year2023/day23.rs) | - |
112+
| 23 | [A Long Walk](https://adventofcode.com/2023/day/23) | [Source](src/year2023/day23.rs) | 89000 |
113113
| 24 | [Never Tell Me The Odds](https://adventofcode.com/2023/day/24) | [Source](src/year2023/day24.rs) | 96 |
114114
| 25 | [Snowverload](https://adventofcode.com/2023/day/25) | [Source](src/year2023/day25.rs) | 157 |
115115

src/year2023/day23.rs

Lines changed: 97 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -3,154 +3,136 @@ use crate::util::hash::*;
33
use crate::util::point::*;
44
use std::collections::VecDeque;
55

6-
pub fn parse(input: &str) -> Grid<u8> {
7-
Grid::parse(input)
6+
pub struct Input {
7+
directed: [u64; 36],
8+
undirected: [u64; 36],
9+
weight: [[u32; 36]; 36],
810
}
911

10-
pub fn part1(grid: &Grid<u8>) -> i32 {
11-
let start = Point::new(1, 0);
12-
let end = Point::new(grid.width - 2, grid.height - 1);
12+
pub fn parse(input: &str) -> Input {
13+
let mut grid = Grid::parse(input);
14+
let width = grid.width;
15+
let height = grid.height;
1316

14-
let mut seen = FastSet::new();
15-
let mut todo = VecDeque::new();
16-
let mut result = 0;
17-
18-
seen.insert(start);
19-
todo.push_back((0, start, seen));
20-
21-
while let Some((cost, pos, seen)) = todo.pop_front() {
22-
if pos == end {
23-
result = result.max(cost);
24-
continue;
25-
}
26-
if !grid.contains(pos) {
27-
continue;
28-
}
17+
// Modify edge of grid to remove the need for boundary checks.
18+
grid[Point::new(1, 0)] = b'#';
19+
grid[Point::new(width - 2, height - 1)] = b'#';
2920

30-
let b = grid[pos];
21+
// Move start and end away from edge.
22+
let start = Point::new(1, 1);
23+
let end = Point::new(width - 2, height - 2);
3124

32-
if b == b'.' || b == b'v' {
33-
let next = pos + DOWN;
34-
let mut copy = seen.clone();
25+
// Points of interest are start, end and junctions.
26+
grid[start] = b'P';
27+
grid[end] = b'P';
3528

36-
if copy.insert(next) {
37-
todo.push_back((cost + 1, next, copy));
38-
}
39-
}
40-
41-
if b == b'.' || b == b'^' {
42-
let next = pos + UP;
43-
let mut copy = seen.clone();
44-
45-
if copy.insert(next) {
46-
todo.push_back((cost + 1, next, copy));
47-
}
48-
}
29+
let mut poi = FastMap::new();
30+
poi.insert(start, 0);
31+
poi.insert(end, 1);
4932

50-
if b == b'.' || b == b'>' {
51-
let next = pos + RIGHT;
52-
let mut copy = seen.clone();
33+
for y in 1..height - 1 {
34+
for x in 1..width - 1 {
35+
let position = Point::new(x, y);
5336

54-
if copy.insert(next) {
55-
todo.push_back((cost + 1, next, copy));
56-
}
57-
}
58-
59-
if b == b'.' || b == b'<' {
60-
let next = pos + LEFT;
61-
let mut copy = seen.clone();
62-
63-
if copy.insert(next) {
64-
todo.push_back((cost + 1, next, copy));
37+
if grid[position] != b'#' {
38+
let neighbors =
39+
ORTHOGONAL.iter().map(|&o| position + o).filter(|&n| grid[n] != b'#').count();
40+
if neighbors > 2 {
41+
grid[position] = b'P';
42+
poi.insert(position, poi.len());
43+
}
6544
}
6645
}
6746
}
6847

69-
result
70-
}
71-
72-
pub fn part2(grid: &Grid<u8>) -> i32 {
73-
let start = Point::new(1, 0);
74-
let end = Point::new(grid.width - 2, grid.height - 1);
75-
76-
let mut poi = FastSet::new();
77-
poi.insert(start);
78-
poi.insert(end);
79-
80-
for y in 0..grid.height {
81-
for x in 0..grid.width {
82-
let p = Point::new(x, y);
83-
if grid[p] != b'#' {
84-
let mut neighbors = 0;
85-
86-
for o in ORTHOGONAL {
87-
let next = p + o;
88-
if grid.contains(next) && grid[next] != b'#' {
89-
neighbors += 1;
48+
// BFS to find distances between POIs.
49+
let mut todo = VecDeque::new();
50+
let mut directed = [0; 36];
51+
let mut undirected = [0; 36];
52+
let mut weight = [[0; 36]; 36];
53+
54+
for (&start, &from) in &poi {
55+
todo.push_back((start, 0, true));
56+
grid[start] = b'#';
57+
58+
while let Some((position, cost, forward)) = todo.pop_front() {
59+
for direction in ORTHOGONAL {
60+
let next = position + direction;
61+
62+
match grid[next] {
63+
b'#' => (),
64+
b'P' => {
65+
let to = poi[&next];
66+
67+
if forward {
68+
directed[from] |= 1 << to;
69+
} else {
70+
directed[to] |= 1 << from;
71+
}
72+
73+
undirected[from] |= 1 << to;
74+
undirected[to] |= 1 << from;
75+
76+
weight[from][to] = cost + 1;
77+
weight[to][from] = cost + 1;
78+
}
79+
b'.' => {
80+
todo.push_back((next, cost + 1, forward));
81+
grid[next] = b'#';
82+
}
83+
_ => {
84+
let same = direction == Point::from(grid[next]);
85+
todo.push_back((next, cost + 1, forward && same));
86+
grid[next] = b'#';
9087
}
91-
}
92-
93-
if neighbors > 2 {
94-
poi.insert(p);
9588
}
9689
}
9790
}
9891
}
9992

100-
let mut edges = FastMap::new();
101-
102-
for &start in &poi {
103-
edges.insert(start, bfs(grid, &poi, start));
104-
}
105-
106-
let mut result = 0;
93+
Input { directed, undirected, weight }
94+
}
10795

108-
let mut seen = FastSet::new();
109-
seen.insert(start);
96+
pub fn part1(input: &Input) -> u32 {
97+
let mut cost = [0; 36];
11098

11199
let mut todo = VecDeque::new();
112-
todo.push_back((start, seen, 0));
100+
todo.push_back(0);
113101

114-
while let Some((pos, seen, cost)) = todo.pop_front() {
115-
if pos == end {
116-
result = result.max(cost);
117-
continue;
118-
}
102+
while let Some(from) = todo.pop_front() {
103+
let mut nodes = input.directed[from];
119104

120-
for &(next, extra) in &edges[&pos] {
121-
if !seen.contains(&next) {
122-
let mut copy = seen.clone();
123-
copy.insert(next);
105+
while nodes > 0 {
106+
let to = nodes.trailing_zeros() as usize;
107+
let mask = 1 << to;
108+
nodes ^= mask;
124109

125-
todo.push_back((next, copy, cost + extra));
126-
}
110+
cost[to] = cost[to].max(cost[from] + input.weight[from][to]);
111+
todo.push_back(to);
127112
}
128113
}
129114

130-
result
115+
2 + cost[1]
131116
}
132117

133-
fn bfs(grid: &Grid<u8>, poi: &FastSet<Point>, start: Point) -> Vec<(Point, i32)> {
134-
let mut todo = VecDeque::new();
135-
let mut seen = FastSet::new();
136-
let mut result = Vec::new();
118+
pub fn part2(input: &Input) -> u32 {
119+
2 + dfs(input, 0, 1, 0)
120+
}
137121

138-
todo.push_back((start, 0));
139-
seen.insert(start);
122+
fn dfs(input: &Input, from: usize, seen: u64, cost: u32) -> u32 {
123+
if from == 1 {
124+
return cost;
125+
}
140126

141-
while let Some((pos, cost)) = todo.pop_front() {
142-
if pos != start && poi.contains(&pos) {
143-
result.push((pos, cost));
144-
continue;
145-
}
127+
let mut nodes = input.undirected[from] & !seen;
128+
let mut result = 0;
146129

147-
for o in ORTHOGONAL {
148-
let next = pos + o;
130+
while nodes > 0 {
131+
let to = nodes.trailing_zeros() as usize;
132+
let mask = 1 << to;
133+
nodes ^= mask;
149134

150-
if grid.contains(next) && grid[next] != b'#' && seen.insert(next) {
151-
todo.push_back((next, cost + 1));
152-
}
153-
}
135+
result = result.max(dfs(input, to, seen | mask, cost + input.weight[from][to]));
154136
}
155137

156138
result

0 commit comments

Comments
 (0)