From 8a15b6271057fcfcd3486fb72d319fa7f3bc6041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20K=C3=B6rber?= Date: Sat, 2 Dec 2023 12:03:21 +0100 Subject: [PATCH] Add day 2 --- 2023/day2/.gitignore | 1 + 2023/day2/Cargo.lock | 7 ++ 2023/day2/Cargo.toml | 8 ++ 2023/day2/example_01 | 5 ++ 2023/day2/example_02 | 5 ++ 2023/day2/src/main.rs | 168 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 194 insertions(+) create mode 100644 2023/day2/.gitignore create mode 100644 2023/day2/Cargo.lock create mode 100644 2023/day2/Cargo.toml create mode 100644 2023/day2/example_01 create mode 100644 2023/day2/example_02 create mode 100644 2023/day2/src/main.rs diff --git a/2023/day2/.gitignore b/2023/day2/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/2023/day2/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/2023/day2/Cargo.lock b/2023/day2/Cargo.lock new file mode 100644 index 0000000..63c2f60 --- /dev/null +++ b/2023/day2/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "day2" +version = "0.1.0" diff --git a/2023/day2/Cargo.toml b/2023/day2/Cargo.toml new file mode 100644 index 0000000..8aa34bb --- /dev/null +++ b/2023/day2/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day2" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/2023/day2/example_01 b/2023/day2/example_01 new file mode 100644 index 0000000..295c36d --- /dev/null +++ b/2023/day2/example_01 @@ -0,0 +1,5 @@ +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 diff --git a/2023/day2/example_02 b/2023/day2/example_02 new file mode 100644 index 0000000..295c36d --- /dev/null +++ b/2023/day2/example_02 @@ -0,0 +1,5 @@ +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 diff --git a/2023/day2/src/main.rs b/2023/day2/src/main.rs new file mode 100644 index 0000000..d8e0339 --- /dev/null +++ b/2023/day2/src/main.rs @@ -0,0 +1,168 @@ +use std::collections::HashMap; + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum Color { + Red, + Green, + Blue, +} + +impl TryFrom<&str> for Color { + type Error = String; + + fn try_from(value: &str) -> Result { + match value { + "red" => Ok(Self::Red), + "green" => Ok(Self::Green), + "blue" => Ok(Self::Blue), + _ => Err(format!("invalid color \"{value}\"")), + } + } +} + +#[derive(Debug)] +struct Game { + id: u32, + draws: Vec>, +} + +impl Game { + fn parse(line: &str) -> Result { + let mut list_of_draws: Vec> = vec![]; + + let (gameinfo, draws) = line + .split_once(':') + .ok_or("line did not contain : delimiter")?; + + let id = { + let (game, id) = gameinfo + .split_once(' ') + .ok_or("gameinfo did not contain a space")?; + if game != "Game" { + return Err(format!("did not contain \"Game\" prefix, found \"{game}\"")); + } + id.parse::() + .map_err(|e| format!("could not parse game id \"{id}\": {e}"))? + }; + + for draw in draws.split(";").map(|s| s.trim()) { + let mut colors: HashMap = HashMap::new(); + for color in draw.split(",").map(|s| s.trim()) { + let (count, color) = color + .split_once(" ") + .ok_or("count and color were not separated by space")?; + let color: Color = color.try_into()?; + let count = count + .parse::() + .map_err(|e| format!("could not parse color count \"{count}\": {e}"))?; + + // insert returns Some(old_value) if the value as already present, this + // is treated as an error + if let Some(_) = colors.insert(color, count) { + return Err("color seen more than once".into()); + } + } + list_of_draws.push(colors); + } + Ok(Self { + draws: list_of_draws, + id, + }) + } + + fn minimum_cube_count(&self) -> HashMap { + let mut required_cubes: HashMap = HashMap::new(); + for draw in &self.draws { + for (color, draw_count) in draw { + match required_cubes.get(color) { + Some(required_cube_count) if required_cube_count < draw_count => { + required_cubes.insert(*color, *draw_count); + } + None => { + required_cubes.insert(*color, *draw_count); + } + _ => (), + } + } + } + required_cubes + } +} + +fn parse_input(input: &str) -> Result, String> { + input + .lines() + .map(|line| line.trim()) + .map(|line| Game::parse(line)) + .collect::, String>>() +} + +fn count_possible_games(games: &Vec, limits: &HashMap) -> Result { + Ok(games + .iter() + .filter_map(|game| { + game.draws + .iter() + .map(|draw| { + limits + .iter() + .map(|(limit_color, limit_count)| { + draw.get(limit_color).unwrap_or(&0) <= limit_count + }) + .all(|possible| possible) + }) + .all(|possible| possible) + .then_some(game.id) + }) + .sum()) +} + +fn minimum_cube_powered(games: &Vec) -> Result { + Ok(games + .into_iter() + .map(|game| game.minimum_cube_count()) + .map(|counts| counts.into_values().product::()) + .sum()) +} + +fn limits() -> HashMap { + HashMap::from([(Color::Red, 12), (Color::Green, 13), (Color::Blue, 14)]) +} + +fn main() -> Result<(), String> { + let input = std::fs::read_to_string("./input").unwrap(); + + let input = parse_input(&input)?; + let count = count_possible_games(&input, &limits())?; + + println!("part 1 : {count}"); + + let power = minimum_cube_powered(&input)?; + + println!("part 2 : {power}"); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn example_01() { + let input = std::fs::read_to_string("./example_01").unwrap(); + assert_eq!( + count_possible_games(&parse_input(&input).unwrap(), &super::limits()).unwrap(), + 8 + ); + } + + #[test] + fn example_02() { + let input = std::fs::read_to_string("./example_02").unwrap(); + assert_eq!( + minimum_cube_powered(&parse_input(&input).unwrap()).unwrap(), + 2286 + ); + } +}