diff --git a/samples/day02.txt b/samples/day02.txt new file mode 100644 index 0000000..25097e8 --- /dev/null +++ b/samples/day02.txt @@ -0,0 +1,3 @@ +A Y +B X +C Z \ No newline at end of file diff --git a/src/bin/day02.rs b/src/bin/day02.rs new file mode 100644 index 0000000..153648a --- /dev/null +++ b/src/bin/day02.rs @@ -0,0 +1,156 @@ +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); + } +}