This commit is contained in:
Shadowfacts 2023-12-07 12:37:45 -05:00
parent f6c6291729
commit 754b7d728c
3 changed files with 1286 additions and 1 deletions

1000
input/day7.txt Normal file

File diff suppressed because it is too large Load Diff

284
src/day07.rs Normal file
View File

@ -0,0 +1,284 @@
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::<Jack>(input));
dbg!(calculate_winnings::<Joker>(input));
}
fn calculate_winnings<J: JRule>(input: &str) -> usize {
let mut hands_and_bids = parse::<J>(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<J: JRule>(s: &str) -> Vec<(Hand<J>, 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<J: JRule> {
card_type: CardType,
j_rule: std::marker::PhantomData<J>,
}
#[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<J: JRule> Card<J> {
fn from(t: CardType) -> Self {
Self {
card_type: t,
j_rule: std::marker::PhantomData,
}
}
}
impl<J: JRule> PartialOrd for Card<J> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<J: JRule> Ord for Card<J> {
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<J: JRule>([Card<J>; 5]);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
enum HandType {
HighCard,
OnePair,
TwoPair,
ThreeOfAKind,
FullHouse,
FourOfAKind,
FiveOfAKind,
}
impl<J: JRule> Hand<J> {
fn parse(s: &str) -> Self {
assert_eq!(s.len(), 5);
let cards = s
.chars()
.map(|c| Card::from(CardType::parse(c)))
.collect::<Vec<_>>()
.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<J: JRule> PartialOrd for Hand<J> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<J: JRule> Ord for Hand<J> {
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::<Jack>([
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::<Jack>::parse("22222").hand_type(),
HandType::FiveOfAKind
);
assert_eq!(
Hand::<Jack>::parse("K2222").hand_type(),
HandType::FourOfAKind
);
assert_eq!(
Hand::<Jack>::parse("KK222").hand_type(),
HandType::FullHouse
);
assert_eq!(
Hand::<Jack>::parse("QK222").hand_type(),
HandType::ThreeOfAKind
);
assert_eq!(Hand::<Jack>::parse("QQ322").hand_type(), HandType::TwoPair);
assert_eq!(Hand::<Jack>::parse("QQ342").hand_type(), HandType::OnePair);
assert_eq!(Hand::<Jack>::parse("234AK").hand_type(), HandType::HighCard);
}
#[test]
fn test_hand_cmp() {
let five_of_a_kind = Hand::<Jack>::parse("22222");
let four_of_a_kind = Hand::<Jack>::parse("2222K");
let four_of_a_kind2 = Hand::<Jack>::parse("2222A");
assert!(five_of_a_kind > four_of_a_kind);
assert!(four_of_a_kind2 > four_of_a_kind);
assert!(Hand::<Jack>::parse("T55J5") < Hand::<Jack>::parse("QQQJA"));
}
#[test]
fn test_hand_type_joker() {
assert_eq!(
Hand::<Joker>::parse("T55J5").hand_type(),
HandType::FourOfAKind
);
}
#[test]
fn test_hand_cmp_joker() {
assert!(Hand::<Joker>::parse("T55J5") < Hand::<Joker>::parse("KTJJT"));
}
}

View File

@ -7,7 +7,8 @@ mod day03;
mod day04;
mod day05;
mod day06;
mod day07;
fn main() {
day06::run();
day07::run();
}