fork(1) download
  1. import scala.annotation.tailrec
  2.  
  3. trait State {
  4. def isAcceptState: Boolean
  5. }
  6.  
  7. trait FSM[T <: FSM[T, A, S], -A, +S <: State] { this: T =>
  8. def state: S
  9.  
  10. def resume(input: A): T
  11. }
  12.  
  13. object FSM {
  14. @tailrec
  15. def resumeUntilAcceptance[T <: FSM[T, A, S], A, S <: State](fsm: T, inputs: Iterator[A]): T = {
  16. if (inputs.hasNext) {
  17. val input = inputs.next
  18. val newFSM = fsm.resume(input)
  19. if (newFSM.state.isAcceptState) {
  20. newFSM
  21. } else {
  22. resumeUntilAcceptance[T, A, S](newFSM, inputs)
  23. }
  24. } else {
  25. throw new Exception("not enough inputs")
  26. }
  27. }
  28. }
  29.  
  30. sealed abstract class BattleAlphabet
  31. case object Action extends BattleAlphabet
  32. case object Bonus extends BattleAlphabet
  33.  
  34. sealed abstract class BattleState extends State
  35. case object PlayerTurn extends BattleState { override def isAcceptState = false }
  36. case object EnemyTurn extends BattleState { override def isAcceptState = false }
  37. case object PlayerWon extends BattleState { override def isAcceptState = true }
  38. case object EnemyWon extends BattleState { override def isAcceptState = true }
  39.  
  40. case class Battle(playerHealth: Int,
  41. enemyHealth: Int,
  42. state: BattleState)
  43. extends FSM[Battle, BattleAlphabet, BattleState] {
  44. override def resume(input: BattleAlphabet) = state match {
  45. case PlayerTurn =>
  46. input match {
  47. case Action =>
  48. val nextState = if (enemyHealth == 1) PlayerWon else EnemyTurn
  49. copy(enemyHealth = enemyHealth - 1, state = nextState)
  50. case Bonus =>
  51. copy(playerHealth = playerHealth + 1)
  52. }
  53. case EnemyTurn =>
  54. input match {
  55. case Action =>
  56. val nextState = if (playerHealth == 1) EnemyWon else PlayerTurn
  57. copy(playerHealth = playerHealth - 1, state = nextState)
  58. case Bonus =>
  59. copy(enemyHealth = enemyHealth + 1)
  60. }
  61. case PlayerWon => this
  62. case EnemyWon => this
  63. }
  64. }
  65.  
  66. object Main extends App {
  67. val battle = Battle(2, 2, PlayerTurn)
  68. println(FSM.resumeUntilAcceptance[Battle, BattleAlphabet, BattleState](battle, List(Action, Bonus, Action, Action, Action).iterator))
  69. }
  70.  
Success #stdin #stdout 0.42s 382080KB
stdin
Standard input is empty
stdout
Battle(0,1,EnemyWon)