diff --git a/day-4/src/part1.rs b/day-4/src/part1.rs index fc84c0c..5ace076 100644 --- a/day-4/src/part1.rs +++ b/day-4/src/part1.rs @@ -1,68 +1,90 @@ #![warn(clippy::all, clippy::pedantic)] -use nom::{ bytes::complete::tag, +use nom::{ + bytes::complete::tag, character::complete, + multi::{fold_many1, separated_list1}, + sequence::{preceded, separated_pair, tuple}, IResult, - multi::separated_list1, - sequence::{preceded, separated_pair}}; +}; +use std::collections::HashSet; struct Card { pub _id: u8, - pub game_numbers: Vec, - pub my_numbers: Vec, + pub game_numbers: HashSet, + pub my_numbers: HashSet, } impl Card { - fn get_score(&self) -> Option{ - let count = self.my_numbers.iter().filter(|x| self.game_numbers.contains(x)).count(); + fn get_win_count(&self) -> usize { + self.my_numbers.intersection(&self.game_numbers).count() + } + fn get_score(&self) -> Option { + let count = self.get_win_count(); if count == 0 { None } else { - Some(2_usize.pow(count as u32- 1)) + Some(2_usize.pow(u32::try_from(count).expect("shouldn't have a lot of cards") - 1)) } } } +/// day 4 part 1 of aoc 2023 +/// +/// # Arguments +/// - input the input for today's puzzle +/// +/// # Panics +/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than +/// usize +#[must_use] pub fn part1(input: &str) -> String { let (_, cards) = parse_input(input).expect("there should be input"); - cards.iter().filter_map(Card::get_score).sum::().to_string() + cards + .iter() + .filter_map(Card::get_score) + .sum::() + .to_string() } -fn parse_num_list (input: &str) -> IResult<&str, Vec> { - separated_list1(complete::space1, complete::u8)(input) +fn parse_num_list(input: &str) -> IResult<&str, HashSet> { + fold_many1( + tuple((complete::u8, complete::space0)), + HashSet::new, + |mut acc, (x, _)| { + acc.insert(x); + acc + }, + )(input) } -fn parse_numbers(input: &str) -> IResult<&str, (Vec, Vec)> { - separated_pair(parse_num_list, barspace, parse_num_list)(input) -} - -fn barspace(input: &str) -> IResult<&str, () > { - let (i, _) = complete::space1(input)?; - let (i,_) = tag("|")(i)?; - let (i, _) = complete::space1(i)?; - Ok((i, ())) -} - -fn colonspace(input: &str) -> IResult<&str, () > { - let (i,_) = tag(":")(input)?; - let (i, _) = complete::space1(i)?; - Ok((i, ())) -} - -fn cardspace(input: &str) -> IResult<&str, () > { - let (input, _) = tag("Card")(input)?; - let (input, _) = complete::space1(input)?; - Ok((input, ())) +fn parse_numbers(input: &str) -> IResult<&str, (HashSet, HashSet)> { + separated_pair( + parse_num_list, + tuple((tag("|"), complete::space1)), + parse_num_list, + )(input) } fn parse_card(input: &str) -> IResult<&str, Card> { - let (input, (id, (my_numbers, game_numbers))) = separated_pair(preceded(cardspace, complete::u8), colonspace, parse_numbers )(input)?; + let (input, (id, (my_numbers, game_numbers))) = separated_pair( + preceded(tuple((tag("Card"), complete::space1)), complete::u8), + tuple((tag(":"), complete::space1)), + parse_numbers, + )(input)?; - Ok((input, Card{_id:id, my_numbers, game_numbers})) + Ok(( + input, + Card { + _id: id, + game_numbers, + my_numbers, + }, + )) } -fn parse_input(input:&str) -> IResult<&str, Vec> { - separated_list1(complete::newline, parse_card)(input) +fn parse_input(input: &str) -> IResult<&str, Vec> { + separated_list1(complete::line_ending, parse_card)(input) } #[cfg(test)] diff --git a/day-4/src/part2.rs b/day-4/src/part2.rs index 9c2bc66..a9e96f2 100644 --- a/day-4/src/part2.rs +++ b/day-4/src/part2.rs @@ -1,19 +1,121 @@ #![warn(clippy::all, clippy::pedantic)] -pub fn part2 (_input: &str) -> String { - "Not Finished".to_string() +use nom::{ + bytes::complete::tag, + character::complete, + combinator::map, + multi::{fold_many1, separated_list1}, + sequence::{preceded, separated_pair, tuple}, + IResult, +}; +use std::collections::{BTreeMap, HashSet}; + +struct Card { + pub id: usize, + pub game_numbers: HashSet, + pub my_numbers: HashSet, +} +impl Card { + fn get_win_count(&self) -> usize { + self.my_numbers.intersection(&self.game_numbers).count() + } +} + +/// day 4 part 1 of aoc 2023 +/// +/// # Arguments +/// - input the input for today's puzzle +/// +/// # Panics +/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than +/// usize +#[must_use] +pub fn part2(input: &str) -> String { + let (_, cards) = parse_input(input).expect("there should be input"); + let mut cards_had = BTreeMap::new(); + for card in cards { + if let Some(x) = cards_had.get_mut(&card.id) { + *x += 1; + } else { + cards_had.insert(card.id, 1); + } + let next_id = card.id + 1; + let last_winner = card.id + card.get_win_count(); + //println!("{} - {next_id} {last_winner}", card.id); + if last_winner < next_id { + continue; + } + let card_count = *cards_had.get(&card.id).expect("already should have cards"); + for id in next_id..=last_winner { + if let Some(x) = cards_had.get_mut(&id) { + *x += card_count; + } else { + cards_had.insert(id, card_count); + } + } + } + //println!("{cards_had:#?}"); + + cards_had.values().sum::().to_string() +} + +fn parse_num_list(input: &str) -> IResult<&str, HashSet> { + fold_many1( + tuple((complete::u8, complete::space0)), + HashSet::new, + |mut acc, (x, _)| { + acc.insert(x); + acc + }, + )(input) +} + +fn parse_numbers(input: &str) -> IResult<&str, (HashSet, HashSet)> { + separated_pair( + parse_num_list, + tuple((tag("|"), complete::space1)), + parse_num_list, + )(input) +} + +fn parse_card(input: &str) -> IResult<&str, Card> { + let (input, (id, (my_numbers, game_numbers))) = separated_pair( + preceded( + tuple((tag("Card"), complete::space1)), + map(complete::u8, usize::from), + ), + tuple((tag(":"), complete::space1)), + parse_numbers, + )(input)?; + + Ok(( + input, + Card { + id, + game_numbers, + my_numbers, + }, + )) +} + +fn parse_input(input: &str) -> IResult<&str, Vec> { + separated_list1(complete::line_ending, parse_card)(input) } #[cfg(test)] mod test { use super::*; - const INPUT: &str = ""; + const INPUT: &str = "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 +Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 +Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 +Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 +Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 +Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11"; #[test] fn part2_works() { let result = part2(INPUT); - assert_eq!(result, "Not Finished".to_string()); + assert_eq!(result, "30".to_string()); } } -