Skip to content

Commit 4f1bf00

Browse files
committed
Optimize day 23 a bit
1 parent 2e5392d commit 4f1bf00

File tree

2 files changed

+88
-59
lines changed

2 files changed

+88
-59
lines changed

Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,3 @@ edition = "2021"
88

99
[dependencies]
1010
aoc-helper = { git = "https://github.com/SkiFire13/adventofcode-helper-rs" }
11-
12-
stacker = "*"

src/day23.rs

Lines changed: 88 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,76 +6,107 @@ pub fn input_generator(input: &str) -> Input {
66
Grid::from_input_chars(input, |c, _, _| c as u8)
77
}
88

9-
fn solve(
10-
input: &Input,
11-
seen: &mut FxHashSet<(usize, usize)>,
12-
(x, y): (usize, usize),
13-
d: usize,
14-
) -> usize {
15-
if y == input.h() - 1 {
16-
return d;
17-
}
9+
fn simplify_graph<const PART2: bool>(input: &Input) -> Vec<ArrayVec<(usize, usize), 4>> {
10+
let start = (0..input.w()).find(|&x| input[(x, 0)] == b'.');
11+
let end = (0..input.w()).find(|&x| input[(x, input.h() - 1)] == b'.');
12+
let (start, end) = (start.unwrap(), end.unwrap());
13+
14+
let mut node_poss = vec![(start, 0), (end, input.h() - 1)];
15+
let mut node_edges = vec![ArrayVec::new(), ArrayVec::new()];
16+
let mut pos_to_node = FxHashMap::from_iter([(node_poss[0], 0), (node_poss[1], 1)]);
17+
let mut explored = FxHashSet::default();
18+
let mut to_visit = vec![0];
19+
20+
explored.reserve(128);
21+
pos_to_node.reserve(64);
22+
23+
while let Some(node) = to_visit.pop() {
24+
for (mut x, mut y) in input.plus_neighbours(node_poss[node]) {
25+
if input[(x, y)] == b'#' || !explored.insert((x, y)) {
26+
continue;
27+
}
28+
29+
let mut d = 1;
30+
let (mut px, mut py) = node_poss[node];
31+
let (mut forward, mut backward) = (true, true);
1832

19-
let mut best = 0;
20-
21-
for (nx, ny) in input.plus_neighbours((x, y)) {
22-
if input[(nx, ny)] != b'#' {
23-
let dx = (nx as isize) - (x as isize);
24-
let dy = (ny as isize) - (y as isize);
25-
match (input[(nx, ny)], (dx, dy)) {
26-
(b'<', (1, 0)) => continue,
27-
(b'>', (-1, 0)) => continue,
28-
(b'v', (0, -1)) => continue,
29-
(b'^', (0, 1)) => continue,
30-
_ => {
31-
if seen.insert((nx, ny)) {
32-
let nd = solve(input, seen, (nx, ny), d + 1);
33-
best = max(best, nd);
34-
seen.remove(&(nx, ny));
35-
}
33+
loop {
34+
let (dx, dy) = (x as isize - px as isize, y as isize - py as isize);
35+
match (if PART2 { b'.' } else { input[(x, y)] }, dx, dy) {
36+
(b'<', 1, 0) | (b'>', -1, 0) | (b'^', 0, 1) | (b'v', 0, -1) => forward = false,
37+
(b'<', -1, 0) | (b'>', 1, 0) | (b'^', 0, -1) | (b'v', 0, 1) => backward = false,
38+
_ => {}
3639
}
40+
41+
if (x, y) == (end, input.h() - 1) {
42+
node_edges[node].push((1, d));
43+
break;
44+
}
45+
46+
let mut iter = input
47+
.plus_neighbours((x, y))
48+
.filter(|&(nx, ny)| input[(nx, ny)] != b'#')
49+
.filter(|&(nx, ny)| (nx, ny) != (px, py));
50+
51+
let Some((nx, ny)) = iter.next() else { break };
52+
53+
if iter.next().is_some() {
54+
let end_node = *pos_to_node.entry((x, y)).or_insert_with(|| {
55+
let new_node = node_poss.len();
56+
node_poss.push((x, y));
57+
node_edges.push(ArrayVec::new());
58+
to_visit.push(new_node);
59+
new_node
60+
});
61+
explored.insert((px, py));
62+
forward.then(|| node_edges[node].push((end_node, d)));
63+
backward.then(|| node_edges[end_node].push((node, d)));
64+
65+
break;
66+
}
67+
68+
(px, py, x, y, d) = (x, y, nx, ny, d + 1);
3769
}
3870
}
3971
}
4072

41-
best
73+
node_edges
4274
}
4375

4476
pub fn part1(input: &Input) -> usize {
45-
let mut seen = FxHashSet::default();
46-
let start = (0..input.w()).find(|&x| input[(x, 0)] == b'.').unwrap();
47-
solve(input, &mut seen, (start, 0), 0)
48-
}
49-
50-
fn solve2(
51-
input: &Input,
52-
seen: &mut FxHashSet<(usize, usize)>,
53-
(x, y): (usize, usize),
54-
d: usize,
55-
) -> usize {
56-
if y == input.h() - 1 {
57-
return d;
77+
fn solve(edges: &[ArrayVec<(usize, usize), 4>], seen: u64, node: usize, d: usize) -> usize {
78+
edges[node]
79+
.iter()
80+
.filter(|&&(next, _)| seen & (1 << next) == 0)
81+
.map(|&(next, cost)| solve(edges, seen | (1 << next), next, d + cost))
82+
.max()
83+
.unwrap_or(if node == 1 { d } else { 0 })
5884
}
85+
solve(&simplify_graph::<false>(input), 0, 0, 0)
86+
}
5987

60-
let mut best = 0;
88+
pub fn part2(input: &Input) -> usize {
89+
fn solve(edges: &[ArrayVec<(usize, usize), 4>], seen: u64, node: usize, d: usize) -> usize {
90+
let branches = edges[node]
91+
.iter()
92+
.filter(|&&(next, _)| seen & (1 << next) == 0)
93+
.map(|&(next, cost)| move || solve(edges, seen | (1 << next), next, d + cost))
94+
.collect::<ArrayVec<_, 4>>();
6195

62-
for (nx, ny) in input.plus_neighbours((x, y)) {
63-
if input[(nx, ny)] != b'#' {
64-
if seen.insert((nx, ny)) {
65-
stacker::maybe_grow(32 * 1024, 1024 * 1024 * 10, || {
66-
let nd = solve2(input, seen, (nx, ny), d + 1);
67-
best = max(best, nd);
68-
seen.remove(&(nx, ny));
69-
});
96+
match &branches[..] {
97+
&[] if node == 1 => d,
98+
&[] => 0,
99+
&[f] => f(),
100+
&[f1, f2] => {
101+
let (d1, d2) = rayon::join(f1, f2);
102+
max(d1, d2)
70103
}
104+
&[f1, f2, f3] => {
105+
let (d1, (d2, d3)) = rayon::join(f1, || rayon::join(f2, f3));
106+
max(d1, max(d2, d3))
107+
}
108+
_ => unreachable!(),
71109
}
72110
}
73-
74-
best
75-
}
76-
77-
pub fn part2(input: &Input) -> usize {
78-
let mut seen = FxHashSet::default();
79-
let start = (0..input.w()).find(|&x| input[(x, 0)] == b'.').unwrap();
80-
solve2(input, &mut seen, (start, 0), 0)
111+
solve(&simplify_graph::<true>(input), 0, 0, 0)
81112
}

0 commit comments

Comments
 (0)