From: alex <> Date: Sun, 15 Dec 2024 16:56:51 +0000 (+0100) Subject: Day15 - part 1 X-Git-Url: https://aoc.elinar.fr/?a=commitdiff_plain;h=23c88e65d3b41c6c875a9d49a90e73c196273752;p=aoc_2024 Day15 - part 1 --- diff --git a/src/day15.rs b/src/day15.rs new file mode 100644 index 0000000..489486e --- /dev/null +++ b/src/day15.rs @@ -0,0 +1,185 @@ +use std::error::Error; +use std::path::Path; +use std::collections::HashMap; + +struct Puzzle { + map: HashMap<(isize, isize), u8>, + robot: (isize, isize), + moves: String, +} + +impl Puzzle { + pub fn new(input: &str) -> Self { + let mut map: HashMap<(isize, isize), u8> = HashMap::new(); + let mut robot: (isize, isize) = (-1, -1); + let (map_str, moves) = input.split_once("\n\n").unwrap(); + map_str.lines().enumerate() + .for_each(|(row, l)| { + l.as_bytes().into_iter().enumerate() + .for_each(|(col, v)| { + map.insert((row as isize, col as isize), *v); + if *v == b'@' { + robot = (row as isize, col as isize); + } + }); + }); + let moves = moves.to_string(); + Self { map, robot, moves } + } + + fn set_type(&mut self, pos: (isize, isize), t: u8) { + if self.map.contains_key(&pos) { + let p = self.map.get_mut(&pos).unwrap(); + *p = t; + } + } + + fn is_of_type(&self, pos: (isize, isize), t: u8) -> bool { + self.map.contains_key(&pos) && *self.map.get(&pos).unwrap() == t + } + + fn is_box(&self, pos: (isize, isize)) -> bool { + self.is_of_type(pos, b'O') + } + + fn is_border(&self, pos: (isize, isize)) -> bool { + self.is_of_type(pos, b'#') + } + + fn push_boxes(&mut self, dir: (isize, isize)) -> bool { + let pos = self.robot; + let mut pos = (pos.0 + dir.0, pos.1 + dir.1); + let mut boxes: Vec<(isize, isize)> = Vec::new(); + while self.is_box(pos) { + boxes.push(pos); + pos = (pos.0 + dir.0, pos.1 + dir.1); + } + if !self.is_of_type(pos, b'.') { + false + } + else { + if let Some(box_last) = boxes.last() { + let next_pos = (box_last.0 + dir.0, box_last.1 + dir.1); + self.set_type(next_pos, b'O'); + } + if let Some(box_first) = boxes.first() { + self.set_type(*box_first, b'.'); + } + true + } + } + + fn robot_can_move(&mut self, dir: (isize, isize)) -> bool { + let pos = self.robot; + let next_pos = (pos.0 + dir.0, pos.1 + dir.1); + !self.is_border(next_pos) && self.push_boxes(dir) + } + + fn run(&mut self) { + let moves = self.moves.clone().as_bytes().to_vec(); + for m in moves.iter() { + let dir = match m { + b'^' => (-1, 0), + b'>' => (0, 1), + b'v' => (1, 0), + b'<' => (0, -1), + b'\n' => (0, 0), + _ => unreachable!() + }; + + if self.robot_can_move(dir) { + let pos = self.robot; + self.set_type(pos, b'.'); + self.robot = (pos.0 + dir.0, pos.1 + dir.1); + self.set_type(self.robot, b'@'); + } + } + } + + fn gps_sum(&self) -> u32 { + self.map.iter() + .filter(|&(_, v)| *v == b'O') + .map(|(pos, _)| (100*pos.0 + pos.1) as u32) + .sum() + } +} + +fn run_part1(input: &str) -> Result> { + println!("Running {} - part 1", get_day()); + + let mut puzzle = Puzzle::new(input); + puzzle.run(); + + Ok(puzzle.gps_sum()) +} + +fn run_part2(input: &str) -> Result> { + println!("Running {} - part 2", get_day()); + + Ok(0) +} + +pub fn run(input: &str) -> Result<(), Box> { + let res = run_part1(input)?; + println!("{res}"); + + let res = run_part2(input)?; + println!("{res}"); + + Ok(()) +} + +fn get_day() -> String { + let filename = file!(); + Path::new(filename).file_stem().unwrap().to_str().unwrap().to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + static TEXT_INPUT_0: &str = "\ +######## +#..O.O.# +##@.O..# +#...O..# +#.#.O..# +#...O..# +#......# +######## + +<^^>>>vv>v<<"; + static TEXT_INPUT: &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 test_part1() { + assert_eq!(2028, run_part1(TEXT_INPUT_0).unwrap()); + assert_eq!(10092, run_part1(TEXT_INPUT).unwrap()); + } + + #[test] + fn test_part2() { + assert_eq!(0, run_part2(TEXT_INPUT).unwrap()); + } +} diff --git a/src/main.rs b/src/main.rs index d015b90..89da6e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ pub mod day11; pub mod day12; pub mod day13; pub mod day14; +pub mod day15; fn main() { let args: Vec = env::args().collect(); @@ -54,6 +55,7 @@ fn run(day: &str, input_file: &str) -> Result<(), Box> { "day12" => day12::run(&input)?, "day13" => day13::run(&input)?, "day14" => day14::run(&input)?, + "day15" => day15::run(&input)?, _ => return Err(format!("unknown or unimplemented day \"{day}\"").into()), } Ok(())