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