Created
December 2, 2023 16:59
-
-
Save Halkcyon/e652b2fb13dfb0bcf5dd5a60a9485bc4 to your computer and use it in GitHub Desktop.
AOC 2023 Day 02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![allow(dead_code)] | |
use std::{env::current_dir, fs}; | |
use nom::{IResult, bytes::complete::{tag, take_until, take_while1}, branch::alt, multi::separated_list0}; | |
pub fn main() -> color_eyre::Result<()> { | |
let pwd = current_dir()?; | |
let input = fs::read_to_string(pwd.join("input/y2023/d02.txt"))?; | |
let pull = Pull { red: 12, green: 13, blue: 14 }; | |
let games = input.split('\n').map(|g| { | |
game(g).unwrap().1 | |
}).collect::<Vec<Game>>(); | |
let sum = games.iter().filter( | |
|g| g.all_lt(&pull) | |
).map(|g| g.number).sum::<u32>(); | |
println!("{sum}"); | |
let total = games.iter().map(|g| { | |
g.guesses.minimum_pull().power() | |
}).sum::<u32>(); | |
println!("{total}"); | |
Ok(()) | |
} | |
#[derive(Debug, PartialEq, Eq)] | |
enum Cube { | |
Red(u32), | |
Green(u32), | |
Blue(u32), | |
} | |
#[derive(Debug, Default, PartialEq, Eq)] | |
struct Pull { | |
red: u32, | |
green: u32, | |
blue: u32, | |
} | |
impl Pull { | |
fn power(&self) -> u32 { | |
self.red * self.green * self.blue | |
} | |
} | |
impl PartialOrd for Pull { | |
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | |
if self.red > other.red || self.green > other.green || self.blue > other.blue { | |
return Some(std::cmp::Ordering::Greater); | |
} | |
(self.red, self.green, self.blue).partial_cmp(&(other.red, other.green, other.blue)) | |
} | |
} | |
#[derive(Debug, PartialEq, Eq)] | |
struct Guesses(Vec<Pull>); | |
impl Guesses { | |
fn minimum_pull(&self) -> Pull { | |
let mut red_max = 0; | |
let mut green_max = 0; | |
let mut blue_max = 0; | |
for &Pull { red, green, blue } in &self.0 { | |
if red > red_max { | |
red_max = red; | |
} | |
if green > green_max { | |
green_max = green; | |
} | |
if blue > blue_max { | |
blue_max = blue; | |
} | |
} | |
Pull { | |
red: red_max, | |
green: green_max, | |
blue: blue_max, | |
} | |
} | |
} | |
#[derive(Debug, PartialEq, Eq)] | |
struct Game { | |
number: u32, | |
guesses: Guesses, | |
} | |
impl Game { | |
fn all_lt(&self, input: &Pull) -> bool { | |
for pull in &self.guesses.0 { | |
if pull > input { | |
return false; | |
} | |
} | |
true | |
} | |
} | |
fn game(input: &str) -> IResult<&str, Game> { | |
let (rest, number) = game_number(input)?; | |
let (_, guesses) = guesses(rest)?; | |
Ok(("", Game { | |
number, | |
guesses, | |
})) | |
} | |
fn game_number(input: &str) -> IResult<&str, u32> { | |
let (rest, _) = tag("Game ")(input)?; | |
let (rest, number) = take_until(":")(rest)?; | |
let (rest, _) = tag(": ")(rest)?; | |
Ok((rest, number.parse().unwrap())) | |
} | |
fn red_cube(input: &str) -> IResult<&str, Cube> { | |
let (rest, count) = take_while1(|c: char| c.is_ascii_digit())(input)?; | |
let (rest, _) = tag(" red")(rest)?; | |
Ok((rest, Cube::Red(count.parse().unwrap()))) | |
} | |
fn green_cube(input: &str) -> IResult<&str, Cube> { | |
let (rest, count) = take_while1(|c: char| c.is_ascii_digit())(input)?; | |
let (rest, _) = tag(" green")(rest)?; | |
Ok((rest, Cube::Green(count.parse().unwrap()))) | |
} | |
fn blue_cube(input: &str) -> IResult<&str, Cube> { | |
let (rest, count) = take_while1(|c: char| c.is_ascii_digit())(input)?; | |
let (rest, _) = tag(" blue")(rest)?; | |
Ok((rest, Cube::Blue(count.parse().unwrap()))) | |
} | |
fn cube(input: &str) -> IResult<&str, Cube> { | |
alt((red_cube, green_cube, blue_cube))(input) | |
} | |
fn pull(input: &str) -> IResult<&str, Pull> { | |
let (rest, cubes) = separated_list0(tag(", "), cube)(input)?; | |
let mut pull = Pull::default(); | |
for cube in cubes { | |
match cube { | |
Cube::Red(count) => pull.red = count, | |
Cube::Green(count) => pull.green = count, | |
Cube::Blue(count) => pull.blue = count, | |
} | |
} | |
Ok((rest, pull)) | |
} | |
fn guesses(input: &str) -> IResult<&str, Guesses> { | |
let (rest, pulls) = separated_list0(tag("; "), pull)(input)?; | |
Ok((rest, Guesses(pulls))) | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
const SAMPLE_INPUT: &str = r#"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 expect_pull1_gt_pull2() { | |
assert!( | |
Pull { red: 20, green: 8, blue: 6 } > Pull { red: 12, green: 13, blue: 14 } | |
); | |
assert!( | |
Pull { red: 12, green: 15, blue: 6 } > Pull { red: 12, green: 13, blue: 14 } | |
); | |
assert!( | |
Pull { red: 12, green: 8, blue: 16 } > Pull { red: 12, green: 13, blue: 14 } | |
); | |
} | |
#[test] | |
fn expect_game_number_parses() { | |
let input = SAMPLE_INPUT.split('\n').next().unwrap(); | |
assert_eq!( | |
game_number(input), | |
Ok(("3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green", 1)), | |
); | |
} | |
#[test] | |
fn expect_cube_parses() { | |
assert_eq!( | |
cube("53 green"), | |
Ok(("", Cube::Green(53))), | |
); | |
} | |
#[test] | |
fn expect_pull_parses() { | |
assert_eq!( | |
pull("1 blue, 2 green"), | |
Ok(("", Pull { red: 0, green: 2, blue: 1 })), | |
); | |
} | |
#[test] | |
fn expect_guesses_parses() { | |
assert_eq!( | |
guesses("3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green"), | |
Ok(("", Guesses(vec![ | |
Pull { red: 4, blue: 3, ..Default::default() }, | |
Pull { red: 1, green: 2, blue: 6 }, | |
Pull { green: 2, ..Default::default() }, | |
]))), | |
); | |
} | |
#[test] | |
fn expect_game_parses() { | |
assert_eq!( | |
game("Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green"), | |
Ok(("", Game { | |
number: 1, | |
guesses: Guesses(vec![ | |
Pull { red: 4, blue: 3, ..Default::default() }, | |
Pull { red: 1, green: 2, blue: 6 }, | |
Pull { green: 2, ..Default::default() }, | |
]), | |
})), | |
); | |
} | |
#[test] | |
fn run_main() { | |
_ = main(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment