Begin 2023 day7 part2 by faffing about with generics
This commit is contained in:
parent
e1b0ca6d2e
commit
cb784f6d6b
@ -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.
|
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?**
|
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?**
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use crate::HandType::{
|
|
||||||
FiveOfAKind, FourOfAKind, FullHouse, HighCard, OnePair, ThreeOfAKind, TwoPair,
|
|
||||||
};
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use HandType::*;
|
||||||
|
|
||||||
const FILE_STR: &str = include_str!("input.txt");
|
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)]
|
#[repr(u8)]
|
||||||
enum CardType {
|
enum NormalCardType {
|
||||||
#[default]
|
#[default]
|
||||||
Two = 0,
|
Two = 0,
|
||||||
Three,
|
Three,
|
||||||
@ -25,9 +25,9 @@ enum CardType {
|
|||||||
Ace,
|
Ace,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<char> for CardType {
|
impl From<char> for NormalCardType {
|
||||||
fn from(value: char) -> Self {
|
fn from(value: char) -> Self {
|
||||||
use CardType::*;
|
use NormalCardType::*;
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
'2' => Two,
|
'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)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
struct Hand {
|
struct Hand<T> {
|
||||||
cards: [CardType; 5],
|
cards: [T; 5],
|
||||||
kind: HandType,
|
kind: HandType,
|
||||||
bet: usize,
|
bet: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hand {
|
impl<T: CardType> Hand<T>
|
||||||
|
where
|
||||||
|
HandType: From<[T; 5]>,
|
||||||
|
{
|
||||||
fn parse(line: &str) -> Self {
|
fn parse(line: &str) -> Self {
|
||||||
let mut parts = line.split_whitespace();
|
let mut parts = line.split_whitespace();
|
||||||
let raw_hand = parts.next().unwrap();
|
let raw_hand = parts.next().unwrap();
|
||||||
let raw_bet = parts.next().unwrap();
|
let raw_bet = parts.next().unwrap();
|
||||||
let cards: [CardType; 5] = raw_hand
|
let cards: [T; 5] = raw_hand
|
||||||
.trim()
|
.trim()
|
||||||
.chars()
|
.chars()
|
||||||
.map(CardType::from)
|
.map(|ch| ch.into())
|
||||||
.collect::<Vec<CardType>>()
|
.collect::<Vec<T>>()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let bet = raw_bet.trim().parse::<usize>().unwrap();
|
let bet = raw_bet.trim().parse::<usize>().unwrap();
|
||||||
let kind = HandType::from(cards.clone());
|
let kind = HandType::from(cards);
|
||||||
|
|
||||||
Hand { cards, kind, bet }
|
Hand { cards, kind, bet }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Hand {
|
impl<T: CardType> PartialOrd for Hand<T> {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(self.cmp(other))
|
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
|
/// Implement the sorting for each hand, first comparing the type of
|
||||||
/// hand, and then comparing the individual cards, if necessary
|
/// hand, and then comparing the individual cards, if necessary
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
@ -88,8 +139,8 @@ impl Ord for Hand {
|
|||||||
return self.kind.cmp(&other.kind);
|
return self.kind.cmp(&other.kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, card) in self.cards.iter().enumerate() {
|
for (i, card) in self.cards.into_iter().enumerate() {
|
||||||
if card != &other.cards[i] {
|
if card != other.cards[i] {
|
||||||
return card.cmp(&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)]
|
#[repr(u8)]
|
||||||
enum HandType {
|
enum HandType {
|
||||||
#[default]
|
#[default]
|
||||||
@ -111,12 +162,18 @@ enum HandType {
|
|||||||
FiveOfAKind,
|
FiveOfAKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<[CardType; 5]> for HandType {
|
impl HandType {
|
||||||
fn from(value: [CardType; 5]) -> Self {
|
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);
|
let mut cards = Vec::from(value);
|
||||||
cards.sort_unstable();
|
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| {
|
cards.into_iter().for_each(|ct| {
|
||||||
match count_map.get(&ct) {
|
match count_map.get(&ct) {
|
||||||
Some(count) => count_map.insert(ct, *count + 1),
|
Some(count) => count_map.insert(ct, *count + 1),
|
||||||
@ -139,17 +196,17 @@ impl From<[CardType; 5]> for HandType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct Game {
|
struct Game<T> {
|
||||||
hands: Vec<Hand>,
|
hands: Vec<Hand<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl<T: CardType> Game<T> {
|
||||||
fn parse(input: &str) -> Self {
|
fn parse(input: &str) -> Game<T> {
|
||||||
let hands = input
|
let hands = input
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.map(Hand::parse)
|
.map(Hand::parse)
|
||||||
.collect::<Vec<Hand>>();
|
.collect::<Vec<Hand<T>>>();
|
||||||
|
|
||||||
Game { hands }
|
Game { hands }
|
||||||
}
|
}
|
||||||
@ -168,14 +225,19 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn part_one() {
|
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();
|
let score = game.get_score_sum();
|
||||||
|
|
||||||
println!("Part 1: Sum of scores: {}", score);
|
println!("Part 1: Sum of scores: {}", score);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn part_two() {
|
||||||
|
let _game: Game<PartTwo> = Game::parse(FILE_STR);
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
part_one();
|
part_one();
|
||||||
|
part_two();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -185,7 +247,10 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_score_sum() {
|
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());
|
assert_eq!(6440, game.get_score_sum());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_wild_score_sum() {}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user