diff options
author | Toby Vincent <tobyv@tobyvin.dev> | 2023-12-03 01:28:04 -0600 |
---|---|---|
committer | Toby Vincent <tobyv@tobyvin.dev> | 2023-12-03 01:28:04 -0600 |
commit | d404a563cab9fe05e437e2ed04ffe9fe56070a32 (patch) | |
tree | 7e013bc670ed41393d0e59f7d44e6daec1ef596d /src | |
parent | 8525793927ad76a5ffa4a5f522b9a80c1a637644 (diff) |
feat: impl day 2
Diffstat (limited to 'src')
-rw-r--r-- | src/day_02.rs | 163 | ||||
-rw-r--r-- | src/lib.rs | 15 | ||||
-rw-r--r-- | src/main.rs | 3 |
3 files changed, 174 insertions, 7 deletions
diff --git a/src/day_02.rs b/src/day_02.rs new file mode 100644 index 0000000..30607b6 --- /dev/null +++ b/src/day_02.rs @@ -0,0 +1,163 @@ +use std::str::FromStr; + +use crate::{Problem, Solution}; + +pub struct Day02; + +impl Problem for Day02 { + const DAY: u8 = 2; + + const INPUT: &'static str = include_str!("../input/day_02.txt"); +} + +impl Solution for Day02 { + type Answer1 = usize; + + type Answer2 = usize; + + fn part_1(input: &str) -> anyhow::Result<Self::Answer1> { + // only 12 red cubes, 13 green cubes, and 14 blue cubes + const MAX_SET: CubeSet = CubeSet { + red: 12, + green: 13, + blue: 14, + }; + + Ok(input + .trim() + .split('\n') + .map(Game::from_str) + .try_collect::<Vec<_>>()? + .iter() + .filter_map(|s| s.is_valid(&MAX_SET).then_some(s.id)) + .sum()) + } + + fn part_2(input: &str) -> anyhow::Result<Self::Answer2> { + Ok(input + .trim() + .split('\n') + .map(Game::from_str) + .try_collect::<Vec<_>>()? + .into_iter() + .map(Game::min_set) + .try_collect::<Vec<_>>()? + .iter() + .map(CubeSet::power) + .sum()) + } +} + +#[derive(Debug, Default)] +struct Game { + id: usize, + sets: Vec<CubeSet>, +} + +impl Game { + fn is_valid(&self, max_set: &CubeSet) -> bool { + self.sets.iter().all(|c| c.is_valid(max_set)) + } + + fn min_set(self) -> Result<CubeSet, anyhow::Error> { + self.sets + .into_iter() + .reduce(|acc, c| c.maximize(acc)) + .ok_or(anyhow::format_err!("No sets found in game")) + } +} + +impl FromStr for Game { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let (id, round) = s + .trim() + .split_once(':') + .ok_or(anyhow::format_err!("Invalid game format"))?; + + let id = id + .strip_prefix("Game ") + .ok_or(anyhow::format_err!("Invalid game format"))? + .parse()?; + + let round = round + .trim() + .split(';') + .map(CubeSet::from_str) + .collect::<Result<_, _>>()?; + + Ok(Game { id, sets: round }) + } +} + +#[derive(Debug, Default)] +struct CubeSet { + red: u16, + green: u16, + blue: u16, +} + +impl CubeSet { + fn is_valid(&self, CubeSet { red, green, blue }: &CubeSet) -> bool { + &self.red <= red && &self.green <= green && &self.blue <= blue + } + + fn maximize(&self, CubeSet { red, green, blue }: CubeSet) -> CubeSet { + CubeSet { + red: self.red.max(red), + green: self.green.max(green), + blue: self.blue.max(blue), + } + } + + fn power(&self) -> usize { + (self.red * self.green * self.blue) as usize + } +} + +impl FromStr for CubeSet { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + s.trim().split(',').try_fold(Self::default(), |mut acc, s| { + let (n, c) = s + .trim() + .split_once(' ') + .ok_or(anyhow::format_err!("Invalid cube format: {s}"))?; + + let n = u16::from_str(n)?; + match c { + "red" => acc.red += n, + "green" => acc.green += n, + "blue" => acc.blue += n, + _ => anyhow::bail!("Invalid cube color"), + }; + + Ok(acc) + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT: &str = indoc::indoc! {" + Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green + Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue + Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red + Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red + Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green + "}; + + #[test] + fn test_part_1() -> anyhow::Result<()> { + Ok(assert_eq!(8, Day02::part_1(INPUT)?)) + } + + #[test] + fn test_part_2() -> anyhow::Result<()> { + Ok(assert_eq!(2286, Day02::part_2(INPUT)?)) + } +} @@ -1,4 +1,13 @@ +#![feature(iterator_try_collect)] + pub mod day_01; +pub mod day_02; + +pub trait Problem { + const DAY: u8; + + const INPUT: &'static str; +} pub trait Solution: Problem { type Answer1: std::fmt::Display + Default; @@ -19,9 +28,3 @@ pub trait Solution: Problem { Ok(()) } } - -pub trait Problem { - const DAY: u8; - - const INPUT: &'static str; -} diff --git a/src/main.rs b/src/main.rs index 689a756..4242965 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ -use aoc_2023::{day_01::Day01, Solution}; +use aoc_2023::{day_01::Day01, day_02::Day02, Solution}; fn main() -> anyhow::Result<()> { Day01::solve()?; + Day02::solve()?; Ok(()) } |