1use crate::util::grid::*;
12use crate::util::point::*;
13use std::collections::VecDeque;
14
15type Input = (i32, usize);
16
17const DIRECTIONS: [Point; 4] = [RIGHT, DOWN, LEFT, UP];
19
20pub fn parse(input: &str) -> Input {
21 let grid = Grid::parse(input);
22 let start = grid.find(b'S').unwrap();
23 let end = grid.find(b'E').unwrap();
24
25 let mut todo_first = VecDeque::new();
29 let mut todo_second = VecDeque::new();
30 let mut seen = grid.same_size_with([i32::MAX; 4]);
32 let mut lowest = i32::MAX;
33
34 todo_first.push_back((start, 0, 0));
35 seen[start][0] = 0;
36
37 while !todo_first.is_empty() {
38 while let Some((position, direction, cost)) = todo_first.pop_front() {
39 if cost >= lowest {
40 continue;
41 }
42 if position == end {
43 lowest = cost;
44 continue;
45 }
46
47 let left = (direction + 3) % 4;
49 let right = (direction + 1) % 4;
50 let next = [
51 (position + DIRECTIONS[direction], direction, cost + 1),
52 (position, left, cost + 1000),
53 (position, right, cost + 1000),
54 ];
55
56 for tuple @ (next_position, next_direction, next_cost) in next {
57 if grid[next_position] != b'#' && next_cost < seen[next_position][next_direction] {
58 if next_direction == direction {
60 todo_first.push_back(tuple);
61 } else {
62 todo_second.push_back(tuple);
63 }
64 seen[next_position][next_direction] = next_cost;
65 }
66 }
67 }
68
69 (todo_first, todo_second) = (todo_second, todo_first);
70 }
71
72 let mut todo = VecDeque::new();
74 let mut path = grid.same_size_with(false);
75
76 for direction in 0..4 {
78 if seen[end][direction] == lowest {
79 todo.push_back((end, direction, lowest));
80 }
81 }
82
83 while let Some((position, direction, cost)) = todo.pop_front() {
84 path[position] = true;
85 if position == start {
86 continue;
87 }
88
89 let left = (direction + 3) % 4;
91 let right = (direction + 1) % 4;
92 let next = [
93 (position - DIRECTIONS[direction], direction, cost - 1),
94 (position, left, cost - 1000),
95 (position, right, cost - 1000),
96 ];
97
98 for (next_position, next_direction, next_cost) in next {
99 if next_cost == seen[next_position][next_direction] {
101 todo.push_back((next_position, next_direction, next_cost));
102 seen[next_position][next_direction] = i32::MAX;
104 }
105 }
106 }
107
108 (lowest, path.bytes.iter().filter(|&&b| b).count())
109}
110
111pub fn part1(input: &Input) -> i32 {
112 input.0
113}
114
115pub fn part2(input: &Input) -> usize {
116 input.1
117}