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