Created
September 12, 2012 00:05
-
-
Save vmarquez/3703153 to your computer and use it in GitHub Desktop.
Simple multiplayer, concurrent 'game' written in scala trying to minimize mutability
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.concurrent.ConcurrentHashMap | |
import java.util.concurrent.CountDownLatch | |
import akka.actor.Actor | |
import akka.dispatch.Future | |
import akka.actor.ActorSystem | |
import akka.actor._ | |
import scala.collection.JavaConversions._ | |
import scala.collection.JavaConversions | |
import akka.pattern._ | |
import akka.util.Timeout | |
import akka.util.duration._ | |
import akka.dispatch.ExecutionContext | |
import java.util.concurrent.Executors | |
object BombGame { | |
val system = ActorSystem("BombGame") | |
def main(args:Array[String]) { | |
val l = new CountDownLatch(3) | |
val u1 = createUser(1) | |
val u2 = createUser(2) | |
val u3 = createUser(3) | |
for (user <- u1.move(4); | |
user <- user.move(5); | |
user <- user.throwBomb(6); | |
user <- user.throwBomb(4)) { | |
l.countDown() | |
} | |
for (user <- u2.move(9); | |
user <- user.move(6); | |
user <- user.throwBomb(7); | |
user <- user.throwBomb(4)) { | |
l.countDown() | |
} | |
for (user <- u3.move(7); | |
user <- user.move(4); | |
user <- user.throwBomb(5); | |
user <- user.throwBomb(5)) { | |
l.countDown() | |
} | |
//print out who is wounded the most? | |
l.await() | |
GlobalState.getAll().foreach(user => println("Id:"+user.id + " Position:"+user.position + " damage:"+user.damage + " hitCount:"+ user.killCount)) | |
} | |
def createUser(i:Int): User = { | |
val user = User(id=i, position=getRandom(),actor= system.actorOf(Props[UserActor], "useractor"+i)) | |
GlobalState.putUser(user) | |
user | |
} | |
def getRandom() = Math.abs(scala.util.Random.nextInt() % 9) | |
} | |
//we need a way to share state with other threads, but we are using mutabiltiy here... | |
//feels like there's got to be a more structured way to do this so I won't forget to update it... | |
//not to mention race conditions for trying to update two users at once transactionally | |
object GlobalState { | |
val userMap = new ConcurrentHashMap[Int,User]() | |
def getUser(id:Int) = Option(userMap(id)) | |
def putUser(u:User): User = { | |
userMap.put(u.id, u) | |
u | |
} | |
def getAll() = userMap.values.toList | |
} | |
abstract class Action | |
case class Move(user:User, position:Int) extends Action | |
case class Look(user:User, position:Int) extends Action | |
case class ThrowBomb(user:User, position:Int) extends Action | |
case class Shot(user:User, position:Int) extends Action | |
case class User(id:Int, position:Int, damage:Int = 0, killCount:Int = 0, actor:ActorRef) { | |
implicit val timeout = Timeout(5 seconds) | |
def move(i:Int) = ask(actor, Move(this,i)).mapTo[User] | |
def look(i:Int) = ask(actor, Look(this,i)).mapTo[(User,Seq[User])] | |
def throwBomb(i:Int) = ask(actor, ThrowBomb(this,i)).mapTo[User] | |
def shot(i:Int) = ask(actor, Shot(this, i)).mapTo[(Boolean,User)] | |
} | |
class UserActor extends Actor { | |
implicit val ec = ExecutionContext.fromExecutorService(Executors.newCachedThreadPool) //not sure if I should be using the EC from the ActorSystem... | |
def updatedUser(u:User) = | |
GlobalState.getUser(u.id).getOrElse(u) | |
def receive = { | |
case Move(u,pos) => | |
val user = updatedUser(u).copy(position=pos) | |
sender ! GlobalState.putUser(user) | |
case Look(u, pos) => | |
val user = updatedUser(u) | |
sender ! (user,GlobalState.getAll().filter(user => user.position == pos )) | |
case ThrowBomb(u,pos) => | |
val users= GlobalState.getAll() | |
val responseFuture = Future.sequence(users.map(_.shot(pos))) | |
val s = sender //uh, ths is weird | |
for (responses <- responseFuture) { | |
val user = updatedUser(u) | |
val kills = responses.filter(t => t._1).size | |
s ! GlobalState.putUser(user.copy(killCount = user.killCount+kills)) | |
} | |
case Shot(u, position) => | |
val user = updatedUser(u) | |
val t = | |
if (user.position == position) | |
(true,GlobalState.putUser(user.copy(damage=user.damage+1))) | |
else | |
(false,user) | |
sender ! t | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment