use std::str::FromStr; use anyhow::{anyhow, Result}; use crate::{Problem, Solution}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct Game { shapes: (Shape, Shape), outcome: Outcome, score: usize, } impl Game { fn new(a: Shape, b: Shape, outcome: Outcome) -> Self { Self { shapes: (a, b), outcome, score: b as usize + outcome as usize, } } } impl From<(Shape, Shape)> for Game { fn from((a, b): (Shape, Shape)) -> Self { Self::new(a, b, (a, b).into()) } } impl From<(Shape, Outcome)> for Game { fn from((a, outcome): (Shape, Outcome)) -> Self { Self::new(a, (a, outcome).into(), outcome) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] enum Outcome { Loss = 0, Draw = 3, Win = 6, } impl From<(Shape, Shape)> for Outcome { fn from((a, b): (Shape, Shape)) -> Self { match (a, b) { (Shape::Rock, Shape::Rock) => Outcome::Draw, (Shape::Rock, Shape::Paper) => Outcome::Win, (Shape::Rock, Shape::Scissors) => Outcome::Loss, (Shape::Paper, Shape::Rock) => Outcome::Loss, (Shape::Paper, Shape::Paper) => Outcome::Draw, (Shape::Paper, Shape::Scissors) => Outcome::Win, (Shape::Scissors, Shape::Rock) => Outcome::Win, (Shape::Scissors, Shape::Paper) => Outcome::Loss, (Shape::Scissors, Shape::Scissors) => Outcome::Draw, } } } impl FromStr for Outcome { type Err = anyhow::Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(Outcome::Loss), "Y" => Ok(Outcome::Draw), "Z" => Ok(Outcome::Win), s => Err(anyhow!("Unknown symbol: {}", s)), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Shape { Rock = 1, Paper = 2, Scissors = 3, } impl From<(Shape, Outcome)> for Shape { fn from((shape, outcome): (Shape, Outcome)) -> Self { match (shape, outcome) { (Shape::Rock, Outcome::Draw) => Shape::Rock, (Shape::Rock, Outcome::Win) => Shape::Paper, (Shape::Rock, Outcome::Loss) => Shape::Scissors, (Shape::Paper, Outcome::Loss) => Shape::Rock, (Shape::Paper, Outcome::Draw) => Shape::Paper, (Shape::Paper, Outcome::Win) => Shape::Scissors, (Shape::Scissors, Outcome::Win) => Shape::Rock, (Shape::Scissors, Outcome::Loss) => Shape::Paper, (Shape::Scissors, Outcome::Draw) => Shape::Scissors, } } } impl FromStr for Shape { type Err = anyhow::Error; fn from_str(s: &str) -> Result { match s { "A" | "X" => Ok(Shape::Rock), "B" | "Y" => Ok(Shape::Paper), "C" | "Z" => Ok(Shape::Scissors), s => Err(anyhow!("Unknown symbol: {}", s)), } } } pub struct Day2; impl Problem for Day2 { const DAY: u8 = 3; const INPUT: &'static str = include_str!("../input/day_2.txt"); } impl Solution for Day2 { type Answer1 = usize; type Answer2 = usize; fn part_1(input: &str) -> Result { input.lines().try_fold(0, |acc, l| { let (a, b) = l .split_once(' ') .ok_or_else(|| anyhow!("Missing deliminator"))?; let a: Shape = a.parse()?; let b: Shape = b.parse()?; let game: Game = (a, b).into(); Ok(acc + game.score) }) } fn part_2(input: &str) -> Result { input.lines().try_fold(0, |acc, l| { let (a, outcome) = l .split_once(' ') .ok_or_else(|| anyhow!("Missing deliminator"))?; let a: Shape = a.parse()?; let outcome: Outcome = outcome.parse()?; let game: Game = (a, outcome).into(); Ok(acc + game.score) }) } } #[cfg(test)] mod tests { use super::*; const INPUT: &str = indoc::indoc! {" A Y B X C Z "}; #[test] fn test_part_1_example() -> Result<()> { Ok(assert_eq!(15, Day2::part_1(INPUT)?)) } #[test] fn test_part_2_example() -> Result<()> { Ok(assert_eq!(12, Day2::part_2(INPUT)?)) } }