use std::{fmt::Display, cmp::Ordering}; use itertools::Itertools; use crate::day_solver::DaySolver; #[cfg(test)] use crate::util::read_file; const CARD_ORDER: [char; 13] = ['A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2']; pub struct Day7 { hands: Vec } pub struct Hand { cards: [char; 5], bid: usize } impl Display for Hand { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} {}", self.cards.iter().collect::(), self.bid) } } impl Hand { fn parse(input: &str) -> Self { let (cards_str, bid_str) = input.split_once(" ").unwrap(); return Hand { cards: cards_str.chars().collect::>().try_into().unwrap(), bid: bid_str.parse().unwrap() } } fn calc_type(&self) -> u8 { let mut found_three_of_kind = false; let mut found_pair = false; for unique_card in self.cards.iter().unique() { let card_appearances = self.cards.iter().filter(|c| c == &unique_card).count(); if card_appearances == 5 { return FIVE_OF_A_KIND; } else if card_appearances == 4 { return FOUR_OF_A_KIND; } else if card_appearances == 3 { found_three_of_kind = true; } else if card_appearances == 2 { if found_pair { return TWO_PAIR } else { found_pair = true } } } if found_three_of_kind && found_pair { return FULL_HOUSE; } else if found_three_of_kind { return THREE_OF_A_KIND; } else if found_pair { return ONE_PAIR; } return HIGH_CARD; } // Calculates the maximum type, given that "J" is a joker fn calc_max_type(&self) -> u8 { if !self.cards.contains(&'J') { return self.calc_type(); } let mut max_type = self.calc_type(); if max_type == FIVE_OF_A_KIND { return max_type }; // It only makes sense to try to map jokers to any of the other cards we have let owned_cards: Vec<&char> = self.cards.iter().filter(|c| c != &&'J').unique().collect(); for i in 0..5 { if self.cards[i] == 'J' { for c in &owned_cards { let mut new_cards = self.cards.clone(); new_cards[i] = *c.to_owned(); let new_hand = Hand { cards: new_cards, bid: 0}; let j_type = new_hand.calc_max_type(); // println!("Trying hand {} as {}, got type {}", self, new_hand, j_type); if j_type > max_type { max_type = j_type; if max_type == FIVE_OF_A_KIND { return max_type } } } } } return max_type; } } const FIVE_OF_A_KIND: u8 = 64; const FOUR_OF_A_KIND: u8 = 32; const FULL_HOUSE: u8 = 16; const THREE_OF_A_KIND: u8 = 8; const TWO_PAIR: u8 = 4; const ONE_PAIR: u8 = 2; const HIGH_CARD: u8 = 0; impl Day7 { pub fn create(input: String) -> Self { // let lines = input.lines(); // Put the input into the day struct return Day7 { hands: input.lines().map(Hand::parse).collect() } } } fn card_rank(card: &char) -> usize { for i in 0..CARD_ORDER.len() { if &CARD_ORDER[i] == card { return i; } } CARD_ORDER.len() } fn compare_card(a: &char, b: &char) -> Ordering { return card_rank(b).partial_cmp(&card_rank(a)).unwrap(); } fn compare_card_pt2(a: &char, b: &char) -> Ordering { if a == &'J' && b == &'J' { return Ordering::Equal } else if a == &'J' { return Ordering::Less } else if b == &'J' { return Ordering::Greater } else { return card_rank(b).partial_cmp(&card_rank(a)).unwrap(); } } impl DaySolver for Day7 { fn solve_part1(&mut self) -> String { let mut hands_and_types: Vec<(&Hand, u8)> = self.hands.iter() .map(|h| (h, h.calc_type())) .collect(); hands_and_types.sort_by(|a, b| { if a.1 != b.1 { return a.1.partial_cmp(&b.1).unwrap(); } else { for i in 0usize..5 { let card_comp = compare_card(&a.0.cards[i], &b.0.cards[i]); if card_comp.is_ne() { return card_comp; } } return Ordering::Equal; } }); return hands_and_types.iter().map(|(h,_)|h).enumerate() .map(|(r, h)| (r + 1) * h.bid) .sum::() .to_string(); } fn solve_part2(&mut self) -> String { let mut hands_and_max_types: Vec<(&Hand, u8)> = self.hands.iter() .map(|h| (h, h.calc_max_type())) .collect(); hands_and_max_types.sort_by(|a, b| { if a.1 != b.1 { return a.1.partial_cmp(&b.1).unwrap(); } else { for i in 0..5 { let card_comp = compare_card_pt2(&a.0.cards[i], &b.0.cards[i]); if card_comp.is_ne() { return card_comp; } } return Ordering::Equal; } }); // for (hand, max_type) in &hands_and_max_types { // println!("Hand {} with type {}", hand, max_type); // } return hands_and_max_types.iter().map(|(h,_)|h).enumerate() .map(|(r, h)| (r + 1) * h.bid) .sum::() .to_string(); } } #[test] fn test_part1() { let mut day = Day7::create(read_file("input/day07_example.txt")); assert_eq!("6440", day.solve_part1()); } #[test] fn test_part2() { let mut day = Day7::create(read_file("input/day07_example.txt")); assert_eq!("5905", day.solve_part2()); }