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