Day 2 ... slightly over-engineered.

This commit is contained in:
2021-12-02 08:33:10 -08:00
parent 6668ba4ed8
commit f7fc8feb03
5 changed files with 1234 additions and 2 deletions

58
Cargo.lock generated
View File

@@ -5,3 +5,61 @@ version = 3
[[package]] [[package]]
name = "advent2021" name = "advent2021"
version = "0.1.0" version = "0.1.0"
dependencies = [
"thiserror",
]
[[package]]
name = "proc-macro2"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"

View File

@@ -3,6 +3,5 @@ name = "advent2021"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
thiserror = "1.0"

6
data/day2_test.txt Normal file
View File

@@ -0,0 +1,6 @@
forward 5
down 5
forward 8
up 3
down 8
forward 2

1000
data/day2a.txt Normal file

File diff suppressed because it is too large Load Diff

169
src/bin/day2.rs Normal file
View File

@@ -0,0 +1,169 @@
use std::num;
use std::str::FromStr;
use thiserror::Error;
#[cfg(test)]
const TEST_DATA: &str = include_str!("../../data/day2_test.txt");
const DAY2A: &str = include_str!("../../data/day2a.txt");
#[derive(Debug, Error, PartialEq)]
enum Oopsie {
#[error("Couldn't understand '{0}' as a command.")]
CouldntParseCommand(String),
#[error("Couldn't parse number: {0}")]
CouldntParseNumber(#[from] num::ParseIntError),
}
#[derive(Debug, Eq, PartialEq)]
enum Command {
Forward(isize),
Down(isize),
Up(isize),
}
impl FromStr for Command {
type Err = Oopsie;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(rest) = s.strip_prefix("forward ") {
Ok(Command::Forward(isize::from_str(rest)?))
} else if let Some(rest) = s.strip_prefix("down ") {
Ok(Command::Down(isize::from_str(rest)?))
} else if let Some(rest) = s.strip_prefix("up ") {
Ok(Command::Up(isize::from_str(rest)?))
} else {
Err(Oopsie::CouldntParseCommand(s.to_string()))
}
}
}
fn from_file_data<T: FromStr>(filedata: &str) -> Result<Vec<T>, T::Err> {
let mut retval = Vec::new();
for line in filedata.lines() {
retval.push(T::from_str(line)?);
}
Ok(retval)
}
#[test]
fn command_parsing() {
assert_eq!(Ok(Command::Forward(5)), Command::from_str("forward 5"));
assert_eq!(Ok(Command::Down(5)), Command::from_str("down 5"));
assert_eq!(Ok(Command::Forward(8)), Command::from_str("forward 8"));
assert_eq!(Ok(Command::Up(3)), Command::from_str("up 3"));
assert_eq!(Ok(Command::Down(8)), Command::from_str("down 8"));
assert_eq!(Ok(Command::Forward(2)), Command::from_str("forward 2"));
}
#[test]
fn file_parsing() {
let result: Result<Vec<Command>, Oopsie> = from_file_data(TEST_DATA);
assert!(result.is_ok());
}
#[derive(Debug)]
struct Submarine {
interpretation: Interpretation,
depth: isize,
position_x: isize,
aim: isize,
}
#[derive(Debug)]
enum Interpretation {
Basic,
WithAim,
}
impl Submarine {
fn new(interpretation: Interpretation) -> Submarine {
Submarine {
interpretation,
depth: 0,
position_x: 0,
aim: 0,
}
}
fn command(&mut self, cmd: &Command) {
match self.interpretation {
Interpretation::Basic => match cmd {
Command::Forward(v) => self.position_x += v,
Command::Up(v) => self.depth -= v,
Command::Down(v) => self.depth += v,
},
Interpretation::WithAim => match cmd {
Command::Forward(v) => {
self.position_x += v;
self.depth += self.aim * v;
}
Command::Up(v) => self.aim -= v,
Command::Down(v) => self.aim += v,
},
}
}
fn run(&mut self, cmds: &[Command]) {
for cmd in cmds.iter() {
self.command(cmd);
}
}
fn distance(&self) -> isize {
self.depth * self.position_x
}
}
#[test]
fn basic_test_intepretation() {
let test_commands = from_file_data(TEST_DATA).unwrap();
let mut test_submarine = Submarine::new(Interpretation::Basic);
test_submarine.run(&test_commands);
assert_eq!(150, test_submarine.distance());
}
#[test]
fn test_intepretation_with_aim() {
let test_commands = from_file_data(TEST_DATA).unwrap();
let mut test_submarine = Submarine::new(Interpretation::WithAim);
test_submarine.run(&test_commands);
assert_eq!(900, test_submarine.distance());
}
// just in case I decide to be fancy and pull some stuff out of this for a
// later challenge
#[test]
fn regression_tests() {
let commands = from_file_data(DAY2A).unwrap();
let mut part1_submarine = Submarine::new(Interpretation::Basic);
part1_submarine.run(&commands);
assert_eq!(2039912, part1_submarine.distance());
let mut part2_submarine = Submarine::new(Interpretation::WithAim);
part2_submarine.run(&commands);
assert_eq!(1942068080, part2_submarine.distance());
}
fn day2() -> Result<(), Oopsie> {
let commands = from_file_data(DAY2A)?;
let mut part1_submarine = Submarine::new(Interpretation::Basic);
part1_submarine.run(&commands);
println!("Part 1 distance: {}", part1_submarine.distance());
let mut part2_submarine = Submarine::new(Interpretation::WithAim);
part2_submarine.run(&commands);
println!("Part 2 distance: {}", part2_submarine.distance());
Ok(())
}
fn main() {
if let Err(e) = day2() {
println!("Whoops: Top-level error: {}", e);
}
}