From 445d09cbd7559832925bf38ec6313d773a6310dc Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Thu, 2 Dec 2021 00:39:42 -0500 Subject: [PATCH] Add day 2 solution --- day2/Cargo.lock | 39 ++++++++++++++ day2/Cargo.toml | 9 ++++ day2/src/main.rs | 132 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 day2/Cargo.lock create mode 100644 day2/Cargo.toml create mode 100644 day2/src/main.rs diff --git a/day2/Cargo.lock b/day2/Cargo.lock new file mode 100644 index 0000000..7c216fb --- /dev/null +++ b/day2/Cargo.lock @@ -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" diff --git a/day2/Cargo.toml b/day2/Cargo.toml new file mode 100644 index 0000000..d0f0888 --- /dev/null +++ b/day2/Cargo.toml @@ -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" diff --git a/day2/src/main.rs b/day2/src/main.rs new file mode 100644 index 0000000..99d963c --- /dev/null +++ b/day2/src/main.rs @@ -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::>(); + + let directions = lines + .iter() + .map(|line| { + parse_line(line) + .unwrap_or_else(|err| panic!("Failed to parse line '{}': {}", line, err)) + }) + .map(|(_, direction)| direction) + .collect::>(); + + println!("Part 1: {}", simulate(&directions, &Part::Part1)); + println!("Part 2: {}", simulate(&directions, &Part::Part2)); +}