"We've been playing games since humanity had civilization - there is something primal about our desire and our ability to play games"
Jane McGonigal
Is a mathematical model of computation used to design both computer programs and sequential logic circuits. It is conceived as an abstract machine that can be in one of a finite number of states.
Wikipedia
Actors are lightweight programmable queues of immutable messages which are processed asynchronously and in a non-concurrent fashion. They can communicate with other actors via immutable messages.
Also known as:Any => Unit
A subtype of Actors that model a Finite-State Machine. It's described as a set of relations of:State(S) x Event(E) -> Actions(A), State(S')
sealed trait Message
case object Ping extends Message
case object Pong extends Message
sealed trait State
case object WaitingPing extends State
case object WaitingPong extends State
case class Data(times: Int)
class PingPongFSM extends Actor with FSM[State, Data] {
startWith(WaitingPing, stateData = new Data(0))
when(WaitingPing) {
case Event(Ping, data:Data) =>
println(s"Received Ping, transition ${data.times}")
goto(WaitingPong) using new Data(data.times + 1)
}
when(WaitingPong) {
case Event(Pong, data:Data) =>
println(s"Received Pong, transition ${data.times}")
goto(WaitingPing) using new Data(data.times + 1)
}
initialize()
}
val pingPongFSM = system.actorOf(Props[PingPongFSM], "PingPongFSM")
pingPongFSM ! Ping
pingPongFSM ! Ping
pingPongFSM ! Pong
pingPongFSM ! Ping
>Received Ping, transition 0
>[WARN] ... unhandled event Ping in state WaitingPong
>Received Pong, transition 1
>Received Ping, transition 2
sealed trait BattleshipState
case object WaitingForPlayers extends BattleshipState
case object PlacingShips extends BattleshipState
case object WaitingForNextPlayer extends BattleshipState
case object CheckingShot extends BattleshipState
case object HitShip extends BattleshipState
case object EndGame extends BattleshipState
sealed trait BattleshipMessages
case class PlaceShip(playerId: Int, id: Short, coord: Coord, size: Short, vertical: Boolean) extends BattleshipMessages
case object ShipsPlaced
case object NextPlayer
case class PlaceShot(playerId: Int, coord: Coord) extends BattleshipMessages
case object Miss extends BattleshipMessages
case object Hit extends BattleshipMessages
case object ShipsAlive extends BattleshipMessages
case object AllShipsSunk extends BattleshipMessages
class BattleShipActor extends ActorLogging
with FSM[BattleshipState, BattleshipData] {
...
when(WaitingForPlayers) {
case Event(PlaceShip(playerId, shipId, coord, size, vertical), data)=>
goto(PlacingShips) using
data.placeShip(playerId, shipId, coord, size, vertical)
}
}
when(PlacingShips) {
case Event(PlaceShip(playerId, shipId, coord, size, vertical), data)=>
stay using data.placeShip(playerId, shipId, coord, size, vertical)
case Event(ShipsPlaced, data) =>
goto(WaitingForNextPlayer)
}
onTransition {
case _ -> PlacingShips =>
if (nextStateData.shipsToPlace.forall(_.isEmpty)) self ! ShipsPlaced
onTransition
only works when there is a transition of a state. Not when staying
when(PlacingShips) {
case Event(PlacedShip, _) =>
goto(CheckingPlacedShips)
}
when(CheckingPlacedShips) {
case Event(PlaceShip(playerId, shipId, coord, size, vertical), data)=>
goto(PlacingShips) using data.placeShip(playerId, shipId, coord, size, vertical)
case Event(ShipsPlaced, _) =>
goto(WaitingForNextPlayer)
}
case _ -> PlacingShips => self ! PlacedShip
case _ -> CheckingPlacedShips =>
if (nextStateData.shipsToPlace.forall(_.isEmpty)) self ! ShipsPlaced
when(WaitingForNextPlayer) {
case Event(NextPlayer, data) =>
stay using data.copy(currentPlayer = data.opponent)
case Event(PlaceShot(playerId, x, y), data) =>
goto(CheckingShot) using data.copy(pendingShot = Some(x, y))
}
when(CheckingShot) {
case Event(Miss, data) =>
goto(WaitingForNextPlayer) using data.copy(pendingShot = None)
case Event(Hit, data) =>
goto(HitShip) using data.shoot.copy(pendingShot = None)
}
when(HitShip) {
case Event(ShipsAlive, data) =>
goto(WaitingForNextPlayer) using data
case Event(AllShipsSunk, _) => goto(EndGame)
}
case _ -> WaitingForNextPlayer =>
log.info(s"End of ${nextStateData.currentPlayer} turn")
self ! NextPlayer
case _ -> CheckingShot =>
if (nextStateData.wouldBeAShot) {
log.info("Ship Hit at ${nextStateData.pendingShot}!")
self ! Hit
}
else {
log.info(s"Missed at ${nextStateData.pendingShot}!")
self ! Miss
}
case _ -> HitShip =>
if (nextStateData.areShipsAlive(nextStateData.opponent))
self ! ShipsAlive
else self ! AllShipsSunk
case _ -> EndGame => log.info("Game is Over")
when(WaitingForNextPlayer, stateTimeout = 30 seconds) {
case Event(NextPlayer, data) =>
stay using data.copy(currentPlayer = data.opponent)
case Event(PlaceShot(playerId, x, y), data) =>
goto(CheckingShot) using data.copy(pendingShot = Some(x, y))
case Event(StateTimeout, data) =>
stay using data.copy(currentPlayer = data.opponent)
}