2020-10-22 19:23:09 +02:00
package rps
2020-10-22 19:57:27 +02:00
const val TURNS = 100
2020-10-22 19:23:09 +02:00
fun main ( ) {
2020-10-22 19:57:27 +02:00
println ( " Outcome from Player 1’s perspective: ${playGame(TURNS)} " )
}
/** Determine the [Outcome] of [first] vs [second] from the perspective of [first]. */
2020-10-29 16:41:24 +01:00
fun determineOutcome ( first : Move , second : Move ) : Outcome = when ( second ) {
in first . strongAgainst ( ) -> Outcome . WIN
first -> Outcome . DRAW
2020-10-22 19:57:27 +02:00
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 } ,
)
2020-10-29 16:41:24 +01:00
fun playGame ( turns : Int ) : GameSummary = generateSequence { randomMove ( ) to randomMove ( ) }
2020-10-22 19:57:27 +02:00
. take ( turns )
2020-10-29 16:41:24 +01:00
. map { ( p1 , p2 ) -> determineOutcome ( p1 , p2 ) }
2020-10-22 19:57:27 +02:00
. toList ( )
. calculateGameSummary ( )
2020-10-29 16:41:24 +01:00
fun randomMove ( ) : Move = Move . values ( ) . random ( )
2020-10-22 19:23:09 +02:00
2020-10-22 19:57:27 +02:00
/ *
* Classes and named tuples below this point .
* ( All in the same file for easier reviewability in a web UI on Github / Gitea ) .
* /
2020-10-22 19:32:38 +02:00
/ * *
* 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 .
* /
2020-10-29 16:41:24 +01:00
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 ) } ) ,
2020-10-22 19:23:09 +02:00
}
// purposely not calling this Result to avoid confusion with kotlin.Result and similar
enum class Outcome {
WIN ,
DRAW ,
LOSS
}
2020-10-22 19:41:03 +02:00
// Named Triple for readability
data class GameSummary ( val wins : Int , val draws : Int , val losses : Int )