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