@@ -9,81 +9,164 @@ const example = [
99] ;
1010
1111Deno . test ( "Day 7: Camel Cards" , async ( t ) => {
12- await t . step ( "Example" , async ( t ) => {
12+ await t . step ( "Part 1" , async ( t ) => {
13+ const cardStrengths = [
14+ "A" ,
15+ "K" ,
16+ "Q" ,
17+ "J" ,
18+ "T" ,
19+ "9" ,
20+ "8" ,
21+ "7" ,
22+ "6" ,
23+ "5" ,
24+ "4" ,
25+ "3" ,
26+ "2" ,
27+ ] ;
28+
29+ await t . step ( "Example" , async ( t ) => {
30+ await t . step (
31+ "it should solve the example" ,
32+ ( ) => assertEquals ( totalWinnings ( cardStrengths , example ) , 6440 ) ,
33+ ) ;
34+ } ) ;
35+
36+ await t . step ( "Solution" , async ( t ) => {
37+ await t . step ( "it should solve" , async ( ) =>
38+ assertEquals (
39+ totalWinnings (
40+ cardStrengths ,
41+ ( await Deno . readTextFile ( "./input/day07.txt" ) ) . split ( "\n" ) ,
42+ ) ,
43+ 251545216 ,
44+ ) ) ;
45+ } ) ;
46+
47+ await t . step ( "handType()" , async ( t ) => {
48+ for (
49+ const [ hand , expectedType ] of [
50+ [ "AAAAA" , HandType . FiveOfAKind ] ,
51+ [ "AA8AA" , HandType . FourOfAKind ] ,
52+ [ "23332" , HandType . FullHouse ] ,
53+ [ "TTT98" , HandType . ThreeOfAKind ] ,
54+ [ "23432" , HandType . TwoPair ] ,
55+ [ "A23A4" , HandType . OnePair ] ,
56+ [ "23456" , HandType . HighCard ] ,
57+ [ `32T3K` , HandType . OnePair ] ,
58+ [ `KTJJT` , HandType . TwoPair ] ,
59+ [ `KK677` , HandType . TwoPair ] ,
60+ [ `T55J5` , HandType . ThreeOfAKind ] ,
61+ [ `QQQJA` , HandType . ThreeOfAKind ] ,
62+ ] as Array < [ string , HandType ] >
63+ ) {
64+ await t . step (
65+ `${ hand } should be type ${ expectedType } ` ,
66+ ( ) => assertEquals ( getHandType ( hand ) , expectedType ) ,
67+ ) ;
68+ }
69+ } ) ;
70+
71+ await t . step ( "compare()" , async ( t ) => {
72+ await t . step (
73+ `33332 and 2AAAA are both four of a kind hands, but 33332 is stronger because its first card is stronger` ,
74+ ( ) => assertEquals ( compareByCards ( cardStrengths ) ( "33332" , "2AAAA" ) , 1 ) ,
75+ ) ;
76+ await t . step (
77+ `77888 and 77788 are both a full house, but 77888 is stronger because its third card is stronger (and both hands have the same first and second card)` ,
78+ ( ) => assertEquals ( compareByCards ( cardStrengths ) ( "77788" , "77888" ) , - 1 ) ,
79+ ) ;
80+ await t . step (
81+ `T55J5 and QQQJA are both three of a kind. QQQJA has a stronger first card` ,
82+ ( ) => assertEquals ( compareByCards ( cardStrengths ) ( "QQQJA" , "T55J5" ) , 1 ) ,
83+ ) ;
84+ await t . step (
85+ `KK677 and KTJJT are both two pair. Their first cards both have the same label, but the second card of KK677 is stronger (K vs T)` ,
86+ ( ) => assertEquals ( compareByCards ( cardStrengths ) ( "KTJJT" , "KK677" ) , - 1 ) ,
87+ ) ;
88+ } ) ;
89+
1390 await t . step (
14- "it should solve the example" ,
15- ( ) => assertEquals ( totalWinnings ( example ) , 6440 ) ,
91+ "rank()" ,
92+ ( ) =>
93+ assertEquals ( example . sort ( byRankAndCards ( cardStrengths ) ) , [
94+ // 32T3K is the only one pair and the other hands are all a stronger type, so it gets rank 1.
95+ `32T3K 765` ,
96+ // KK677 and KTJJT are both two pair. Their first cards both have the same label, but the second card of KK677 is stronger (K vs T), so KTJJT gets rank 2 and KK677 gets rank 3.
97+ `KTJJT 220` ,
98+ `KK677 28` ,
99+ // T55J5 and QQQJA are both three of a kind. QQQJA has a stronger first card, so it gets rank 5 and T55J5 gets rank 4.
100+ `T55J5 684` ,
101+ `QQQJA 483` ,
102+ ] ) ,
16103 ) ;
17104 } ) ;
18105
19- await t . step ( "Solution" , async ( t ) => {
20- await t . step ( "it should solve" , async ( t ) =>
106+ await t . step ( "Part 2" , async ( t ) => {
107+ const cardStrengths = [
108+ "A" ,
109+ "K" ,
110+ "Q" ,
111+ "T" ,
112+ "9" ,
113+ "8" ,
114+ "7" ,
115+ "6" ,
116+ "5" ,
117+ "4" ,
118+ "3" ,
119+ "2" ,
120+ "J" ,
121+ ] ;
122+
123+ await t . step ( "Example" , async ( t ) => {
124+ await t . step (
125+ "it should solve the example" ,
126+ ( ) =>
127+ assertEquals ( totalWinningsWithJoker ( cardStrengths , example ) , 5905 ) ,
128+ ) ;
129+ } ) ;
130+
131+ await t . step ( "it should solve" , async ( ) =>
21132 assertEquals (
22- totalWinnings (
133+ totalWinningsWithJoker (
134+ cardStrengths ,
23135 ( await Deno . readTextFile ( "./input/day07.txt" ) ) . split ( "\n" ) ,
24136 ) ,
25- 251545216 ,
137+ 250384185 ,
26138 ) ) ;
27139 } ) ;
140+ } ) ;
28141
29- await t . step ( "handType()" , async ( t ) => {
30- for (
31- const [ hand , expectedType ] of [
32- [ "AAAAA" , HandType . FiveOfAKind ] ,
33- [ "AA8AA" , HandType . FourOfAKind ] ,
34- [ "23332" , HandType . FullHouse ] ,
35- [ "TTT98" , HandType . ThreeOfAKind ] ,
36- [ "23432" , HandType . TwoPair ] ,
37- [ "A23A4" , HandType . OnePair ] ,
38- [ "23456" , HandType . HighCard ] ,
39- [ `32T3K` , HandType . OnePair ] ,
40- [ `KTJJT` , HandType . TwoPair ] ,
41- [ `KK677` , HandType . TwoPair ] ,
42- [ `T55J5` , HandType . ThreeOfAKind ] ,
43- [ `QQQJA` , HandType . ThreeOfAKind ] ,
44- ] as Array < [ string , HandType ] >
45- ) {
46- await t . step (
47- `${ hand } should be type ${ expectedType } ` ,
48- ( ) => assertEquals ( getHandType ( hand ) , expectedType ) ,
49- ) ;
50- }
51- } ) ;
52-
53- await t . step ( "compare()" , async ( t ) => {
54- await t . step (
55- `33332 and 2AAAA are both four of a kind hands, but 33332 is stronger because its first card is stronger` ,
56- ( ) => assertEquals ( compareByCards ( "33332" , "2AAAA" ) , 1 ) ,
57- ) ;
58- await t . step (
59- `77888 and 77788 are both a full house, but 77888 is stronger because its third card is stronger (and both hands have the same first and second card)` ,
60- ( ) => assertEquals ( compareByCards ( "77788" , "77888" ) , - 1 ) ,
61- ) ;
62- await t . step (
63- `T55J5 and QQQJA are both three of a kind. QQQJA has a stronger first card` ,
64- ( ) => assertEquals ( compareByCards ( "QQQJA" , "T55J5" ) , 1 ) ,
65- ) ;
66- await t . step (
67- `KK677 and KTJJT are both two pair. Their first cards both have the same label, but the second card of KK677 is stronger (K vs T)` ,
68- ( ) => assertEquals ( compareByCards ( "KTJJT" , "KK677" ) , - 1 ) ,
142+ const totalWinnings = ( cardStrengths : string [ ] , hands : string [ ] ) : number => {
143+ const rankedHands = hands . sort ( byRankAndCards ( cardStrengths ) ) ;
144+ return hands
145+ . map ( parseHandBids )
146+ . reduce (
147+ ( total , [ hand , bidAmount ] ) =>
148+ total + ( rankedHands . indexOf ( `${ hand } ${ bidAmount } ` ) + 1 ) * bidAmount ,
149+ 0 ,
69150 ) ;
70- } ) ;
71-
72- await t . step ( "rank()" , ( ) =>
73- assertEquals ( example . sort ( byRankAndCards ) , [
74- // 32T3K is the only one pair and the other hands are all a stronger type, so it gets rank 1.
75- `32T3K 765` ,
76- // KK677 and KTJJT are both two pair. Their first cards both have the same label, but the second card of KK677 is stronger (K vs T), so KTJJT gets rank 2 and KK677 gets rank 3.
77- `KTJJT 220` ,
78- `KK677 28` ,
79- // T55J5 and QQQJA are both three of a kind. QQQJA has a stronger first card, so it gets rank 5 and T55J5 gets rank 4.
80- `T55J5 684` ,
81- `QQQJA 483` ,
82- ] ) ) ;
83- } ) ;
151+ } ;
84152
85- const totalWinnings = ( hands : string [ ] ) : number => {
86- const rankedHands = hands . sort ( byRankAndCards ) ;
153+ const totalWinningsWithJoker = (
154+ cardStrengths : string [ ] ,
155+ hands : string [ ] ,
156+ ) : number => {
157+ const rankedHands = hands . sort (
158+ byRankAndCards ( cardStrengths , ( hand : string ) => {
159+ let type = getHandType ( hand ) ;
160+ const s = new Set ( hand ) ;
161+ if ( ! s . has ( "J" ) ) return type ; // No Jokers
162+ // Find the best possible hand
163+ for ( const card of [ ...s . values ( ) ] . filter ( ( c ) => c !== "J" ) ) {
164+ let newType = getHandType ( hand . replaceAll ( "J" , card ) ) ;
165+ type = newType < type ? newType : type ;
166+ }
167+ return type ;
168+ } ) ,
169+ ) ;
87170 return hands
88171 . map ( parseHandBids )
89172 . reduce (
@@ -98,30 +181,20 @@ const parseHandBids = (handBid: string): [string, number] => {
98181 return [ hand , parseInt ( bid , 10 ) ] ;
99182} ;
100183
101- const cardStrength = [
102- "A" ,
103- "K" ,
104- "Q" ,
105- "J" ,
106- "T" ,
107- "9" ,
108- "8" ,
109- "7" ,
110- "6" ,
111- "5" ,
112- "4" ,
113- "3" ,
114- "2" ,
115- ] ;
116-
117- const byRankAndCards = ( handBid1 : string , handBid2 : string ) => {
118- const [ hand1 ] = parseHandBids ( handBid1 ) ;
119- const [ hand2 ] = parseHandBids ( handBid2 ) ;
120- const handType1 = getHandType ( hand1 ) ;
121- const handType2 = getHandType ( hand2 ) ;
122- if ( handType1 < handType2 ) return 1 ;
123- if ( handType1 > handType2 ) return - 1 ;
124- return compareByCards ( hand1 , hand2 ) ;
184+ const byRankAndCards = (
185+ cardStrengths : string [ ] ,
186+ getHandTypeFN ?: typeof getHandType ,
187+ ) => {
188+ const compareStrength = compareByCards ( cardStrengths ) ;
189+ return ( handBid1 : string , handBid2 : string ) => {
190+ const [ hand1 ] = parseHandBids ( handBid1 ) ;
191+ const [ hand2 ] = parseHandBids ( handBid2 ) ;
192+ const handType1 = ( getHandTypeFN ?? getHandType ) ( hand1 ) ;
193+ const handType2 = ( getHandTypeFN ?? getHandType ) ( hand2 ) ;
194+ if ( handType1 < handType2 ) return 1 ;
195+ if ( handType1 > handType2 ) return - 1 ;
196+ return compareStrength ( hand1 , hand2 ) ;
197+ } ;
125198} ;
126199
127200/**
@@ -186,17 +259,22 @@ const cardsInSets = (hand: string): number[] => {
186259 return [ ...new Set ( hand ) . values ( ) ] . map ( count ) . sort ( desc ) ;
187260} ;
188261
189- const compareByCards = ( hand1 : string , hand2 : string ) : number => {
190- for ( let i = 0 ; i < hand1 . length ; i ++ ) {
191- const c1 = hand1 [ i ] ;
192- const c2 = hand2 [ i ] ;
193- if ( c1 === c2 ) continue ;
194- return compareCard ( c1 , c2 ) ;
195- }
196- return 0 ;
197- } ;
262+ const compareByCards =
263+ ( cardStrength : string [ ] ) => ( hand1 : string , hand2 : string ) : number => {
264+ for ( let i = 0 ; i < hand1 . length ; i ++ ) {
265+ const c1 = hand1 [ i ] ;
266+ const c2 = hand2 [ i ] ;
267+ if ( c1 === c2 ) continue ;
268+ return compareCard ( cardStrength , c1 , c2 ) ;
269+ }
270+ return 0 ;
271+ } ;
198272
199- const compareCard = ( card1 : string , card2 : string ) : number => {
273+ const compareCard = (
274+ cardStrength : string [ ] ,
275+ card1 : string ,
276+ card2 : string ,
277+ ) : number => {
200278 if ( cardStrength . indexOf ( card1 ) < cardStrength . indexOf ( card2 ) ) return 1 ;
201279 if ( cardStrength . indexOf ( card1 ) > cardStrength . indexOf ( card2 ) ) return - 1 ;
202280 return 0 ;
0 commit comments