use aoc_2022::*; use color_eyre::eyre::Result; use itertools::Itertools; use tracing::*; use tracing_subscriber::EnvFilter; #[derive(Debug, Clone, Copy)] enum Shape { Rock, Paper, Scissors, } impl Shape { fn score(&self) -> i32 { match self { Shape::Rock => 1, Shape::Paper => 2, Shape::Scissors => 3, } } } #[derive(Debug, Clone, Copy)] enum Outcome { Lose, Draw, Win, } #[derive(Debug, Clone, Copy)] struct Round { opponent: Shape, me: Shape, } impl Round { fn score(&self) -> i32 { let shape_score = self.me.score(); let result_score = match (self.me, self.opponent) { (Shape::Scissors, Shape::Paper) => 6, (Shape::Paper, Shape::Rock) => 6, (Shape::Rock, Shape::Scissors) => 6, (Shape::Paper, Shape::Scissors) => 0, (Shape::Rock, Shape::Paper) => 0, (Shape::Scissors, Shape::Rock) => 0, _ => 3, }; shape_score + result_score } fn expected_outcome(&self) -> Outcome { match self.me { Shape::Rock => Outcome::Lose, Shape::Paper => Outcome::Draw, Shape::Scissors => Outcome::Win, } } fn expected_score(&self) -> i32 { let result_score = match self.expected_outcome() { Outcome::Lose => 0, Outcome::Draw => 3, Outcome::Win => 6, }; let shape_score = match (self.expected_outcome(), self.opponent) { (Outcome::Lose, Shape::Rock) => Shape::Scissors.score(), (Outcome::Lose, Shape::Paper) => Shape::Rock.score(), (Outcome::Lose, Shape::Scissors) => Shape::Paper.score(), (Outcome::Win, Shape::Rock) => Shape::Paper.score(), (Outcome::Win, Shape::Paper) => Shape::Scissors.score(), (Outcome::Win, Shape::Scissors) => Shape::Rock.score(), _ => self.opponent.score(), }; result_score + shape_score } } type Input = Vec; type Output = i32; fn part_1(input: &Input) -> Output { input.iter().map(Round::score).sum() } fn part_2(input: &Input) -> Output { input.iter().map(Round::expected_score).sum() } fn parse_input(pathname: &str) -> Input { file_to_lines(pathname) .iter() .map(|s| { let mut split_str = s.split(" "); let opponent = split_str.next().unwrap(); let me = split_str.next().unwrap(); Round { opponent: match opponent { "A" => Shape::Rock, "B" => Shape::Paper, "C" => Shape::Scissors, _ => panic!("invalid choice"), }, me: match me { "X" => Shape::Rock, "Y" => Shape::Paper, "Z" => Shape::Scissors, _ => panic!("invalid choice"), }, } }) .collect() } fn main() -> Result<()> { tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .with_target(false) .with_file(true) .with_line_number(true) .without_time() .compact() .init(); color_eyre::install()?; let input = parse_input("inputs/day02.txt"); info!("Part 1: {}", part_1(&input)); info!("Part 2: {}", part_2(&input)); Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_part_1() { let sample = parse_input("samples/day02.txt"); assert_eq!(part_1(&sample), 15); } #[test] fn test_part_2() { let sample = parse_input("samples/day02.txt"); assert_eq!(part_2(&sample), 12); } }