Added better error reporting in case the files are missing, and slightly improved 2022 - Day 2

This commit is contained in:
Dreaded_X 2022-12-02 22:21:14 +01:00
parent cdaf5ebc6f
commit 8db3d5116c
Signed by: Dreaded_X
GPG Key ID: 76BDEC4E165D8AD9
5 changed files with 71 additions and 58 deletions

View File

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

View File

@ -1,24 +1,25 @@
// -- Setup & Runners -- // -- Setup & Runners --
use anyhow::Result;
use aoc::Solver; use aoc::Solver;
pub struct Day; pub struct Day;
fn main() { fn main() -> Result<()> {
Day::solve(); Day::solve()
} }
#[test] #[test]
fn part1_test1() { fn part1_test1() -> Result<()> {
Day::test(aoc::Part::ONE, "test-1", 24000); Day::test(aoc::Part::ONE, "test-1", 24000)
} }
#[test] #[test]
fn part2_test1() { fn part2_test1() -> Result<()> {
Day::test(aoc::Part::TWO, "test-1", 45000); Day::test(aoc::Part::TWO, "test-1", 45000)
} }
#[test] #[test]
fn part1_solution() { fn part1_solution() -> Result<()> {
Day::test(aoc::Part::ONE, "input", 70116); Day::test(aoc::Part::ONE, "input", 70116)
} }
#[test] #[test]
fn part2_solution() { fn part2_solution() -> Result<()> {
Day::test(aoc::Part::TWO, "input", 206582); Day::test(aoc::Part::TWO, "input", 206582)
} }
// -- Solution -- // -- Solution --

View File

@ -1,52 +1,53 @@
// -- Setup & Runners -- // -- Setup & Runners --
use anyhow::Result;
use aoc::Solver; use aoc::Solver;
pub struct Day; pub struct Day;
fn main() { fn main() -> Result<()> {
Day::solve(); Day::solve()
} }
#[test] #[test]
fn part1_test1() { fn part1_test1() -> Result<()> {
Day::test(aoc::Part::ONE, "test-1", 15); Day::test(aoc::Part::ONE, "test-1", 15)
} }
#[test] #[test]
fn part2_test1() { fn part2_test1() -> Result<()> {
Day::test(aoc::Part::TWO, "test-1", 12); Day::test(aoc::Part::TWO, "test-1", 12)
} }
#[test] #[test]
fn part1_solution() { fn part1_solution() -> Result<()> {
Day::test(aoc::Part::ONE, "input", 14264); Day::test(aoc::Part::ONE, "input", 14264)
} }
#[test] #[test]
fn part2_solution() { fn part2_solution() -> Result<()> {
Day::test(aoc::Part::TWO, "input", 12382); Day::test(aoc::Part::TWO, "input", 12382)
} }
// -- Implementation for hand -- // -- Implementation for hand --
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
enum Hand { enum Hand {
Rock = 0, Rock,
Paper = 1, Paper,
Scissors = 2, Scissors,
} }
impl Hand { impl Hand {
// Return hand that this hand loses to // 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 // Returns the next enum (cyclical) as that one always loses from the current one
Hand::from((*self as u8 + 1) % 3) Hand::from((*self as u8 + 1) % 3)
} }
// Return hand that this hand wins from // 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 // 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) 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 { match input {
'X' => self.wins(), "X" => self.wins_from(),
'Y' => *self, "Y" => *self,
'Z' => self.loses(), "Z" => self.loses_to(),
_ => panic!("Unexpected input") _ => panic!("Unexpected input")
} }
} }
@ -75,27 +76,30 @@ impl From<u8> for Hand {
} }
} }
impl From<char> for Hand { impl From<&str> for Hand {
fn from(value: char) -> Self { fn from(s: &str) -> Self {
match value { match s {
'A' | 'X' => Self::Rock, "A" | "X" => Self::Rock,
'B' | 'Y' => Self::Paper, "B" | "Y" => Self::Paper,
'C' | 'Z' => Self::Scissors, "C" | "Z" => Self::Scissors,
value => panic!("Unknown input: {value}"), _ => panic!("Invalid input: {s}"),
} }
} }
} }
// -- Helper functions -- // -- Helper functions --
// Convert the round to a tuple containing the actions of both players // Convert the round to a tuple containing the actions of both players
fn round_to_letters(round: &str) -> (char, char) { fn convert(round: &str) -> (&str, &str) {
if let &[a, _, b] = round.as_bytes() { match round.split_once(" ") {
(a as char, b as char) Some((a, b)) => (a, b),
} else { None => panic!("Invalid input: {round}"),
panic!("Unexpected input");
} }
} }
fn calc_score(sum: u32, (a, b): (Hand, Hand)) -> u32 {
sum + b.play(&a) + b.value()
}
// -- Solution -- // -- Solution --
impl aoc::Solver for Day { impl aoc::Solver for Day {
fn day() -> u8 { fn day() -> u8 {
@ -105,19 +109,19 @@ impl aoc::Solver for Day {
fn part1(input: &str) -> u32 { fn part1(input: &str) -> u32 {
input.lines() input.lines()
.filter(|round| round.len() > 0) .filter(|round| round.len() > 0)
.map(round_to_letters) .map(convert)
.map(|(a, b)| (Hand::from(a), Hand::from(b))) .map(|(a, b)| (Hand::from(a), Hand::from(b)))
.map(|(a, b)| b.play(&a) + b.value()) .fold(0, calc_score)
.sum()
} }
fn part2(input: &str) -> u32 { fn part2(input: &str) -> u32 {
input.lines() input.lines()
.filter(|round| round.len() > 0) .filter(|round| round.len() > 0)
.map(round_to_letters) .map(convert)
.map(|(a, b)| (Hand::from(a), b)) .map(|(a, b)| {
.map(|(a, b)| (a, a.strategy(b))) let opponent = Hand::from(a);
.map(|(a, b)| b.play(&a) + b.value()) (opponent, opponent.strategy(b))
.sum() })
.fold(0, calc_score)
} }
} }

View File

@ -1,18 +1,19 @@
// -- Setup & Runners -- // -- Setup & Runners --
use anyhow::Result;
use aoc::Solver; use aoc::Solver;
pub struct Day; pub struct Day;
fn main() { fn main() -> Result<()> {
Day::solve(); Day::solve()
} }
#[test] #[test]
fn part1_test1() { fn part1_test1() -> Result<()> {
Day::test(aoc::Part::ONE, "test-1", 0); Day::test(aoc::Part::ONE, "test-1", 0)
} }
// -- Solution -- // -- Solution --
impl aoc::Solver for Day { impl aoc::Solver for Day {
fn day() -> u8 { fn day() -> u8 {
0 todo!("Day not set")
} }
fn part1(input: &str) -> u32 { fn part1(input: &str) -> u32 {

View File

@ -1,5 +1,7 @@
use std::fs; use std::fs;
use anyhow::{Context, Result};
pub enum Part { pub enum Part {
ONE, ONE,
TWO TWO
@ -10,7 +12,7 @@ pub trait Solver {
fn part1(input: &str) -> u32; fn part1(input: &str) -> u32;
fn part2(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 // Select the right function
let fun = match part { let fun = match part {
Part::ONE => Self::part1, Part::ONE => Self::part1,
@ -18,15 +20,19 @@ pub trait Solver {
}; };
// Read the test input // 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 that the result matches the expected value
assert_eq!(fun(&input), result); assert_eq!(fun(&input), result);
Ok(())
} }
fn solve() { fn solve() -> Result<()> {
let input = fs::read_to_string(format!("input/{}/input", Self::day())).expect("Input file does not exist!"); 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 1: {}", Self::part1(&input));
println!("Part 2: {}", Self::part2(&input)); println!("Part 2: {}", Self::part2(&input));
Ok(())
} }
} }