1
0
Fork 0
2022/src/bin/day02.rs

154 lines
3.3 KiB
Rust
Raw Normal View History

use std::str::FromStr;
use aoc_2022::*;
type Input = Vec<Round>;
type Output = i32;
#[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,
}
}
}
impl FromStr for Shape {
type Err = Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err(eyre!("empty string given"));
}
match s {
"A" | "X" => Ok(Shape::Rock),
"B" | "Y" => Ok(Shape::Paper),
"C" | "Z" => Ok(Shape::Scissors),
_ => Err(eyre!("unknown shape {0}", s)),
}
}
}
#[derive(Debug, Clone, Copy)]
enum Outcome {
Lose,
Draw,
Win,
}
#[derive(Debug, Clone, Copy)]
struct Round {
opponent: Shape,
me: Shape,
}
impl FromStr for Round {
type Err = Report;
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())
}
}
struct Day02;
impl Solution<Input, Output> for Day02 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
file_to_structs(pathname)
}
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 main() -> Result<()> {
Day02::main()
}
test_sample!(day_02, Day02, 15, 12);