diff --git a/2023/day7/README.md b/2023/day7/README.md index aef8e91..b47d1e0 100644 --- a/2023/day7/README.md +++ b/2023/day7/README.md @@ -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?** + diff --git a/2023/day7/src/main.rs b/2023/day7/src/main.rs index 0b68661..b9ac03b 100644 --- a/2023/day7/src/main.rs +++ b/2023/day7/src/main.rs @@ -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 for CardType { +impl From for NormalCardType { fn from(value: char) -> Self { - use CardType::*; + use NormalCardType::*; match value { '2' => Two, @@ -48,39 +48,90 @@ impl From 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 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 {} +impl CardType for NormalCardType {} +impl CardType for WildJokerCardType {} + #[derive(Debug, Default, PartialEq, Eq)] -struct Hand { - cards: [CardType; 5], +struct Hand { + cards: [T; 5], kind: HandType, bet: usize, } -impl Hand { +impl Hand +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::>() + .map(|ch| ch.into()) + .collect::>() .try_into() .unwrap(); let bet = raw_bet.trim().parse::().unwrap(); - let kind = HandType::from(cards.clone()); + let kind = HandType::from(cards); Hand { cards, kind, bet } } } -impl PartialOrd for Hand { +impl PartialOrd for Hand { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for Hand { +impl Ord for Hand { /// 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 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 = HashMap::new(); + let mut count_map: HashMap = 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, +struct Game { + hands: Vec>, } -impl Game { - fn parse(input: &str) -> Self { +impl Game { + fn parse(input: &str) -> Game { let hands = input .split('\n') .filter(|s| !s.is_empty()) .map(Hand::parse) - .collect::>(); + .collect::>>(); Game { hands } } @@ -168,14 +225,19 @@ impl Game { } fn part_one() { - let mut game = Game::parse(FILE_STR); + let mut game: Game = Game::parse(FILE_STR); let score = game.get_score_sum(); println!("Part 1: Sum of scores: {}", score); } +fn part_two() { + let _game: Game = 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 = Game::parse(EXAMPLE_FILE_STR); assert_eq!(6440, game.get_score_sum()); } + + #[test] + fn get_wild_score_sum() {} }