Skip to content

Commit 8cfb683

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

File tree

2 files changed

+257
-0
lines changed

2 files changed

+257
-0
lines changed

Diff for: data/examples/15.txt

+21
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^<<^

Diff for: src/bin/15.rs

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

0 commit comments

Comments
 (0)