Communication with lichess
parent
cab58c9f98
commit
13101a42c1
|
@ -13,4 +13,7 @@ object Global extends GlobalSettings {
|
|||
override def onBadRequest(request: RequestHeader, error: String) = {
|
||||
BadRequest("Bad Request: " + error)
|
||||
}
|
||||
|
||||
override def onError(request: RequestHeader, ex: Throwable): Result =
|
||||
InternalServerError(ex.getMessage)
|
||||
}
|
||||
|
|
|
@ -12,11 +12,12 @@ object Application extends Controller {
|
|||
val env = new HttpEnv(Play.unsafeApplication.configuration.underlying)
|
||||
val json = "application/json"
|
||||
|
||||
def sync(id: String, color: String, version: Int, fullId: String) = Action {
|
||||
Ok(jsonify(
|
||||
env.syncer.sync(id, color, version, fullId).unsafePerformIO
|
||||
)) as json
|
||||
}
|
||||
def sync(id: String, color: String, version: Int, fullId: Option[String]) =
|
||||
Action {
|
||||
Ok(jsonify(
|
||||
env.syncer.sync(id, color, version, fullId).unsafePerformIO
|
||||
)) as json
|
||||
}
|
||||
|
||||
def move(fullId: String) = Action { implicit request ⇒
|
||||
(moveForm.bindFromRequest.value toValid "Invalid move" flatMap { move =>
|
||||
|
@ -27,5 +28,15 @@ object Application extends Controller {
|
|||
)
|
||||
}
|
||||
|
||||
def updateVersion(gameId: String) = Action {
|
||||
env.server.updateVersion(gameId).unsafePerformIO
|
||||
Ok("ok")
|
||||
}
|
||||
|
||||
def endGame(gameId: String) = Action {
|
||||
env.server.endGame(gameId).unsafePerformIO
|
||||
Ok("ok")
|
||||
}
|
||||
|
||||
def index = TODO
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
# Home page
|
||||
GET / controllers.Application.index
|
||||
POST /move/:fullId controllers.Application.move(fullId: String)
|
||||
POST /sync/:id/:color/:version/:fullId controllers.Application.sync(id: String, color: String, version: Int, fullId: String)
|
||||
GET /sync/:id/:color/:version/:fullId controllers.Application.sync(id: String, color: String, version: Int, fullId: Option[String])
|
||||
POST /update-version/:gameId controllers.Application.updateVersion(gameId: String)
|
||||
POST /end-game/:gameId controllers.Application.endGame(gameId: String)
|
||||
|
||||
# Map static resources from the /public folder to the /assets URL path
|
||||
GET /assets/*file controllers.Assets.at(path="/public", file)
|
||||
|
|
|
@ -21,6 +21,7 @@ trait Dependencies {
|
|||
val config = "com.typesafe.config" % "config" % "0.3.0"
|
||||
val json = "com.codahale" %% "jerkson" % "0.5.0"
|
||||
val guava = "com.google.guava" % "guava" % "11.0.2"
|
||||
val apache = "org.apache.commons" % "commons-lang3" % "3.1"
|
||||
|
||||
// benchmark
|
||||
val instrumenter = "com.google.code.java-allocation-instrumenter" % "java-allocation-instrumenter" % "2.0"
|
||||
|
@ -51,7 +52,7 @@ object ApplicationBuild extends Build with Resolvers with Dependencies {
|
|||
) dependsOn (system)
|
||||
|
||||
lazy val system = Project("system", file("system"), settings = buildSettings).settings(
|
||||
libraryDependencies ++= Seq(scalaz, config, json, casbah, salat, guava, slf4j)
|
||||
libraryDependencies ++= Seq(scalaz, config, json, casbah, salat, guava, slf4j, apache)
|
||||
) dependsOn (chess)
|
||||
|
||||
lazy val chess = Project("chess", file("chess"), settings = buildSettings).settings(
|
||||
|
|
4
routes
4
routes
|
@ -1,4 +1,4 @@
|
|||
lichess_move ANY /move/{id}
|
||||
/*lichess_move ANY /move/{id}*/
|
||||
lichess_say ANY /talk/{id}
|
||||
lichess_resign ANY /resign/{id}
|
||||
lichess_abort ANY /abort/{id}
|
||||
|
@ -13,4 +13,4 @@ lichess_decline_draw_offer ANY /decline-draw-offer/{id}
|
|||
lichess_moretime ANY /moretime/{id}
|
||||
lichess_status ANY /status
|
||||
lichess_ping ANY /ping
|
||||
lichess_sync ANY /sync/{id}/{color}/{version}/{playerFullId}
|
||||
/*lichess_sync ANY /sync/{id}/{color}/{version}/{playerFullId}*/
|
||||
|
|
|
@ -32,6 +32,15 @@ final class Server(repo: GameRepo, ai: Ai, versionMemo: VersionMemo) {
|
|||
)
|
||||
}
|
||||
|
||||
def updateVersion(gameId: String): IO[Unit] =
|
||||
repo game gameId flatMap versionMemo.put
|
||||
|
||||
def endGame(gameId: String): IO[Unit] = for {
|
||||
g1 ← repo game gameId
|
||||
g2 = g1 withEvents List(EndEvent())
|
||||
_ ← repo.applyDiff(g1, g2)
|
||||
} yield ()
|
||||
|
||||
private def purePlay(
|
||||
g1: DbGame,
|
||||
origString: String,
|
||||
|
|
|
@ -6,6 +6,7 @@ import memo._
|
|||
import scalaz.effects._
|
||||
import scala.annotation.tailrec
|
||||
import scala.math.max
|
||||
import org.apache.commons.lang3.StringEscapeUtils.escapeXml
|
||||
|
||||
final class Syncer(
|
||||
repo: GameRepo,
|
||||
|
@ -17,18 +18,19 @@ final class Syncer(
|
|||
gameId: String,
|
||||
colorString: String,
|
||||
version: Int,
|
||||
fullId: String): IO[Map[String, Any]] = {
|
||||
fullId: Option[String]): IO[Map[String, Any]] = {
|
||||
for {
|
||||
color ← io { Color(colorString) err "Invalid color" }
|
||||
_ ← io { versionWait(gameId, color, version) }
|
||||
gameAndPlayer ← repo.player(gameId, color)
|
||||
(game, player) = gameAndPlayer
|
||||
isPrivate = fullId some { game.isPlayerFullId(player, _) } none false
|
||||
_ ← versionMemo put game
|
||||
} yield {
|
||||
player.eventStack eventsSince version map { events ⇒
|
||||
Map(
|
||||
"v" -> player.eventStack.lastVersion,
|
||||
"e" -> (events map (_.export)),
|
||||
"e" -> renderEvents(events, isPrivate),
|
||||
"p" -> game.player.color.name,
|
||||
"t" -> game.turns
|
||||
)
|
||||
|
@ -36,6 +38,22 @@ final class Syncer(
|
|||
}
|
||||
} except (e ⇒ io(failMap))
|
||||
|
||||
private def renderEvents(events: List[Event], isPrivate: Boolean) =
|
||||
if (isPrivate) events map {
|
||||
case MessageEvent(author, message) ⇒ renderMessage(author, message)
|
||||
case e ⇒ e.export
|
||||
}
|
||||
else events filter {
|
||||
case MessageEvent(_, _) | RedirectEvent(_) ⇒ false
|
||||
case _ ⇒ true
|
||||
} map (_.export)
|
||||
|
||||
// TODO author=system messages should be translated!!
|
||||
private def renderMessage(author: String, message: String) = Map(
|
||||
"type" -> "message",
|
||||
"html" -> """<li class="%s">%s</li>""".format(author, escapeXml(message))
|
||||
)
|
||||
|
||||
private def versionWait(gameId: String, color: Color, version: Int) {
|
||||
@tailrec
|
||||
def wait(loop: Int): Unit = {
|
||||
|
|
|
@ -14,7 +14,7 @@ final class VersionMemo(repo: GameRepo) {
|
|||
}
|
||||
|
||||
def put(gameId: String, color: Color, version: Int): IO[Unit] = io {
|
||||
cache.put((gameId.pp, color.pp == White), version.pp)
|
||||
cache.put((gameId, color == White), version)
|
||||
}
|
||||
|
||||
def put(game: DbGame): IO[Unit] = for {
|
||||
|
|
|
@ -31,7 +31,11 @@ case class DbGame(
|
|||
case Black ⇒ blackPlayer
|
||||
}
|
||||
|
||||
def player(id: String): Option[DbPlayer] = players find (_.id == id)
|
||||
def player(playerId: String): Option[DbPlayer] =
|
||||
players find (_.id == playerId)
|
||||
|
||||
def isPlayerFullId(player: DbPlayer, fullId: String): Boolean =
|
||||
(fullId.size == DbGame.fullIdSize) && player.id == (fullId drop 8)
|
||||
|
||||
def player: DbPlayer = player(if (0 == turns % 2) White else Black)
|
||||
|
||||
|
@ -112,11 +116,16 @@ case class DbGame(
|
|||
)
|
||||
}
|
||||
|
||||
def withEvents(events: List[Event]): DbGame = copy(
|
||||
whitePlayer = whitePlayer withEvents events,
|
||||
blackPlayer = blackPlayer withEvents events
|
||||
)
|
||||
|
||||
def playable = status < Aborted
|
||||
|
||||
def aiLevel: Option[Int] = players find (_.isAi) flatMap (_.aiLevel)
|
||||
|
||||
def mapPlayers(f: DbPlayer => DbPlayer) = copy(
|
||||
def mapPlayers(f: DbPlayer ⇒ DbPlayer) = copy(
|
||||
whitePlayer = f(whitePlayer),
|
||||
blackPlayer = f(blackPlayer)
|
||||
)
|
||||
|
|
|
@ -17,6 +17,10 @@ case class DbPlayer(
|
|||
def newEvts(events: List[Event]): String =
|
||||
(eventStack withEvents events).optimize.encode
|
||||
|
||||
def withEvents(events: List[Event]) = copy(
|
||||
evts = newEvts(events)
|
||||
)
|
||||
|
||||
def encodePieces(allPieces: Iterable[(Pos, Piece, Boolean)]): String =
|
||||
allPieces withFilter (_._2.color == color) map {
|
||||
case (pos, piece, dead) ⇒ pos.piotr.toString + {
|
||||
|
|
|
@ -179,9 +179,7 @@ object CheckEvent extends EventDecoder {
|
|||
|
||||
case class MessageEvent(author: String, message: String) extends Event {
|
||||
def encode = "M" + author + " " + message.replace("|", "(pipe)")
|
||||
def export = Map(
|
||||
"type" -> "message",
|
||||
"message" -> List(author, message))
|
||||
def export = Map.empty
|
||||
}
|
||||
object MessageEvent extends EventDecoder {
|
||||
def decode(str: String) = str.split(' ').toList match {
|
||||
|
|
Loading…
Reference in New Issue