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() {
73 match b {
74 b'<' | b'>' => narrow(&mut grid, &mut position, Point::from(b)),
75 b'^' | b'v' => wide(&mut grid, &mut position, Point::from(b), &mut todo),
76 _ => (),
77 }
78 }
79
80 gps(&grid, b'[')
81}
82
83fn narrow(grid: &mut Grid<u8>, start: &mut Point, direction: Point) {
84 let mut position = *start + direction;
85 let mut size = 1;
86
87 while grid[position] != b'.' && grid[position] != b'#' {
89 position += direction;
90 size += 1;
91 }
92
93 if grid[position] == b'.' {
95 let mut previous = b'.';
96 let mut position = *start + direction;
97
98 for _ in 0..size {
99 swap(&mut previous, &mut grid[position]);
100 position += direction;
101 }
102
103 *start += direction;
105 }
106}
107
108fn wide(grid: &mut Grid<u8>, start: &mut Point, direction: Point, todo: &mut Vec<Point>) {
109 if grid[*start + direction] == b'.' {
111 *start += direction;
112 return;
113 }
114
115 todo.clear();
117 todo.push(ORIGIN);
119 todo.push(*start);
120 let mut index = 1;
121
122 while index < todo.len() {
123 let next = todo[index] + direction;
124 index += 1;
125
126 let (first, second) = match grid[next] {
128 b'[' => (next, next + RIGHT),
129 b']' => (next + LEFT, next),
130 b'#' => return, _ => continue, };
133
134 if first != todo[todo.len() - 2] {
136 todo.push(first);
137 todo.push(second);
138 }
139 }
140
141 for &point in todo[2..].iter().rev() {
143 grid[point + direction] = grid[point];
144 grid[point] = b'.';
145 }
146
147 *start += direction;
149}
150
151fn stretch(grid: &Grid<u8>) -> Grid<u8> {
152 let mut next = Grid::new(grid.width * 2, grid.height, b'.');
153
154 for y in 0..grid.height {
155 for x in 0..grid.width {
156 let (left, right) = match grid[Point::new(x, y)] {
158 b'#' => (b'#', b'#'),
159 b'O' => (b'[', b']'),
160 b'@' => (b'@', b'.'),
161 _ => continue,
162 };
163
164 next[Point::new(2 * x, y)] = left;
165 next[Point::new(2 * x + 1, y)] = right;
166 }
167 }
168
169 next
170}
171
172fn gps(grid: &Grid<u8>, needle: u8) -> i32 {
173 let mut result = 0;
174
175 for y in 0..grid.height {
176 for x in 0..grid.width {
177 let point = Point::new(x, y);
178 if grid[point] == needle {
179 result += 100 * point.y + point.x;
180 }
181 }
182 }
183
184 result
185}