Skip to content

Commit

Permalink
Day 15: Warehouse Woes
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 18, 2024
1 parent ad793c3 commit 09ef91b
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ Development occurs in language-specific directories:
|[Day12.hs](hs/src/Day12.hs)|[Day12.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day12.kt)|[day12.py](py/aoc2024/day12.py)|[day12.rs](rs/src/day12.rs)|
|[Day13.hs](hs/src/Day13.hs)|[Day13.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day13.kt)|[day13.py](py/aoc2024/day13.py)|[day13.rs](rs/src/day13.rs)|
|[Day14.hs](hs/src/Day14.hs)|[Day14.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day14.kt)|[day14.py](py/aoc2024/day14.py)|[day14.rs](rs/src/day14.rs)|
|[Day15.hs](hs/src/Day15.hs)|[Day15.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day15.kt)|[day15.py](py/aoc2024/day15.py)||
|[Day15.hs](hs/src/Day15.hs)|[Day15.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day15.kt)|[day15.py](py/aoc2024/day15.py)|[day15.rs](rs/src/day15.rs)|
|[Day16.hs](hs/src/Day16.hs)|[Day16.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day16.kt)|[day16.py](py/aoc2024/day16.py)||
|[Day17.hs](hs/src/Day17.hs)|[Day17.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day17.kt)|||
8 changes: 7 additions & 1 deletion rs/benches/criterion.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use aoc2024::{
day1, day10, day11, day12, day13, day14, day2, day3, day4, day5, day6, day7, day8, day9,
day1, day10, day11, day12, day13, day14, day15, day2, day3, day4, day5, day6, day7, day8, day9,
};
use criterion::{black_box, Criterion};
use std::env;
Expand Down Expand Up @@ -100,6 +100,12 @@ fn aoc2024_bench(c: &mut Criterion) -> io::Result<()> {
g.bench_function("part 2", |b| b.iter(|| day14::part2(black_box(&data))));
g.finish();

let data = get_day_input(15)?;
let mut g = c.benchmark_group("day 15");
g.bench_function("part 1", |b| b.iter(|| day15::part1(black_box(&data))));
g.bench_function("part 2", |b| b.iter(|| day15::part2(black_box(&data))));
g.finish();

Ok(())
}

Expand Down
189 changes: 189 additions & 0 deletions rs/src/day15.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
use std::collections::BTreeSet;
use std::ops::IndexMut;

type YX = (usize, usize);

fn parse(data: &str) -> Option<(Vec<Vec<u8>>, YX, &str)> {
let (map, moves) = data.split_once("\n\n").unwrap_or((data, ""));
let map = map
.lines()
.map(|line| line.bytes().collect())
.collect::<Vec<Vec<_>>>();
let robot = map.iter().enumerate().find_map(|(y, line)| {
line.iter()
.enumerate()
.find(|(_, b)| **b == b'@')
.map(|(x, _)| (y, x))
});
Some((map, robot?, moves))
}

fn move_robot<Line>(map: &mut [Line], robot: YX, c: char) -> Option<YX>
where
Line: IndexMut<usize, Output = u8>,
{
let (dy, dx) = match c {
'^' => (usize::MAX, 0),
'v' => (1, 0),
'<' => (0, usize::MAX),
'>' => (0, 1),
_ => return None,
};
let mut levels = vec![];
let mut front: BTreeSet<_> = [robot].into();
while !front.is_empty() {
let back = front;
front = BTreeSet::new();
for (y, x) in &back {
let (y, x) = (y.wrapping_add(dy), x.wrapping_add(dx));
match map[y][x] {
b'.' => continue,
b'O' => {
front.insert((y, x));
}
b'[' => {
front.insert((y, x));
if dy != 0 {
front.insert((y, x + 1));
}
}
b']' => {
front.insert((y, x));
if dy != 0 {
front.insert((y, x - 1));
}
}
_ => return None,
}
}
levels.push(back);
}
for front in levels.into_iter().rev() {
for (y, x) in front {
map[y.wrapping_add(dy)][x.wrapping_add(dx)] = map[y][x];
map[y][x] = b'.';
}
}
Some((robot.0.wrapping_add(dy), robot.1.wrapping_add(dx)))
}

pub fn part1(data: &str) -> Option<usize> {
let (mut map, mut robot, moves) = parse(data)?;
for c in moves.chars() {
robot = move_robot(&mut map, robot, c).unwrap_or(robot);
}
Some(
map.into_iter()
.enumerate()
.flat_map(|(y, line)| {
line.into_iter().enumerate().filter_map(move |(x, b)| {
if b == b'O' {
Some(100 * y + x)
} else {
None
}
})
})
.sum(),
)
}

pub fn part2(data: &str) -> Option<usize> {
let (mut map, mut robot, moves) = parse(data)?;
for line in &mut map {
*line = line
.iter()
.flat_map(|&b| match b {
b'@' => *b"@.",
b'O' => *b"[]",
_ => [b, b],
})
.collect();
}
robot.1 *= 2;
for c in moves.chars() {
robot = move_robot(&mut map, robot, c).unwrap_or(robot);
}
Some(
map.into_iter()
.enumerate()
.flat_map(|(y, line)| {
line.into_iter().enumerate().filter_map(move |(x, b)| {
if b == b'[' {
Some(100 * y + x)
} else {
None
}
})
})
.sum(),
)
}

#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use pretty_assertions::assert_eq;

static EXAMPLE_1: &str = indoc! {"
##########
#..O..O.O#
#......O.#
#.OO..O.O#
#[email protected].#
#O#..O...#
#O..O..O.#
#.OO.O.OO#
#....O...#
##########
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^
"};

static EXAMPLE_2: &str = indoc! {"
########
#..O.O.#
##@.O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
<^^>>>vv<v>>v<<
"};

static EXAMPLE_3: &str = indoc! {"
#######
#...#.#
#.....#
#..OO@#
#..O..#
#.....#
#######
<vv<<^^<<^^
"};

#[test]
fn part1_examples() {
assert_eq!(Some(2028), part1(EXAMPLE_2));
assert_eq!(Some(10092), part1(EXAMPLE_1));
}

#[test]
fn part2_examples() {
assert_eq!(Some(618), part2(EXAMPLE_3));
assert_eq!(Some(9021), part2(EXAMPLE_1));
}
}
1 change: 1 addition & 0 deletions rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod day11;
pub mod day12;
pub mod day13;
pub mod day14;
pub mod day15;
pub mod day2;
pub mod day3;
pub mod day4;
Expand Down
10 changes: 9 additions & 1 deletion rs/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::anyhow;
use aoc2024::{
day1, day10, day11, day12, day13, day14, day2, day3, day4, day5, day6, day7, day8, day9,
day1, day10, day11, day12, day13, day14, day15, day2, day3, day4, day5, day6, day7, day8, day9,
};
use std::collections::HashSet;
use std::env;
Expand Down Expand Up @@ -131,5 +131,13 @@ fn main() -> anyhow::Result<()> {
println!();
}

if args.is_empty() || args.contains("15") {
println!("Day 15");
let data = get_day_input(15)?;
println!("{:?}", day15::part1(&data).ok_or(anyhow!("None"))?);
println!("{:?}", day15::part2(&data).ok_or(anyhow!("None"))?);
println!();
}

Ok(())
}

0 comments on commit 09ef91b

Please sign in to comment.