Skip to content

Instantly share code, notes, and snippets.

@oxlade39
Last active August 29, 2015 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oxlade39/11220125 to your computer and use it in GitHub Desktop.
Save oxlade39/11220125 to your computer and use it in GitHub Desktop.
Akka demonstration of the Monty Hall problem
name := "doorchoice"
version := "1.0"
scalaVersion := "2.11.0"
resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
libraryDependencies +=
"com.typesafe.akka" %% "akka-actor" % "2.3.2"
package com.doorchoice
import scala.util.Random
import akka.actor._
import com.doorchoice.DoorChoice.{SwitchingStrategy, GameStrategyRunner, StickingStrategy}
import akka.util.Timeout
import akka.actor.Terminated
import java.util.concurrent.TimeUnit
object DoorChoice {
val NumberOfDoors = 3
sealed trait Prize
case object Car extends Prize
case object Donkey extends Prize
case class Select(door: Int) {
assert (door < NumberOfDoors)
}
case class OpenedDoorWithDonkey(to: Int) {
assert (to < NumberOfDoors)
}
sealed trait GameOption
case object Switch extends GameOption
case object Stay extends GameOption
sealed trait Result
case object Win
case object Lose
def options() = Random.shuffle(Car :: List.fill(NumberOfDoors - 1)(Donkey))
class Game extends Actor with ActorLogging {
val doors = options()
def start: Receive = {
case Select(door) =>
val indicesOfDonkeys =
doors.zipWithIndex.filter(_._1 == Donkey).filterNot(_._2 == door).map(_._2)
val opened: Int = Random.shuffle(indicesOfDonkeys).head
sender ! OpenedDoorWithDonkey(opened)
context.become(selected(door, opened))
}
def selected(door: Int, opened: Int): Receive = {
case Switch =>
if (doors(door) == Donkey && doors(opened) == Donkey) sender ! Win
else sender ! Lose
case Stay =>
if (doors(door) == Car) sender ! Win
else sender ! Lose
}
def receive: Receive = start
}
abstract class Strategy extends Actor with ActorLogging {
import akka.pattern._
import concurrent.duration._
import context.dispatcher
implicit val timeout = Timeout(5 minutes)
val game = context.actorOf(Props[Game], "game")
def chooseOption(): GameOption
val winOrLoose = for {
open <- (game ? Select(Random.nextInt(3))).mapTo[OpenedDoorWithDonkey]
result <- game ? chooseOption()
} yield result
winOrLoose pipeTo self
def receive: Actor.Receive = {
case Win =>
log.debug("We WON!!!!!")
context.parent ! Win
context.stop(self)
case Lose =>
log.debug("We LOST!!!!!")
context.parent ! Lose
context.stop(self)
}
}
class StickingStrategy extends Strategy {
override def chooseOption(): GameOption = Stay
}
class SwitchingStrategy extends Strategy {
override def chooseOption(): GameOption = Switch
}
class GameStrategyRunner(count: Int, strategy: Props) extends Actor with ActorLogging {
var children = ((0 until count) map (i => context.watch(context.actorOf(strategy, s"strategy-$i")))).toSet
var winCount = 0
var loseCount = 0
def receive: Actor.Receive = {
case Win => winCount += 1
case Lose => loseCount += 1
case Terminated(strategy) =>
children -= strategy
if (children.isEmpty) {
log.info("Total result: Wins {}, Losses {}", winCount, loseCount)
context.stop(self)
}
}
}
}
object Play extends App {
val system = ActorSystem("sys")
val startTime = System.nanoTime()
system.actorOf(Props(new Actor with ActorLogging {
var strategies = Set(
context.watch(context.actorOf(Props(new GameStrategyRunner(1000000, Props[StickingStrategy])), "sticking")),
context.watch(context.actorOf(Props(new GameStrategyRunner(1000000, Props[SwitchingStrategy])), "switching"))
)
def receive: Receive = {
case Terminated(a) =>
strategies -= a
if (strategies.isEmpty) {
val nsTaken = System.nanoTime() - startTime
log.info("total time taken: {}ns == {}ms == {}s", nsTaken, TimeUnit.NANOSECONDS.toMillis(nsTaken), TimeUnit.NANOSECONDS.toSeconds(nsTaken))
system.shutdown()
}
}
}), "guardian")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment