const INPUT: &str = include_str!("../../data/day12"); #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum Rotate { Left, Back, Right, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum Action { Forward(u32), RotateWaypoint(Rotate), MoveWaypoint(Direction, u32), } impl Action { fn parse(line: &str) -> Self { let dir = match line.as_bytes()[0] { b'N' => |n| Self::MoveWaypoint(Direction::North, n), b'E' => |n| Self::MoveWaypoint(Direction::East, n), b'W' => |n| Self::MoveWaypoint(Direction::West, n), b'S' => |n| Self::MoveWaypoint(Direction::South, n), b'F' => Self::Forward, _ => { let rot = match line { "L90"|"R270" => Rotate::Left, "L180"|"R180" => Rotate::Back, "L270"|"R90" => Rotate::Right, _ => panic!("Invalid action line: {:?}", line), }; return Self::RotateWaypoint(rot); }, }; dir(line[1..].parse().unwrap()) } fn parse_list(input: &str) -> Vec { input.lines().map(Self::parse).collect() } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum Direction { North, East, South, West, } impl Direction { fn rotate(self, rotate: Rotate) -> Self { match (rotate, self) { (Rotate::Left, Self::East)|(Rotate::Right, Self::West)|(Rotate::Back, Self::South) => Self::North, (Rotate::Left, Self::South)|(Rotate::Right, Self::North)|(Rotate::Back, Self::West) => Self::East, (Rotate::Left, Self::West)|(Rotate::Right, Self::East)|(Rotate::Back, Self::North) => Self::South, (Rotate::Left, Self::North)|(Rotate::Right, Self::South)|(Rotate::Back, Self::East) => Self::West, } } } impl From for Point { fn from(d: Direction) -> Self { match d { Direction::North => Self { north: 1, east: 0 }, Direction::East => Self { north: 0, east: 1 }, Direction::South => Self { north: -1, east: 0 }, Direction::West => Self { north: 0, east: -1 }, } } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] struct Point { north: i64, east: i64, } impl std::ops::Add for Point { type Output = Self; fn add(self, rhs: Point) -> Self::Output { Self { north: self.north + rhs.north, east: self.east + rhs.east, } } } impl std::ops::Mul for u32 { type Output = Point; fn mul(self, rhs: Point) -> Self::Output { Point { north: (self as i64) * rhs.north, east: (self as i64) * rhs.east, } } } impl std::ops::Mul for Rotate { type Output = Point; fn mul(self, rhs: Point) -> Self::Output { match self { Self::Left => Point { north: rhs.east, east: -rhs.north }, Self::Right => Point { north: -rhs.east, east: rhs.north }, Self::Back => Point { north: -rhs.north, east: -rhs.east }, } } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] struct ShipPart1 { direction: Direction, position: Point, } impl ShipPart1 { fn new() -> Self { Self { direction: Direction::East, position: Point { north: 0, east: 0 }, } } fn apply(&mut self, action: Action) { let (dir, inc) = match action { Action::RotateWaypoint(rotate) => { self.direction = self.direction.rotate(rotate); return; }, Action::Forward(inc) => { (self.direction, inc) }, Action::MoveWaypoint(dir, inc) => (dir, inc), }; self.position = self.position + inc * Point::from(dir); } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] struct ShipPart2 { waypoint: Point, position: Point, } impl ShipPart2 { fn new() -> Self { Self { waypoint: Point { north: 1, east: 10 }, position: Point { north: 0, east: 0 }, } } fn apply(&mut self, action: Action) { match action { Action::RotateWaypoint(rotate) => { self.waypoint = rotate * self.waypoint; }, Action::Forward(inc) => { self.position = self.position + inc * self.waypoint; }, Action::MoveWaypoint(dir, inc) => { self.waypoint = self.waypoint + inc * Point::from(dir); } } } } fn main() { let actions = Action::parse_list(INPUT); let mut ship = ShipPart1::new(); for &action in &actions { ship.apply(action); } println!("Manhattan distance: {}", ship.position.north.abs() + ship.position.east.abs()); let mut ship = ShipPart2::new(); for &action in &actions { ship.apply(action); } println!("Manhattan distance: {}", ship.position.north.abs() + ship.position.east.abs()); }