1use std::mem::swap;
28
29use crate::util::grid::*;
30use crate::util::point::*;
31
32type Input = (Grid<u8>, Vec<u8>);
33
34pub fn parse(input: &str) -> Input {
35 let (prefix, suffix) = input.split_once("\n\n").unwrap();
36 let grid = Grid::parse(prefix);
37 let moves = suffix.bytes().filter(|&b| b != b'\n').collect();
39 (grid, moves)
40}
41
42pub fn part1(input: &Input) -> i32 {
43 let (grid, moves) = input;
44
45 let mut grid = grid.clone();
47 let mut position = grid.find(b'@').unwrap();
48 grid[position] = b'.';
49
50 for &b in moves {
51 narrow(&mut grid, &mut position, Point::from(b));
52 }
53
54 gps(&grid, b'O')
55}
56
57pub fn part2(input: &Input) -> i32 {
58 let (grid, moves) = input;
59
60 let mut grid = stretch(grid);
61 let mut position = grid.find(b'@').unwrap();
62 grid[position] = b'.';
63
64 let mut todo = Vec::with_capacity(50);
66
67 for &b in moves {
69 match b {
70 b'<' | b'>' => narrow(&mut grid, &mut position, Point::from(b)),
71 b'^' | b'v' => wide(&mut grid, &mut position, Point::from(b), &mut todo),
72 _ => unreachable!(),
73 }
74 }
75
76 gps(&grid, b'[')
77}
78
79fn narrow(grid: &mut Grid<u8>, start: &mut Point, direction: Point) {
80 let mut position = *start + direction;
81 let mut size = 1;
82
83 while !matches!(grid[position], b'.' | b'#') {
85 position += direction;
86 size += 1;
87 }
88
89 if grid[position] == b'.' {
91 let mut previous = b'.';
92 let mut position = *start + direction;
93
94 for _ in 0..size {
95 swap(&mut previous, &mut grid[position]);
96 position += direction;
97 }
98
99 *start += direction;
101 }
102}
103
104fn wide(grid: &mut Grid<u8>, start: &mut Point, direction: Point, todo: &mut Vec<Point>) {
105 if grid[*start + direction] == b'.' {
107 *start += direction;
108 return;
109 }
110
111 todo.clear();
113 todo.push(ORIGIN);
115 todo.push(*start);
116 let mut index = 1;
117
118 while index < todo.len() {
119 let next = todo[index] + direction;
120 index += 1;
121
122 let (first, second) = match grid[next] {
124 b'[' => (next, next + RIGHT),
125 b']' => (next + LEFT, next),
126 b'#' => return, _ => continue, };
129
130 if first != todo[todo.len() - 2] {
132 todo.push(first);
133 todo.push(second);
134 }
135 }
136
137 for &point in todo[2..].iter().rev() {
139 grid[point + direction] = grid[point];
140 grid[point] = b'.';
141 }
142
143 *start += direction;
145}
146
147fn stretch(grid: &Grid<u8>) -> Grid<u8> {
148 let bytes = grid
149 .bytes
150 .iter()
151 .flat_map(|&b| match b {
152 b'#' => *b"##",
153 b'O' => *b"[]",
154 b'@' => *b"@.",
155 _ => *b"..",
156 })
157 .collect();
158
159 Grid { width: grid.width * 2, height: grid.height, bytes }
160}
161
162fn gps(grid: &Grid<u8>, needle: u8) -> i32 {
163 let mut result = 0;
164
165 for y in 0..grid.height {
166 for x in 0..grid.width {
167 if grid[Point::new(x, y)] == needle {
168 result += 100 * y + x;
169 }
170 }
171 }
172
173 result
174}