Skip to content

Commit a9250d5

Browse files
committed
15th day
1 parent 8b8d9e6 commit a9250d5

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed

data/examples/15.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
##########
2+
#..O..O.O#
3+
#......O.#
4+
#.OO..O.O#
5+
6+
#O#..O...#
7+
#O..O..O.#
8+
#.OO.O.OO#
9+
#....O...#
10+
##########
11+
12+
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
13+
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
14+
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
15+
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
16+
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
17+
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
18+
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
19+
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
20+
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
21+
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^

src/bin/15.rs

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
advent_of_code::solution!(15);
2+
3+
use advent_of_code::maneatingape::grid::*;
4+
use advent_of_code::maneatingape::point::*;
5+
6+
struct Block {}
7+
8+
impl Block {
9+
const WALL: u8 = b'#';
10+
const EMPTY: u8 = b'.';
11+
const CRATE: u8 = b'O';
12+
const ROBOT: u8 = b'@';
13+
}
14+
15+
fn parse_data(input: &str) -> (Grid<u8>, Vec<Point>) {
16+
let (left, right) = input.split_once("\n\n").unwrap();
17+
18+
let grid = Grid::parse(left);
19+
let instructions = right
20+
.bytes()
21+
.filter(|x| !x.is_ascii_whitespace())
22+
.map(Point::from)
23+
.collect();
24+
25+
(grid, instructions)
26+
}
27+
28+
fn part_x<F>(mut grid: Grid<u8>, instructions: Vec<Point>, can_move_f: F) -> u32
29+
where
30+
F: Fn(&Grid<u8>, Point, Point, &mut Vec<Point>) -> bool,
31+
{
32+
let mut robot_position = grid.find(Block::ROBOT).unwrap();
33+
34+
let mut affected_crates = vec![];
35+
for direction in instructions {
36+
if can_move_f(&grid, robot_position, direction, &mut affected_crates) {
37+
robot_position += direction;
38+
39+
affected_crates.drain(..).rev().for_each(|c| {
40+
grid[c] = Block::EMPTY;
41+
grid[c + direction] = Block::CRATE;
42+
});
43+
}
44+
}
45+
46+
let mut result = 0;
47+
for y in 0..grid.height {
48+
for x in 0..grid.width {
49+
let p = Point::new(x, y);
50+
if grid[p] == Block::CRATE {
51+
result += y as u32 * 100 + x as u32;
52+
}
53+
}
54+
}
55+
56+
result
57+
}
58+
59+
fn can_move_part_one(
60+
grid: &Grid<u8>,
61+
position: Point,
62+
direction: Point,
63+
affected_crates: &mut Vec<Point>,
64+
) -> bool {
65+
let next_position = position + direction;
66+
67+
if grid[next_position] == Block::WALL {
68+
affected_crates.clear();
69+
return false;
70+
}
71+
72+
if grid[next_position] == Block::CRATE {
73+
affected_crates.push(next_position);
74+
return can_move_part_one(grid, next_position, direction, affected_crates);
75+
}
76+
77+
true
78+
}
79+
80+
fn can_move_part_two(
81+
grid: &Grid<u8>,
82+
position: Point,
83+
direction: Point,
84+
affected_crates: &mut Vec<Point>,
85+
) -> bool {
86+
let next_position = position + direction;
87+
88+
if grid[next_position] == Block::WALL {
89+
affected_crates.clear();
90+
return false;
91+
}
92+
93+
if direction == UP || direction == DOWN {
94+
for next_crate_position in [next_position, next_position + LEFT] {
95+
if grid[next_crate_position] == Block::CRATE {
96+
affected_crates.push(next_crate_position);
97+
if !can_move_part_two_vertical(
98+
grid,
99+
next_crate_position,
100+
direction,
101+
affected_crates,
102+
) {
103+
return false;
104+
}
105+
}
106+
}
107+
}
108+
109+
if direction == LEFT || direction == RIGHT {
110+
let next_crate_position = match direction {
111+
LEFT => next_position + LEFT,
112+
RIGHT => next_position,
113+
_ => unreachable!(),
114+
};
115+
116+
if grid[next_crate_position] == Block::CRATE {
117+
affected_crates.push(next_crate_position);
118+
return can_move_part_two_horizontal(
119+
grid,
120+
next_crate_position,
121+
direction,
122+
affected_crates,
123+
);
124+
}
125+
}
126+
127+
true
128+
}
129+
130+
fn can_move_part_two_vertical(
131+
grid: &Grid<u8>,
132+
position: Point,
133+
direction: Point,
134+
affected_crates: &mut Vec<Point>,
135+
) -> bool {
136+
let next_position = position + direction;
137+
let next_position_l = next_position + LEFT;
138+
let next_position_r = next_position + RIGHT;
139+
140+
if grid[next_position] == Block::WALL || grid[next_position_r] == Block::WALL {
141+
affected_crates.clear();
142+
return false;
143+
}
144+
145+
for next_crate_position in [next_position_l, next_position, next_position_r] {
146+
if grid[next_crate_position] == Block::CRATE {
147+
affected_crates.push(next_crate_position);
148+
if !can_move_part_two_vertical(grid, next_crate_position, direction, affected_crates) {
149+
return false;
150+
}
151+
}
152+
}
153+
154+
true
155+
}
156+
157+
fn can_move_part_two_horizontal(
158+
grid: &Grid<u8>,
159+
position: Point,
160+
direction: Point,
161+
affected_crates: &mut Vec<Point>,
162+
) -> bool {
163+
let next_position = position + direction;
164+
let next_next_position = next_position + direction;
165+
166+
if direction == LEFT && grid[next_position] == Block::WALL {
167+
affected_crates.clear();
168+
return false;
169+
}
170+
171+
if direction == RIGHT && grid[next_next_position] == Block::WALL {
172+
affected_crates.clear();
173+
return false;
174+
}
175+
176+
if grid[next_next_position] == Block::CRATE {
177+
affected_crates.push(next_next_position);
178+
return can_move_part_two_horizontal(grid, next_next_position, direction, affected_crates);
179+
}
180+
181+
true
182+
}
183+
184+
pub fn part_one(input: &str) -> Option<u32> {
185+
let (grid, instructions) = parse_data(input);
186+
187+
let result = part_x(grid, instructions, can_move_part_one);
188+
189+
Some(result)
190+
}
191+
192+
pub fn part_two(input: &str) -> Option<u32> {
193+
let (grid, instructions) = parse_data(input);
194+
195+
let mut big_grid_data = Vec::with_capacity(grid.height as usize * grid.width as usize * 2);
196+
for &el in &grid.bytes {
197+
match el {
198+
Block::WALL => big_grid_data.extend([Block::WALL, Block::WALL]),
199+
Block::EMPTY => big_grid_data.extend([Block::EMPTY, Block::EMPTY]),
200+
Block::CRATE => big_grid_data.extend([Block::CRATE, Block::EMPTY]),
201+
Block::ROBOT => big_grid_data.extend([Block::ROBOT, Block::EMPTY]),
202+
_ => unreachable!(),
203+
}
204+
}
205+
206+
let grid = Grid {
207+
width: grid.width * 2,
208+
height: grid.height,
209+
bytes: big_grid_data,
210+
};
211+
212+
let result = part_x(grid, instructions, can_move_part_two);
213+
214+
Some(result)
215+
}
216+
217+
#[cfg(test)]
218+
mod tests {
219+
use super::*;
220+
221+
#[test]
222+
fn test_part_one() {
223+
let input = advent_of_code::template::read_file("examples", DAY);
224+
let result = part_one(&input);
225+
assert_eq!(result, Some(10092));
226+
}
227+
228+
#[test]
229+
fn test_part_two() {
230+
let input = advent_of_code::template::read_file("examples", DAY);
231+
let result = part_two(&input);
232+
assert_eq!(result, Some(9021));
233+
}
234+
}

0 commit comments

Comments
 (0)