From 5bf6913682d8ea677aa9be0a82b7d585b588b71e Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Thu, 7 Dec 2023 16:27:03 +0200 Subject: Day 7 --- 2023/src/bin/day_7.rs | 168 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 159 insertions(+), 9 deletions(-) (limited to '2023/src/bin') diff --git a/2023/src/bin/day_7.rs b/2023/src/bin/day_7.rs index b3a610b..3b629bd 100644 --- a/2023/src/bin/day_7.rs +++ b/2023/src/bin/day_7.rs @@ -1,19 +1,169 @@ -use nom::IResult; -use std::fs; +use nom::{ + branch::alt, + character::complete::{char as nom_char, line_ending, space1, u32 as nom_u32}, + combinator::{map, value}, + multi::separated_list1, + sequence::tuple, + IResult, +}; +use std::{collections::BTreeMap, fs}; fn main() -> Result<(), Box> { - let input = fs::read_to_string("inputs/day_2.txt")?; - let parsed = Example::parser(&input).unwrap().1; - dbg!(&parsed); + let input = fs::read_to_string("inputs/day_7.txt")?; + + { + let mut games_without_jokers = CardGame::parser(false)(&input).unwrap().1; + games_without_jokers.sort(); + dbg!(games_without_jokers.calculate_winnings()); + } + + { + let mut games_with_jokers = CardGame::parser(true)(&input).unwrap().1; + games_with_jokers.sort(); + dbg!(games_with_jokers.calculate_winnings()); + } Ok(()) } #[derive(Debug)] -struct Example; +struct CardGame(Vec); + +#[derive(Debug, PartialEq, Eq)] +struct CardHand { + cards: [Card; 5], + bid: u32, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +struct Card(u8); + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +enum CardRank { + HighCard, + OnePair, + TwoPair, + ThreeOfAKind, + FullHouse, + FourOfAKind, + FiveOfAKind, +} + +impl CardGame { + fn parser(with_jokers: bool) -> impl FnMut(&str) -> IResult<&str, Self> { + move |input: &str| { + map( + separated_list1(line_ending, CardHand::parser(with_jokers)), + CardGame, + )(input) + } + } + + fn sort(&mut self) { + self.0.sort() + } + + fn calculate_winnings(&self) -> u32 { + self.0 + .iter() + .enumerate() + .map(|(i, hand)| hand.bid * (i as u32 + 1)) + .sum() + } +} + +impl CardHand { + fn parser(with_jokers: bool) -> impl FnMut(&str) -> IResult<&str, Self> { + move |input: &str| { + map( + tuple(( + Card::parser(with_jokers), + Card::parser(with_jokers), + Card::parser(with_jokers), + Card::parser(with_jokers), + Card::parser(with_jokers), + space1, + nom_u32, + )), + |(c1, c2, c3, c4, c5, _, bid)| CardHand { + cards: [c1, c2, c3, c4, c5], + bid, + }, + )(input) + } + } + + fn card_rank(&self) -> CardRank { + let mut cards_set: BTreeMap = BTreeMap::new(); + for card in &self.cards { + *cards_set.entry(*card).or_insert(0) += 1; + } + + let jokers = cards_set.get(&Card(1)).cloned().unwrap_or(0); + cards_set.remove(&Card(1)); + + let mut card_counts: Vec = cards_set.into_values().collect(); + card_counts.sort_by(|a, b| b.cmp(a)); + + if card_counts.len() == 0 { + // all 5 were jokers! + CardRank::FiveOfAKind + } else { + card_counts[0] += jokers; + if card_counts[0] == 5 { + CardRank::FiveOfAKind + } else if card_counts[0] == 4 { + CardRank::FourOfAKind + } else if card_counts[0] == 3 && card_counts[1] == 2 { + CardRank::FullHouse + } else if card_counts[0] == 3 { + CardRank::ThreeOfAKind + } else if card_counts[0] == 2 && card_counts[1] == 2 { + CardRank::TwoPair + } else if card_counts[0] == 2 { + CardRank::OnePair + } else { + CardRank::HighCard + } + } + } +} + +impl Card { + fn parser(with_jokers: bool) -> impl FnMut(&str) -> IResult<&str, Self> { + move |input: &str| { + map( + alt(( + value(2, nom_char('2')), + value(3, nom_char('3')), + value(4, nom_char('4')), + value(5, nom_char('5')), + value(6, nom_char('6')), + value(7, nom_char('7')), + value(8, nom_char('8')), + value(9, nom_char('9')), + value(10, nom_char('T')), + value(if with_jokers { 1 } else { 11 }, nom_char('J')), + value(12, nom_char('Q')), + value(13, nom_char('K')), + value(14, nom_char('A')), + )), + Card, + )(input) + } + } +} + +impl std::cmp::Ord for CardHand { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.card_rank() + .cmp(&other.card_rank()) + .then(self.cards.cmp(&other.cards)) + } +} -impl Example { - fn parser(_input: &str) -> IResult<&str, Self> { - todo!() +impl std::cmp::PartialOrd for CardHand { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } -- cgit v1.2.3