generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathY2021D21.kt
97 lines (81 loc) · 3.92 KB
/
Y2021D21.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
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
package aockt.y2021
import io.github.jadarma.aockt.core.Solution
object Y2021D21 : Solution {
/**
* Simulates a practice round of the Dirac Dice game with a 100-sided deterministic die and a winning score of 1000.
* @return The number of times the dice was rolled, and the scores of each player.
*/
private fun playPracticeRound(
playerOneStartSquare: Int = 0,
playerTwoStartSquare: Int = 0,
): Triple<Int, Int, Int> {
require(playerOneStartSquare in 1..10) { "Invalid starting position for player 1." }
require(playerOneStartSquare in 1..10) { "Invalid starting position for player 2." }
val position = mutableListOf(playerOneStartSquare - 1, playerTwoStartSquare - 1)
val score = mutableListOf(0, 0)
var player = 0
var rolls = 0
fun nextRoll() = rolls++ % 100 + 1
fun gameWon() = score.any { it >= 1000 }
fun play(player: Int) {
val increment = List(3) { nextRoll() }.sum()
val current = position[player]
position[player] = (current + increment) % 10
score[player] += position[player] + 1
}
while (!gameWon()) {
play(player)
player = (player + 1) % 2
}
return Triple(rolls, score[0], score[1])
}
/**
* Simulates all the parallel universes with a three sided quantum die and returns the win rate of the players.
* @return In how many universes each player won.
*/
private fun playQuantumRound(
playerOneStartSquare: Int = 0,
playerTwoStartSquare: Int = 0,
): Pair<Long, Long> {
require(playerOneStartSquare in 1..10) { "Invalid starting position for player 1." }
require(playerOneStartSquare in 1..10) { "Invalid starting position for player 2." }
val possibleRolls = with(listOf(1, 2, 3)) { flatMap { a -> flatMap { b -> map { c -> listOf(a, b, c) } } } }
.groupingBy { it.sum() }
.eachCount()
.mapValues { it.value.toLong() }
fun play(position: List<Int>, score: List<Int>, player: Int): Pair<Long, Long> =
possibleRolls.map { (roll, count) ->
val nextPosition = position.toMutableList().apply { set(player, (position[player] + roll) % 10) }
val nextScore = score.toMutableList().apply { set(player, (score[player] + nextPosition[player] + 1)) }
when (nextScore.indexOfFirst { it >= 21 }) {
0 -> count to 0L
1 -> 0L to count
else -> play(nextPosition, nextScore, (player + 1) % 2).run { first * count to second * count }
}
}.reduce { acc, wins -> acc.first + wins.first to acc.second + wins.second }
return play(
position = listOf(playerOneStartSquare - 1, playerTwoStartSquare - 1),
score = listOf(0, 0),
player = 0,
)
}
/** Parse the [input] and return the starting positions of player one and two respectively. */
private fun parseInput(input: String): Pair<Int, Int> = runCatching {
val (line1, line2) = input.lines()
val lineRegex = Regex("""^Player (\d+) starting position: (\d+)$""")
val (p1, pos1) = lineRegex.matchEntire(line1)!!.destructured
val (p2, pos2) = lineRegex.matchEntire(line2)!!.destructured
require(p1 == "1" && p2 == "2")
pos1.toInt() to pos2.toInt()
}.getOrElse { throw IllegalArgumentException("Invalid input.", it) }
override fun partOne(input: String): Int {
val (p1, p2) = parseInput(input)
val (rolls, scoreP1, scoreP2) = playPracticeRound(p1, p2)
return rolls * minOf(scoreP1, scoreP2)
}
override fun partTwo(input: String): Long {
val (p1, p2) = parseInput(input)
val (universesWhereP1Won, universesWhereP2Won) = playQuantumRound(p1, p2)
return maxOf(universesWhereP1Won, universesWhereP2Won)
}
}