advent-of-code/2023/day2/rust/src/main.rs

116 lines
2.5 KiB
Rust
Raw Normal View History

2023-12-02 13:55:56 +01:00
#![warn(clippy::pedantic)]
use std::{cmp::max, io::stdin, str::FromStr};
use enum_map::{enum_map, Enum, EnumMap};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Enum)]
enum Colour {
Red,
Green,
Blue,
}
impl FromStr for Colour {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"red" => Ok(Self::Red),
"green" => Ok(Self::Green),
"blue" => Ok(Self::Blue),
_ => Err(()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Draw {
cubes: EnumMap<Colour, usize>,
}
impl Draw {
pub fn is_valid(&self) -> bool {
let max_values = enum_map! {
Colour::Red => 12,
Colour::Green => 13,
Colour::Blue => 14,
};
self.cubes.iter().all(|(c, n)| *n <= max_values[c])
}
}
impl FromStr for Draw {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let cubes = s
.split(", ")
.map(|c| {
let (n, colour) = c.split_once(' ').unwrap();
let n = n.parse().unwrap();
let colour = colour.parse().unwrap();
(colour, n)
})
.collect();
Ok(Draw { cubes })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Game {
draws: Vec<Draw>,
}
impl Game {
pub fn is_valid(&self) -> bool {
self.draws.iter().all(Draw::is_valid)
}
pub fn minimum_cubes_power(&self) -> usize {
self.draws
.iter()
.map(|draw| draw.cubes)
.fold(EnumMap::default(), |mut acc, cubes| {
for (c, n) in cubes {
acc[c] = max(acc[c], n);
}
acc
})
.values()
.product()
}
}
impl FromStr for Game {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (_, draws) = s.split_once(": ").unwrap();
let draws = draws
.split("; ")
.map(|draw| draw.parse().unwrap())
.collect();
Ok(Game { draws })
}
}
fn main() {
let games: Vec<Game> = stdin()
.lines()
.map(|l| l.unwrap().parse().unwrap())
.collect();
let valid_game_ids = games
.iter()
.enumerate()
.filter_map(|(i, g)| g.is_valid().then_some(i + 1));
println!("{}", valid_game_ids.sum::<usize>());
let powers = games.iter().map(Game::minimum_cubes_power);
println!("{}", powers.sum::<usize>());
}