AoC_2020/src/day12.rs

156 lines
4.1 KiB
Rust

use aoc_runner_derive::{aoc, aoc_generator};
#[derive(Debug)]
pub enum Direction {
North,
East,
South,
West,
}
impl Direction {
fn from_int(n: i64) -> Self {
match n % 4 {
0 => Self::East,
1 => Self::South,
2 => Self::West,
3 => Self::North,
_ => unreachable!(),
}
}
}
#[derive(Debug)]
pub enum Command {
Direction(Direction, i64),
Left(i64),
Right(i64),
Forward(i64),
}
#[derive(Debug)]
pub struct Ship {
rot: i64,
pos: (i64, i64),
wp: (i64, i64),
}
impl Ship {
fn new() -> Self {
Self {
rot: 0,
pos: (0, 0),
wp: (10, 1),
}
}
fn facing(&self) -> Direction {
Direction::from_int(self.rot)
}
fn cmd_p1(&mut self, cmd: &Command) {
match cmd {
Command::Direction(Direction::North, n) => {
self.pos.1 += n;
}
Command::Direction(Direction::South, n) => {
self.pos.1 -= n;
}
Command::Direction(Direction::East, n) => {
self.pos.0 += n;
}
Command::Direction(Direction::West, n) => {
self.pos.0 -= n;
}
Command::Left(n) => {
self.rot -= n;
}
Command::Right(n) => {
self.rot += n;
}
Command::Forward(n) => self.cmd_p1(&Command::Direction(self.facing(), *n)),
}
}
fn cmd_p2(&mut self, cmd: &Command) {
match cmd {
Command::Direction(Direction::North, n) => {
self.wp.1 += n;
}
Command::Direction(Direction::South, n) => {
self.wp.1 -= n;
}
Command::Direction(Direction::East, n) => {
self.wp.0 += n;
}
Command::Direction(Direction::West, n) => {
self.wp.0 -= n;
}
Command::Right(n) => {
let wp = self.wp;
self.wp = match n % 4 {
0 => wp,
1 => (wp.1, -wp.0),
2 => (-wp.0, -wp.1),
3 => (-wp.1, wp.0),
_ => unreachable!(),
};
}
Command::Left(n) => {
let wp = self.wp;
self.wp = match n % 4 {
0 => wp,
1 => (-wp.1, wp.0),
2 => (-wp.0, -wp.1),
3 => (wp.1, -wp.0),
_ => unreachable!(),
};
}
Command::Forward(n) => {
self.pos.0 += self.wp.0 * n;
self.pos.1 += self.wp.1 * n;
}
}
}
}
#[aoc_generator(day12)]
pub fn input_generator(input: &str) -> Vec<Command> {
input
.lines()
.map(|l| {
let mut chars = l.chars();
let cmd = chars.next().unwrap();
let num = chars.collect::<String>().parse().unwrap();
match cmd {
'N' => Command::Direction(Direction::North, num),
'S' => Command::Direction(Direction::South, num),
'E' => Command::Direction(Direction::East, num),
'W' => Command::Direction(Direction::West, num),
'L' if num % 90 == 0 => Command::Left((num / 90) % 4),
'R' if num % 90 == 0 => Command::Right((num / 90) % 4),
'F' => Command::Forward(num),
_ => panic!("Invalid command: {}", l),
}
})
.collect::<Vec<Command>>()
}
#[aoc(day12, part1)]
pub fn solve_part1(input: &[Command]) -> usize {
let mut ship = Ship::new();
for cmd in input {
ship.cmd_p1(cmd);
}
return (ship.pos.0.abs() as usize) + (ship.pos.1.abs() as usize);
}
#[aoc(day12, part2)]
pub fn solve_part2(input: &[Command]) -> usize {
let mut ship = Ship::new();
for cmd in input {
ship.cmd_p2(cmd);
}
return (ship.pos.0.abs() as usize) + (ship.pos.1.abs() as usize);
}