2022-12-02 11:13:13 +01:00
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
2022-12-02 10:24:20 +01:00
|
|
|
|
use aoc_2022::*;
|
|
|
|
|
|
2022-12-08 22:43:34 +01:00
|
|
|
|
type Input = Vec<Round>;
|
|
|
|
|
type Output = i32;
|
2022-12-02 10:24:20 +01:00
|
|
|
|
|
2022-12-02 15:20:48 +01:00
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
2022-12-02 10:24:20 +01:00
|
|
|
|
enum Shape {
|
|
|
|
|
Rock,
|
|
|
|
|
Paper,
|
|
|
|
|
Scissors,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Shape {
|
|
|
|
|
fn score(&self) -> i32 {
|
|
|
|
|
match self {
|
|
|
|
|
Shape::Rock => 1,
|
|
|
|
|
Shape::Paper => 2,
|
|
|
|
|
Shape::Scissors => 3,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 11:13:13 +01:00
|
|
|
|
impl FromStr for Shape {
|
2022-12-07 22:56:22 +01:00
|
|
|
|
type Err = Report;
|
2022-12-02 11:13:13 +01:00
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
if s.is_empty() {
|
2022-12-07 22:56:22 +01:00
|
|
|
|
return Err(eyre!("empty string given"));
|
2022-12-02 11:13:13 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match s {
|
|
|
|
|
"A" | "X" => Ok(Shape::Rock),
|
|
|
|
|
"B" | "Y" => Ok(Shape::Paper),
|
|
|
|
|
"C" | "Z" => Ok(Shape::Scissors),
|
2022-12-07 22:56:22 +01:00
|
|
|
|
_ => Err(eyre!("unknown shape ‹{0}›", s)),
|
2022-12-02 11:13:13 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 10:24:20 +01:00
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
|
enum Outcome {
|
|
|
|
|
Lose,
|
|
|
|
|
Draw,
|
|
|
|
|
Win,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
|
struct Round {
|
|
|
|
|
opponent: Shape,
|
|
|
|
|
me: Shape,
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 11:13:13 +01:00
|
|
|
|
impl FromStr for Round {
|
2022-12-07 22:56:22 +01:00
|
|
|
|
type Err = Report;
|
2022-12-02 11:13:13 +01:00
|
|
|
|
|
|
|
|
|
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?,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 15:20:48 +01:00
|
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 10:24:20 +01:00
|
|
|
|
impl Round {
|
|
|
|
|
fn score(&self) -> i32 {
|
|
|
|
|
let shape_score = self.me.score();
|
2022-12-02 15:20:48 +01:00
|
|
|
|
let result_score = find_result(&(self.me, self.opponent));
|
2022-12-02 10:24:20 +01:00
|
|
|
|
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 {
|
2022-12-02 15:20:48 +01:00
|
|
|
|
find_result_for_outcome(self.opponent, self.expected_outcome())
|
2022-12-02 10:24:20 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-08 22:43:34 +01:00
|
|
|
|
struct Day02;
|
|
|
|
|
impl Solution<Input, Output> for Day02 {
|
|
|
|
|
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
|
|
|
|
|
file_to_structs(pathname)
|
|
|
|
|
}
|
2022-12-02 10:24:20 +01:00
|
|
|
|
|
2022-12-08 22:43:34 +01:00
|
|
|
|
fn part_1(input: &Input) -> Output {
|
|
|
|
|
input.iter().map(Round::score).sum()
|
|
|
|
|
}
|
2022-12-02 10:24:20 +01:00
|
|
|
|
|
2022-12-08 22:43:34 +01:00
|
|
|
|
fn part_2(input: &Input) -> Output {
|
|
|
|
|
input.iter().map(Round::expected_score).sum()
|
|
|
|
|
}
|
2022-12-02 10:24:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
2022-12-08 22:43:34 +01:00
|
|
|
|
Day02::main()
|
2022-12-02 10:24:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-08 22:43:34 +01:00
|
|
|
|
test_sample!(day_02, Day02, 15, 12);
|