From 312460ffa5ba1398a15c5abb082ce917914cb997 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Mon, 16 Dec 2024 10:11:47 +0100 Subject: [PATCH] y2024d15 --- y2024/src/days/d15.rs | 425 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 409 insertions(+), 16 deletions(-) diff --git a/y2024/src/days/d15.rs b/y2024/src/days/d15.rs index 6e60854..92611dd 100644 --- a/y2024/src/days/d15.rs +++ b/y2024/src/days/d15.rs @@ -3,17 +3,54 @@ use std::error::Error; use itertools::Itertools; pub fn process_part1(input: &str) -> u32 { - let (grid, movements) = input.split_once("\n").unwrap(); - let grid = parse_grid(grid); + let (grid, movements) = input.split_once("\n\n").unwrap(); + let (mut grid, mut robot) = parse_grid(grid); let movements = parse_movements(movements); - for movement in movements {} + for movement in movements { + match movement { + Movement::Up => move_up(&mut robot, &mut grid), + Movement::Down => move_down(&mut robot, &mut grid), + Movement::Left => move_left(&mut robot, &mut grid), + Movement::Right => move_right(&mut robot, &mut grid), + } + } + log_grid(grid.clone()); + grid.concat().iter().map(|tile| tile.get_gps()).sum() } pub fn process_part2(input: &str) -> u32 { - 0 + let (grid, movements) = input.split_once("\n\n").unwrap(); + let (mut grid, mut robot) = parse_large_grid(grid); + let movements = parse_movements(movements); + for movement in movements { + match movement { + Movement::Up => move_up_large(&mut robot, &mut grid), + Movement::Down => move_down_large(&mut robot, &mut grid), + Movement::Left => move_left_large(&mut robot, &mut grid), + Movement::Right => move_right_large(&mut robot, &mut grid), + } + } + log_grid(grid.clone()); + grid.concat().iter().map(|tile| tile.get_gps()).sum() } -fn parse_grid(input: &str) -> Vec> { +fn log_grid(grid: Vec>) { + for row in grid { + for tile in row { + match tile.entity { + GridTileType::Robot => print!("@"), + GridTileType::Crate => print!("O"), + GridTileType::Wall => print!("#"), + GridTileType::Nothing => print!("."), + GridTileType::BigCrateLeft => print!("["), + GridTileType::BigCrateRight => print!("]"), + } + } + println!(); + } +} + +fn parse_large_grid(input: &str) -> (Vec>, GridTile) { let mut grid_entities = Vec::new(); let mut robot = GridTile { entity: GridTileType::Robot, @@ -23,12 +60,79 @@ fn parse_grid(input: &str) -> Vec> { let mut row_entities = Vec::new(); line.chars().enumerate().for_each(|(xidx, chara)| { if let Ok(entity) = GridTileType::from_character(chara) { - match entity {} + match entity { + GridTileType::Robot => { + robot.coords = (xidx as u32 * 2, yidx as u32); + row_entities.push(robot); + row_entities.push(GridTile::default()); + } + GridTileType::Crate => { + let entity = GridTile { + entity: GridTileType::BigCrateLeft, + coords: (xidx as u32 * 2, yidx as u32), + }; + row_entities.push(entity); + let entity = GridTile { + entity: GridTileType::BigCrateRight, + coords: (xidx as u32 * 2 + 1, yidx as u32), + }; + row_entities.push(entity); + } + GridTileType::Wall => { + let entity = GridTile { + entity, + coords: (xidx as u32 * 2, yidx as u32), + }; + row_entities.push(entity); + let entity = GridTile { + entity: GridTileType::Wall, + coords: (xidx as u32 * 2 + 1, yidx as u32), + }; + row_entities.push(entity); + } + GridTileType::Nothing => { + row_entities.push(GridTile::default()); + row_entities.push(GridTile::default()); + } + GridTileType::BigCrateLeft | GridTileType::BigCrateRight => (), + } } }); grid_entities.push(row_entities); }); - grid_entities + (grid_entities, robot) +} + +fn parse_grid(input: &str) -> (Vec>, GridTile) { + let mut grid_entities = Vec::new(); + let mut robot = GridTile { + entity: GridTileType::Robot, + ..Default::default() + }; + input.lines().enumerate().for_each(|(yidx, line)| { + let mut row_entities = Vec::new(); + line.chars().enumerate().for_each(|(xidx, chara)| { + if let Ok(entity) = GridTileType::from_character(chara) { + match entity { + GridTileType::Robot => { + robot.coords = (xidx as u32, yidx as u32); + row_entities.push(robot); + } + GridTileType::Crate | GridTileType::Wall => { + let entity = GridTile { + entity, + coords: (xidx as u32, yidx as u32), + }; + row_entities.push(entity); + } + GridTileType::Nothing => row_entities.push(GridTile::default()), + GridTileType::BigCrateLeft | GridTileType::BigCrateRight => (), + } + } + }); + grid_entities.push(row_entities); + }); + (grid_entities, robot) } fn parse_movements(input: &str) -> Vec { @@ -42,18 +146,277 @@ fn parse_movements(input: &str) -> Vec { .concat() } -fn move_up(robot: &mut GridTile, grid: &mut Vec>) {} +fn move_up(robot: &mut GridTile, grid: &mut [Vec]) { + let (robot_x, robot_y) = robot.coords; + let mut moving_tiles = Vec::new(); + for above in (0..robot_y).rev() { + let tile = grid[above as usize][robot_x as usize]; + match tile.entity { + GridTileType::Robot => (), + GridTileType::Crate => moving_tiles.push(tile), + GridTileType::Wall => return, + GridTileType::Nothing => break, + GridTileType::BigCrateLeft | GridTileType::BigCrateRight => (), + } + } + for tile in moving_tiles.iter_mut() { + tile.coords.1 -= 1; + grid[tile.coords.1 as usize][tile.coords.0 as usize] = *tile; + } + grid[robot.coords.1 as usize][robot.coords.0 as usize] = GridTile::default(); + robot.coords.1 -= 1; + grid[robot.coords.1 as usize][robot.coords.0 as usize] = *robot; +} -fn move_down(robot: &mut GridTile, grid: &mut Vec>) {} +fn move_down(robot: &mut GridTile, grid: &mut [Vec]) { + let (robot_x, robot_y) = robot.coords; + let height = grid.len(); + let mut moving_tiles = Vec::new(); + for below in robot_y..height as u32 { + let tile = grid[below as usize][robot_x as usize]; + match tile.entity { + GridTileType::Robot => (), + GridTileType::Crate => moving_tiles.push(tile), + GridTileType::Wall => return, + GridTileType::Nothing => break, + GridTileType::BigCrateLeft | GridTileType::BigCrateRight => (), + } + } + for tile in moving_tiles.iter_mut() { + tile.coords.1 += 1; + grid[tile.coords.1 as usize][tile.coords.0 as usize] = *tile; + } + grid[robot.coords.1 as usize][robot.coords.0 as usize] = GridTile::default(); + robot.coords.1 += 1; + grid[robot.coords.1 as usize][robot.coords.0 as usize] = *robot; +} -fn move_left(robot: &mut GridTile, grid: &mut Vec>) {} +fn move_left(robot: &mut GridTile, grid: &mut [Vec]) { + let (robot_x, robot_y) = robot.coords; + let mut moving_tiles = Vec::new(); + for left in (0..robot_x).rev() { + let tile = grid[robot_y as usize][left as usize]; + match tile.entity { + GridTileType::Robot => (), + GridTileType::Crate => moving_tiles.push(tile), + GridTileType::Wall => return, + GridTileType::Nothing => break, + GridTileType::BigCrateLeft | GridTileType::BigCrateRight => (), + } + } + for tile in moving_tiles.iter_mut() { + tile.coords.0 -= 1; + grid[tile.coords.1 as usize][tile.coords.0 as usize] = *tile; + } + grid[robot.coords.1 as usize][robot.coords.0 as usize] = GridTile::default(); + robot.coords.0 -= 1; + grid[robot.coords.1 as usize][robot.coords.0 as usize] = *robot; +} -fn move_right(robot: &mut GridTile, grid: &mut Vec>) {} +fn move_right(robot: &mut GridTile, grid: &mut [Vec]) { + let (robot_x, robot_y) = robot.coords; + let mut moving_tiles = Vec::new(); + let width = grid[0].len(); + for right in robot_x..width as u32 { + let tile = grid[robot_y as usize][right as usize]; + match tile.entity { + GridTileType::Robot => (), + GridTileType::Crate => moving_tiles.push(tile), + GridTileType::Wall => return, + GridTileType::Nothing => break, + GridTileType::BigCrateLeft | GridTileType::BigCrateRight => (), + } + } + for tile in moving_tiles.iter_mut() { + tile.coords.0 += 1; + grid[tile.coords.1 as usize][tile.coords.0 as usize] = *tile; + } + grid[robot.coords.1 as usize][robot.coords.0 as usize] = GridTile::default(); + robot.coords.0 += 1; + grid[robot.coords.1 as usize][robot.coords.0 as usize] = *robot; +} + +fn get_above(tile: &GridTile, grid: &[Vec]) -> Vec { + let (tile_x, tile_y) = tile.coords; + let mut above_tiles = Vec::new(); + let above = grid[tile_y as usize - 1][tile_x as usize]; + match above.entity { + GridTileType::Robot => (), + GridTileType::Crate => { + above_tiles.push(above); + above_tiles.extend_from_slice(&get_above(&above, grid)); + } + GridTileType::BigCrateLeft => { + above_tiles.push(above); + let right = grid[above.coords.1 as usize][above.coords.0 as usize + 1]; + above_tiles.push(above); + above_tiles.push(right); + above_tiles.extend_from_slice(&get_above(&above, grid)); + above_tiles.extend_from_slice(&get_above(&right, grid)); + } + GridTileType::BigCrateRight => { + above_tiles.push(above); + let left = grid[above.coords.1 as usize][above.coords.0 as usize - 1]; + above_tiles.push(above); + above_tiles.push(left); + above_tiles.extend_from_slice(&get_above(&above, grid)); + above_tiles.extend_from_slice(&get_above(&left, grid)); + } + GridTileType::Wall => above_tiles.push(above), + GridTileType::Nothing => (), + } + above_tiles +} + +fn get_below(tile: &GridTile, grid: &[Vec]) -> Vec { + let (tile_x, tile_y) = tile.coords; + let mut below_tiles = Vec::new(); + let below = grid[tile_y as usize + 1][tile_x as usize]; + match below.entity { + GridTileType::Robot => (), + GridTileType::Crate => { + below_tiles.push(below); + below_tiles.extend_from_slice(&get_below(&below, grid)); + } + GridTileType::BigCrateLeft => { + below_tiles.push(below); + let right = grid[below.coords.1 as usize][below.coords.0 as usize + 1]; + below_tiles.push(below); + below_tiles.push(right); + below_tiles.extend_from_slice(&get_below(&below, grid)); + below_tiles.extend_from_slice(&get_below(&right, grid)); + } + GridTileType::BigCrateRight => { + below_tiles.push(below); + let left = grid[below.coords.1 as usize][below.coords.0 as usize - 1]; + below_tiles.push(below); + below_tiles.push(left); + below_tiles.extend_from_slice(&get_below(&below, grid)); + below_tiles.extend_from_slice(&get_below(&left, grid)); + } + + GridTileType::Wall => below_tiles.push(below), + GridTileType::Nothing => (), + } + below_tiles +} + +fn get_left(tile: &GridTile, grid: &[Vec]) -> Vec { + let (tile_x, tile_y) = tile.coords; + let mut left_tiles = Vec::new(); + let left = grid[tile_y as usize][tile_x as usize - 1]; + match left.entity { + GridTileType::Robot => (), + GridTileType::Crate | GridTileType::BigCrateLeft | GridTileType::BigCrateRight => { + left_tiles.push(left); + left_tiles.extend_from_slice(&get_left(&left, grid)); + } + + GridTileType::Wall => left_tiles.push(left), + GridTileType::Nothing => (), + } + left_tiles +} + +fn get_right(tile: &GridTile, grid: &[Vec]) -> Vec { + let (tile_x, tile_y) = tile.coords; + let mut right_tiles = Vec::new(); + let right = grid[tile_y as usize][tile_x as usize + 1]; + match right.entity { + GridTileType::Robot => (), + GridTileType::Crate | GridTileType::BigCrateLeft | GridTileType::BigCrateRight => { + right_tiles.push(right); + right_tiles.extend_from_slice(&get_right(&right, grid)); + } + + GridTileType::Wall => right_tiles.push(right), + GridTileType::Nothing => (), + } + right_tiles +} + +fn move_up_large(robot: &mut GridTile, grid: &mut [Vec]) { + let mut moving_tiles = get_above(robot, grid); + if moving_tiles + .iter() + .map(|tile| tile.entity) + .contains(&GridTileType::Wall) + { + return; + } + moving_tiles.sort_by(|tile_a, tile_b| tile_a.coords.1.cmp(&tile_b.coords.1)); + for tile in moving_tiles.iter_mut() { + grid[tile.coords.1 as usize][tile.coords.0 as usize] = GridTile::default(); + tile.coords.1 -= 1; + grid[tile.coords.1 as usize][tile.coords.0 as usize] = *tile; + } + grid[robot.coords.1 as usize][robot.coords.0 as usize] = GridTile::default(); + robot.coords.1 -= 1; + grid[robot.coords.1 as usize][robot.coords.0 as usize] = *robot; +} + +fn move_down_large(robot: &mut GridTile, grid: &mut [Vec]) { + let mut moving_tiles = get_below(robot, grid); + if moving_tiles + .iter() + .map(|tile| tile.entity) + .contains(&GridTileType::Wall) + { + return; + } + moving_tiles.sort_by(|tile_a, tile_b| tile_b.coords.1.cmp(&tile_a.coords.1)); + for tile in moving_tiles.iter_mut() { + grid[tile.coords.1 as usize][tile.coords.0 as usize] = GridTile::default(); + tile.coords.1 += 1; + grid[tile.coords.1 as usize][tile.coords.0 as usize] = *tile; + } + grid[robot.coords.1 as usize][robot.coords.0 as usize] = GridTile::default(); + robot.coords.1 += 1; + grid[robot.coords.1 as usize][robot.coords.0 as usize] = *robot; +} + +fn move_left_large(robot: &mut GridTile, grid: &mut [Vec]) { + let mut moving_tiles = get_left(robot, grid); + if moving_tiles + .iter() + .map(|tile| tile.entity) + .contains(&GridTileType::Wall) + { + return; + } + for tile in moving_tiles.iter_mut() { + tile.coords.0 -= 1; + grid[tile.coords.1 as usize][tile.coords.0 as usize] = *tile; + } + grid[robot.coords.1 as usize][robot.coords.0 as usize] = GridTile::default(); + robot.coords.0 -= 1; + grid[robot.coords.1 as usize][robot.coords.0 as usize] = *robot; +} + +fn move_right_large(robot: &mut GridTile, grid: &mut [Vec]) { + let mut moving_tiles = get_right(robot, grid); + if moving_tiles + .iter() + .map(|tile| tile.entity) + .contains(&GridTileType::Wall) + { + return; + } + for tile in moving_tiles.iter_mut() { + tile.coords.0 += 1; + grid[tile.coords.1 as usize][tile.coords.0 as usize] = *tile; + } + grid[robot.coords.1 as usize][robot.coords.0 as usize] = GridTile::default(); + robot.coords.0 += 1; + grid[robot.coords.1 as usize][robot.coords.0 as usize] = *robot; +} #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] enum GridTileType { Robot, Crate, + BigCrateLeft, + BigCrateRight, Wall, #[default] Nothing, @@ -79,7 +442,7 @@ struct GridTile { impl GridTile { fn get_gps(&self) -> u32 { - if self.entity == GridTileType::Crate { + if self.entity == GridTileType::Crate || self.entity == GridTileType::BigCrateLeft { self.coords.0 + self.coords.1 * 100 } else { 0 @@ -150,9 +513,25 @@ vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<^<^^>>>^vv<^v^v^<>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<>< v^^>>><<^^<>>^v^v^<<>^<^v^v><^<<<><<^vv>>v>v^<<^"; + const INPUT_LARGE_BOX_SIMPLE: &str = "##### +#..O. +#...."; + + const INPUT_LARGE_BOX_FULL: &str = "####### +#...#.# +#.....# +#..OO@# +#..O..# +#.....# +####### + +>><<^^<>>^v^v^<<>^<^v^v><^<<<><<^vv>>v>v^<<^"; } #[test] - fn part2() { - let result = process_part2(INPUT_SMALL); - assert_eq!(result, 0); + fn part2_simple() { + let (grid, _) = parse_large_grid(INPUT_LARGE_BOX_SIMPLE); + let flat_grid = grid.concat(); + let result: u32 = flat_grid.iter().map(|tile| tile.get_gps()).sum(); + assert_eq!(result, 106); + } + + #[test] + fn part2_full() { + let result = process_part2(INPUT_LARGE_BOX_FULL); + assert_eq!(result, 618); + } + + #[test] + fn part2_large() { + let result = process_part2(INPUT_LARGE); + assert_eq!(result, 9021); } }