Added better error reporting in case the files are missing, and slightly improved 2022 - Day 2
This commit is contained in:
parent
cdaf5ebc6f
commit
8db3d5116c
|
@ -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"
|
||||
|
|
|
@ -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 --
|
||||
|
|
|
@ -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<u8> for Hand {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<char> 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user