Created
October 5, 2012 00:13
-
-
Save vmarquez/3837265 to your computer and use it in GitHub Desktop.
This is a simple 'mulit-player, concurrent' game that attempts to reduce the number of mutable variables to a minimum.
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.dispatch.Future | |
import akka.dispatch.Promise | |
import scala.collection.JavaConversions._ | |
import scala.collection.JavaConversions | |
import akka.dispatch.ExecutionContext | |
import java.util.concurrent.Executors | |
import java.util.concurrent.atomic.AtomicReference | |
object BombGameNoActorsOrVars { | |
def main(args:Array[String]) { | |
val l = new CountDownLatch(3) | |
implicit val stateMap = new StateMap[Int,User]() | |
val u1 = createUser(1) | |
val u2 = createUser(2) | |
val u3 = createUser(3) | |
for (user <- u1.move(4); | |
_ = Thread.sleep(4000); //shows that the other two users can proceed while this one is waiting | |
user <- user.move(5); | |
user <- user.throwBomb(6); | |
user <- user.throwBomb(4)) { | |
println("finished user1") | |
l.countDown() | |
} | |
for (user <- u2.move(9); | |
user <- user.move(6); | |
user <- user.throwBomb(6); | |
user <- user.throwBomb(6)) { | |
println("finished user 2") | |
l.countDown() | |
} | |
for (user <- u3.move(7); | |
user <- user.move(4); | |
user <- user.throwBomb(0); | |
user <- user.throwBomb(0)) { | |
println("finihsed user 3") | |
l.countDown() | |
} | |
l.await() | |
println("show the stats of the game") | |
stateMap.getAll().foreach(user => println("Id:"+user.id + " Position:"+user.position + " damage:"+user.damage + " hitCount:"+ user.killCount)) | |
} | |
def createUser(i:Int)(implicit userMap: StateMap[Int,User]): User = { | |
val user = User(id=i, position=getRandom()) | |
userMap.put(i,user) | |
user | |
} | |
def getRandom() = Math.abs(scala.util.Random.nextInt() % 9) | |
} | |
case class User(id: Int, position: Int, damage: Int = 0, killCount: Int = 0)(implicit userMap: StateMap[Int,User]) { | |
implicit val ec = ExecutionContext.fromExecutorService(Executors.newCachedThreadPool) | |
implicit val getRef = ()=>userMap.get(id) | |
def move(i: Int) = wrapPromise(p => { | |
p.success(user => user.copy(position=i)) | |
}) | |
def throwBomb(pos:Int): Future[User] = wrapPromise(p => { | |
val users = userMap.getAll() | |
val responseFuture = Future.sequence(users.filter(u => u.position == pos).map(_.shot(pos))) | |
for (responses <- responseFuture) | |
p.success(u => u.copy(killCount = u.killCount+responses.size)) | |
}) | |
def shot(i:Int): Future[User] = wrapPromise(p => { | |
p.success(u => u.copy(damage = u.damage+1)) | |
}) | |
def wrapPromise(f: SynchronizedPromise[User]=>Unit): Future[User] = { | |
val promise = SynchronizedPromise[User]() | |
f(promise) | |
promise.future | |
} | |
} | |
//we need a way to share state with other threads, but we are using mutabiltiy here... | |
//kind of weird I just have a mutalbe map floating around in an object. Better ways to do this? | |
class StateMap[K,V] { | |
private val userMap = new ConcurrentHashMap[K,AtomicReference[V]]() | |
def put(k: K,v: V) = userMap.put(k,new AtomicReference(v)) | |
def get(k:K) = userMap.get(k) | |
def getAll() = userMap.values().map(_.get) | |
} | |
class SynchronizedPromise[T](getRef: ()=>AtomicReference[T]) { | |
implicit val ec = ExecutionContext.fromExecutorService(Executors.newCachedThreadPool) | |
private val innerPromise = Promise[T]() | |
def map[A](f: T=>A) = innerPromise.map(f) | |
def flatMap[A](f: T=>Future[A]) = innerPromise.flatMap(f) | |
def success(f: T=>T) { | |
val ref = getRef() | |
var success = false | |
do { | |
val originalval = ref.get | |
success = ref.compareAndSet(originalval, f(originalval)) | |
} while(!success) | |
innerPromise.success(ref.get()) | |
} | |
def future = innerPromise.future | |
} | |
object SynchronizedPromise { | |
def apply[T]()(implicit getRef:()=>AtomicReference[T]) = | |
new SynchronizedPromise[T](getRef) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment