diff options
author | cos <cos> | 2020-12-12 10:58:42 +0100 |
---|---|---|
committer | cos <cos> | 2020-12-12 22:13:06 +0100 |
commit | d8118966d1070fecc700f3532ea139ccdcb9d894 (patch) | |
tree | d0b695971dbed287d78d76fb240323f78a662049 /2020/rust/day12/src/main.rs | |
parent | fb10e41b64192cf2aa0961d43426e12a8fb7ba1c (diff) | |
download | adventofcode-d8118966d1070fecc700f3532ea139ccdcb9d894.zip |
Add day12, 2020
Diffstat (limited to '2020/rust/day12/src/main.rs')
-rw-r--r-- | 2020/rust/day12/src/main.rs | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/2020/rust/day12/src/main.rs b/2020/rust/day12/src/main.rs new file mode 100644 index 0000000..a24732c --- /dev/null +++ b/2020/rust/day12/src/main.rs @@ -0,0 +1,303 @@ +use anyhow::Result; +use std::env::args; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::ops::{Add, AddAssign, Mul}; +use std::path::Path; +use std::convert::TryFrom; +use std::error::Error; +use std::fmt; + +#[derive(Clone, Copy, Debug, PartialEq)] +enum FerryDirection { + North, + East, + South, + West, +} + +#[derive(Clone, Copy, Debug)] +enum FerryAction { + North(isize), + East(isize), + South(isize), + West(isize), + Left(isize), + Right(isize), + Forward(isize), +} + +#[derive(Clone, Copy)] +struct FerryPos { + x: isize, + y: isize, + direction: FerryDirection, +} + +impl FerryPos { + fn get_manhattan(&self) -> isize { + self.x.abs() + self.y.abs() + } + + fn move_forward(&mut self, steps: isize) { + match self.direction { + FerryDirection::North => *self += FerryAction::North(steps), + FerryDirection::East => *self += FerryAction::East(steps), + FerryDirection::South => *self += FerryAction::South(steps), + FerryDirection::West => *self += FerryAction::West(steps), + } + } + + fn turn(&mut self, degrees: isize) { + let mut steps = degrees / 90; + while steps < 0 { + steps += 4; + } + for _step in 0..steps { + self.direction = match self.direction { + FerryDirection::North => FerryDirection::East, + FerryDirection::East => FerryDirection::South, + FerryDirection::South => FerryDirection::West, + FerryDirection::West => FerryDirection::North, + }; + } + } + + fn rotate(&mut self, degrees: isize) { + let mut steps = degrees / 90; + while steps < 0 { + steps += 4; + } + for step in 0..steps { + let tmp = self.x; + match step % 4 { + 0 => { + self.x = -self.y; + self.y = tmp; + }, + 1 => { + self.x = -self.y; + self.y = tmp; + }, + 2 => { + self.x = -self.y; + self.y = tmp; + }, + 3 => { + self.x = -self.y; + self.y = tmp; + }, + _ => panic!("Invalid step count"), + }; + } + } +} + +impl fmt::Debug for FerryPos { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FerryPos") + .field("direction", &self.direction) + .field("x", &self.x) + .field("y", &self.y) + .finish() + } +} + +impl AddAssign<FerryAction> for FerryPos { + fn add_assign(&mut self, other: FerryAction) { + match other { + FerryAction::North(steps) => *self = Self { + direction: self.direction, + x: self.x, + y: self.y - steps, + }, + FerryAction::East(steps) => *self = Self { + direction: self.direction, + x: self.x + steps, + y: self.y, + }, + FerryAction::South(steps) => *self = Self { + direction: self.direction, + x: self.x, + y: self.y + steps, + }, + FerryAction::West(steps) => *self = Self { + direction: self.direction, + x: self.x - steps, + y: self.y, + }, + FerryAction::Forward(steps) => self.move_forward(steps), + FerryAction::Left(degrees) => self.turn(-degrees), + FerryAction::Right(degrees) => self.turn(degrees), + }; + } +} + +impl Add for FerryPos { + type Output = FerryPos; + + fn add(self, other: FerryPos) -> Self{ + FerryPos { + direction: self.direction, + x: self.x + other.x, + y: self.y + other.y, + } + } +} + +impl AddAssign for FerryPos { + fn add_assign(&mut self, other: Self) { + *self = *self + other; + } +} + +impl Mul<isize> for FerryPos { + type Output = Self; + + fn mul(self, rhs: isize) -> Self { + let mut res = FerryPos { + direction: self.direction, + x: 0, + y: 0, + }; + for _i in 0..rhs { + res += self; + } + + res + } +} + +#[derive(Debug)] +enum FerryActionError { + Error, +} + +impl fmt::Display for FerryActionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "FerryActionError") + } +} + +impl Error for FerryActionError { } + +impl TryFrom<String> for FerryAction { + type Error = FerryActionError; + + fn try_from(string: String) -> Result<Self, Self::Error> { + FerryAction::try_from(string.as_str()) + } +} + +impl TryFrom<&str> for FerryAction { + type Error = FerryActionError; + + fn try_from(string: &str) -> Result<Self, Self::Error> { + let action = string.get(0..1).ok_or(FerryActionError::Error)?; + let distance = string.get(1..).ok_or(FerryActionError::Error)?; + let num = distance.parse().or(Err(FerryActionError::Error))?; + match action { + "N" => Ok(FerryAction::North(num)), + "E" => Ok(FerryAction::East(num)), + "S" => Ok(FerryAction::South(num)), + "W" => Ok(FerryAction::West(num)), + "R" => Ok(FerryAction::Right(num)), + "L" => Ok(FerryAction::Left(num)), + "F" => Ok(FerryAction::Forward(num)), + _ => Err(FerryActionError::Error), + } + } +} + +fn read_input<T: AsRef<Path>>(filename: T) -> Result<Vec<FerryAction>> { + let f = File::open(filename)?; + let reader = BufReader::new(f); + + reader.lines() + .filter(|v| &v.as_ref().unwrap_or(&String::from("")). + get(0..1) != &Some("#")) // Allow comment lines starting with #-character + .map(|v| Ok(FerryAction::try_from(v?)?)) + .collect() +} + +fn part1(movements: &[FerryAction]) -> isize { + let mut ferry_pos = FerryPos { + x: 0, + y: 0, + direction: FerryDirection::East, + }; + + for movement in movements { + ferry_pos += *movement; + } + + ferry_pos.get_manhattan() +} + +fn part2(actions: &[FerryAction]) -> isize { + let mut ferry_pos = FerryPos { + x: 0, + y: 0, + direction: FerryDirection::East, + }; + let mut waypoint_pos = FerryPos { + x: 10, + y: -1, + direction: FerryDirection::East, + }; + + for action in actions { + match action { + FerryAction::Forward(steps) => ferry_pos += waypoint_pos.clone() * *steps, + FerryAction::Left(degrees) => waypoint_pos.rotate(-*degrees), + FerryAction::Right(degrees) => waypoint_pos.rotate(*degrees), + FerryAction::East(steps) => waypoint_pos += FerryPos { + direction: FerryDirection::East, + x: *steps, + y: 0, + }, + FerryAction::North(steps) => waypoint_pos += FerryPos { + direction: FerryDirection::East, + x: 0, + y: -*steps, + }, + FerryAction::West(steps) => waypoint_pos += FerryPos { + direction: FerryDirection::East, + x: -*steps, + y: 0, + }, + FerryAction::South(steps) => waypoint_pos += FerryPos { + direction: FerryDirection::East, + x: 0, + y: *steps, + }, + } + } + + ferry_pos.get_manhattan() +} + +fn main() { + let (do_part_1, do_part_2) = aoc::do_parts(); + + let filename = match args().nth(1) { + Some(f) => f, + None => { + eprintln!("Missing input filename"); + std::process::exit(1); + }, + }; + match read_input(filename) { + Ok(input) => { + if do_part_1 { + let solution = part1(&input); + println!("Part1, {}", solution); + } + if do_part_2 { + let solution = part2(&input); + println!("Part2, {}", solution); + } + }, + Err(err) => eprintln!("Could not read input: {}", err), + } +} |