summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--2020/rust/Cargo.toml2
-rw-r--r--2020/rust/day12/Cargo.toml9
-rw-r--r--2020/rust/day12/src/main.rs303
3 files changed, 313 insertions, 1 deletions
diff --git a/2020/rust/Cargo.toml b/2020/rust/Cargo.toml
index 19751a7..66b8506 100644
--- a/2020/rust/Cargo.toml
+++ b/2020/rust/Cargo.toml
@@ -12,7 +12,7 @@ members = [
"day09",
"day10",
"day11",
-# "day12",
+ "day12",
# "day13",
# "day14",
# "day15",
diff --git a/2020/rust/day12/Cargo.toml b/2020/rust/day12/Cargo.toml
new file mode 100644
index 0000000..a5f509b
--- /dev/null
+++ b/2020/rust/day12/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "day12"
+version = "0.1.0"
+authors = ["cos <cos>"]
+edition = "2018"
+
+[dependencies]
+aoc = { path = "../aoc" }
+anyhow = "1.0"
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),
+ }
+}