Begin 2023 day7 part2 by faffing about with generics

This commit is contained in:
Timothy Warren 2023-12-15 18:47:52 -05:00
parent e1b0ca6d2e
commit cb784f6d6b
2 changed files with 126 additions and 29 deletions

View File

@ -76,3 +76,35 @@ Now, you can determine the total winnings of this set of hands by adding up the
with its rank (765 * 1 + 220 * 2 + 28 * 3 + 684 * 4 + 483 * 5). So the total winnings in this example are 6440.
Find the rank of every hand in your set. **What are the total winnings?**
## Part Two
To make things a little more interesting, the Elf introduces one additional rule. Now, `J` cards are jokers -
wildcards that can act like whatever card would make the hand the strongest type possible.
To balance this, `J` **cards are now the weakest** individual cards, weaker even than `2`. The other cards stay in the
same order: `A`, `K`, `Q`, `T`, `9`, `8`, `7`, `6`, `5`, `4`, `3`, `2`, `J`.
J cards can pretend to be whatever card is best for the purpose of determining hand type; for example, `QJJQ2`
is now considered **four of a kind**. However, for the purpose of breaking ties between two hands of the same type,
`J` is always treated as `J`, not the card it's pretending to be: `JKKK2` is weaker than `QQQQ2` because J is weaker
than Q.
Now, the above example goes very differently:
```
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483
```
- `32T3K` is still the only one pair; it doesn't contain any jokers, so its strength doesn't increase.
- `KK677` is now the only two pair, making it the second-weakest hand.
- `T55J5`, `KTJJT`, and `QQQJA` are now all four of a kind! `T55J5` gets rank 3, `QQQJA` gets rank 4, and `KTJJT` gets rank 5.
With the new joker rule, the total winnings in this example are `5905`.
Using the new joker rule, find the rank of every hand in your set. **What are the new total winnings?**

View File

@ -1,14 +1,14 @@
use crate::HandType::{
FiveOfAKind, FourOfAKind, FullHouse, HighCard, OnePair, ThreeOfAKind, TwoPair,
};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use HandType::*;
const FILE_STR: &str = include_str!("input.txt");
#[derive(Clone, Debug, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, Debug, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[repr(u8)]
enum CardType {
enum NormalCardType {
#[default]
Two = 0,
Three,
@ -25,9 +25,9 @@ enum CardType {
Ace,
}
impl From<char> for CardType {
impl From<char> for NormalCardType {
fn from(value: char) -> Self {
use CardType::*;
use NormalCardType::*;
match value {
'2' => Two,
@ -48,39 +48,90 @@ impl From<char> for CardType {
}
}
#[derive(Copy, Clone, Debug, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[repr(u8)]
enum WildJokerCardType {
#[default]
Joker = 0,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Jack,
Queen,
King,
Ace,
}
impl From<char> for WildJokerCardType {
fn from(value: char) -> Self {
use WildJokerCardType::*;
match value {
'J' => Joker,
'2' => Two,
'3' => Three,
'4' => Four,
'5' => Five,
'6' => Six,
'7' => Seven,
'8' => Eight,
'9' => Nine,
'T' => Jack,
'Q' => Queen,
'K' => King,
'A' => Ace,
_ => panic!("Invalid card character"),
}
}
}
type PartOne = NormalCardType;
type PartTwo = WildJokerCardType;
trait CardType: Eq + Copy + PartialOrd + Ord + Debug + Hash + From<char> {}
impl CardType for NormalCardType {}
impl CardType for WildJokerCardType {}
#[derive(Debug, Default, PartialEq, Eq)]
struct Hand {
cards: [CardType; 5],
struct Hand<T> {
cards: [T; 5],
kind: HandType,
bet: usize,
}
impl Hand {
impl<T: CardType> Hand<T>
where
HandType: From<[T; 5]>,
{
fn parse(line: &str) -> Self {
let mut parts = line.split_whitespace();
let raw_hand = parts.next().unwrap();
let raw_bet = parts.next().unwrap();
let cards: [CardType; 5] = raw_hand
let cards: [T; 5] = raw_hand
.trim()
.chars()
.map(CardType::from)
.collect::<Vec<CardType>>()
.map(|ch| ch.into())
.collect::<Vec<T>>()
.try_into()
.unwrap();
let bet = raw_bet.trim().parse::<usize>().unwrap();
let kind = HandType::from(cards.clone());
let kind = HandType::from(cards);
Hand { cards, kind, bet }
}
}
impl PartialOrd for Hand {
impl<T: CardType> PartialOrd for Hand<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Hand {
impl<T: CardType> Ord for Hand<T> {
/// Implement the sorting for each hand, first comparing the type of
/// hand, and then comparing the individual cards, if necessary
fn cmp(&self, other: &Self) -> Ordering {
@ -88,8 +139,8 @@ impl Ord for Hand {
return self.kind.cmp(&other.kind);
}
for (i, card) in self.cards.iter().enumerate() {
if card != &other.cards[i] {
for (i, card) in self.cards.into_iter().enumerate() {
if card != other.cards[i] {
return card.cmp(&other.cards[i]);
}
}
@ -98,7 +149,7 @@ impl Ord for Hand {
}
}
#[derive(Debug, Default, PartialOrd, Ord, PartialEq, Eq, Copy, Clone)]
#[derive(Debug, Default, PartialOrd, Ord, PartialEq, Eq, Copy, Clone, Hash)]
#[repr(u8)]
enum HandType {
#[default]
@ -111,12 +162,18 @@ enum HandType {
FiveOfAKind,
}
impl From<[CardType; 5]> for HandType {
fn from(value: [CardType; 5]) -> Self {
impl HandType {
fn from_cards(value: [WildJokerCardType; 5]) -> Self {
todo!();
}
}
impl<T: CardType> From<[T; 5]> for HandType {
fn from(value: [T; 5]) -> Self {
let mut cards = Vec::from(value);
cards.sort_unstable();
let mut count_map: HashMap<CardType, usize> = HashMap::new();
let mut count_map: HashMap<T, usize> = HashMap::new();
cards.into_iter().for_each(|ct| {
match count_map.get(&ct) {
Some(count) => count_map.insert(ct, *count + 1),
@ -139,17 +196,17 @@ impl From<[CardType; 5]> for HandType {
}
#[derive(Debug, Default)]
struct Game {
hands: Vec<Hand>,
struct Game<T> {
hands: Vec<Hand<T>>,
}
impl Game {
fn parse(input: &str) -> Self {
impl<T: CardType> Game<T> {
fn parse(input: &str) -> Game<T> {
let hands = input
.split('\n')
.filter(|s| !s.is_empty())
.map(Hand::parse)
.collect::<Vec<Hand>>();
.collect::<Vec<Hand<T>>>();
Game { hands }
}
@ -168,14 +225,19 @@ impl Game {
}
fn part_one() {
let mut game = Game::parse(FILE_STR);
let mut game: Game<PartOne> = Game::parse(FILE_STR);
let score = game.get_score_sum();
println!("Part 1: Sum of scores: {}", score);
}
fn part_two() {
let _game: Game<PartTwo> = Game::parse(FILE_STR);
}
fn main() {
part_one();
part_two();
}
#[cfg(test)]
@ -185,7 +247,10 @@ mod test {
#[test]
fn get_score_sum() {
let mut game = Game::parse(EXAMPLE_FILE_STR);
let mut game: Game<PartOne> = Game::parse(EXAMPLE_FILE_STR);
assert_eq!(6440, game.get_score_sum());
}
#[test]
fn get_wild_score_sum() {}
}