Day 2 ... slightly over-engineered.
This commit is contained in:
58
Cargo.lock
generated
58
Cargo.lock
generated
@@ -5,3 +5,61 @@ version = 3
|
||||
[[package]]
|
||||
name = "advent2021"
|
||||
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"
|
||||
|
||||
@@ -3,6 +3,5 @@ name = "advent2021"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0"
|
||||
|
||||
6
data/day2_test.txt
Normal file
6
data/day2_test.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
forward 5
|
||||
down 5
|
||||
forward 8
|
||||
up 3
|
||||
down 8
|
||||
forward 2
|
||||
1000
data/day2a.txt
Normal file
1000
data/day2a.txt
Normal file
File diff suppressed because it is too large
Load Diff
169
src/bin/day2.rs
Normal file
169
src/bin/day2.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user