Complete day 7

This commit is contained in:
Tristan Daniël Maat 2023-12-07 18:22:15 +01:00
parent c3ea85a5ad
commit b1167fe42e
Signed by: tlater
GPG key ID: 49670FD774E43268
3 changed files with 1140 additions and 0 deletions

135
day7/day7.kt Normal file
View file

@ -0,0 +1,135 @@
import java.io.File
typealias Hand = List<Char>
fun main() {
val input = File("input")
val bets = parseHands(input)
println(part1(bets))
println(part2(bets))
}
fun part1(bets: List<Pair<Hand, Int>>): Int {
return bets.sortedBy { handValue(it.first) }.foldIndexed(0) { i, acc, bet ->
acc + (i + 1) * bet.second
}
}
fun part2(bets: List<Pair<Hand, Int>>): Int {
// println(
// bets.sortedBy { handValueJoker(it.first) }.forEach {
// println("${it} - ${handValueJoker(it.first)}")
// }
// )
return bets.sortedBy { handValueJoker(it.first) }.foldIndexed(0) { i, acc, bet ->
acc + (i + 1) * bet.second
}
}
fun cardValue(card: Char): Int {
return when (card) {
'A' -> 12
'K' -> 11
'Q' -> 10
'J' -> 9
'T' -> 8
in '2'..'9' -> card.digitToInt() - 2
else -> throw Exception("Invalid card")
}
}
fun type(cards: Hand): Int {
return cards.groupBy { it }.mapValues { it.value.count() }.entries.fold(0) { acc, entry ->
acc +
when (entry.value) {
5 -> 6000000
4 -> 5000000
3 -> 3000000
2 -> 1000000
1 -> 0
else -> throw Exception("Too many cards")
}
}
}
fun handValue(cards: Hand): Int {
// Each hand has 5 cards, the possible card values are 2-9+TJQKA,
// and the order matters.
//
// This means the hands themselves are basically a 5-digit base 13
// number.
//
// In addition, particular groupings of the cards matter. This is
// represented by the type - to condense this into an easily
// usable number, the type is therefore encoded in digits just
// beyond the max of the 5-digit base 13 numbers. For convenient
// programming, this means the type is encoded by the millions
// digit in base 10, hence the odd type return numbers.
return type(cards) + cards.fold(0) { acc, label -> acc * 13 + cardValue(label) }
}
fun cardValueJoker(card: Char): Int {
return when (card) {
'A' -> 12
'K' -> 11
'Q' -> 10
'T' -> 9
in '2'..'9' -> card.digitToInt() - 1
'J' -> 0
else -> throw Exception("Invalid card")
}
}
fun typeJoker(cards: Hand): Int {
var groups: MutableMap<Char, Int> =
cards.groupBy { it }.mapValues { it.value.count() }.toMutableMap()
// If there is only one group, we return early with the highest
// value (this is to catch edge cases where there are only jokers)
if (groups.entries.size == 1) {
return 6000000
}
val jokers = groups.remove('J') ?: 0
return groups.entries.sortedByDescending { 13 * it.value + cardValueJoker(it.key) }.foldIndexed(
0
) { i, acc, entry ->
acc +
when (entry.value + if (i == 0) jokers else 0) {
5 -> 6000000
4 -> 5000000
3 -> 3000000
2 -> 1000000
1 -> 0
else -> throw Exception("Too many cards")
}
}
}
fun handValueJoker(cards: Hand): Int {
// This is just a bit more complicated; the card value doesn't
// change much, but the *type* changes quite a bit.
//
// Rather than just giving the best type the highest value, we now
// need to take into account scenarios in which ties are broken by
// jokers. To do this, we sort the groups by the value of their
// cards and the number of them.
//
// Since the tie breaker in this case is again the value of the
// card, this is kind of recursive.
return typeJoker(cards) + cards.fold(0) { acc, label -> acc * 13 + cardValueJoker(label) }
// 247838255 is too low
}
fun parseHands(input: File): List<Pair<Hand, Int>> {
return input.useLines {
it
.map {
val (hand, bid) = it.split(' ')
hand.toList() to bid.toInt()
}
.toList()
}
}

1000
day7/input Normal file

File diff suppressed because it is too large Load diff

5
day7/test Normal file
View file

@ -0,0 +1,5 @@
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483