Add day 2 solution
parent
5fb79bde9d
commit
445d09cbd7
|
@ -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"
|
|
@ -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"
|
|
@ -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));
|
||||
}
|
Loading…
Reference in New Issue