You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
4.2 KiB

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<Self> {
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<Direction> 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<Point> 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<Point> 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<Point> 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());
}