use std::{cmp::Ordering, collections::HashMap, iter::zip}; use nom::{ character::complete::{alphanumeric1, multispace1, u64}, multi::separated_list1, sequence::separated_pair, IResult, Parser, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum HandType { HighCard, OnePair, TwoPair, ThreeKind, FullHouse, FourKind, FiveKind, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum CardType { Two, Three, Four, Five, Six, Seven, Eight, Nine, T, Jack, Queen, King, Ace, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum CardType2 { Jack, Two, Three, Four, Five, Six, Seven, Eight, Nine, T, Queen, King, Ace, } fn parse_input(input: &str) -> IResult<&str, Vec<(String, u64)>> { let (input, result) = separated_list1( multispace1, separated_pair(alphanumeric1::<&str, _>, multispace1, u64) .map(|(hand, bid)| (hand.to_string(), bid)), )(input)?; Ok((input, result)) } fn classify_hand(hand: &String) -> HandType { use HandType::*; let mut card_numbers: HashMap = HashMap::new(); for c in hand.to_owned().chars() { card_numbers.insert( c, match card_numbers.get(&c) { Some(n) => n + 1, None => 1, }, ); } let mut card_numbers = card_numbers.values().collect::>(); card_numbers.sort_unstable(); match card_numbers[..] { [1, 1, 1, 1, 1] => HighCard, [1, 1, 1, 2] => OnePair, [1, 2, 2] => TwoPair, [1, 1, 3] => ThreeKind, [2, 3] => FullHouse, [1, 4] => FourKind, [5] => FiveKind, _ => unreachable!(), } } fn classify_hand2(hand: &String) -> HandType { use HandType::*; let mut card_numbers: HashMap = HashMap::new(); for c in hand.to_owned().chars() { card_numbers.insert( c, match card_numbers.get(&c) { Some(n) => n + 1, None => 1, }, ); } if let Some(jokers) = card_numbers.remove(&'J') { let mut card_numbers = card_numbers.values().collect::>(); card_numbers.sort_unstable(); let highest = card_numbers.pop().unwrap_or(&0) + jokers; card_numbers.push(&highest); match card_numbers[..] { [1, 1, 1, 2] => OnePair, [1, 1, 3] => ThreeKind, [2, 3] => FullHouse, [1, 4] => FourKind, [5] => FiveKind, _ => unreachable!(), } } else { let mut card_numbers = card_numbers.values().collect::>(); card_numbers.sort_unstable(); match card_numbers[..] { [1, 1, 1, 1, 1] => HighCard, [1, 1, 1, 2] => OnePair, [1, 2, 2] => TwoPair, [1, 1, 3] => ThreeKind, [2, 3] => FullHouse, [1, 4] => FourKind, [5] => FiveKind, _ => unreachable!(), } } } fn classify_card(card: char) -> CardType { use CardType::*; match card { 'A' => Ace, 'K' => King, 'Q' => Queen, 'J' => Jack, 'T' => T, '9' => Nine, '8' => Eight, '7' => Seven, '6' => Six, '5' => Five, '4' => Four, '3' => Three, '2' => Two, _ => unreachable!(), } } fn classify_card2(card: char) -> CardType2 { use CardType2::*; match card { 'A' => Ace, 'K' => King, 'Q' => Queen, 'T' => T, '9' => Nine, '8' => Eight, '7' => Seven, '6' => Six, '5' => Five, '4' => Four, '3' => Three, '2' => Two, 'J' => Jack, _ => unreachable!(), } } fn compare_hands(hand_1: &String, hand_2: &String) -> Ordering { let (hand_type_1, hand_type_2) = (classify_hand(hand_1), classify_hand(hand_2)); if hand_type_1 == hand_type_2 { for (card_1, card_2) in zip(hand_1.to_owned().chars(), hand_2.to_owned().chars()) { if card_1 != card_2 { return classify_card(card_1).cmp(&classify_card(card_2)); } } Ordering::Equal } else { hand_type_1.cmp(&hand_type_2) } } fn compare_hands2(hand_1: &String, hand_2: &String) -> Ordering { let (hand_type_1, hand_type_2) = (classify_hand2(hand_1), classify_hand2(hand_2)); if hand_type_1 == hand_type_2 { for (card_1, card_2) in zip(hand_1.to_owned().chars(), hand_2.to_owned().chars()) { if card_1 != card_2 { return classify_card2(card_1).cmp(&classify_card2(card_2)); } } Ordering::Equal } else { hand_type_1.cmp(&hand_type_2) } } fn solve_1(plays: &Vec<(String, u64)>) -> u64 { let mut plays = plays.clone(); plays.sort_unstable_by(|(hand_1, _bid1), (hand_2, _bid2)| compare_hands(hand_1, hand_2)); plays .into_iter() .enumerate() .map(|(rank, (_hand, bid))| (rank as u64 + 1) * bid) .sum() } fn solve_2(plays: &Vec<(String, u64)>) -> u64 { let mut plays = plays.clone(); plays.sort_unstable_by(|(hand_1, _bid1), (hand_2, _bid2)| compare_hands2(hand_1, hand_2)); plays .into_iter() .enumerate() .map(|(rank, (_hand, bid))| (rank as u64 + 1) * bid) .sum() } fn main() { println!("Hello, this is Patrick!"); let input_text = include_str!("../input.txt"); let (_, plays) = parse_input(input_text).unwrap(); println!("The total winnings are {}", solve_1(&plays)); println!("The new total winnings are {}", solve_2(&plays)); } #[cfg(test)] mod tests { use super::*; #[test] fn test_1() { let input_text = include_str!("../test_input.txt"); let (_, plays) = parse_input(input_text).unwrap(); assert_eq!(solve_1(&plays), 6440); } #[test] fn test_2() { let input_text = include_str!("../test_input.txt"); let (_, plays) = parse_input(input_text).unwrap(); assert_eq!(solve_2(&plays), 5905); } }