|
1 |
| -use std::{ |
2 |
| - collections::{HashSet, VecDeque}, |
3 |
| - fs, |
4 |
| -}; |
| 1 | +use std::fs; |
5 | 2 |
|
6 |
| -fn bfs( |
| 3 | +const DIRS: [(i32, i32); 4] = [(1, 0), (0, 1), (-1, 0), (0, -1)]; |
| 4 | + |
| 5 | +fn find_non_branching_path( |
7 | 6 | grid: &[u8],
|
8 | 7 | width: usize,
|
9 |
| - height: usize, |
10 | 8 | start: (usize, usize),
|
11 | 9 | end: (usize, usize),
|
12 | 10 | ) -> 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; |
21 | 19 | }
|
22 |
| - seen.insert((x, y)); |
| 20 | + } |
23 | 21 |
|
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 | + } |
27 | 39 |
|
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'#' |
36 | 45 | {
|
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; |
40 | 48 | }
|
| 49 | + |
| 50 | + // we're in a dead end |
| 51 | + unreachable!("Path must not branch"); |
41 | 52 | }
|
| 53 | + |
| 54 | + pos = (nx as usize, ny as usize); |
| 55 | + result.push(pos); |
42 | 56 | }
|
43 | 57 |
|
44 |
| - unreachable!() |
| 58 | + result |
45 | 59 | }
|
46 | 60 |
|
47 | 61 | 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<_>>(); |
58 | 71 |
|
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); |
69 | 81 | }
|
70 | 82 | }
|
| 83 | + } |
71 | 84 |
|
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; |
75 | 87 |
|
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 | + } |
85 | 99 |
|
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; |
90 | 105 | }
|
91 | 106 | }
|
92 |
| - |
93 |
| - println!("{}", total); |
94 | 107 | }
|
| 108 | + |
| 109 | + println!("{}", total1); |
| 110 | + println!("{}", total2); |
95 | 111 | }
|
0 commit comments