1
0
Fork 0
2022/src/bin/day02.rs
Matej Focko 53cf875f6f
tests: rename to easily distinguish days
Signed-off-by: Matej Focko <mfocko@redhat.com>
2022-12-03 14:51:43 +01:00

201 lines
4.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::str::FromStr;
use aoc_2022::*;
use color_eyre::eyre::Result;
use itertools::Itertools;
use thiserror::Error;
use tracing::*;
use tracing_subscriber::EnvFilter;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Shape {
Rock,
Paper,
Scissors,
}
impl Shape {
fn score(&self) -> i32 {
match self {
Shape::Rock => 1,
Shape::Paper => 2,
Shape::Scissors => 3,
}
}
}
#[derive(Error, Debug)]
enum ShapeError {
#[error("empty string given")]
Empty,
#[error("unknown shape {0}")]
UnknownShape(String),
}
impl FromStr for Shape {
type Err = ShapeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err(ShapeError::Empty);
}
match s {
"A" | "X" => Ok(Shape::Rock),
"B" | "Y" => Ok(Shape::Paper),
"C" | "Z" => Ok(Shape::Scissors),
_ => Err(ShapeError::UnknownShape(s.to_string())),
}
}
}
#[derive(Debug, Clone, Copy)]
enum Outcome {
Lose,
Draw,
Win,
}
#[derive(Debug, Clone, Copy)]
struct Round {
opponent: Shape,
me: Shape,
}
#[derive(Error, Debug)]
enum RoundError {
#[error("invalid shape {0}")]
InvalidShape(#[from] ShapeError),
}
impl FromStr for Round {
type Err = RoundError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut split_str = s.split(' ');
let opponent = split_str.next().unwrap().parse::<Shape>();
let me = split_str.next().unwrap().parse::<Shape>();
Ok(Round {
opponent: opponent?,
me: me?,
})
}
}
fn find_strategy<P>(predicate: P) -> (usize, (Shape, Shape))
where
P: FnMut(&(usize, (Shape, Shape))) -> bool,
{
// R P S
// R 3 0 6
// P 6 3 0
// S 0 6 3
vec![
// Loss
(Shape::Rock, Shape::Paper),
(Shape::Paper, Shape::Scissors),
(Shape::Scissors, Shape::Rock),
// Draw
(Shape::Rock, Shape::Rock),
(Shape::Paper, Shape::Paper),
(Shape::Scissors, Shape::Scissors),
// Win
(Shape::Rock, Shape::Scissors),
(Shape::Paper, Shape::Rock),
(Shape::Scissors, Shape::Paper),
]
.into_iter()
.enumerate()
.find_or_first(predicate)
.unwrap()
}
fn find_result(strategy: &(Shape, Shape)) -> i32 {
3 * (find_strategy(|&(_, st)| st == *strategy).0 as i32 / 3)
}
fn find_result_for_outcome(opponent: Shape, outcome: Outcome) -> i32 {
let range = match outcome {
Outcome::Lose => 0..3,
Outcome::Draw => 3..6,
Outcome::Win => 6..9,
};
let (i, (shape, _)) = find_strategy(|&(i, (_, op))| range.contains(&i) && op == opponent);
3 * (i as i32 / 3) + shape.score()
}
impl Round {
fn score(&self) -> i32 {
let shape_score = self.me.score();
let result_score = find_result(&(self.me, self.opponent));
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 {
find_result_for_outcome(self.opponent, self.expected_outcome())
}
}
type Input = Vec<Round>;
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_structs(pathname)
}
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 day_02 {
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);
}
}