59 lines
1.7 KiB
Kotlin
59 lines
1.7 KiB
Kotlin
package rps
|
|
|
|
const val TURNS = 100
|
|
|
|
fun main() {
|
|
println("Outcome from Player 1’s perspective: ${playGame(TURNS)}")
|
|
}
|
|
|
|
/** Determine the [Outcome] of [first] vs [second] from the perspective of [first]. */
|
|
fun determineOutcome(first: Move, second: Move): Outcome = when(second) {
|
|
in first.strongAgainst() -> Outcome.WIN
|
|
first -> Outcome.DRAW
|
|
else -> Outcome.LOSS
|
|
}
|
|
|
|
// Note: This could be slightly more efficiently and directly on the Sequence
|
|
// (but a lot less elegantly) with an imperative loop and three mutable integers.
|
|
fun List<Outcome>.calculateGameSummary() = GameSummary(
|
|
wins = count { it == Outcome.WIN },
|
|
draws = count { it == Outcome.DRAW },
|
|
losses = count { it == Outcome.LOSS },
|
|
)
|
|
|
|
fun playGame(turns: Int): GameSummary = generateSequence { randomMove() to randomMove() }
|
|
.take(turns)
|
|
.map { (p1, p2) -> determineOutcome(p1, p2) }
|
|
.toList()
|
|
.calculateGameSummary()
|
|
|
|
fun randomMove(): Move = Move.values().random()
|
|
|
|
/*
|
|
* Classes and named tuples below this point.
|
|
* (All in the same file for easier reviewability in a web UI on Github/Gitea).
|
|
*/
|
|
|
|
/**
|
|
* A possible move in a game of rock-paper-scissors.
|
|
*
|
|
* [strongAgainst] is a function for lazy evaluation because otherwise we can’t access SCISSORS before it’s been defined.
|
|
*/
|
|
enum class Move(val strongAgainst: () -> Set<Move>) {
|
|
ROCK({ setOf(SCISSORS, LIZARD) }),
|
|
PAPER({ setOf(ROCK, SPOCK) }),
|
|
SCISSORS({ setOf(PAPER, LIZARD) }),
|
|
LIZARD({ setOf(SPOCK, PAPER) }),
|
|
SPOCK({ setOf(SCISSORS, ROCK) }),
|
|
}
|
|
|
|
// purposely not calling this Result to avoid confusion with kotlin.Result and similar
|
|
enum class Outcome {
|
|
WIN,
|
|
DRAW,
|
|
LOSS
|
|
}
|
|
|
|
// Named Triple for readability
|
|
data class GameSummary(val wins: Int, val draws: Int, val losses: Int)
|