Skip to content

Instantly share code, notes, and snippets.

@Halkcyon
Created December 2, 2023 16:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Halkcyon/e652b2fb13dfb0bcf5dd5a60a9485bc4 to your computer and use it in GitHub Desktop.
Save Halkcyon/e652b2fb13dfb0bcf5dd5a60a9485bc4 to your computer and use it in GitHub Desktop.
AOC 2023 Day 02
#![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