Skip to content

Commit 129e624

Browse files
committed
AOC 2024 Day 15 Part 1
1 parent 52ce337 commit 129e624

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
lines changed

aoc/2024/rust/data/examples/15.txt

Whitespace-only changes.

aoc/2024/rust/src/bin/15.rs

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
advent_of_code::solution!(15);
2+
3+
use std::collections::HashMap;
4+
5+
use glam::IVec2;
6+
7+
#[derive(Debug, PartialEq)]
8+
enum Cell {
9+
Wall,
10+
Box,
11+
Robot,
12+
Empty,
13+
}
14+
15+
impl Cell {
16+
fn from_char(c: char) -> Self {
17+
match c {
18+
'#' => Cell::Wall,
19+
'O' => Cell::Box,
20+
'@' => Cell::Robot,
21+
_ => Cell::Empty,
22+
}
23+
}
24+
}
25+
26+
#[derive(Debug)]
27+
enum Dir {
28+
N,
29+
E,
30+
S,
31+
W,
32+
}
33+
34+
impl Dir {
35+
fn from_char(c: char) -> Self {
36+
match c {
37+
'^' => Self::N,
38+
'>' => Self::E,
39+
'v' => Self::S,
40+
'<' => Self::W,
41+
_ => panic!("Bad direction {c}"),
42+
}
43+
}
44+
}
45+
46+
fn parse_grid(input: &str) -> HashMap<IVec2, Cell> {
47+
input
48+
.lines()
49+
.enumerate()
50+
.flat_map(|(y, line)| {
51+
line.chars()
52+
.enumerate()
53+
.filter(|(_, c)| *c != '.')
54+
.map(move |(x, c)| (IVec2::new(x as i32, y as i32), Cell::from_char(c)))
55+
})
56+
.collect::<HashMap<_, _>>()
57+
}
58+
59+
fn parse_moves(input: &str) -> Vec<Dir> {
60+
input
61+
.chars()
62+
.filter(|c| *c != '\n')
63+
.map(Dir::from_char)
64+
.collect()
65+
}
66+
67+
fn parse_input(input: &str) -> (HashMap<IVec2, Cell>, Vec<Dir>) {
68+
let mut sp = input.split("\n\n");
69+
let grid = parse_grid(sp.next().unwrap());
70+
let moves = parse_moves(sp.next().unwrap());
71+
(grid, moves)
72+
}
73+
74+
fn make_move(current_position: &IVec2, grid: &mut HashMap<IVec2, Cell>, dir: Dir) -> IVec2 {
75+
let v = match dir {
76+
Dir::N => IVec2::NEG_Y,
77+
Dir::E => IVec2::X,
78+
Dir::S => IVec2::Y,
79+
Dir::W => IVec2::NEG_X,
80+
};
81+
if let Some(c) = grid.get(&(current_position + v)) {
82+
match c {
83+
Cell::Box => {
84+
let first_box = current_position + v;
85+
for i in 2..50 {
86+
let dest = current_position + v * i;
87+
match grid.get(&dest) {
88+
Some(dest_content) => {
89+
match dest_content {
90+
Cell::Wall => {
91+
return *current_position;
92+
}
93+
Cell::Box => continue,
94+
_ => {
95+
// We found an empty cell.
96+
grid.insert(dest, Cell::Box);
97+
grid.remove(&first_box);
98+
return first_box;
99+
}
100+
}
101+
}
102+
None => {
103+
// We found an empty cell.
104+
grid.insert(dest, Cell::Box);
105+
grid.remove(&first_box);
106+
return first_box;
107+
}
108+
}
109+
}
110+
}
111+
Cell::Wall => {
112+
return *current_position;
113+
}
114+
_ => {
115+
return current_position + v;
116+
}
117+
}
118+
} else {
119+
// The destination cell is free.
120+
return current_position + v;
121+
}
122+
unreachable!("Should have returned next position")
123+
}
124+
125+
pub fn part_one(input: &str) -> Option<u32> {
126+
let (mut grid, moves) = parse_input(input);
127+
let (start, _) = grid.iter().find(|(_, v)| **v == Cell::Robot).unwrap();
128+
let mut current_position = *start;
129+
grid.remove(&current_position);
130+
for dir in moves {
131+
current_position = make_move(&current_position, &mut grid, dir);
132+
}
133+
Some(
134+
grid.iter()
135+
.map(|(k, v)| if *v == Cell::Box { 100 * k.y + k.x } else { 0 })
136+
.sum::<i32>() as _,
137+
)
138+
}
139+
140+
// TODO: Day 15 part 2
141+
pub fn part_two(_input: &str) -> Option<u32> {
142+
None
143+
}
144+
145+
#[cfg(test)]
146+
mod tests {
147+
use super::*;
148+
use test_case::test_case;
149+
150+
#[test_case(
151+
r#"########
152+
#..O.O.#
153+
##@.O..#
154+
#...O..#
155+
#.#.O..#
156+
#...O..#
157+
#......#
158+
########
159+
160+
<^^>>>vv<v>>v<<"#,
161+
Some(2028); "Small grid"
162+
)]
163+
#[test_case(
164+
r#"##########
165+
#..O..O.O#
166+
#......O.#
167+
#.OO..O.O#
168+
169+
#O#..O...#
170+
#O..O..O.#
171+
#.OO.O.OO#
172+
#....O...#
173+
##########
174+
175+
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
176+
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
177+
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
178+
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
179+
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
180+
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
181+
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
182+
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
183+
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
184+
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^"#,
185+
Some(10092); "Large grid"
186+
)]
187+
fn test_part_one(input: &str, expected: Option<u32>) {
188+
let result = part_one(input);
189+
assert_eq!(result, expected);
190+
}
191+
192+
#[test]
193+
fn test_part_two() {
194+
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
195+
assert_eq!(result, None);
196+
}
197+
}

0 commit comments

Comments
 (0)