use std::str::FromStr;
#[derive(Clone,Copy,PartialEq,Eq)]
enum Cardinal {
N,
S,
E,
W
}
#[derive(Clone,Copy,PartialEq,Eq)]
enum Direction {
C(Cardinal,u16),
T(i8),
F(u16),
}
#[derive(Debug)]
enum ParseDirectionError {
I(std::num::ParseIntError),
D(char),
Z,
}
impl From<std::num::ParseIntError> for ParseDirectionError {
fn from(src: std::num::ParseIntError) -> Self {
Self::I(src)
}
}
impl std::fmt::Display for ParseDirectionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParseDirectionError::I(e) => e.fmt(f),
ParseDirectionError::D(c) => write!(f, "Unrecognised direction code: {}", c),
ParseDirectionError::Z => write!(f, "Empty string"),
}
}
}
impl FromStr for Direction {
type Err = ParseDirectionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() == 0 {
Err(Self::Err::Z)?;
};
let (c,ns) = s.split_at(1);
match c {
"N" => {
let d = ns.parse::<u16>()?;
Ok(Direction::C(Cardinal::N,d))
},
"S" => {
let d = ns.parse::<u16>()?;
Ok(Direction::C(Cardinal::S,d))
},
"E" => {
let d = ns.parse::<u16>()?;
Ok(Direction::C(Cardinal::E,d))
},
"W" => {
let d = ns.parse::<u16>()?;
Ok(Direction::C(Cardinal::W,d))
},
"L" => {
let d = ns.parse::<i16>()?;
Ok(Direction::T(((4 - (d / 90)) % 4) as i8))
},
"R" => {
let d = ns.parse::<i16>()?;
Ok(Direction::T(((d / 90) % 4) as i8))
},
"F" => {
let d = ns.parse::<u16>()?;
Ok(Direction::F(d))
},
_ => Err(ParseDirectionError::D(c.chars().next().unwrap())),
}
}
}
#[derive(Clone,Copy,PartialEq,Eq)]
struct Ship {
x: i32,
y: i32,
bearing: Cardinal,
waypoint_x: i32,
waypoint_y: i32,
}
impl Ship {
fn new() -> Self {
Ship {x: 0, y: 0, bearing: Cardinal::E, waypoint_x: 10, waypoint_y: -1 }
}
fn step(&mut self, dir: Direction, revised: bool) {
if revised {
match dir {
Direction::C(c,d) => match c {
Cardinal::N => { self.waypoint_y -= d as i32; },
Cardinal::S => { self.waypoint_y += d as i32; },
Cardinal::E => { self.waypoint_x += d as i32; },
Cardinal::W => { self.waypoint_x -= d as i32; },
},
Direction::T(qt) => match qt % 4 {
0 => {},
1 => {
let (x,y) = (self.waypoint_x, self.waypoint_y);
self.waypoint_x = -y;
self.waypoint_y = x;
},
2 => {
let (x,y) = (self.waypoint_x, self.waypoint_y);
self.waypoint_x = -x;
self.waypoint_y = -y;
},
3 => {
let (x,y) = (self.waypoint_x, self.waypoint_y);
self.waypoint_x = y;
self.waypoint_y = -x;
},
_ => panic!("_ % 4 >= 4 !???"),
},
Direction::F(d) => {
self.x += self.waypoint_x * d as i32;
self.y += self.waypoint_y * d as i32;
},
}
} else {
match dir {
Direction::C(c,d) => match c {
Cardinal::N => { self.y -= d as i32; },
Cardinal::S => { self.y += d as i32; },
Cardinal::E => { self.x += d as i32; },
Cardinal::W => { self.x -= d as i32; },
},
Direction::T(qt) => {
let sqt: i8 = match self.bearing {
Cardinal::N => 8,
Cardinal::S => 10,
Cardinal::E => 9,
Cardinal::W => 11,
};
self.bearing = match (sqt + qt) % 4 {
0 => Cardinal::N,
1 => Cardinal::E,
2 => Cardinal::S,
3 => Cardinal::W,
_ => panic!("_ % 4 >= 4 !???"),
};
},
Direction::F(d) => self.step(Direction::C(self.bearing, d), false),
}
}
}
}
fn main() {
use std::io::BufRead;
let args: Vec<_> = std::env::args().collect();
let mut ship = Ship::new();
let file = std::io::BufReader::new(std::fs::File::open(<String as AsRef<std::path::Path>>::as_ref(&args[1])).unwrap());
let revised = match args[2].as_ref() {
"1" => false,
"2" => true,
_ => panic!("Version number (1 or 2) expected")
};
for line in file.lines() {
ship.step(line.unwrap().parse().unwrap(), revised);
}
println!("{}", ship.x.abs() + ship.y.abs());
}