Skip to main content

aoc/year2023/
day07.rs

1//! # Camel Cards
2//!
3//! The types of each hand are computed from the frequency of the cards ordered in descending order.
4//! For example, a full house would have 1 card with a frequency of 3 and a second with a
5//! frequency of 2, giving `[3, 2]`. Similarly, two pair would be `[2, 2, 1]`.
6//! Array comparisons will sort the hand types in order.
7//!
8//! To make comparisons faster the frequencies and the card ranks are packed into a `usize`:
9//!
10//! * `55222` => `0x3200055222`
11//! * `32T3K` => `0x2111032a3d`
12//!
13//! For part two we add the number of jokers to the highest frequency (which could already be
14//! jokers!).
15//!
16//! * `QQQJA` => `0x41000ccc1a`
17use std::cmp::Reverse;
18
19use crate::util::parse::*;
20
21pub struct Hand {
22    cards: [u8; 5],
23    bid: usize,
24}
25
26pub fn parse(input: &str) -> Vec<Hand> {
27    input
28        .lines()
29        .map(|line| {
30            let (prefix, suffix) = line.split_at(5);
31            let cards = prefix.as_bytes().try_into().unwrap();
32            let bid = suffix.unsigned();
33            Hand { cards, bid }
34        })
35        .collect()
36}
37
38pub fn part1(input: &[Hand]) -> usize {
39    sort(input, 11)
40}
41
42pub fn part2(input: &[Hand]) -> usize {
43    sort(input, 1)
44}
45
46fn sort(input: &[Hand], j: usize) -> usize {
47    let mut hands: Vec<_> = input
48        .iter()
49        .map(|&Hand { cards, bid }| {
50            let rank = cards.map(|b| match b {
51                b'A' => 14,
52                b'K' => 13,
53                b'Q' => 12,
54                b'J' => j,
55                b'T' => 10,
56                _ => b.to_decimal(),
57            });
58
59            let mut freq = [0; 15];
60            for r in rank {
61                freq[r] += 1;
62            }
63
64            let jokers = freq[1];
65            freq[1] = 0;
66            freq.sort_unstable_by_key(|&card| Reverse(card));
67            freq[0] += jokers;
68
69            // To speed up comparisons, pack the frequencies and card ranks
70            // into the nibbles of a `usize`.
71            let key = freq[..5].iter().chain(&rank).fold(0, |key, &value| (key << 4) | value);
72
73            (key, bid)
74        })
75        .collect();
76
77    hands.sort_unstable();
78    hands.iter().zip(1..).map(|((_, bid), rank)| bid * rank).sum()
79}