Day 2 complete

This commit is contained in:
Dylan Thies
2023-12-02 14:40:22 -05:00
parent ac6662445d
commit 5f9d9af50c
7 changed files with 433 additions and 0 deletions

13
day-2/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "day-2"
version.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom.workspace = true
itertools.workspace = true
log.workspace = true

4
day-2/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod part1;
pub use crate::part1::part1;
pub mod part2;
pub use crate::part2::part2;

12
day-2/src/main.rs Normal file
View File

@@ -0,0 +1,12 @@
#![warn(clippy::all, clippy::pedantic)]
use day_2::part1;
use day_2::part2;
fn main() {
let input = include_str!("./input.txt");
let part1_result = part1(input);
println!("part 1: {part1_result}");
let part2_result = part2(input);
println!("part 2: {part2_result}");
}

106
day-2/src/part1.rs Normal file
View File

@@ -0,0 +1,106 @@
use log::debug;
use nom::{
bytes::complete::tag,
character::complete::{self, newline},
multi::separated_list1,
sequence::{preceded, separated_pair},
};
#[derive(Debug)]
struct Round {
pub red_n: u32,
pub green_n: u32,
pub blue_n: u32,
}
#[derive(Debug)]
struct Game {
pub id: u32,
pub rounds: Vec<Round>,
}
impl Game {
fn to_part1(&self) -> Option<u32> {
if self
.rounds
.iter()
.find_map(|r| {
//TODO if inverted use find_map
if r.red_n > 12 || r.green_n > 13 || r.blue_n > 14 {
Some(self.id)
} else {
None
}
})
.is_some()
{
None
} else {
Some(self.id)
}
}
}
pub fn part1(input: &str) -> String {
let (_, games) = process_input(input).expect("there should be input");
debug!("{games:?}");
games
.iter()
.filter_map(Game::to_part1)
.sum::<u32>()
.to_string()
}
fn process_block(input: &str) -> nom::IResult<&str, (u32, String)> {
let (i, (cnt, color)) =
separated_pair(complete::u32, complete::space1, complete::alpha1)(input)?;
Ok((i, (cnt, color.to_owned())))
}
fn process_round(input: &str) -> nom::IResult<&str, Round> {
let (i, blocks) = separated_list1(tag(", "), process_block)(input)?;
let mut round = Round {
red_n: 0,
green_n: 0,
blue_n: 0,
};
for (cnt, color) in blocks {
match color.as_str() {
"red" => round.red_n = cnt,
"green" => round.green_n = cnt,
"blue" => round.blue_n = cnt,
_ => panic!("this should be a color name"),
};
}
Ok((i, round))
}
fn process_game(input: &str) -> nom::IResult<&str, Game> {
let (i, (id, rounds)) = separated_pair(
preceded(tag("Game "), complete::u32),
tag(": "),
separated_list1(tag("; "), process_round),
)(input)?;
Ok((i, Game { id, rounds }))
}
fn process_input(input: &str) -> nom::IResult<&str, Vec<Game>> {
separated_list1(newline, process_game)(input)
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "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 part1_works() {
let result = part1(INPUT);
assert_eq!(result, "8".to_string());
}
}

97
day-2/src/part2.rs Normal file
View File

@@ -0,0 +1,97 @@
use nom::{
bytes::complete::tag,
character::complete::{self, newline},
multi::separated_list1,
sequence::{preceded, separated_pair},
};
#[derive(Debug)]
struct Round {
pub red_n: u32,
pub green_n: u32,
pub blue_n: u32,
}
#[derive(Debug)]
struct Game {
pub _id: u32,
pub rounds: Vec<Round>,
}
impl Game {
fn to_power(&self) -> u64 {
let (r, g, b) = self.rounds.iter().fold((0_u64, 0_u64, 0_u64), |acc, x| {
let (mut val_r, mut val_g, mut val_b) = acc;
if u64::from(x.red_n) > acc.0 {
val_r = x.red_n.into();
}
if u64::from(x.green_n) > acc.1 {
val_g = x.green_n.into();
}
if u64::from(x.blue_n) > acc.2 {
val_b = x.blue_n.into();
}
(val_r, val_g, val_b)
});
r * g * b
}
}
pub fn part2(input: &str) -> String {
let (_, games) = process_input(input).expect("there should be input");
games.iter().map(Game::to_power).sum::<u64>().to_string()
}
fn process_block(input: &str) -> nom::IResult<&str, (u32, String)> {
let (i, (cnt, color)) =
separated_pair(complete::u32, complete::space1, complete::alpha1)(input)?;
Ok((i, (cnt, color.to_owned())))
}
fn process_round(input: &str) -> nom::IResult<&str, Round> {
let (i, blocks) = separated_list1(tag(", "), process_block)(input)?;
let mut round = Round {
red_n: 0,
green_n: 0,
blue_n: 0,
};
for (cnt, color) in blocks {
match color.as_str() {
"red" => round.red_n = cnt,
"green" => round.green_n = cnt,
"blue" => round.blue_n = cnt,
_ => panic!("this should be a color name"),
};
}
Ok((i, round))
}
fn process_game(input: &str) -> nom::IResult<&str, Game> {
let (i, (id, rounds)) = separated_pair(
preceded(tag("Game "), complete::u32),
tag(": "),
separated_list1(tag("; "), process_round),
)(input)?;
Ok((i, Game { _id: id, rounds }))
}
fn process_input(input: &str) -> nom::IResult<&str, Vec<Game>> {
separated_list1(newline, process_game)(input)
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "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 part2_works() {
let result = part2(INPUT);
assert_eq!(result, "2286".to_string());
}
}