diff --git a/samples/day07.txt b/samples/day07.txt new file mode 100644 index 0000000..e3500c3 --- /dev/null +++ b/samples/day07.txt @@ -0,0 +1,5 @@ +32T3K 765 +T55J5 684 +KK677 28 +KTJJT 220 +QQQJA 483 diff --git a/src/bin/day07.rs b/src/bin/day07.rs new file mode 100644 index 0000000..5c9d2ff --- /dev/null +++ b/src/bin/day07.rs @@ -0,0 +1,215 @@ +use std::{ + cmp::{self, Reverse}, + str::FromStr, +}; + +use aoc_2023::*; + +type Output1 = i32; +type Output2 = Output1; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +enum HandType { + HighCard, + OnePair, + TwoPair, + ThreeOfAKind, + FullHouse, + FourOfAKind, + FiveOfAKind, +} + +lazy_static! { + static ref CARDS: Vec = + vec!['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A',]; + static ref CARDS_2: Vec = + vec!['J', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'Q', 'K', 'A',]; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Hand { + cards: Vec, + bid: i32, +} + +impl FromStr for Hand { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + let parts = s.split_ascii_whitespace().collect_vec(); + + let cards = parts[0].chars().collect_vec(); + let bid = parts[1].parse().unwrap(); + + Ok(Self { cards, bid }) + } +} + +impl Ord for Hand { + fn cmp(&self, other: &Self) -> cmp::Ordering { + let my_type = self.hand_type(); + let other_type = other.hand_type(); + + match my_type.partial_cmp(&other_type) { + Some(cmp::Ordering::Equal) => {} + Some(ord) => return ord, + _ => {} + } + + for (my, other) in self.cards.iter().zip(other.cards.iter()) { + let l = CARDS + .iter() + .enumerate() + .find_map(|(i, c)| if c == my { Some(i) } else { None }) + .unwrap(); + let r = CARDS + .iter() + .enumerate() + .find_map(|(i, c)| if c == other { Some(i) } else { None }) + .unwrap(); + + match l.partial_cmp(&r) { + Some(cmp::Ordering::Equal) => {} + Some(ord) => return ord, + _ => {} + } + } + + cmp::Ordering::Equal + } +} + +impl PartialOrd for Hand { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Hand { + fn hand_type(&self) -> HandType { + let freqs = self.cards.iter().counts(); + + let mut pairs = 0; + let mut triplets = 0; + + for &value in freqs.values() { + match value { + 5 => { + return HandType::FiveOfAKind; + } + 4 => { + return HandType::FourOfAKind; + } + 3 => { + triplets += 1; + } + 2 => { + pairs += 1; + } + _ => {} + } + } + + if pairs == 1 && triplets == 1 { + return HandType::FullHouse; + } + + if triplets > 0 { + return HandType::ThreeOfAKind; + } + + match pairs { + 2 => HandType::TwoPair, + 1 => HandType::OnePair, + _ => HandType::HighCard, + } + } + + fn hand_type_with_joker(&self) -> HandType { + let mut freqs = self.cards.iter().counts(); + let jokers = *freqs.get(&'J').unwrap_or(&0); + + freqs.remove(&'J'); + let mut freqs = freqs.values().cloned().collect_vec(); + freqs.sort_by_key(|&c| Reverse(c)); + + if jokers == 5 || freqs[0] + jokers == 5 { + HandType::FiveOfAKind + } else if freqs[0] + jokers == 4 { + HandType::FourOfAKind + } else if freqs[0] + jokers == 3 && freqs[1] == 2 { + HandType::FullHouse + } else if freqs[0] + jokers == 3 { + HandType::ThreeOfAKind + } else if freqs[0] == 2 && freqs[1] == 2 { + HandType::TwoPair + } else if freqs[0] + jokers == 2 { + HandType::OnePair + } else { + HandType::HighCard + } + } +} + +struct Day07 { + hands: Vec, +} +impl Solution for Day07 { + fn new>(pathname: P) -> Self { + let hands = file_to_structs(pathname); + + Self { hands } + } + + fn part_1(&mut self) -> Output1 { + self.hands.sort(); + self.hands + .iter() + .enumerate() + .map(|(r, h)| (r as i32 + 1) * h.bid) + .sum() + } + + fn part_2(&mut self) -> Output2 { + self.hands.sort_by(|x, y| { + let my_type = x.hand_type_with_joker(); + let other_type = y.hand_type_with_joker(); + + match my_type.partial_cmp(&other_type) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord.unwrap(), + } + + for (my, other) in x.cards.iter().zip(y.cards.iter()) { + let l = CARDS_2 + .iter() + .enumerate() + .find_map(|(i, c)| if c == my { Some(i) } else { None }) + .unwrap(); + let r = CARDS_2 + .iter() + .enumerate() + .find_map(|(i, c)| if c == other { Some(i) } else { None }) + .unwrap(); + + match l.partial_cmp(&r) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord.unwrap(), + } + } + + unreachable!() + }); + self.hands + .iter() + .enumerate() + .map(|(r, h)| (r as i32 + 1) * h.bid) + .sum() + } +} + +fn main() -> Result<()> { + Day07::main() +} + +test_sample!(day_07, Day07, 6440, 5905);