]> aoc.elinar.fr Git - aoc_2024/commitdiff
Day15 - part 1
authoralex <>
Sun, 15 Dec 2024 16:56:51 +0000 (17:56 +0100)
committeralex <>
Sun, 15 Dec 2024 16:56:51 +0000 (17:56 +0100)
src/day15.rs [new file with mode: 0644]
src/main.rs

diff --git a/src/day15.rs b/src/day15.rs
new file mode 100644 (file)
index 0000000..489486e
--- /dev/null
@@ -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<u32, Box<dyn Error>> {
+    println!("Running {} - part 1", get_day());
+
+    let mut puzzle = Puzzle::new(input);
+    puzzle.run();
+
+    Ok(puzzle.gps_sum())
+}
+
+fn run_part2(input: &str) -> Result<u32, Box<dyn Error>> {
+    println!("Running {} - part 2", get_day());
+
+    Ok(0)
+}
+
+pub fn run(input: &str) -> Result<(), Box<dyn Error>> {
+    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>>v<<";
+    static TEXT_INPUT: &str = "\
+##########
+#..O..O.O#
+#......O.#
+#.OO..O.O#
+#..O@..O.#
+#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^<<^";
+
+    #[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());
+    }
+}
index d015b9042c2df0d6aaebacd0f85aa285c35f1463..89da6e6bf42e4b976bcbfb4a16231e3b29fc3a24 100644 (file)
@@ -18,6 +18,7 @@ pub mod day11;
 pub mod day12;
 pub mod day13;
 pub mod day14;
+pub mod day15;
 
 fn main() {
     let args: Vec<String> = env::args().collect();
@@ -54,6 +55,7 @@ fn run(day: &str, input_file: &str) -> Result<(), Box<dyn Error>> {
         "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(())