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