summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bin/day15.rs198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/bin/day15.rs b/src/bin/day15.rs
new file mode 100644
index 0000000..c55a3f1
--- /dev/null
+++ b/src/bin/day15.rs
@@ -0,0 +1,198 @@
+use std::collections::HashSet;
+
+static DAY: u8 = 15;
+
+fn main() {
+ let input = advent::read_lines(DAY);
+ println!("{DAY}a: {}", box_coordinates(&input));
+ println!("{DAY}b: {}", 0);
+}
+
+#[derive(Eq, PartialEq, Hash, Clone, Copy)]
+struct Position {
+ x: isize,
+ y: isize,
+}
+
+impl Position {
+ fn next(&self, direction: Direction) -> Position {
+ let (x, y) = match direction {
+ Direction::Up => (self.x, self.y - 1),
+ Direction::Down => (self.x, self.y + 1),
+ Direction::Left => (self.x - 1, self.y),
+ Direction::Right => (self.x + 1, self.y),
+ };
+ Position { x, y }
+ }
+}
+
+#[derive(Clone, Copy)]
+enum Direction {
+ Up,
+ Down,
+ Left,
+ Right,
+}
+
+impl Direction {
+ fn new(input: char) -> Direction {
+ match input {
+ '^' => Direction::Up,
+ 'v' => Direction::Down,
+ '<' => Direction::Left,
+ '>' => Direction::Right,
+ _ => unimplemented!(),
+ }
+ }
+}
+
+struct Map {
+ floor: HashSet<Position>,
+ boxes: HashSet<Position>,
+ robot: Position,
+ directions: Vec<Direction>,
+}
+
+impl Map {
+ fn new(input: &[String]) -> Map {
+ let mut floor = HashSet::new();
+ let mut boxes = HashSet::new();
+ let mut robot = Position { x: 0, y: 0 };
+ let mut split_line = 0;
+ for (y, line) in input.iter().enumerate() {
+ if line.is_empty() {
+ split_line = y;
+ break;
+ }
+ for (x, c) in line.chars().enumerate() {
+ let pos = Position { x: x as isize, y: y as isize };
+ match c {
+ '.' => {
+ floor.insert(pos);
+ },
+ 'O' => {
+ floor.insert(pos);
+ boxes.insert(pos);
+ },
+ '@' => {
+ floor.insert(pos);
+ robot = pos;
+ },
+ _ => {}
+ }
+ }
+ }
+ let mut directions = Vec::new();
+ for line in input.iter().skip(split_line) {
+ for direction in line.chars().map(Direction::new) {
+ directions.push(direction);
+ }
+ }
+ Map { floor, boxes, robot, directions }
+ }
+
+ fn _print_map(&self) {
+ let max_x = self.floor.iter().map(|pos| pos.x).max().unwrap();
+ let max_y = self.floor.iter().map(|pos| pos.y).max().unwrap();
+ for y in 0 ..= max_y {
+ for x in 0 ..= max_x {
+ let pos = Position { x, y };
+ if self.robot == pos {
+ print!("@");
+ } else if self.boxes.contains(&pos) {
+ print!("O");
+ } else if self.floor.contains(&pos) {
+ print!(".");
+ } else {
+ print!("#");
+ }
+ }
+ println!();
+ }
+ println!();
+ }
+
+ fn coordinates(&self) -> isize {
+ self.boxes.iter()
+ .map(|pos| pos.y * 100 + pos.x)
+ .sum()
+ }
+
+ fn move_to(&mut self, pos: &Position, direction: Direction) -> bool {
+ if !self.floor.contains(pos) {
+ return false;
+ }
+ if self.boxes.contains(pos) {
+ let next_pos = pos.next(direction);
+ if self.move_to(&next_pos, direction) {
+ self.boxes.remove(pos);
+ self.boxes.insert(next_pos);
+ } else {
+ return false;
+ }
+ }
+ true
+ }
+
+ fn move_robot(&mut self) {
+ for direction in self.directions.clone().iter() {
+ let next_pos = self.robot.next(*direction);
+ if self.move_to(&next_pos, *direction) {
+ self.robot = next_pos;
+ }
+ }
+ }
+}
+
+fn box_coordinates(input: &[String]) -> isize {
+ let mut map = Map::new(input);
+ map.move_robot();
+ map.coordinates()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test() {
+ let input = [
+ "########",
+ "#..O.O.#",
+ "##@.O..#",
+ "#...O..#",
+ "#.#.O..#",
+ "#...O..#",
+ "#......#",
+ "########",
+ "",
+ "<^^>>>vv<v>>v<<",
+ ].iter().map(|&x| String::from(x)).collect::<Vec<_>>();
+ assert_eq!(box_coordinates(&input), 2028);
+
+ let input = [
+ "##########",
+ "#..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^<<^",
+ ].iter().map(|&x| String::from(x)).collect::<Vec<_>>();
+ assert_eq!(box_coordinates(&input), 10092);
+ }
+}