Event export and basic sync: can play with the AI

This commit is contained in:
Thibault Duplessis 2012-03-17 10:14:12 +01:00
parent ef73f21eed
commit 8a3922cb50
7 changed files with 73 additions and 22 deletions

View file

@ -5,14 +5,17 @@ import DataForm._
import play.api._ import play.api._
import play.api.mvc._ import play.api.mvc._
import play.api.libs.json._ import com.codahale.jerkson.Json.{ generate => jsonify }
object Application extends Controller { object Application extends Controller {
val env = new HttpEnv(Play.unsafeApplication.configuration.underlying) val env = new HttpEnv(Play.unsafeApplication.configuration.underlying)
val json = "application/json"
def sync(id: String, color: String, version: Int, fullId: String) = Action { def sync(id: String, color: String, version: Int, fullId: String) = Action {
Ok(toJson(env.syncer.sync(id, color, version, fullId))) Ok(jsonify(
env.syncer.sync(id, color, version, fullId).unsafePerformIO
)) as json
} }
def move(fullId: String) = Action { implicit request def move(fullId: String) = Action { implicit request

View file

@ -14,13 +14,13 @@ trait Dependencies {
val scalaz = "org.scalaz" %% "scalaz-core" % "6.0.4" val scalaz = "org.scalaz" %% "scalaz-core" % "6.0.4"
val specs2 = "org.specs2" %% "specs2" % "1.8.2" val specs2 = "org.specs2" %% "specs2" % "1.8.2"
val redis = "net.debasishg" %% "redisclient" % "2.4.2" val redis = "net.debasishg" %% "redisclient" % "2.4.2"
val json = "net.liftweb" %% "lift-json" % "2.4"
val casbah = "com.mongodb.casbah" %% "casbah" % "2.1.5-1" val casbah = "com.mongodb.casbah" %% "casbah" % "2.1.5-1"
val salat = "com.novus" %% "salat-core" % "0.0.8-SNAPSHOT" val salat = "com.novus" %% "salat-core" % "0.0.8-SNAPSHOT"
val slf4j = "org.slf4j" % "slf4j-nop" % "1.6.4" val slf4j = "org.slf4j" % "slf4j-nop" % "1.6.4"
val scalalib = "com.github.ornicar" %% "scalalib" % "1.23" val scalalib = "com.github.ornicar" %% "scalalib" % "1.23"
val hasher = "com.roundeights" % "hasher" % "0.3" from "http://cloud.github.com/downloads/Nycto/Hasher/hasher_2.9.1-0.3.jar" val hasher = "com.roundeights" % "hasher" % "0.3" from "http://cloud.github.com/downloads/Nycto/Hasher/hasher_2.9.1-0.3.jar"
val config = "com.typesafe.config" % "config" % "0.3.0" val config = "com.typesafe.config" % "config" % "0.3.0"
val json = "com.codahale" %% "jerkson" % "0.5.0"
// benchmark // benchmark
val instrumenter = "com.google.code.java-allocation-instrumenter" % "java-allocation-instrumenter" % "2.0" val instrumenter = "com.google.code.java-allocation-instrumenter" % "java-allocation-instrumenter" % "2.0"

View file

@ -6,6 +6,8 @@ import scalaz.effects._
final class Syncer(repo: GameRepo) { final class Syncer(repo: GameRepo) {
val reload = Map("reload" -> true)
def sync( def sync(
id: String, id: String,
colorString: String, colorString: String,
@ -15,16 +17,16 @@ final class Syncer(repo: GameRepo) {
color io { Color(colorString) err "Invalid color" } color io { Color(colorString) err "Invalid color" }
gameAndPlayer repo.player(id, color) gameAndPlayer repo.player(id, color)
(g, p) = gameAndPlayer (g, p) = gameAndPlayer
//events io { eventsSince(p, version) } } yield {
} yield Map( p.eventStack eventsSince version map { events
"v" -> p.eventStack.version, Map(
//"e" -> events map (_.toMap), "v" -> p.eventStack.lastVersion,
"p" -> g.player, "e" -> (events map (_.export)),
"t" -> g.turns "p" -> g.player.color.name,
) "t" -> g.turns
} except (e io(Map("reload" -> true))) )
} getOrElse reload
def eventsSince(player: DbPlayer, version: Int): List[Event] = }
player.eventStack.eventsSince(version) | Nil } except (e io(reload))
} }

View file

@ -15,6 +15,9 @@ trait SystemEnv {
repo = gameRepo, repo = gameRepo,
ai = ai) ai = ai)
def syncer = new Syncer(
repo = gameRepo)
lazy val ai: Ai = new StupidAi lazy val ai: Ai = new StupidAi
def gameRepo = new GameRepo( def gameRepo = new GameRepo(

View file

@ -6,6 +6,7 @@ import Pos.{ piotr, allPiotrs }
sealed trait Event { sealed trait Event {
def encode: String def encode: String
def export: Map[String, Any]
} }
object Event { object Event {
@ -48,6 +49,8 @@ object EventDecoder {
case class StartEvent() extends Event { case class StartEvent() extends Event {
def encode = "s" def encode = "s"
def export = Map(
"type" -> "start")
} }
object StartEvent extends EventDecoder { object StartEvent extends EventDecoder {
def decode(str: String) = Some(StartEvent()) def decode(str: String) = Some(StartEvent())
@ -55,6 +58,11 @@ object StartEvent extends EventDecoder {
case class MoveEvent(orig: Pos, dest: Pos, color: Color) extends Event { case class MoveEvent(orig: Pos, dest: Pos, color: Color) extends Event {
def encode = "m" + orig.piotr + dest.piotr + color.letter def encode = "m" + orig.piotr + dest.piotr + color.letter
def export = Map(
"type" -> "move",
"from" -> orig.key,
"to" -> dest.key,
"color" -> color.name)
} }
object MoveEvent extends EventDecoder { object MoveEvent extends EventDecoder {
def apply(move: Move): MoveEvent = MoveEvent(move.orig, move.dest, move.piece.color) def apply(move: Move): MoveEvent = MoveEvent(move.orig, move.dest, move.piece.color)
@ -72,6 +80,11 @@ case class PossibleMovesEvent(moves: Map[Pos, List[Pos]]) extends Event {
def encode = "p" + (moves map { def encode = "p" + (moves map {
case (orig, dests) (orig :: dests) map (_.piotr) mkString case (orig, dests) (orig :: dests) map (_.piotr) mkString
} mkString ",") } mkString ",")
def export = Map(
"type" -> "possible_moves",
"possible_moves" -> (moves map {
case (o, d) o.key -> (d map (_.key) mkString)
}))
} }
object PossibleMovesEvent extends EventDecoder { object PossibleMovesEvent extends EventDecoder {
def decode(str: String) = Some(PossibleMovesEvent( def decode(str: String) = Some(PossibleMovesEvent(
@ -90,6 +103,9 @@ object PossibleMovesEvent extends EventDecoder {
case class EnpassantEvent(killed: Pos) extends Event { case class EnpassantEvent(killed: Pos) extends Event {
def encode = "E" + killed.piotr def encode = "E" + killed.piotr
def export = Map(
"type" -> "enpassant",
"killed" -> killed.key)
} }
object EnpassantEvent extends EventDecoder { object EnpassantEvent extends EventDecoder {
def decode(str: String) = for { def decode(str: String) = for {
@ -100,7 +116,11 @@ object EnpassantEvent extends EventDecoder {
case class CastlingEvent(king: (Pos, Pos), rook: (Pos, Pos), color: Color) extends Event { case class CastlingEvent(king: (Pos, Pos), rook: (Pos, Pos), color: Color) extends Event {
def encode = "c" + king._1.piotr + king._2.piotr + rook._1.piotr + rook._2.piotr + color.letter def encode = "c" + king._1.piotr + king._2.piotr + rook._1.piotr + rook._2.piotr + color.letter
def export = Map(
"type" -> "castling",
"king" -> List(king._1.key, king._2.key),
"rook" -> List(rook._1.key, rook._2.key),
"color" -> color.name)
} }
object CastlingEvent extends EventDecoder { object CastlingEvent extends EventDecoder {
def decode(str: String) = str.toList match { def decode(str: String) = str.toList match {
@ -119,6 +139,9 @@ object CastlingEvent extends EventDecoder {
case class RedirectEvent(url: String) extends Event { case class RedirectEvent(url: String) extends Event {
def encode = "r" + url def encode = "r" + url
def export = Map(
"type" -> "redirect",
"url" -> url)
} }
object RedirectEvent extends EventDecoder { object RedirectEvent extends EventDecoder {
def decode(str: String) = Some(RedirectEvent(str)) def decode(str: String) = Some(RedirectEvent(str))
@ -126,6 +149,10 @@ object RedirectEvent extends EventDecoder {
case class PromotionEvent(role: PromotableRole, pos: Pos) extends Event { case class PromotionEvent(role: PromotableRole, pos: Pos) extends Event {
def encode = "P" + role.forsyth + pos.piotr def encode = "P" + role.forsyth + pos.piotr
def export = Map(
"type" -> "promotion",
"key" -> pos.key,
"pieceClass" -> role.toString)
} }
object PromotionEvent extends EventDecoder { object PromotionEvent extends EventDecoder {
def decode(str: String) = str.toList match { def decode(str: String) = str.toList match {
@ -139,6 +166,9 @@ object PromotionEvent extends EventDecoder {
case class CheckEvent(pos: Pos) extends Event { case class CheckEvent(pos: Pos) extends Event {
def encode = "C" + pos.piotr def encode = "C" + pos.piotr
def export = Map(
"type" -> "check",
"key" -> pos.key)
} }
object CheckEvent extends EventDecoder { object CheckEvent extends EventDecoder {
def decode(str: String) = for { def decode(str: String) = for {
@ -149,6 +179,9 @@ object CheckEvent extends EventDecoder {
case class MessageEvent(author: String, message: String) extends Event { case class MessageEvent(author: String, message: String) extends Event {
def encode = "M" + author + " " + message.replace("|", "(pipe)") def encode = "M" + author + " " + message.replace("|", "(pipe)")
def export = Map(
"type" -> "message",
"message" -> List(author, message))
} }
object MessageEvent extends EventDecoder { object MessageEvent extends EventDecoder {
def decode(str: String) = str.split(' ').toList match { def decode(str: String) = str.split(' ').toList match {
@ -161,6 +194,8 @@ object MessageEvent extends EventDecoder {
case class EndEvent() extends Event { case class EndEvent() extends Event {
def encode = "e" def encode = "e"
def export = Map(
"type" -> "end")
} }
object EndEvent extends EventDecoder { object EndEvent extends EventDecoder {
def decode(str: String) = Some(EndEvent()) def decode(str: String) = Some(EndEvent())
@ -168,6 +203,8 @@ object EndEvent extends EventDecoder {
case class ThreefoldEvent() extends Event { case class ThreefoldEvent() extends Event {
def encode = "t" def encode = "t"
def export = Map(
"type" -> "threefold_repetition")
} }
object ThreefoldEvent extends EventDecoder { object ThreefoldEvent extends EventDecoder {
def decode(str: String) = Some(ThreefoldEvent()) def decode(str: String) = Some(ThreefoldEvent())
@ -175,6 +212,8 @@ object ThreefoldEvent extends EventDecoder {
case class ReloadTableEvent() extends Event { case class ReloadTableEvent() extends Event {
def encode = "R" def encode = "R"
def export = Map(
"type" -> "reload_table")
} }
object ReloadTableEvent extends EventDecoder { object ReloadTableEvent extends EventDecoder {
def decode(str: String) = Some(ReloadTableEvent()) def decode(str: String) = Some(ReloadTableEvent())
@ -182,6 +221,10 @@ object ReloadTableEvent extends EventDecoder {
case class MoretimeEvent(color: Color, seconds: Int) extends Event { case class MoretimeEvent(color: Color, seconds: Int) extends Event {
def encode = "T" + color.letter + seconds def encode = "T" + color.letter + seconds
def export = Map(
"type" -> "moretime",
"color" -> color.name,
"seconds" -> seconds)
} }
object MoretimeEvent extends EventDecoder { object MoretimeEvent extends EventDecoder {
def decode(str: String) = for { def decode(str: String) = for {

View file

@ -29,14 +29,12 @@ case class EventStack(events: List[(Int, Event)]) {
) )
} }
def version: Int = events.lastOption map (_._1) getOrElse 0
def eventsSince(version: Int): Option[List[Event]] = for { def eventsSince(version: Int): Option[List[Event]] = for {
first <- firstVersion first firstVersion
if version >= first - 1 if version >= first - 1
last <- lastVersion last lastVersion
if version <= last if version <= last
} yield sortedEvents dropWhile { ve => ve._1 <= version } map (_._2) } yield sortedEvents dropWhile { ve ve._1 <= version } map (_._2)
def withEvents(newEvents: List[Event]): EventStack = { def withEvents(newEvents: List[Event]): EventStack = {
@ -45,7 +43,7 @@ case class EventStack(events: List[(Int, Event)]) {
case event :: rest (v + 1, event) :: versionEvents(v + 1, rest) case event :: rest (v + 1, event) :: versionEvents(v + 1, rest)
} }
copy(events = events ++ versionEvents(version, newEvents)) copy(events = events ++ versionEvents(lastVersion getOrElse 0, newEvents))
} }
} }

View file

@ -123,7 +123,9 @@ B p p
"high version number" in { "high version number" in {
found must beIO.like { found must beIO.like {
case vg vg must beSuccess.like { case vg vg must beSuccess.like {
case g g.player(White).eventStack.version must be_>(20) case g g.player(White).eventStack.lastVersion must beSome.like {
case v => v must be_>(20)
}
} }
} }
} }