Skip to content

Instantly share code, notes, and snippets.

@sortega
Created June 25, 2013 22:43
Show Gist options
  • Save sortega/5863141 to your computer and use it in GitHub Desktop.
Save sortega/5863141 to your computer and use it in GitHub Desktop.
Rock, paper, scissors in Scala (and extended to Spock and frieds FTW)
package pogame
object ExtendedGame extends POGame {
sealed trait ExtendedHand extends POrdered[ExtendedHand] {
val weaknesses: Seq[ExtendedHand]
def defeatedBy(other: POrdered[ExtendedHand]): Boolean = weaknesses.contains(other)
}
case object Rock extends ExtendedHand { val weaknesses = Seq[ExtendedHand](Paper, Spock) }
case object Paper extends ExtendedHand { val weaknesses = Seq[ExtendedHand](Scissors, Lizard) }
case object Scissors extends ExtendedHand { val weaknesses = Seq[ExtendedHand](Rock, Spock) }
case object Spock extends ExtendedHand { val weaknesses = Seq[ExtendedHand](Paper, Lizard) }
case object Lizard extends ExtendedHand { val weaknesses = Seq[ExtendedHand](Rock, Scissors) }
type Hand = ExtendedHand
}
package pogame
import org.scalatest.FlatSpec
import org.scalatest.matchers.MustMatchers
import pogame.ExtendedGame._
class ExtendedGameTest extends FlatSpec with MustMatchers {
"Extended game" must "honor Spock powers" in {
ExtendedGame.round(1.0, "Sheldon" -> Spock, "Leonard" -> Scissors, "Howard" -> Rock) must be (Map(
"Sheldon" -> 1.0,
"Leonard" -> 0.0,
"Howard" -> 0.0
))
}
}
package pogame
import scala.collection.immutable
/**
* Hands have a partial order defined by the defeatedBy function.
*/
trait POrdered[T] {
def defeatedBy(other: POrdered[T]): Boolean
}
/**
* Partial ordered game (rock-paper-scissors like).
*/
abstract class POGame {
type Hand <: POrdered[Hand]
/**
* Given the hands of the players distribute a price
* @param amount Amount to distribute between the winners
* @param hands Player hands
* @return What get every player
*/
def round(amount: Double, hands: Map[Any, Hand]): Map[Any, Double] = {
def isNotDefeated(hand: Hand) = !hands.values.exists(hand.defeatedBy(_))
val undefeated = hands.collect { case (player, hand) if isNotDefeated(hand) => player }
val winners = if (undefeated.isEmpty) hands.keySet else undefeated
uniformDistribution(hands.keySet, 0) ++ uniformDistribution(winners, amount)
}
def round(amount: Double, hands: (Any, Hand)*): Map[Any, Double] = round(amount, hands.toMap)
private def uniformDistribution(keys: Iterable[Any], amount: Double) = {
val share = amount / keys.size
keys.map(key => (key, share)).toMap
}
}
package pogame
object SimpleGame extends POGame {
type Hand = SimpleHand
sealed abstract class SimpleHand extends POrdered[SimpleHand] {
val weakness: SimpleHand
def defeatedBy(other: POrdered[SimpleHand]): Boolean = weakness == other
}
case object Rock extends SimpleHand { val weakness = Paper }
case object Paper extends SimpleHand { val weakness = Scissors }
case object Scissors extends SimpleHand { val weakness = Rock }
}
package pogame
import org.scalatest.FlatSpec
import org.scalatest.matchers.MustMatchers
import pogame.SimpleGame._
class SimpleGameTest extends FlatSpec with MustMatchers {
"A simple game round" must "be won by a non defeated hand" in {
SimpleGame.round(1.0, "winner" -> Rock, "loser" -> Scissors) must be (Map(
"winner" -> 1.0,
"loser" -> 0.0
))
}
it must "distribute the price among winners" in {
SimpleGame.round(1.0, "winner1" -> Paper, "winner2" -> Paper, "loser" -> Rock) must be (Map(
"winner1" -> 0.5,
"winner2" -> 0.5,
"loser" -> 0.0
))
}
it must "distribute the price in a tie" in {
SimpleGame.round(3.0, "p1" -> Rock, "p2" -> Paper, "p3" -> Scissors) must be (Map(
"p1" -> 1.0,
"p2" -> 1.0,
"p3" -> 1.0
))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment