|
@@ -0,0 +1,236 @@
|
|
|
+package models
|
|
|
+
|
|
|
+import akka.actor._
|
|
|
+import scala.concurrent.duration._
|
|
|
+import play.api.libs.concurrent._
|
|
|
+import play.api.Play.current
|
|
|
+import play.api.libs.concurrent.Execution.Implicits._
|
|
|
+
|
|
|
+case class Card(word: String, taboo: Set[String]) {
|
|
|
+
|
|
|
+ def isTaboo(text: String) = {
|
|
|
+ val lower = text.toLowerCase
|
|
|
+ def contains(word: String) = {
|
|
|
+ lower.indexOf(word.toLowerCase) >= 0
|
|
|
+ }
|
|
|
+
|
|
|
+ // check if text contains word or anything in taboo
|
|
|
+ (taboo + word).map(contains).foldLeft(false)(_ || _)
|
|
|
+ }
|
|
|
+
|
|
|
+ def isCorrect(text: String) = {
|
|
|
+ text.toLowerCase.indexOf(word.toLowerCase) >= 0
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+class Team {
|
|
|
+ var members = List.empty[String]
|
|
|
+ var player = ""
|
|
|
+ var guessers = Set.empty[String]
|
|
|
+
|
|
|
+ def hasPlayer(user: String) = members.indexOf(user) >= 0
|
|
|
+
|
|
|
+ def nextPlayer() = {
|
|
|
+ val index = (members.indexOf(player) + 1) % members.size
|
|
|
+ player = members(index)
|
|
|
+ guessers = members.filterNot(_ == player).toSet
|
|
|
+ player
|
|
|
+ }
|
|
|
+
|
|
|
+ def join(user: String) = {
|
|
|
+ if(!hasPlayer(user)) {
|
|
|
+ members = members :+ user
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ def leave(user: String) = {
|
|
|
+ members = members.filterNot(_ == user)
|
|
|
+ }
|
|
|
+
|
|
|
+ def isEmpty = members.isEmpty
|
|
|
+
|
|
|
+ def size = members.size
|
|
|
+}
|
|
|
+
|
|
|
+case class Round(team: Team, monitors: Set[String])
|
|
|
+
|
|
|
+case class Information(text: String)
|
|
|
+case class Guess(username: String, text: String)
|
|
|
+case object Pass
|
|
|
+case class Taboo(username: String)
|
|
|
+
|
|
|
+case class Correct(username: String, card: Card)
|
|
|
+case class Invalid(card: Card)
|
|
|
+case class Passed(card: Card)
|
|
|
+case class Tabooed(username: String, card: Card)
|
|
|
+
|
|
|
+case object NextCard
|
|
|
+case object PrepRound
|
|
|
+case object StartRound
|
|
|
+case object End
|
|
|
+case class Points(points: Int)
|
|
|
+
|
|
|
+class TabooGame(val chatActor: ActorRef) extends Actor {
|
|
|
+ var ready = false
|
|
|
+ var round: Option[Round] = None
|
|
|
+ var roundActor: ActorRef = null
|
|
|
+
|
|
|
+ val teamA: Team = new Team
|
|
|
+ val teamB: Team = new Team
|
|
|
+
|
|
|
+ var currentTeam: Team = teamB
|
|
|
+ var opposingTeam: Team = teamA
|
|
|
+
|
|
|
+ def receive = {
|
|
|
+ case Join(username) =>
|
|
|
+ if(teamA.size < 2 || teamA.size <= teamB.size) {
|
|
|
+ teamA.join(username)
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ teamB.join(username)
|
|
|
+ }
|
|
|
+ if(round.isEmpty) {
|
|
|
+ self ! PrepRound
|
|
|
+ }
|
|
|
+
|
|
|
+ case Quit(username) =>
|
|
|
+ teamA.leave(username)
|
|
|
+ teamB.leave(username)
|
|
|
+ if(!round.isEmpty && (teamA.size < 2 || (currentTeam == teamB && teamB.size < 2))) {
|
|
|
+ roundActor ! End
|
|
|
+ }
|
|
|
+
|
|
|
+ case Talk(username, text) => round match {
|
|
|
+ case Some(round) =>
|
|
|
+ if(username == round.team.player) {
|
|
|
+ if(text == "/pass") {
|
|
|
+ roundActor ! Pass
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ roundActor ! Information(text)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(round.team.guessers(username)) {
|
|
|
+ roundActor ! Guess(username, text)
|
|
|
+ }
|
|
|
+ else if(round.monitors(username) && text == "/taboo") {
|
|
|
+ roundActor ! Taboo(username)
|
|
|
+ }
|
|
|
+
|
|
|
+ case None =>
|
|
|
+ if(username == currentTeam.player && text == "/start") {
|
|
|
+ self ! StartRound
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ case Correct(username, card) =>
|
|
|
+ nextCard(username+" got it right!", card)
|
|
|
+
|
|
|
+ case Invalid(card) =>
|
|
|
+ nextCard("Uh-uh! "+player+" said a taboo word. Sorry.", card)
|
|
|
+
|
|
|
+ case Passed(card) =>
|
|
|
+ nextCard(player+" has passed.", card)
|
|
|
+
|
|
|
+ case Tabooed(username, card) =>
|
|
|
+ nextCard("Oh no! "+player+" apparently said a taboo word. :(", card)
|
|
|
+
|
|
|
+ case Points(points) =>
|
|
|
+ chatActor ! Talk("*GM", "The round is over. The team got "+points)
|
|
|
+ round = None
|
|
|
+ ready = false
|
|
|
+ self ! PrepRound
|
|
|
+
|
|
|
+ case NextCard =>
|
|
|
+ val card = randomCard()
|
|
|
+ roundActor ! card
|
|
|
+
|
|
|
+ (round.get.monitors + player).foreach { user =>
|
|
|
+ chatActor ! Tell(
|
|
|
+ "*GM",
|
|
|
+ "The word is "+card.word+". The taboo words are: "+card.taboo.reduceLeft(_+" "+_),
|
|
|
+ user)
|
|
|
+ }
|
|
|
+
|
|
|
+ case PrepRound =>
|
|
|
+ if(!ready && teamA.size >= 2) {
|
|
|
+ ready = true
|
|
|
+
|
|
|
+ if(teamB.size < 2) {
|
|
|
+ currentTeam = teamA
|
|
|
+ opposingTeam = teamB
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ val temp = currentTeam
|
|
|
+ currentTeam = opposingTeam
|
|
|
+ opposingTeam = temp
|
|
|
+ }
|
|
|
+ currentTeam.nextPlayer()
|
|
|
+
|
|
|
+ chatActor ! Talk("*GM", "Next round, the player will be "+currentTeam.player)
|
|
|
+ chatActor ! Tell("*GM", "Type /start to start the round", currentTeam.player)
|
|
|
+ }
|
|
|
+
|
|
|
+ case StartRound =>
|
|
|
+ round = Some(Round(currentTeam, opposingTeam.members.toSet))
|
|
|
+ roundActor = context.actorOf(Props[TabooRound])
|
|
|
+ Akka.system.scheduler.scheduleOnce(1 minute, roundActor, End)
|
|
|
+ self ! NextCard
|
|
|
+ }
|
|
|
+
|
|
|
+ def player = round.get.team.player
|
|
|
+
|
|
|
+ def randomCard() = Card("test", Set("a", "b", "c", "d", "e"))
|
|
|
+
|
|
|
+ def nextCard(message: String, oldCard: Card) = {
|
|
|
+ chatActor ! Talk("*GM", message+" The word was "+oldCard.word+".")
|
|
|
+ self ! NextCard
|
|
|
+ }
|
|
|
+
|
|
|
+ self ! PrepRound
|
|
|
+}
|
|
|
+
|
|
|
+class TabooRound extends Actor {
|
|
|
+ var card: Option[Card] = None
|
|
|
+ var points = 0
|
|
|
+
|
|
|
+ def receive = {
|
|
|
+ case newCard: Card =>
|
|
|
+ card = Some(newCard)
|
|
|
+
|
|
|
+ case Guess(username, text) => card.map { card =>
|
|
|
+ if(card.isCorrect(text)) {
|
|
|
+ points += 1
|
|
|
+ sender ! Correct(username, card)
|
|
|
+ this.card = None
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ case Information(text) => card.map { card =>
|
|
|
+ if(card.isTaboo(text)) {
|
|
|
+ points -= 1
|
|
|
+ sender ! Invalid(card)
|
|
|
+ this.card = None
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ case Pass => card.map { card =>
|
|
|
+ points -= 1
|
|
|
+ sender ! Passed(card)
|
|
|
+ this.card = None
|
|
|
+ }
|
|
|
+
|
|
|
+ case Taboo(username) => card.map { card =>
|
|
|
+ points -= 1
|
|
|
+ sender ! Tabooed(username, card)
|
|
|
+ this.card = None
|
|
|
+ }
|
|
|
+
|
|
|
+ case End =>
|
|
|
+ sender ! Points(points)
|
|
|
+ context.stop(self)
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|