//! Day 2: Cube Conundrum use once_cell::sync::Lazy; use regex::Regex; pub const DAY: u8 = 2; pub const TITLE: &str = "Cube Conundrum"; #[derive(Debug)] struct Game { id: u32, draws: Vec, } #[derive(Debug, Default)] struct Draw { r: u32, g: u32, b: u32, } impl Draw { fn max(&self, other: &Draw) -> Self { Self { r: self.r.max(other.r), g: self.g.max(other.g), b: self.b.max(other.b), } } fn power(&self) -> u32 { self.r * self.g * self.b } } fn parse_games(input: Vec) -> Vec { static LINE: Lazy = Lazy::new(|| Regex::new(r"^Game (\d+): (.+)$").unwrap()); input .iter() .map(|line| { let cap = LINE.captures(line).expect(line); let id = cap[1].parse().unwrap(); let draws = cap[2] .split("; ") .map(|gstr| { let (mut r, mut g, mut b) = (0, 0, 0); for p in gstr.split(", ") { if let Some(n) = p.strip_suffix(" red") { r = n.parse().unwrap(); } else if let Some(n) = p.strip_suffix(" green") { g = n.parse().unwrap(); } else if let Some(n) = p.strip_suffix(" blue") { b = n.parse().unwrap(); } else { panic!("invalid part: {p}") } } Draw { r, g, b } }) .collect(); Game { id, draws } }) .collect() } pub fn part1(input: Vec) -> String { parse_games(input) .iter() .filter_map(|g| { if g.draws.iter().all(|d| d.r <= 12 && d.g <= 13 && d.b <= 14) { Some(g.id) } else { None } }) .sum::() .to_string() } pub fn part2(input: Vec) -> String { parse_games(input) .iter() .map(|g| { g.draws .iter() .fold(Draw::default(), |acc, d| acc.max(d)) .power() }) .sum::() .to_string() } #[cfg(test)] mod tests { use super::*; #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "8"); } #[test] fn t_part1() { assert_eq!(part1(crate::read_input(DAY)), "2006"); } #[test] fn t_example2() { assert_eq!(part2(crate::read_example(DAY, 2)), "2286"); } #[test] fn t_part2() { assert_eq!(part2(crate::read_input(DAY)), "84911"); } }