-
Notifications
You must be signed in to change notification settings - Fork 0
/
day 7.dib
244 lines (180 loc) · 9.7 KB
/
day 7.dib
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#!meta
{"kernelInfo":{"defaultKernelName":"fsharp","items":[{"aliases":[],"languageName":"fsharp","name":"fsharp"}]}}
#!markdown
# --- Day 7: Camel Cards ---
Your all-expenses-paid trip turns out to be a one-way, five-minute ride in an airship. (At least it's a cool airship!) It drops you off at the edge of a vast desert and descends back to Island Island.
"Did you bring the parts?"
You turn around to see an Elf completely covered in white clothing, wearing goggles, and riding a large camel.
"Did you bring the parts?" she asks again, louder this time. You aren't sure what parts she's looking for; you're here to figure out why the sand stopped.
"The parts! For the sand, yes! Come with me; I will show you." She beckons you onto the camel.
After riding a bit across the sands of Desert Island, you can see what look like very large rocks covering half of the horizon. The Elf explains that the rocks are all along the part of Desert Island that is directly above Island Island, making it hard to even get there. Normally, they use big machines to move the rocks and filter the sand, but the machines have broken down because Desert Island recently stopped receiving the parts they need to fix the machines.
You've already assumed it'll be your job to figure out why the parts stopped when she asks if you can help. You agree automatically.
Because the journey will take a few days, she offers to teach you the game of Camel Cards. Camel Cards is sort of similar to poker except it's designed to be easier to play while riding a camel.
In Camel Cards, you get a list of hands, and your goal is to order them based on the strength of each hand. A hand consists of five cards labeled one of A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, or 2. The relative strength of each card follows this order, where A is the highest and 2 is the lowest.
Every hand is exactly one type. From strongest to weakest, they are:
* Five of a kind, where all five cards have the same label: `AAAAA`
* Four of a kind, where four cards have the same label and one card has a different label: `AA8AA`
* Full house, where three cards have the same label, and the remaining two cards share a different label: `23332`
* Three of a kind, where three cards have the same label, and the remaining two cards are each different from any other card in the hand: `TTT98`
* Two pair, where two cards share one label, two other cards share a second label, and the remaining card has a third label: `23432`
* One pair, where two cards share one label, and the other three cards have a different label from the pair and each other: `A23A4`
* High card, where all cards' labels are distinct: `23456`
Hands are primarily ordered based on type; for example, every full house is stronger than any three of a kind.
If two hands have the same type, a second ordering rule takes effect. Start by comparing the first card in each hand. If these cards are different, the hand with the stronger first card is considered stronger. If the first card in each hand have the same label, however, then move on to considering the second card in each hand. If they differ, the hand with the higher second card wins; otherwise, continue with the third card in each hand, then the fourth, then the fifth.
So, `33332` and `2AAAA` are both four of a kind hands, but `33332` is stronger because its first card is stronger. Similarly, `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).
To play Camel Cards, you are given a list of hands and their corresponding bid (your puzzle input). For example:
```
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483
```
This example shows five hands; each hand is followed by its bid amount. Each hand wins an amount equal to its bid multiplied by its rank, where the weakest hand gets rank 1, the second-weakest hand gets rank 2, and so on up to the strongest hand. Because there are five hands in this example, the strongest hand will have rank 5 and its bid will be multiplied by 5.
So, the first step is to put the hands in order of strength:
* `32T3K` is the only one pair and the other hands are all a stronger type, so it gets rank 1.
* `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.
* `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.
Now, you can determine the total winnings of this set of hands by adding up the result of multiplying each hand's bid 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?
#!fsharp
let cardRank = function
| c when c >= '2' && c <= '9' -> int c - int '0'
| 'T' -> 10
| 'J' -> 11
| 'Q' -> 12
| 'K' -> 13
| 'A' -> 14
| c -> raise (InvalidOperationException $"Unknown card value { c }")
"23456789TJQKA"
|> Seq.map cardRank
|> Array.ofSeq
#!fsharp
let handRank (hand: char seq) =
hand
|> Seq.countBy id
|> Seq.choose (fun (card, count) -> if count > 1 then Some count else None)
|> Seq.sortDescending
|> Array.ofSeq
|> function
| [| 5 |] -> 7
| [| 4 |] -> 6
| [| 3; 2 |] -> 5
| [| 3 |] -> 4
| [| 2; 2 |] -> 3
| [| 2 |] -> 2
| [||] -> 1
| groups -> raise (InvalidOperationException $"Can't rank hand { hand } - unexpected grouping count { groups }")
[|
"AAAAA"
"AA8AA"
"23332"
"TTT98"
"23432"
"A23A4"
"23456"
|]
|> Array.map handRank
#!fsharp
type Hand = {
cards: string
bid: int
}
// For use with Seq.sortWith, etc. Will give a positive number if hand1 > hand2, so the lower hand will be sorted first
let handComparer handRanker cardRanker (hand1: Hand) (hand2: Hand) =
match (handRanker hand1.cards), (handRanker hand2.cards) with
| rank1, rank2 when rank1 <> rank2 -> rank1 - rank2
| _ ->
let firstMismatchingCard =
Seq.zip hand1.cards hand2.cards
|> Seq.tryFind (fun (a, b) -> a <> b)
match firstMismatchingCard with
| Some (card1, card2) -> (cardRanker card1) - (cardRanker card2)
| None -> 0
let parseInput (input: string) = {
cards = input[..5]
bid = int input[6..]
}
let sampleInput =
[|
"32T3K 765"
"T55J5 684"
"KK677 28"
"KTJJT 220"
"QQQJA 483"
|]
|> Array.map parseInput
sampleInput
let part1HandComparer = handComparer handRank cardRank
#!fsharp
sampleInput
|> Array.sortWith part1HandComparer
#!fsharp
type Game = Hand array
let winningsForHand (rank: int) (hand: Hand) =
(rank + 1) * hand.bid
let totalWinnings (handComparer: Hand -> Hand -> int) (game: Game) =
game
|> Array.sortWith handComparer
|> Seq.mapi winningsForHand
|> Seq.sum
sampleInput |> totalWinnings part1HandComparer
#!fsharp
#!import shared.fs
open Shared
let challengeInput = readAndParseInput "day 7 input.txt" parseInput
challengeInput |> totalWinnings part1HandComparer
#!markdown
## --- 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?
#!fsharp
let cardRank' = function
| c when c >= '2' && c <= '9' -> int c - int '0'
| 'T' -> 10
| 'J' -> 1
| 'Q' -> 12
| 'K' -> 13
| 'A' -> 14
| c -> raise (InvalidOperationException $"Unknown card value { c }")
let handRank' (hand: char seq) =
let jokerCount = hand |> Seq.where ((=) 'J') |> Seq.length
hand
|> Seq.filter ((<>) 'J')
|> Seq.countBy id
|> Seq.choose (fun (card, count) -> if count > 1 then Some count else None)
|> Seq.sortDescending
|> Array.ofSeq
|> function
| [||] when jokerCount = 0 -> [||] // No jokers, no pairs
| [||] -> [| min 5 (1 + jokerCount) |] // Joker will make a pair with another card. min 5 in case hand is entirely jokers
| [| x |] -> [| x + jokerCount |]
| [| x; y |] -> [| x + jokerCount; y |]
| _ -> raise (InvalidOperationException $"More than two pairs found in hand { hand } - this should not be possible")
|> function
| [| 5 |] -> 7
| [| 4 |] -> 6
| [| 3; 2 |] -> 5
| [| 3 |] -> 4
| [| 2; 2 |] -> 3
| [| 2 |] -> 2
| [||] -> 1
| groups -> raise (InvalidOperationException $"Can't rank hand { hand } - unexpected grouping count { groups }")
let part2HandComparer = handComparer handRank' cardRank'
sampleInput |> totalWinnings part2HandComparer
#!fsharp
challengeInput |> totalWinnings part2HandComparer