diff --git a/2022/Cargo.toml b/2022/Cargo.toml index 706d2e1..d56d162 100644 --- a/2022/Cargo.toml +++ b/2022/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0" diff --git a/2022/src/bin/day1.rs b/2022/src/bin/day1.rs index c11ef31..8bd56fe 100644 --- a/2022/src/bin/day1.rs +++ b/2022/src/bin/day1.rs @@ -1,24 +1,25 @@ // -- Setup & Runners -- +use anyhow::Result; use aoc::Solver; pub struct Day; -fn main() { - Day::solve(); +fn main() -> Result<()> { + Day::solve() } #[test] -fn part1_test1() { - Day::test(aoc::Part::ONE, "test-1", 24000); +fn part1_test1() -> Result<()> { + Day::test(aoc::Part::ONE, "test-1", 24000) } #[test] -fn part2_test1() { - Day::test(aoc::Part::TWO, "test-1", 45000); +fn part2_test1() -> Result<()> { + Day::test(aoc::Part::TWO, "test-1", 45000) } #[test] -fn part1_solution() { - Day::test(aoc::Part::ONE, "input", 70116); +fn part1_solution() -> Result<()> { + Day::test(aoc::Part::ONE, "input", 70116) } #[test] -fn part2_solution() { - Day::test(aoc::Part::TWO, "input", 206582); +fn part2_solution() -> Result<()> { + Day::test(aoc::Part::TWO, "input", 206582) } // -- Solution -- diff --git a/2022/src/bin/day2.rs b/2022/src/bin/day2.rs index 5c90bf3..484454b 100644 --- a/2022/src/bin/day2.rs +++ b/2022/src/bin/day2.rs @@ -1,52 +1,53 @@ // -- Setup & Runners -- +use anyhow::Result; use aoc::Solver; pub struct Day; -fn main() { - Day::solve(); +fn main() -> Result<()> { + Day::solve() } #[test] -fn part1_test1() { - Day::test(aoc::Part::ONE, "test-1", 15); +fn part1_test1() -> Result<()> { + Day::test(aoc::Part::ONE, "test-1", 15) } #[test] -fn part2_test1() { - Day::test(aoc::Part::TWO, "test-1", 12); +fn part2_test1() -> Result<()> { + Day::test(aoc::Part::TWO, "test-1", 12) } #[test] -fn part1_solution() { - Day::test(aoc::Part::ONE, "input", 14264); +fn part1_solution() -> Result<()> { + Day::test(aoc::Part::ONE, "input", 14264) } #[test] -fn part2_solution() { - Day::test(aoc::Part::TWO, "input", 12382); +fn part2_solution() -> Result<()> { + Day::test(aoc::Part::TWO, "input", 12382) } // -- Implementation for hand -- #[derive(Debug, Copy, Clone)] enum Hand { - Rock = 0, - Paper = 1, - Scissors = 2, + Rock, + Paper, + Scissors, } impl Hand { // Return hand that this hand loses to - fn loses(&self) -> Hand { + fn loses_to(&self) -> Hand { // Returns the next enum (cyclical) as that one always loses from the current one Hand::from((*self as u8 + 1) % 3) } // Return hand that this hand wins from - fn wins(&self) -> Hand { + fn wins_from(&self) -> Hand { // Returns the previous enum (cyclical) as that one always wins from the current one Hand::from((*self as i8 - 1).rem_euclid(3) as u8) } - fn strategy(&self, input: char) -> Hand { + fn strategy(&self, input: &str) -> Hand { match input { - 'X' => self.wins(), - 'Y' => *self, - 'Z' => self.loses(), + "X" => self.wins_from(), + "Y" => *self, + "Z" => self.loses_to(), _ => panic!("Unexpected input") } } @@ -75,27 +76,30 @@ impl From for Hand { } } -impl From for Hand { - fn from(value: char) -> Self { - match value { - 'A' | 'X' => Self::Rock, - 'B' | 'Y' => Self::Paper, - 'C' | 'Z' => Self::Scissors, - value => panic!("Unknown input: {value}"), +impl From<&str> for Hand { + fn from(s: &str) -> Self { + match s { + "A" | "X" => Self::Rock, + "B" | "Y" => Self::Paper, + "C" | "Z" => Self::Scissors, + _ => panic!("Invalid input: {s}"), } } } // -- Helper functions -- // Convert the round to a tuple containing the actions of both players -fn round_to_letters(round: &str) -> (char, char) { - if let &[a, _, b] = round.as_bytes() { - (a as char, b as char) - } else { - panic!("Unexpected input"); +fn convert(round: &str) -> (&str, &str) { + match round.split_once(" ") { + Some((a, b)) => (a, b), + None => panic!("Invalid input: {round}"), } } +fn calc_score(sum: u32, (a, b): (Hand, Hand)) -> u32 { + sum + b.play(&a) + b.value() +} + // -- Solution -- impl aoc::Solver for Day { fn day() -> u8 { @@ -105,19 +109,19 @@ impl aoc::Solver for Day { fn part1(input: &str) -> u32 { input.lines() .filter(|round| round.len() > 0) - .map(round_to_letters) + .map(convert) .map(|(a, b)| (Hand::from(a), Hand::from(b))) - .map(|(a, b)| b.play(&a) + b.value()) - .sum() + .fold(0, calc_score) } fn part2(input: &str) -> u32 { input.lines() .filter(|round| round.len() > 0) - .map(round_to_letters) - .map(|(a, b)| (Hand::from(a), b)) - .map(|(a, b)| (a, a.strategy(b))) - .map(|(a, b)| b.play(&a) + b.value()) - .sum() + .map(convert) + .map(|(a, b)| { + let opponent = Hand::from(a); + (opponent, opponent.strategy(b)) + }) + .fold(0, calc_score) } } diff --git a/2022/src/bin/template.rs b/2022/src/bin/template.rs index a359efc..ae8a825 100644 --- a/2022/src/bin/template.rs +++ b/2022/src/bin/template.rs @@ -1,18 +1,19 @@ // -- Setup & Runners -- +use anyhow::Result; use aoc::Solver; pub struct Day; -fn main() { - Day::solve(); +fn main() -> Result<()> { + Day::solve() } #[test] -fn part1_test1() { - Day::test(aoc::Part::ONE, "test-1", 0); +fn part1_test1() -> Result<()> { + Day::test(aoc::Part::ONE, "test-1", 0) } // -- Solution -- impl aoc::Solver for Day { fn day() -> u8 { - 0 + todo!("Day not set") } fn part1(input: &str) -> u32 { diff --git a/2022/src/lib.rs b/2022/src/lib.rs index 53ac4f2..0400cec 100644 --- a/2022/src/lib.rs +++ b/2022/src/lib.rs @@ -1,5 +1,7 @@ use std::fs; +use anyhow::{Context, Result}; + pub enum Part { ONE, TWO @@ -10,7 +12,7 @@ pub trait Solver { fn part1(input: &str) -> u32; fn part2(input: &str) -> u32; - fn test(part: Part, name: &str, result: u32) { + fn test(part: Part, name: &str, result: u32) -> Result<()> { // Select the right function let fun = match part { Part::ONE => Self::part1, @@ -18,15 +20,19 @@ pub trait Solver { }; // Read the test input - let input = fs::read_to_string(format!("input/{}/{name}", Self::day())).expect("Test file does not exist!"); + let input = fs::read_to_string(format!("input/{}/{name}", Self::day())).with_context(|| format!("Failed to read '{}' for day {}", name, Self::day()))?; // Assert that the result matches the expected value assert_eq!(fun(&input), result); + + Ok(()) } - fn solve() { - let input = fs::read_to_string(format!("input/{}/input", Self::day())).expect("Input file does not exist!"); + fn solve() -> Result<()> { + let input = fs::read_to_string(format!("input/{}/input", Self::day())).with_context(|| format!("Failed to read 'input' for day {}", Self::day()))?; println!("Part 1: {}", Self::part1(&input)); println!("Part 2: {}", Self::part2(&input)); + + Ok(()) } }