generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathY2022D02.kt
83 lines (69 loc) · 3.4 KB
/
Y2022D02.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
package aockt.y2022
import aockt.y2022.Y2022D02.RpsChoice.*
import aockt.y2022.Y2022D02.RpsChoice.Companion.versus
import io.github.jadarma.aockt.core.Solution
object Y2022D02 : Solution {
private enum class RpsChoice(val score: Int) {
Rock(1),
Paper(2),
Scissors(3);
val winsAgainst: RpsChoice get() = winTable[this]!!
val losesAgainst: RpsChoice get() = loseTable[this]!!
companion object {
/** Lookup table that determines which choice beats which other. */
val winTable = mapOf(Rock to Scissors, Paper to Rock, Scissors to Paper)
/** Lookup table that determines which choice loses to which other. */
val loseTable = mapOf(Rock to Paper, Paper to Scissors, Scissors to Rock)
/**
* Compares two choices from the perspective of the first and returns the round score.
* Returns 3 for draw, 6 for player win, and 0 for [other] win.
*/
infix fun RpsChoice.versus(other: RpsChoice): Int = when (other) {
this -> 3
winsAgainst -> 6
else -> 0
}
}
}
@JvmInline
private value class RpsStrategy(
private val strategy: List<Pair<RpsChoice, RpsChoice>>,
) : List<Pair<RpsChoice, RpsChoice>> by strategy {
/** Play a game of Rock-Paper-Scissors and return the final score. */
fun simulateGame(): Int = strategy.sumOf { (opponent, player) -> player.score + (player versus opponent) }
companion object {
private val inputRegex = Regex("""^([ABC]) ([XYZ])$""")
/** Converts a character to an [RpsChoice]. */
private fun Char.toRpsChoice() = when (this) {
'A' -> Rock
'B' -> Paper
'C' -> Scissors
else -> throw IllegalArgumentException("Character '$this' is not a valid encoding for RPS.")
}
/** Converts a character to an [RpsChoice] depending on the [opponent] move. */
private fun Char.toOutcomeChoice(opponent: RpsChoice): RpsChoice = when (this) {
'X' -> opponent.winsAgainst
'Y' -> opponent
'Z' -> opponent.losesAgainst
else -> throw IllegalArgumentException("Character '$this' is not a valid encoding for RPS.")
}
/** Returns the [RpsStrategy] described by the [input], throwing [IllegalArgumentException] if invalid. */
fun parse(input: String, partTwo: Boolean = false): RpsStrategy =
input
.lines()
.map {
val match = inputRegex.matchEntire(it)
requireNotNull(match) { "Line '$it' is not a valid strategy." }
val opponent = match.groupValues[1].first().toRpsChoice()
val player = match.groupValues[2].first().let { p ->
if (partTwo) p.toOutcomeChoice(opponent)
else (p - ('X' - 'A')).toRpsChoice()
}
opponent to player
}
.let(::RpsStrategy)
}
}
override fun partOne(input: String) = RpsStrategy.parse(input, partTwo = false).simulateGame()
override fun partTwo(input: String) = RpsStrategy.parse(input, partTwo = true).simulateGame()
}