Taboo.scala 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. package models
  2. import akka.actor._
  3. import scala.concurrent.duration._
  4. import play.api.libs.concurrent._
  5. import play.api.libs.json._
  6. import play.api.Play.current
  7. import play.api.libs.concurrent.Execution.Implicits._
  8. object Team {
  9. implicit val writes = new Writes[Team] {
  10. def writes(o: Team) = Json.obj(
  11. "members" -> o.members,
  12. "player" -> o.player,
  13. "guessers" -> o.guessers
  14. )
  15. }
  16. }
  17. class Team {
  18. var members = List.empty[String]
  19. var player = ""
  20. var guessers = Set.empty[String]
  21. def hasPlayer(user: String) = members.indexOf(user) >= 0
  22. def nextPlayer() = {
  23. val index = (members.indexOf(player) + 1) % members.size
  24. player = members(index)
  25. guessers = members.filterNot(_ == player).toSet
  26. player
  27. }
  28. def join(user: String) = {
  29. if(!hasPlayer(user)) {
  30. members = members :+ user
  31. }
  32. }
  33. def leave(user: String) = {
  34. members = members.filterNot(_ == user)
  35. }
  36. def isEmpty = members.isEmpty
  37. def size = members.size
  38. }
  39. object Round {
  40. implicit val writes = new Writes[Round] {
  41. def writes(o: Round) = Json.obj(
  42. "team" -> o.team,
  43. "monitors" -> o.monitors
  44. )
  45. }
  46. }
  47. case class Round(team: Team, monitors: Set[String])
  48. case class Information(text: String)
  49. case class Guess(username: String, text: String)
  50. case object Pass
  51. case class Taboo(username: String)
  52. case object End
  53. case class Score(kind: String, points: Int, card: Card, username: String = "")
  54. case object NextCard
  55. case object PrepRound
  56. case object StartRound
  57. case class EndRound(points: Int, card: Option[Card])
  58. class TabooGame(val chatActor: ActorRef) extends Actor {
  59. var ready = false
  60. var round: Option[Round] = None
  61. var roundActor: ActorRef = null
  62. val teamA: Team = new Team
  63. val teamB: Team = new Team
  64. var currentTeam: Team = teamB
  65. var opposingTeam: Team = teamA
  66. def receive = {
  67. case Join(username) =>
  68. if(teamA.size < 2 || teamA.size <= teamB.size) {
  69. teamA.join(username)
  70. }
  71. else {
  72. teamB.join(username)
  73. }
  74. announceStatus("join", username)
  75. if(round.isEmpty) {
  76. self ! PrepRound
  77. }
  78. case Quit(username) =>
  79. teamA.leave(username)
  80. teamB.leave(username)
  81. announceStatus("quit", username)
  82. if(teamA.size < 2 || currentTeam == teamB && teamB.size < 2) {
  83. if(ready) {
  84. ready = false
  85. }
  86. if(!round.isEmpty) {
  87. roundActor ! End
  88. }
  89. }
  90. case Talk(username, "/status") => announceStatus()
  91. case Talk(username, text) => round match {
  92. case Some(round) =>
  93. if(username == round.team.player) {
  94. if(text == "/pass") {
  95. roundActor ! Pass
  96. }
  97. else {
  98. roundActor ! Information(text)
  99. }
  100. }
  101. else if(round.team.guessers(username)) {
  102. roundActor ! Guess(username, text)
  103. }
  104. else if(round.monitors(username) && text == "/taboo") {
  105. roundActor ! Taboo(username)
  106. }
  107. case None =>
  108. if(username == currentTeam.player && text == "/start") {
  109. self ! StartRound
  110. }
  111. }
  112. case Score(kind, points, card, user) =>
  113. chatActor ! Announce(Json.obj(
  114. "kind" -> "point",
  115. "action" -> kind,
  116. "card" -> card,
  117. "user" -> user
  118. ))
  119. self ! NextCard
  120. case NextCard =>
  121. val card = Card.getRandom()
  122. roundActor ! card
  123. val message = Json.obj(
  124. "kind" -> "card",
  125. "card" -> card
  126. )
  127. (round.get.monitors + player).foreach { user =>
  128. chatActor ! Tell(user, message)
  129. }
  130. case PrepRound =>
  131. if(!ready && teamA.size >= 2) {
  132. ready = true
  133. if(teamB.size < 2) {
  134. currentTeam = teamA
  135. opposingTeam = teamB
  136. }
  137. else {
  138. val temp = currentTeam
  139. currentTeam = opposingTeam
  140. opposingTeam = temp
  141. }
  142. currentTeam.nextPlayer()
  143. chatActor ! Announce(Json.obj(
  144. "kind" -> "roundReady",
  145. "player" -> currentTeam.player
  146. ))
  147. }
  148. case StartRound =>
  149. round = Some(Round(currentTeam, opposingTeam.members.toSet))
  150. roundActor = context.actorOf(Props[TabooRound])
  151. Akka.system.scheduler.scheduleOnce(1 minute, roundActor, End)
  152. chatActor ! Announce(Json.obj(
  153. "kind" -> "roundStart",
  154. "round" -> round
  155. ))
  156. self ! NextCard
  157. case EndRound(points, card) =>
  158. chatActor ! Announce(Json.obj(
  159. "kind" -> "roundEnd",
  160. "points" -> points,
  161. "card" -> card
  162. ))
  163. round = None
  164. ready = false
  165. self ! PrepRound
  166. }
  167. def player = round.get.team.player
  168. def announceStatus(kind: String = "status", user: String = "*GM") {
  169. chatActor ! Announce(Json.obj(
  170. "kind" -> kind,
  171. "user" -> user,
  172. "teamA" -> teamA,
  173. "teamB" -> teamB
  174. ))
  175. }
  176. self ! PrepRound
  177. }
  178. class TabooRound extends Actor {
  179. var card: Option[Card] = None
  180. var points = 0
  181. def receive = {
  182. case newCard: Card =>
  183. card = Some(newCard)
  184. case Guess(username, text) => card.map { card =>
  185. if(card.isCorrect(text)) {
  186. points += 1
  187. sender ! Score("correct", points, card, username)
  188. this.card = None
  189. }
  190. }
  191. case Information(text) => card.map { card =>
  192. if(card.isTaboo(text)) {
  193. points -= 1
  194. sender ! Score("invalid", points, card)
  195. this.card = None
  196. }
  197. }
  198. case Pass => card.map { card =>
  199. points -= 1
  200. sender ! Score("pass", points, card)
  201. this.card = None
  202. }
  203. case Taboo(username) => card.map { card =>
  204. points -= 1
  205. sender ! Score("taboo", points, card)
  206. this.card = None
  207. }
  208. case End =>
  209. sender ! EndRound(points, card)
  210. context.stop(self)
  211. }
  212. }