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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
|
|
@ -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 --
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user