use std::str::FromStr;
#[derive(Clone, Copy)]
enum Direction {
Forward,
Up,
Down,
}
#[derive(Clone, Copy)]
struct Instruction {
direction: Direction,
distance: i64,
}
enum InstructionParseError {
Direction,
Distance(std::num::ParseIntError),
}
impl From<std::num::ParseIntError> for InstructionParseError {
fn from(src: std::num::ParseIntError) -> Self {
Self::Distance(src)
}
}
impl FromStr for Instruction {
type Err = InstructionParseError;
fn from_str(source: &str) -> Result<Self, Self::Err> {
let mut words = source.split_whitespace();
let ds = words.next().ok_or(InstructionParseError::Direction)?;
let direction = match ds {
"forward" => Ok(Direction::Forward),
"down" => Ok(Direction::Down),
"up" => Ok(Direction::Up),
_ => Err(InstructionParseError::Direction),
}?;
let distance: i64 = words.next().unwrap_or("").parse()?;
Ok(Self {
direction,
distance,
})
}
}
fn trace_1<I: Iterator<Item = Instruction>>(source: I) -> (i64, i64) {
let mut x = 0;
let mut y = 0;
for i in source {
match i.direction {
Direction::Forward => {
x += i.distance;
}
Direction::Up => {
y -= i.distance;
}
Direction::Down => {
y += i.distance;
}
}
}
(x, y)
}
fn trace_2<I: Iterator<Item = Instruction>>(source: I) -> (i64, i64) {
let mut aim: i64 = 0;
let mut x = 0;
let mut y = 0;
for i in source {
match i.direction {
Direction::Forward => {
x += i.distance;
y += aim * i.distance;
}
Direction::Up => {
aim -= i.distance;
}
Direction::Down => {
aim += i.distance;
}
}
}
(x, y)
}
fn main() {
use std::io::BufRead;
let args: Vec<_> = std::env::args().collect();
let file = std::io::BufReader::new(
std::fs::File::open(<String as AsRef<std::path::Path>>::as_ref(
&args[1],
))
.unwrap(),
);
let instructions: Vec<Instruction> = file
.lines()
.flat_map(|line| {
line.ok()
.and_then(|line| str::parse::<Instruction>(line.as_ref()).ok())
})
.collect();
let (x1, y1) = trace_1(instructions.iter().copied());
println!("Version 1:");
println!("x: {}; y: {}; product: {}", x1, y1, x1 * y1);
let (x2, y2) = trace_2(instructions.iter().copied());
println!();
println!("Version 2:");
println!("x: {}; y: {}; product: {}", x2, y2, x2 * y2);
}