Skip to content

Commit 04e1837

Browse files
2024: Optimize day 20
1 parent ec835ab commit 04e1837

File tree

1 file changed

+84
-68
lines changed

1 file changed

+84
-68
lines changed

2024/day20/src/main.rs

Lines changed: 84 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,111 @@
1-
use std::{
2-
collections::{HashSet, VecDeque},
3-
fs,
4-
};
1+
use std::fs;
52

6-
fn bfs(
3+
const DIRS: [(i32, i32); 4] = [(1, 0), (0, 1), (-1, 0), (0, -1)];
4+
5+
fn find_non_branching_path(
76
grid: &[u8],
87
width: usize,
9-
height: usize,
108
start: (usize, usize),
119
end: (usize, usize),
1210
) -> Vec<(usize, usize)> {
13-
let mut seen = HashSet::new();
14-
15-
let mut queue = VecDeque::new();
16-
queue.push_back((start.0, start.1, vec![start]));
17-
18-
while let Some((x, y, path)) = queue.pop_front() {
19-
if seen.contains(&(x, y)) {
20-
continue;
11+
// find start direction
12+
let mut dir = 0;
13+
for (i, d) in DIRS.iter().enumerate() {
14+
let nx = start.0 as i32 + d.0;
15+
let ny = start.1 as i32 + d.1;
16+
if grid[ny as usize * width + nx as usize] != b'#' {
17+
dir = i;
18+
break;
2119
}
22-
seen.insert((x, y));
20+
}
2321

24-
if x == end.0 && y == end.1 {
25-
return path;
26-
}
22+
// follow path and only turn right or left until we reach the end
23+
let mut pos = start;
24+
let mut result = vec![start];
25+
while pos != end {
26+
let nx = pos.0 as i32 + DIRS[dir].0;
27+
let ny = pos.1 as i32 + DIRS[dir].1;
28+
29+
if grid[ny as usize * width + nx as usize] == b'#' {
30+
// can we turn right?
31+
let right = (dir + 1) % 4;
32+
if grid[(pos.1 as i32 + DIRS[right].1) as usize * width
33+
+ (pos.0 as i32 + DIRS[right].0) as usize]
34+
!= b'#'
35+
{
36+
dir = right;
37+
continue;
38+
}
2739

28-
for (dx, dy) in [(1, 0), (0, 1), (-1, 0), (0, -1)] {
29-
let nx = x as i32 + dx;
30-
let ny = y as i32 + dy;
31-
if nx >= 0
32-
&& ny >= 0
33-
&& nx < width as i32
34-
&& ny < height as i32
35-
&& grid[ny as usize * width + nx as usize] != b'#'
40+
// can we turn left?
41+
let left = (dir + 3) % 4;
42+
if grid[(pos.1 as i32 + DIRS[left].1) as usize * width
43+
+ (pos.0 as i32 + DIRS[left].0) as usize]
44+
!= b'#'
3645
{
37-
let mut new_path = path.clone();
38-
new_path.push((nx as usize, ny as usize));
39-
queue.push_back((nx as usize, ny as usize, new_path));
46+
dir = left;
47+
continue;
4048
}
49+
50+
// we're in a dead end
51+
unreachable!("Path must not branch");
4152
}
53+
54+
pos = (nx as usize, ny as usize);
55+
result.push(pos);
4256
}
4357

44-
unreachable!()
58+
result
4559
}
4660

4761
fn main() {
48-
for part1 in [true, false] {
49-
let input = fs::read_to_string("input.txt").expect("Could not read file");
50-
let lines = input.lines().collect::<Vec<_>>();
51-
let width = lines[0].len();
52-
let height = lines.len();
53-
let grid = lines
54-
.iter()
55-
.flat_map(|l| l.as_bytes())
56-
.copied()
57-
.collect::<Vec<_>>();
62+
let input = fs::read_to_string("input.txt").expect("Could not read file");
63+
let lines = input.lines().collect::<Vec<_>>();
64+
let width = lines[0].len();
65+
let height = lines.len();
66+
let grid = lines
67+
.iter()
68+
.flat_map(|l| l.as_bytes())
69+
.copied()
70+
.collect::<Vec<_>>();
5871

59-
let mut start = (0, 0);
60-
let mut end = (0, 0);
61-
for y in 0..height {
62-
for x in 0..width {
63-
let c = grid[y * width + x];
64-
if c == b'S' {
65-
start = (x, y);
66-
} else if c == b'E' {
67-
end = (x, y);
68-
}
72+
let mut start = (0, 0);
73+
let mut end = (0, 0);
74+
for y in 0..height {
75+
for x in 0..width {
76+
let c = grid[y * width + x];
77+
if c == b'S' {
78+
start = (x, y);
79+
} else if c == b'E' {
80+
end = (x, y);
6981
}
7082
}
83+
}
7184

72-
let path = bfs(&grid, width, height, start, end);
73-
let max = path.len() - 1;
74-
let max_cheat_len = if part1 { 2 } else { 20 };
85+
let path = find_non_branching_path(&grid, width, start, end);
86+
let max = path.len() - 1;
7587

76-
let mut total = 0;
77-
for i in 0..path.len() {
78-
for j in i + 1..path.len() {
79-
let s = path[i];
80-
let e = path[j];
81-
let dist = (s.0.abs_diff(e.0) + s.1.abs_diff(e.1)) as usize;
82-
if dist > max_cheat_len {
83-
continue;
84-
}
88+
let mut total1 = 0;
89+
let mut total2 = 0;
90+
for i in 0..path.len() {
91+
for j in i + 101..path.len() {
92+
let s = path[i];
93+
let e = path[j];
94+
let dist = s.0.abs_diff(e.0) + s.1.abs_diff(e.1);
95+
let m = max - (j - i) + dist;
96+
if m >= max || max - m < 100 {
97+
continue;
98+
}
8599

86-
let m = path.len() - (j - i + 1) + dist;
87-
if m < max && max - m >= 100 {
88-
total += 1;
89-
}
100+
if dist <= 2 {
101+
total1 += 1;
102+
}
103+
if dist <= 20 {
104+
total2 += 1;
90105
}
91106
}
92-
93-
println!("{}", total);
94107
}
108+
109+
println!("{}", total1);
110+
println!("{}", total2);
95111
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy