generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathY2023D04.kt
70 lines (58 loc) · 2.78 KB
/
Y2023D04.kt
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
package aockt.y2023
import aockt.util.parse
import io.github.jadarma.aockt.core.Solution
import kotlin.math.pow
object Y2023D04 : Solution {
/**
* Information about an elven scratchcard.
*
* @property id The ID.
* @property winningNumbers The winning numbers, written on the left of the pipe.
* @property cardNumbers The numbers you have, written on the right of the pipe.
* @property matchingCount How many [cardNumbers] are also [winningNumbers].
* @property value The estimated numerical value of the card based on its [matchingCount].
* @property prizeCards The IDs of the cards that can be claimed for this card based on its [matchingCount].
*/
private data class Scratchcard(val id: Int, val winningNumbers: Set<Int>, val cardNumbers: Set<Int>) {
val matchingCount = cardNumbers.intersect(winningNumbers).size
val value: Int = when (matchingCount) {
0 -> 0
else -> 2.0.pow(matchingCount - 1).toInt()
}
val prizeCards: Set<Int> = when (matchingCount) {
0 -> emptySet()
else -> List(size = matchingCount) { id + 1 + it }.toSet()
}
}
/** Parses the [input] and returns the scratchcards. */
private fun parseInput(input: String): Set<Scratchcard> = parse {
val cardRegex = Regex("""^Card\s+(\d+): ([\d ]+) \| ([\d ]+)$""")
fun parseNumberSet(input: String): Set<Int> =
input.split(' ').filter(String::isNotBlank).map(String::toInt).toSet()
input
.lineSequence()
.map { line -> cardRegex.matchEntire(line)!!.destructured }
.map { (id, left, right) -> Scratchcard(id.toInt(), parseNumberSet(left), parseNumberSet(right)) }
.toSet()
}
/**
* Takes a set of [Scratchcard]s and collects other cards as prises until all cards cave been claimed.
* Assumes the set contains cards of consecutive IDs without gaps.
* Returns pairs of scratchcards and their total count, indexed by id.
*/
private fun Set<Scratchcard>.tradeIn(): Map<Int, Pair<Scratchcard, Int>> = buildMap {
[email protected] { put(it.id, it to 1) }
val maxLevel = keys.maxOf { it }
for (id in 1..maxLevel) {
require(id in keys) { "Invalid input. Missing info for card #$id/$maxLevel." }
}
for (id in 1..maxLevel) {
val (card, copies) = getValue(id)
for (prizeId in card.prizeCards.filter { it <= maxLevel }) {
put(prizeId, getValue(prizeId).run { copy(second = second + copies) })
}
}
}
override fun partOne(input: String) = parseInput(input).sumOf(Scratchcard::value)
override fun partTwo(input: String) = parseInput(input).tradeIn().values.sumOf { it.second }
}