pub fn run() { let input = include_str!("../input/day7.txt"); // let input = r#"32T3K 765 // T55J5 684 // KK677 28 // KTJJT 220 // QQQJA 483"#; dbg!(calculate_winnings::(input)); dbg!(calculate_winnings::(input)); } fn calculate_winnings(input: &str) -> usize { let mut hands_and_bids = parse::(input); hands_and_bids.sort(); let mut winnings = 0; for (i, (_, bid)) in hands_and_bids.iter().enumerate() { let rank = i + 1; winnings += rank * *bid as usize; } winnings } fn parse(s: &str) -> Vec<(Hand, u32)> { s.lines() .map(|l| { let (hand, bid) = l.trim().split_once(" ").unwrap(); (Hand::parse(hand), bid.parse().unwrap()) }) .collect() } trait JRule: Sized + Eq + std::fmt::Debug { fn cmp_card(l: CardType, r: CardType) -> std::cmp::Ordering; fn modify_counts(counts: &mut [u8; 14]); } #[derive(PartialEq, Eq, Debug)] struct Jack; impl JRule for Jack { fn cmp_card(l: CardType, r: CardType) -> std::cmp::Ordering { (l as usize).cmp(&(r as usize)) } fn modify_counts(_counts: &mut [u8; 14]) {} } #[derive(PartialEq, Eq, Debug)] struct Joker; impl JRule for Joker { fn cmp_card(l: CardType, r: CardType) -> std::cmp::Ordering { let l = if l == CardType::J { 0 } else { l as usize }; let r = if r == CardType::J { 0 } else { r as usize }; l.cmp(&r) } fn modify_counts(counts: &mut [u8; 14]) { let joker_count = counts[CardType::J as usize]; counts[CardType::J as usize] = 0; let (max_idx, _) = counts.iter().enumerate().max_by_key(|(_i, v)| **v).unwrap(); counts[max_idx] += joker_count; } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct Card { card_type: CardType, j_rule: std::marker::PhantomData, } #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] enum CardType { Two = 1, Three, Four, Five, Six, Seven, Eight, Nine, Ten, J, Queen, King, Ace, } impl CardType { fn parse(c: char) -> Self { match c { '2' => Self::Two, '3' => Self::Three, '4' => Self::Four, '5' => Self::Five, '6' => Self::Six, '7' => Self::Seven, '8' => Self::Eight, '9' => Self::Nine, 'T' => Self::Ten, 'J' => Self::J, 'Q' => Self::Queen, 'K' => Self::King, 'A' => Self::Ace, _ => panic!("invalid char {}", c), } } } impl Card { fn from(t: CardType) -> Self { Self { card_type: t, j_rule: std::marker::PhantomData, } } } impl PartialOrd for Card { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Card { fn cmp(&self, other: &Self) -> std::cmp::Ordering { J::cmp_card(self.card_type, other.card_type) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct Hand([Card; 5]); #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] enum HandType { HighCard, OnePair, TwoPair, ThreeOfAKind, FullHouse, FourOfAKind, FiveOfAKind, } impl Hand { fn parse(s: &str) -> Self { assert_eq!(s.len(), 5); let cards = s .chars() .map(|c| Card::from(CardType::parse(c))) .collect::>() .try_into() .unwrap(); Self(cards) } fn counts(&self) -> [u8; 14] { let mut counts = [0; 14]; for c in self.0.iter() { counts[c.card_type as usize] += 1; } counts } fn hand_type(&self) -> HandType { let mut counts = self.counts(); J::modify_counts(&mut counts); if counts.contains(&5) { HandType::FiveOfAKind } else if counts.contains(&4) { HandType::FourOfAKind } else if counts.contains(&3) { if counts.contains(&2) { HandType::FullHouse } else { HandType::ThreeOfAKind } } else { let pair_count = counts.iter().filter(|x| **x == 2).count(); if pair_count == 2 { HandType::TwoPair } else if pair_count == 1 { HandType::OnePair } else { assert!(counts.iter().filter(|x| **x == 1).count() == 5); HandType::HighCard } } } } impl PartialOrd for Hand { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Hand { fn cmp(&self, other: &Self) -> std::cmp::Ordering { let self_type = self.hand_type(); let other_type = other.hand_type(); if self_type > other_type { std::cmp::Ordering::Greater } else if self_type < other_type { std::cmp::Ordering::Less } else { self.0 .iter() .zip(other.0.iter()) .map(|(a, b)| a.cmp(b)) .filter(|ord| !ord.is_eq()) .next() .unwrap() } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_card_order() { assert!(CardType::Two < CardType::Ace); } #[test] fn test_parse_hand() { assert_eq!( Hand::parse("234AK"), Hand::([ Card::from(CardType::Two), Card::from(CardType::Three), Card::from(CardType::Four), Card::from(CardType::Ace), Card::from(CardType::King) ]) ); } #[test] fn test_hand_type() { assert_eq!( Hand::::parse("22222").hand_type(), HandType::FiveOfAKind ); assert_eq!( Hand::::parse("K2222").hand_type(), HandType::FourOfAKind ); assert_eq!( Hand::::parse("KK222").hand_type(), HandType::FullHouse ); assert_eq!( Hand::::parse("QK222").hand_type(), HandType::ThreeOfAKind ); assert_eq!(Hand::::parse("QQ322").hand_type(), HandType::TwoPair); assert_eq!(Hand::::parse("QQ342").hand_type(), HandType::OnePair); assert_eq!(Hand::::parse("234AK").hand_type(), HandType::HighCard); } #[test] fn test_hand_cmp() { let five_of_a_kind = Hand::::parse("22222"); let four_of_a_kind = Hand::::parse("2222K"); let four_of_a_kind2 = Hand::::parse("2222A"); assert!(five_of_a_kind > four_of_a_kind); assert!(four_of_a_kind2 > four_of_a_kind); assert!(Hand::::parse("T55J5") < Hand::::parse("QQQJA")); } #[test] fn test_hand_type_joker() { assert_eq!( Hand::::parse("T55J5").hand_type(), HandType::FourOfAKind ); } #[test] fn test_hand_cmp_joker() { assert!(Hand::::parse("T55J5") < Hand::::parse("KTJJT")); } }