From 4dfdaca58c0b396897bc80ce5ab9d9cb4821a9be Mon Sep 17 00:00:00 2001 From: Keenan Tims Date: Sat, 14 Dec 2024 22:49:21 -0800 Subject: [PATCH] day15: part 1 solution + beginning of part 2 --- .aoc_tiles/tiles/2024/15.png | Bin 3441 -> 8401 bytes README.md | 5 +- src/day15.rs | 292 +++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + utils/grid/lib.rs | 19 ++- 5 files changed, 314 insertions(+), 3 deletions(-) create mode 100644 src/day15.rs diff --git a/.aoc_tiles/tiles/2024/15.png b/.aoc_tiles/tiles/2024/15.png index 607c12987485201109bfcbd308c8395cac1b3f23..8a0a40a28b9ae8f880395234d8f26504daf0cde4 100644 GIT binary patch literal 8401 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 diff --git a/README.md b/README.md index 983e72b..3aaedd1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- 2024 - 28 ⭐ - Rust + 2024 - 30 ⭐ - Rust

@@ -44,4 +44,7 @@ + + + diff --git a/src/day15.rs b/src/day15.rs new file mode 100644 index 0000000..d4b6810 --- /dev/null +++ b/src/day15.rs @@ -0,0 +1,292 @@ +use std::{ + fmt::Display, + io::{BufRead, Cursor, Lines}, + iter, + str::FromStr, +}; + +use aoc_runner_derive::aoc; +use grid::{AsCoord2d, Coord2d, Grid}; +use itertools::{rev, Itertools}; + +struct Warehouse { + map: Grid, + robot_pos: Coord2d, +} + +impl Display for Warehouse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.map.fmt(f) + } +} + +impl Warehouse { + fn step_robot(&mut self, m: Move) { + match m { + Move::Left => { + let to_left = &self.map.row(self.robot_pos.y()).unwrap()[0..self.robot_pos.x() as usize]; + let left_chunks = to_left + .chunk_by(|a, b| a == b || (*a == b'[' && *b == b']')) + .collect_vec(); + match left_chunks.last().unwrap().last().unwrap() { + b'.' => { + self.map + .swap(&self.robot_pos, (self.robot_pos.x() - 1, self.robot_pos.y())); + self.robot_pos.x -= 1 + } + b'O' | b'[' | b']' => { + if left_chunks[left_chunks.len() - 2].last().unwrap() == &b'.' { + let y = self.robot_pos.y(); + // swap the whole chunk left + for x_target in self.robot_pos.x() - left_chunks.last().unwrap().len() as i64 + ..=self.robot_pos.x() as i64 + { + self.map.swap((x_target, y), (x_target - 1, y)); + } + self.robot_pos.x -= 1; + } + } + b'#' => {} + c => panic!("unexpected char {}", c), + } + } + Move::Right => { + let to_right = + &self.map.row(self.robot_pos.y()).unwrap()[self.robot_pos.x() as usize + 1..self.map.width()]; + let right_chunks = to_right + .chunk_by(|a, b| a == b || (*a == b'[' && *b == b']')) + .collect_vec(); + match right_chunks[0][0] { + b'.' => { + self.map + .swap(&self.robot_pos, (self.robot_pos.x() + 1, self.robot_pos.y())); + self.robot_pos.x += 1 + } + b'O' | b'[' | b']' => { + if right_chunks[1][0] == b'.' { + let y = self.robot_pos.y(); + // swap the whole chunk right + for x_target in + (self.robot_pos.x() + 1..=self.robot_pos.x() + 1 + right_chunks[0].len() as i64).rev() + { + self.map.swap((x_target, y), (x_target - 1, y)); + } + self.robot_pos.x += 1; + } + } + b'#' => {} + c => panic!("unexpected char {}", c), + } + } + Move::Up => { + let to_up = &self.map.col(self.robot_pos.x()).unwrap()[0..self.robot_pos.y() as usize]; + let up_chunks = to_up.chunk_by(|a, b| a == b).collect_vec(); + match up_chunks.last().unwrap().last().unwrap() { + b'.' => { + self.map + .swap(&self.robot_pos, (self.robot_pos.x(), self.robot_pos.y() - 1)); + self.robot_pos.y -= 1 + } + b'O' => { + if **up_chunks[up_chunks.len() - 2].last().unwrap() == b'.' { + let x = self.robot_pos.x(); + // swap the whole chunk left + for y_target in + self.robot_pos.y() - up_chunks.last().unwrap().len() as i64..=self.robot_pos.y() as i64 + { + self.map.swap((x, y_target), (x, y_target - 1)); + } + self.robot_pos.y -= 1; + } + } + b'#' => {} + c => panic!("unexpected char {}", c), + } + } + Move::Down => { + let to_down = + &self.map.col(self.robot_pos.x()).unwrap()[self.robot_pos.y() as usize + 1..self.map.height()]; + let down_chunks = to_down.chunk_by(|a, b| a == b).collect_vec(); + match down_chunks[0][0] { + b'.' => { + self.map + .swap(&self.robot_pos, (self.robot_pos.x(), self.robot_pos.y() + 1)); + self.robot_pos.y += 1; + } + b'O' => { + if *down_chunks[1][0] == b'.' { + let x = self.robot_pos.x(); + // swap the whole chunk down + for y_target in + (self.robot_pos.y() + 1..=self.robot_pos.y() + 1 + down_chunks[0].len() as i64).rev() + { + self.map.swap((x, y_target), (x, y_target - 1)); + } + self.robot_pos.y += 1; + } + } + b'#' => {} + c => panic!("unexpected char {}", c), + } + } + } + } + + fn embiggen(&mut self) { + let new_lines = (0..self.map.height()) + .map(|r| self.map.row(r as i64).unwrap()) + .map(|row| { + row.iter() + .flat_map(|c| match c { + b'#' => ['#', '#'], + b'O' => ['[', ']'], + b'.' => ['.', '.'], + b'@' => ['@', '.'], + c => panic!("unexpected character {}", c), + }) + .collect::() + }) + .join("\n"); + self.map = Grid::from(Cursor::new(new_lines.as_str())); + self.robot_pos = self.map.find(&b'@').unwrap().to_coord(); + } +} + +#[derive(Debug)] +enum Move { + Left, + Right, + Up, + Down, +} + +impl Display for Move { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Left => f.write_str("Left"), + Self::Right => f.write_str("Right"), + Self::Up => f.write_str("Up"), + Self::Down => f.write_str("Down"), + } + } +} + +impl From for Move { + fn from(c: char) -> Self { + match c { + '<' => Self::Left, + '>' => Self::Right, + '^' => Self::Up, + 'v' => Self::Down, + c => panic!("invalid move {}", c), + } + } +} + +#[derive(Debug)] +struct MovePlan(Vec); + +impl FromStr for MovePlan { + type Err = Box; + fn from_str(s: &str) -> Result { + Ok(MovePlan( + s.chars().filter(|c| *c != '\n').map(|c| Move::from(c)).collect(), + )) + } +} + +fn parse(input: &str) -> (Warehouse, MovePlan) { + let lines = input.lines().collect_vec(); + let parts = lines.split(|l| l.is_empty()).map(|ls| ls.join("\n")).collect_vec(); + let map: Grid = parts[0].parse().unwrap(); + let wh = Warehouse { + robot_pos: map.find(&b'@').unwrap().to_coord(), + map, + }; + let moves = parts[1].parse().unwrap(); + + (wh, moves) +} + +#[aoc(day15, part1)] +pub fn part1(input: &str) -> i64 { + let (mut wh, moves) = parse(input); + // println!("map:\n {}\nmoves: {:?}", wh, moves); + for m in moves.0 { + // println!("{}", m); + wh.step_robot(m); + // println!("{}", wh); + } + wh.map + .data + .iter() + .enumerate() + .filter(|(i, v)| **v == b'O') + .map(|(i, _)| wh.map.coord(i as i64).unwrap().y() * 100 + wh.map.coord(i as i64).unwrap().x()) + .sum() +} + +#[aoc(day15, part2)] +pub fn part2(input: &str) -> i64 { + let (mut wh, moves) = parse(input); + wh.embiggen(); + + let moves: MovePlan = ">>>>>>>>>>>>".parse().unwrap(); + + println!("{}", wh); + for m in moves.0 { + println!("{}", m); + wh.step_robot(m); + println!("{}", wh); + } + + 0 +} + +#[cfg(test)] +mod tests { + use super::*; + const EXAMPLE1: &str = "######## +#..O.O.# +##@.O..# +#...O..# +#.#.O..# +#...O..# +#......# +######## + +<^^>>>vv>v<<"; + const EXAMPLE2: &str = "########## +#..O..O.O# +#......O.# +#.OO..O.O# +#..O@..O.# +#O#..O...# +#O..O..O.# +#.OO.O.OO# +#....O...# +########## + +^v>^vv^v>v<>v^v<<><>>v^v^>^<<<><^ +vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<^<^^>>>^<>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^v^^<^^vv< +<>^^^^>>>v^<>vvv^>^^^vv^^>v<^^^^v<>^>vvvv><>>v^<<^^^^^ +^><^><>>><>^^<<^^v>>><^^>v>>>^v><>^v><<<>vvvv>^<><<>^>< +^>><>^v<><^vvv<^^<><^v<<<><<<^^<^>>^<<<^>>^v^>>^v>vv>^<<^v<>><<><<>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^ +<><^^>^^^<>^vv<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<> +^^>vv<^v^v^<>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<>< +v^^>>><<^^<>>^v^v^<<>^<^v^v><^<<<><<^vv>>v>v^<<^"; + + #[test] + fn part1_example() { + assert_eq!(part1(EXAMPLE1), 2028); + assert_eq!(part1(EXAMPLE2), 10092); + } + + #[test] + fn part2_example() { + // assert_eq!(part2(EXAMPLE1), 0); + assert_eq!(part2(EXAMPLE2), 9021); + } +} diff --git a/src/lib.rs b/src/lib.rs index 7b521e4..52da659 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,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; diff --git a/utils/grid/lib.rs b/utils/grid/lib.rs index 9e56c96..99baf3c 100644 --- a/utils/grid/lib.rs +++ b/utils/grid/lib.rs @@ -152,7 +152,7 @@ impl Grid { pub fn height(&self) -> usize { self.data.len() / self.width() } - fn pos(&self, c: &C) -> i64 { + pub fn pos(&self, c: &C) -> i64 { c.y() * self.width + c.x() } pub fn coord(&self, pos: i64) -> Option<(i64, i64)> { @@ -203,13 +203,21 @@ impl Grid { } } pub fn row(&self, y: i64) -> Option<&[T]> { - if y < self.height() as i64 { + if y < self.height() as i64 && y >= 0 { Some(&self.data[self.pos(&(0, y)) as usize..self.pos(&(self.width, y)) as usize]) } else { None } } + pub fn col(&self, x: i64) -> Option> { + if x < self.width() as i64 && x >= 0 { + Some((0..self.height()).map(|y| self.get(&(x, y as i64)).unwrap()).collect()) + } else { + None + } + } + pub fn find(&self, haystack: &T) -> Option<(i64, i64)> { self.coord( self.data @@ -231,6 +239,13 @@ impl Grid { } } + pub fn swap(&mut self, a: A, b: B) { + match (self.valid_pos(&a), self.valid_pos(&b)) { + (Some(a), Some(b)) => self.data.swap(a, b), + _ => {} + } + } + // fn window_compare_impl(&self, needle: &[T]) -> Vec<(i64, i64)> { // if (self.width as usize) < needle.len() { // return Vec::new();