Add day 2 solution

master
Nick Krichevsky 2021-12-02 00:39:42 -05:00
parent 5fb79bde9d
commit 445d09cbd7
3 changed files with 180 additions and 0 deletions

39
day2/Cargo.lock generated Normal file
View File

@ -0,0 +1,39 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "day2"
version = "0.1.0"
dependencies = [
"nom",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
dependencies = [
"memchr",
"minimal-lexical",
"version_check",
]
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"

9
day2/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "day2"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom = "7.1"

132
day2/src/main.rs Normal file
View File

@ -0,0 +1,132 @@
#![warn(clippy::all, clippy::pedantic)]
use nom::{
branch::alt,
bytes::complete::{tag, take_while1},
character::is_digit,
combinator::{eof, map_res},
sequence::{separated_pair, terminated},
IResult,
};
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
enum Part {
Part1,
Part2,
}
/// The direction of a sub command
enum Direction {
Forward(i32),
Down(i32),
Up(i32),
}
/// The location of the sub
#[derive(Default)]
struct Location {
position: i32,
depth: i32,
aim: i32,
}
impl Location {
/// Produce a new location that is moved based based on simple directionality:
/// forward/up/down map to positions and depths
fn perform_directional_move(&self, direction: &Direction) -> Location {
match direction {
Direction::Up(n) => Location {
position: self.position,
depth: self.depth - n,
aim: self.aim,
},
Direction::Down(n) => Location {
position: self.position,
depth: self.depth + n,
aim: self.aim,
},
Direction::Forward(n) => Location {
position: self.position + n,
depth: self.depth,
aim: self.aim,
},
}
}
/// Produce a new location that is moved based on the current aim; depth is controlled by a multiple of aim
fn perform_aim_based_move(&self, direction: &Direction) -> Location {
match direction {
Direction::Up(n) => Location {
position: self.position,
depth: self.depth,
aim: self.aim - n,
},
Direction::Down(n) => Location {
position: self.position,
depth: self.depth,
aim: self.aim + n,
},
Direction::Forward(n) => Location {
position: self.position + n,
depth: self.depth + (self.aim * n),
aim: self.aim,
},
}
}
}
fn simulate(directions: &[Direction], part: &Part) -> i32 {
let final_location =
directions
.iter()
.fold(Location::default(), |memo, direction| match part {
Part::Part1 => memo.perform_directional_move(direction),
Part::Part2 => memo.perform_aim_based_move(direction),
});
final_location.position * final_location.depth
}
fn parse_line(line: &str) -> IResult<&str, Direction> {
let parse_direction = alt((tag("forward"), tag("down"), tag("up")));
let (_, (raw_direction, magnitude)) = terminated(
separated_pair(
parse_direction,
tag(" "),
map_res(take_while1(|c| is_digit(c as u8)), str::parse),
),
eof,
)(line)?;
let direction = match raw_direction {
"forward" => Direction::Forward,
"down" => Direction::Down,
"up" => Direction::Up,
_ => panic!("invalid direction returned by parser; this can't happen"),
}(magnitude);
Ok(("", direction))
}
fn main() {
let input_file_name = env::args().nth(1).expect("No input filename specified");
let input_file = File::open(input_file_name).expect("Could not open input file");
let lines = BufReader::new(input_file)
.lines()
.map(|res| res.expect("Failed to read line"))
.collect::<Vec<_>>();
let directions = lines
.iter()
.map(|line| {
parse_line(line)
.unwrap_or_else(|err| panic!("Failed to parse line '{}': {}", line, err))
})
.map(|(_, direction)| direction)
.collect::<Vec<_>>();
println!("Part 1: {}", simulate(&directions, &Part::Part1));
println!("Part 2: {}", simulate(&directions, &Part::Part2));
}