Day 7
This commit is contained in:
parent
f6c6291729
commit
754b7d728c
File diff suppressed because it is too large
Load Diff
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -7,7 +7,8 @@ mod day03;
|
|||
mod day04;
|
||||
mod day05;
|
||||
mod day06;
|
||||
mod day07;
|
||||
|
||||
fn main() {
|
||||
day06::run();
|
||||
day07::run();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue