From 51c9faef3e30afb795a0320936ba8f9fcc9fa1ad Mon Sep 17 00:00:00 2001 From: ProgramFOX Date: Fri, 1 May 2015 10:32:18 +0200 Subject: [PATCH 1/9] Fix pluralization "This tournaments" => "This tournament" --- app/views/tournament/faq.scala.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/tournament/faq.scala.html b/app/views/tournament/faq.scala.html index 137ab1fd94..b6a5ee5eba 100644 --- a/app/views/tournament/faq.scala.html +++ b/app/views/tournament/faq.scala.html @@ -17,9 +17,9 @@

Is it rated?

@rated.map { r => @if(r) { - This tournaments is rated and will affect your rating. + This tournament is rated and will affect your rating. } else { - This tournaments is *not* rated and will *not* affect your rating. + This tournament is *not* rated and will *not* affect your rating. } }.getOrElse { Some tournaments are rated and will affect your rating. From 31f987ee17bb9314106ad51dd0afa639badc82da Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 3 May 2015 14:14:22 +0200 Subject: [PATCH 2/9] properly render equal eval in analysis UI --- ui/analyse/src/analyse.js | 5 +++-- ui/analyse/src/util.js | 5 +++++ ui/analyse/src/view.js | 5 +++-- 3 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 ui/analyse/src/util.js diff --git a/ui/analyse/src/analyse.js b/ui/analyse/src/analyse.js index d31b38ee37..1d63f0a96e 100644 --- a/ui/analyse/src/analyse.js +++ b/ui/analyse/src/analyse.js @@ -1,4 +1,5 @@ var treePath = require('./path'); +var defined = require('./util').defined; module.exports = function(game, analysis) { @@ -16,8 +17,8 @@ module.exports = function(game, analysis) { var applyAnalysis = function(tree, analysed) { analysed.forEach(function(ana, i) { if (!tree[i]) return; - if (ana.mate) tree[i].mate = ana.mate; - else if (ana.eval) tree[i].eval = ana.eval; + if (defined(ana.mate)) tree[i].mate = ana.mate; + else if (defined(ana.eval)) tree[i].eval = ana.eval; if (ana.comment) tree[i].comments.push(ana.comment); if (ana.variation) tree[i].variations.push(makeTree(ana.variation.split(' '), i + 1)); }); diff --git a/ui/analyse/src/util.js b/ui/analyse/src/util.js new file mode 100644 index 0000000000..3b2895542f --- /dev/null +++ b/ui/analyse/src/util.js @@ -0,0 +1,5 @@ +module.exports = { + defined: function(v) { + return typeof v !== 'undefined'; + } +}; diff --git a/ui/analyse/src/view.js b/ui/analyse/src/view.js index 2b820bd74c..ddf188d422 100644 --- a/ui/analyse/src/view.js +++ b/ui/analyse/src/view.js @@ -1,6 +1,7 @@ var m = require('mithril'); var chessground = require('chessground'); var classSet = require('chessground').util.classSet; +var defined = require('./util').defined; var game = require('game').game; var partial = require('chessground').util.partial; var renderStatus = require('game').view.status; @@ -43,8 +44,8 @@ function renderMove(ctrl, move, path) { 'href': '#' + path[0].ply }, children: [ - move.eval ? renderEvalTag(renderEval(move.eval)) : ( - move.mate ? renderEvalTag('#' + move.mate) : null + defined(move.eval) ? renderEvalTag(renderEval(move.eval)) : ( + defined(move.mate) ? renderEvalTag('#' + move.mate) : null ), move.san ] From 082b02197a9480551f78d9b89c5f520b40880af5 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 3 May 2015 14:18:20 +0200 Subject: [PATCH 3/9] more analysis tweaks --- app/controllers/Analyse.scala | 4 ++-- modules/analyse/src/main/Evaluation.scala | 2 ++ modules/analyse/src/main/Info.scala | 14 +++++++------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/controllers/Analyse.scala b/app/controllers/Analyse.scala index 18fcbf8f29..74e99ab52e 100644 --- a/app/controllers/Analyse.scala +++ b/app/controllers/Analyse.scala @@ -44,8 +44,8 @@ object Analyse extends LilaController { } def postAnalysis(id: String) = Action.async(parse.text) { req => - env.analyser.complete(id, req.body, req.remoteAddress) recoverWith { - case e: lila.common.LilaException => fufail(s"${req.remoteAddress} ${e.message}") + env.analyser.complete(id, req.body, req.remoteAddress) recover { + case e: lila.common.LilaException => logwarn(s"AI ${req.remoteAddress} ${e.message}") } andThenAnyway { Env.hub.socket.round ! Tell(id, AnalysisAvailable) } inject Ok diff --git a/modules/analyse/src/main/Evaluation.scala b/modules/analyse/src/main/Evaluation.scala index f913ae2053..4b8ec5996a 100644 --- a/modules/analyse/src/main/Evaluation.scala +++ b/modules/analyse/src/main/Evaluation.scala @@ -9,6 +9,8 @@ case class Evaluation( def checkMate = mate == Some(0) + def invalid = score.isEmpty && mate.isEmpty + override def toString = s"Evaluation ${score.fold("?")(_.showPawns)} ${mate | 0} ${line.mkString(" ")}" } diff --git a/modules/analyse/src/main/Info.scala b/modules/analyse/src/main/Info.scala index da58acaacb..c5627d16a1 100644 --- a/modules/analyse/src/main/Info.scala +++ b/modules/analyse/src/main/Info.scala @@ -44,13 +44,13 @@ object Info { lazy val start = Info(0, Evaluation.start.score, none, Nil) - def decode(ply: Int, str: String): Option[Info] = str.split(separator).toList match { - case Nil => Info(ply).some - case List(cp) => Info(ply, Score(cp)).some - case List(cp, ma) => Info(ply, Score(cp), parseIntOption(ma)).some - case List(cp, ma, va) => Info(ply, Score(cp), parseIntOption(ma), va.split(' ').toList).some - case List(cp, ma, va, be) => Info(ply, Score(cp), parseIntOption(ma), va.split(' ').toList, UciMove piotr be).some - case _ => none + def decode(ply: Int, str: String): Option[Info] = str.split(separator) match { + case Array() => Info(ply).some + case Array(cp) => Info(ply, Score(cp)).some + case Array(cp, ma) => Info(ply, Score(cp), parseIntOption(ma)).some + case Array(cp, ma, va) => Info(ply, Score(cp), parseIntOption(ma), va.split(' ').toList).some + case Array(cp, ma, va, be) => Info(ply, Score(cp), parseIntOption(ma), va.split(' ').toList, UciMove piotr be).some + case _ => none } def decodeList(str: String): Option[List[Info]] = { From 3a5253eac4051eca1e5b87787856a46e25a34905 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 3 May 2015 16:52:38 +0200 Subject: [PATCH 4/9] remove stockfish OwnBook option --- modules/ai/src/main/Config.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/ai/src/main/Config.scala b/modules/ai/src/main/Config.scala index 1f0c851ff9..a170593d46 100644 --- a/modules/ai/src/main/Config.scala +++ b/modules/ai/src/main/Config.scala @@ -43,13 +43,11 @@ private[ai] case class Config( case r: PlayReq => List( setoption("Skill Level", skill(r.level)), setoption("UCI_Chess960", r.chess960), - setoption("UCI_KingOfTheHill", r.kingOfTheHill), - setoption("OwnBook", ownBook(r.level))) + setoption("UCI_KingOfTheHill", r.kingOfTheHill)) case r: AnalReq => List( setoption("Skill Level", skillMax), setoption("UCI_Chess960", r.chess960), - setoption("UCI_KingOfTheHill", r.kingOfTheHill), - setoption("OwnBook", true)) + setoption("UCI_KingOfTheHill", r.kingOfTheHill)) } def go(req: Req): List[String] = req match { From 62ca7f4b855713a77d4c42d24a90e8a498342a24 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 3 May 2015 17:21:46 +0200 Subject: [PATCH 5/9] ignore new stockfish extra output --- modules/ai/src/main/ActorFSM.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/ai/src/main/ActorFSM.scala b/modules/ai/src/main/ActorFSM.scala index 99c44d2613..9b55917b89 100644 --- a/modules/ai/src/main/ActorFSM.scala +++ b/modules/ai/src/main/ActorFSM.scala @@ -45,7 +45,7 @@ private[ai] final class ActorFSM( goto(Running) } when(Running) { - case Event(Out(t), Some(job)) if t startsWith "info depth" => + case Event(Out(t), Some(job)) if (t startsWith "info depth") && relevantLine(t) => stay using (job + t).some case Event(Out(t), Some(job)) if t startsWith "bestmove" => job.sender ! (job complete t) @@ -63,6 +63,11 @@ private[ai] final class ActorFSM( goto(IsReady) using Job(req, sender, Nil).some } + private def relevantLine(l: String) = + !(l contains "currmovenumber") && + !(l contains "lowerbound") && + !(l contains "upperbound") + override def postStop() { println(s"======== $name\n${lastWrite mkString "\n"}\n========") process.destroy() From 41afccf79bc2df2d398e4c29418c7726a2bcc18f Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 3 May 2015 18:03:18 +0200 Subject: [PATCH 6/9] prepare multiple variant support for A.I. nodes --- app/controllers/Ai.scala | 9 +++++-- modules/ai/src/main/ActorFSM.scala | 6 ++--- modules/ai/src/main/Client.scala | 10 ++++---- modules/ai/src/main/Config.scala | 17 ++++++------- modules/ai/src/main/Env.scala | 4 ++-- modules/ai/src/main/Queue.scala | 4 ++-- modules/ai/src/main/Server.scala | 8 +++---- modules/ai/src/main/actorApi.scala | 32 ++++++++++++++++++------- modules/analyse/src/main/Analyser.scala | 2 +- modules/hub/src/main/actorApi.scala | 17 ++++++++----- 10 files changed, 65 insertions(+), 44 deletions(-) diff --git a/app/controllers/Ai.scala b/app/controllers/Ai.scala index cd1c41fde9..e0b5686672 100644 --- a/app/controllers/Ai.scala +++ b/app/controllers/Ai.scala @@ -2,6 +2,7 @@ package controllers import play.api.mvc._ +import lila.ai.actorApi.{ KingOfTheHill, Variant } import lila.api.Context import lila.app._ @@ -10,12 +11,16 @@ import play.api.Play.current object Ai extends LilaController { + private def requestVariant(req: RequestHeader): Variant = + if (getBool("kingOfTheHill", req)) KingOfTheHill + else Variant(~get("variant", req)) + def move = Action.async { req => Env.ai.server.move( uciMoves = get("uciMoves", req) ?? (_.split(' ').toList), initialFen = get("initialFen", req), level = getInt("level", req) | 1, - kingOfTheHill = getBool("kingOfTheHill", req) + variant = requestVariant(req) ) fold ( err => { logwarn("[ai] stockfish server play: " + err) @@ -32,7 +37,7 @@ object Ai extends LilaController { uciMoves = get("uciMoves", req) ?? (_.split(' ').toList), initialFen = get("initialFen", req), requestedByHuman = getBool("human", req), - kingOfTheHill = getBool("kingOfTheHill", req) + variant = requestVariant(req) ).effectFold( err => WS.url(replyToUrl).post(err.toString), infos => WS.url(replyToUrl).post(lila.analyse.Info encodeList infos) diff --git a/modules/ai/src/main/ActorFSM.scala b/modules/ai/src/main/ActorFSM.scala index 9b55917b89..17315edfe6 100644 --- a/modules/ai/src/main/ActorFSM.scala +++ b/modules/ai/src/main/ActorFSM.scala @@ -28,11 +28,11 @@ private[ai] final class ActorFSM( config.init foreach process.write loginfo(s"[$name] stockfish is ready") job.fold(goto(Idle))(start) - case Event(req: Req, none) => stay using Job(req, sender, Nil).some + case Event(req: Req, none) => stay using Job(req, sender, None).some } when(Idle) { case Event(Out(t), _) => sys error s"[$name] Unexpected engine output $t" - case Event(req: Req, _) => start(Job(req, sender, Nil)) + case Event(req: Req, _) => start(Job(req, sender, None)) } when(IsReady) { case Event(Out("readyok"), Some(Job(req, _, _))) => @@ -60,7 +60,7 @@ private[ai] final class ActorFSM( case Job(req, sender, _) => config prepare req foreach process.write process write "isready" - goto(IsReady) using Job(req, sender, Nil).some + goto(IsReady) using Job(req, sender, None).some } private def relevantLine(l: String) = diff --git a/modules/ai/src/main/Client.scala b/modules/ai/src/main/Client.scala index b2d0f55e3a..c993d9bc5c 100644 --- a/modules/ai/src/main/Client.scala +++ b/modules/ai/src/main/Client.scala @@ -26,7 +26,7 @@ final class Client( for { fen ← GameRepo initialFen game uciMoves ← uciMemo get game - moveResult ← move(uciMoves.toList, fen, level, game.variant.kingOfTheHill) + moveResult ← move(uciMoves.toList, fen, level, Variant(game.variant)) uciMove ← (UciMove(moveResult.move) toValid s"${game.id} wrong bestmove: $moveResult").future result ← game.toChess(uciMove.orig, uciMove.dest, uciMove.promotion).future (c, move) = result @@ -37,25 +37,25 @@ final class Client( private val networkLatency = 1 second - def move(uciMoves: List[String], initialFen: Option[String], level: Int, kingOfTheHill: Boolean): Fu[MoveResult] = { + def move(uciMoves: List[String], initialFen: Option[String], level: Int, variant: Variant): Fu[MoveResult] = { implicit val timeout = makeTimeout(config.playTimeout + networkLatency) sendRequest(true) { WS.url(s"$endpoint/move").withQueryString( "uciMoves" -> uciMoves.mkString(" "), "initialFen" -> ~initialFen, "level" -> level.toString, - "kingOfTheHill" -> (kingOfTheHill ?? "1")) + "variant" -> variant.toString) } map MoveResult.apply } - def analyse(gameId: String, uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean, kingOfTheHill: Boolean) { + def analyse(gameId: String, uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean, variant: Variant) { WS.url(s"$endpoint/analyse").withQueryString( "replyUrl" -> callbackUrl.replace("%", gameId), "uciMoves" -> uciMoves.mkString(" "), "initialFen" -> ~initialFen, "human" -> requestedByHuman.fold("1", "0"), "gameId" -> gameId, - "kingOfTheHill" -> (kingOfTheHill ?? "1")).post("go") + "variant" -> variant.toString).post("go") } private def sendRequest(retriable: Boolean)(req: WSRequestHolder): Fu[String] = diff --git a/modules/ai/src/main/Config.scala b/modules/ai/src/main/Config.scala index a170593d46..478c2d0cad 100644 --- a/modules/ai/src/main/Config.scala +++ b/modules/ai/src/main/Config.scala @@ -39,16 +39,13 @@ private[ai] case class Config( setoption("Threads", nbThreads), setoption("Ponder", false)) - def prepare(req: Req) = req match { - case r: PlayReq => List( - setoption("Skill Level", skill(r.level)), - setoption("UCI_Chess960", r.chess960), - setoption("UCI_KingOfTheHill", r.kingOfTheHill)) - case r: AnalReq => List( - setoption("Skill Level", skillMax), - setoption("UCI_Chess960", r.chess960), - setoption("UCI_KingOfTheHill", r.kingOfTheHill)) - } + def prepare(req: Req) = (req match { + case r: PlayReq => setoption("Skill Level", skill(r.level)) + case r: AnalReq => setoption("Skill Level", skillMax) + }) :: List( + setoption("UCI_Chess960", req.variant == Chess960), + setoption("UCI_KingOfTheHill", req.variant == KingOfTheHill), + setoption("UCI_ThreeCheck", req.variant == ThreeCheck)) def go(req: Req): List[String] = req match { case r: PlayReq => List( diff --git a/modules/ai/src/main/Env.scala b/modules/ai/src/main/Env.scala index 8bfc62bc8c..9e5b4b523c 100644 --- a/modules/ai/src/main/Env.scala +++ b/modules/ai/src/main/Env.scala @@ -43,8 +43,8 @@ final class Env( // api actor system.actorOf(Props(new Actor { def receive = { - case lila.hub.actorApi.ai.Analyse(gameId, uciMoves, fen, requestedByHuman, kingOfTheHill) => - client.analyse(gameId, uciMoves, fen, requestedByHuman, kingOfTheHill) + case lila.hub.actorApi.ai.Analyse(gameId, uciMoves, fen, requestedByHuman, variant) => + client.analyse(gameId, uciMoves, fen, requestedByHuman, actorApi.Variant(variant)) } }), name = ActorName) diff --git a/modules/ai/src/main/Queue.scala b/modules/ai/src/main/Queue.scala index d9651f663f..f27c3e7718 100644 --- a/modules/ai/src/main/Queue.scala +++ b/modules/ai/src/main/Queue.scala @@ -58,14 +58,14 @@ private[ai] final class Queue(config: Config) extends Actor { tasks += Task(req, sender, timeout) } - case FullAnalReq(moves, fen, requestedByHuman, kingOfTheHill) if (requestedByHuman || tasks.size < maxTasks) => + case FullAnalReq(moves, fen, requestedByHuman, variant) if (requestedByHuman || tasks.size < maxTasks) => val mrSender = sender val size = moves.size implicit val timeout = makeTimeout { if (requestedByHuman) 1.hour else 24.hours } val futures = (0 to size) map moves.take map { serie => - self ? AnalReq(serie, fen, size, requestedByHuman, kingOfTheHill) mapTo manifest[Option[Evaluation]] + self ? AnalReq(serie, fen, size, requestedByHuman, variant) mapTo manifest[Option[Evaluation]] } Future.fold(futures)(Vector[Option[Evaluation]]())(_ :+ _) addFailureEffect { case e => mrSender ! Status.Failure(e) diff --git a/modules/ai/src/main/Server.scala b/modules/ai/src/main/Server.scala index c7127150e1..a271190a92 100644 --- a/modules/ai/src/main/Server.scala +++ b/modules/ai/src/main/Server.scala @@ -15,17 +15,17 @@ private[ai] final class Server( config: Config, uciMemo: lila.game.UciMemo) { - def move(uciMoves: List[String], initialFen: Option[String], level: Int, kingOfTheHill: Boolean): Fu[MoveResult] = { + def move(uciMoves: List[String], initialFen: Option[String], level: Int, variant: Variant): Fu[MoveResult] = { implicit val timeout = makeTimeout(config.playTimeout) - queue ? PlayReq(uciMoves, initialFen map chess960Fen, level, kingOfTheHill) mapTo + queue ? PlayReq(uciMoves, initialFen map chess960Fen, level, variant) mapTo manifest[Option[String]] flatten "[stockfish] play failed" map MoveResult.apply } - def analyse(uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean, kingOfTheHill: Boolean): Fu[List[Info]] = { + def analyse(uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean, variant: Variant): Fu[List[Info]] = { implicit val timeout = makeTimeout { if (requestedByHuman) 1.hour else 24.hours } - (queue ? FullAnalReq(uciMoves take config.analyseMaxPlies, initialFen map chess960Fen, requestedByHuman, kingOfTheHill)) mapTo manifest[List[Info]] + (queue ? FullAnalReq(uciMoves take config.analyseMaxPlies, initialFen map chess960Fen, requestedByHuman, variant)) mapTo manifest[List[Info]] } private def chess960Fen(fen: String) = (Forsyth << fen).fold(fen) { situation => diff --git a/modules/ai/src/main/actorApi.scala b/modules/ai/src/main/actorApi.scala index 8b4ecd9559..4673934c64 100644 --- a/modules/ai/src/main/actorApi.scala +++ b/modules/ai/src/main/actorApi.scala @@ -18,22 +18,36 @@ sealed trait Stream { def text: String } case class Out(text: String) extends Stream case class Err(text: String) extends Stream +sealed trait Variant +case object Standard extends Variant +case object Chess960 extends Variant +case object KingOfTheHill extends Variant +case object ThreeCheck extends Variant + +object Variant { + def apply(str: String): Variant = str.toLowerCase match { + case "chess960" => Chess960 + case "kingOfTheHill" => KingOfTheHill + case "threeCheck" => ThreeCheck + case _ => Standard + } + def apply(v: chess.variant.Variant): Variant = apply(v.key) +} + sealed trait Req { def moves: List[String] def fen: Option[String] def analyse: Boolean def requestedByHuman: Boolean def priority: Int - def kingOfTheHill: Boolean - - def chess960 = fen.isDefined + def variant: Variant } case class PlayReq( moves: List[String], fen: Option[String], level: Int, - kingOfTheHill: Boolean) extends Req { + variant: Variant) extends Req { val analyse = false val requestedByHuman = true @@ -46,7 +60,7 @@ case class AnalReq( fen: Option[String], totalSize: Int, requestedByHuman: Boolean, - kingOfTheHill: Boolean) extends Req { + variant: Variant) extends Req { val priority = if (requestedByHuman) -totalSize @@ -61,15 +75,15 @@ case class FullAnalReq( moves: List[String], fen: Option[String], requestedByHuman: Boolean, - kingOfTheHill: Boolean) + variant: Variant) -case class Job(req: Req, sender: akka.actor.ActorRef, buffer: List[String]) { +case class Job(req: Req, sender: akka.actor.ActorRef, lastLine: Option[String]) { - def +(str: String) = if (req.analyse) copy(buffer = str :: buffer) else this + def +(str: String) = if (req.analyse) copy(lastLine = str.some) else this // bestmove xyxy ponder xyxy def complete(str: String): Option[Any] = req match { case _: PlayReq => str split ' ' lift 1 - case _: AnalReq => buffer.headOption map EvaluationParser.apply + case _: AnalReq => lastLine map EvaluationParser.apply } } diff --git a/modules/analyse/src/main/Analyser.scala b/modules/analyse/src/main/Analyser.scala index 087296bef0..4449aede15 100644 --- a/modules/analyse/src/main/Analyser.scala +++ b/modules/analyse/src/main/Analyser.scala @@ -50,7 +50,7 @@ final class Analyser( chess.Replay(game.pgnMoves, initialFen, game.variant).fold( fufail(_), replay => { - ai ! lila.hub.actorApi.ai.Analyse(game.id, UciDump(replay), initialFen, requestedByHuman = !auto, game.variant.kingOfTheHill) + ai ! lila.hub.actorApi.ai.Analyse(game.id, UciDump(replay), initialFen, requestedByHuman = !auto, game.variant) AnalysisRepo byId id flatten "Missing analysis" } ) diff --git a/modules/hub/src/main/actorApi.scala b/modules/hub/src/main/actorApi.scala index b6f1c7a45a..56eec8ca2f 100644 --- a/modules/hub/src/main/actorApi.scala +++ b/modules/hub/src/main/actorApi.scala @@ -49,11 +49,11 @@ case class Shutup(userId: String, text: String) } package shutup { - case class RecordPublicForumMessage(userId: String, text: String) - case class RecordTeamForumMessage(userId: String, text: String) - case class RecordPrivateMessage(userId: String, toUserId: String, text: String) - case class RecordPrivateChat(chatId: String, userId: String, text: String) - case class RecordPublicChat(chatId: String, userId: String, text: String) +case class RecordPublicForumMessage(userId: String, text: String) +case class RecordTeamForumMessage(userId: String, text: String) +case class RecordPrivateMessage(userId: String, toUserId: String, text: String) +case class RecordPrivateChat(chatId: String, userId: String, text: String) +case class RecordPublicChat(chatId: String, userId: String, text: String) } package mod { @@ -163,7 +163,12 @@ case class MakeTeam(id: String, name: String) } package ai { -case class Analyse(gameId: String, uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean, kingOfTheHill: Boolean) +case class Analyse( + gameId: String, + uciMoves: List[String], + initialFen: Option[String], + requestedByHuman: Boolean, + variant: chess.variant.Variant) case class AutoAnalyse(gameId: String) } From 0ed8b5c9757c2a66047af30ff5e7108916df9994 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 3 May 2015 18:29:32 +0200 Subject: [PATCH 7/9] remove AI debug --- app/controllers/Ai.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/Ai.scala b/app/controllers/Ai.scala index e0b5686672..82743c2440 100644 --- a/app/controllers/Ai.scala +++ b/app/controllers/Ai.scala @@ -32,7 +32,6 @@ object Ai extends LilaController { def analyse = Action.async { req => get("replyUrl", req) foreach { replyToUrl => - println(s"analyse gameId ${get("gameId", req)}") Env.ai.server.analyse( uciMoves = get("uciMoves", req) ?? (_.split(' ').toList), initialFen = get("initialFen", req), From 57dd57b6489bf83ad17e7e9328561cdcc04b03f9 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 3 May 2015 18:32:55 +0200 Subject: [PATCH 8/9] fix AI BC for chess960 --- app/controllers/Ai.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/Ai.scala b/app/controllers/Ai.scala index 82743c2440..0f5f9cefec 100644 --- a/app/controllers/Ai.scala +++ b/app/controllers/Ai.scala @@ -2,7 +2,7 @@ package controllers import play.api.mvc._ -import lila.ai.actorApi.{ KingOfTheHill, Variant } +import lila.ai.actorApi.{ Chess960, KingOfTheHill, Variant } import lila.api.Context import lila.app._ @@ -12,7 +12,8 @@ import play.api.Play.current object Ai extends LilaController { private def requestVariant(req: RequestHeader): Variant = - if (getBool("kingOfTheHill", req)) KingOfTheHill + if (get("initialFen", req).isDefined) Chess960 + else if (getBool("kingOfTheHill", req)) KingOfTheHill else Variant(~get("variant", req)) def move = Action.async { req => From 889b8366d2de251c726867799367a53f7fd960a2 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 3 May 2015 19:12:24 +0200 Subject: [PATCH 9/9] fix A.I. variant support, add support for threecheck play & analysis --- app/templating/SetupHelper.scala | 3 ++- app/views/setup/ai.scala.html | 2 +- modules/ai/src/main/ActorFSM.scala | 4 +++- modules/ai/src/main/Config.scala | 2 +- modules/ai/src/main/actorApi.scala | 4 ++-- modules/game/src/main/Game.scala | 3 ++- modules/setup/src/main/Config.scala | 4 ++-- modules/setup/src/main/FormFactory.scala | 2 +- modules/setup/src/main/Mappings.scala | 2 +- 9 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/templating/SetupHelper.scala b/app/templating/SetupHelper.scala index c20de0e403..3165f8ce7c 100644 --- a/app/templating/SetupHelper.scala +++ b/app/templating/SetupHelper.scala @@ -54,9 +54,10 @@ trait SetupHelper { self: I18nHelper => translatedVariantChoices(ctx) :+ variantTuple(chess.variant.FromPosition) - def translatedVariantChoicesWithFenAndKingOfTheHill(implicit ctx: Context) = + def translatedAiVariantChoices(implicit ctx: Context) = translatedVariantChoices(ctx) :+ variantTuple(chess.variant.KingOfTheHill) :+ + variantTuple(chess.variant.ThreeCheck) :+ variantTuple(chess.variant.FromPosition) def translatedVariantChoicesWithVariantsAndFen(implicit ctx: Context) = diff --git a/app/views/setup/ai.scala.html b/app/views/setup/ai.scala.html index a83c710f75..a8d0b2328a 100644 --- a/app/views/setup/ai.scala.html +++ b/app/views/setup/ai.scala.html @@ -1,7 +1,7 @@ @(form: Form[_], ratings: Map[Int, Int])(implicit ctx: Context) @fields = { -@setup.variant(form, translatedVariantChoicesWithFenAndKingOfTheHill) +@setup.variant(form, translatedAiVariantChoices) @fenInput(form("fen"), true) @setup.timeMode(form, lila.setup.AiConfig) @trans.level() diff --git a/modules/ai/src/main/ActorFSM.scala b/modules/ai/src/main/ActorFSM.scala index 17315edfe6..0e3ead11fa 100644 --- a/modules/ai/src/main/ActorFSM.scala +++ b/modules/ai/src/main/ActorFSM.scala @@ -31,7 +31,9 @@ private[ai] final class ActorFSM( case Event(req: Req, none) => stay using Job(req, sender, None).some } when(Idle) { - case Event(Out(t), _) => sys error s"[$name] Unexpected engine output $t" + case Event(Out(t), _) if t.nonEmpty => + logwarn(s"""[$name] Unexpected engine output: "$t"""") + stay case Event(req: Req, _) => start(Job(req, sender, None)) } when(IsReady) { diff --git a/modules/ai/src/main/Config.scala b/modules/ai/src/main/Config.scala index 478c2d0cad..0acd920cc4 100644 --- a/modules/ai/src/main/Config.scala +++ b/modules/ai/src/main/Config.scala @@ -45,7 +45,7 @@ private[ai] case class Config( }) :: List( setoption("UCI_Chess960", req.variant == Chess960), setoption("UCI_KingOfTheHill", req.variant == KingOfTheHill), - setoption("UCI_ThreeCheck", req.variant == ThreeCheck)) + setoption("UCI_3Check", req.variant == ThreeCheck)) def go(req: Req): List[String] = req match { case r: PlayReq => List( diff --git a/modules/ai/src/main/actorApi.scala b/modules/ai/src/main/actorApi.scala index 4673934c64..156cd25789 100644 --- a/modules/ai/src/main/actorApi.scala +++ b/modules/ai/src/main/actorApi.scala @@ -27,8 +27,8 @@ case object ThreeCheck extends Variant object Variant { def apply(str: String): Variant = str.toLowerCase match { case "chess960" => Chess960 - case "kingOfTheHill" => KingOfTheHill - case "threeCheck" => ThreeCheck + case "kingofthehill" => KingOfTheHill + case "threecheck" => ThreeCheck case _ => Standard } def apply(v: chess.variant.Variant): Variant = apply(v.key) diff --git a/modules/game/src/main/Game.scala b/modules/game/src/main/Game.scala index 9f282c25ca..9696f9ee12 100644 --- a/modules/game/src/main/Game.scala +++ b/modules/game/src/main/Game.scala @@ -426,7 +426,8 @@ object Game { val analysableVariants: Set[Variant] = Set( chess.variant.Standard, chess.variant.Chess960, - chess.variant.KingOfTheHill) + chess.variant.KingOfTheHill, + chess.variant.ThreeCheck) val unanalysableVariants: Set[Variant] = Variant.all.toSet -- analysableVariants diff --git a/modules/setup/src/main/Config.scala b/modules/setup/src/main/Config.scala index 5f693847c6..75d581c972 100644 --- a/modules/setup/src/main/Config.scala +++ b/modules/setup/src/main/Config.scala @@ -103,8 +103,8 @@ trait BaseConfig { val variantDefault = chess.variant.Standard val variantsWithFen = variants :+ chess.variant.FromPosition.id - val variantsWithFenAndKingOfTheHill = - variants :+ chess.variant.KingOfTheHill.id :+ chess.variant.FromPosition.id + val aiVariants = + variants :+ chess.variant.KingOfTheHill.id :+ chess.variant.ThreeCheck.id :+ chess.variant.FromPosition.id val variantsWithVariants = variants :+ chess.variant.KingOfTheHill.id :+ chess.variant.ThreeCheck.id :+ chess.variant.Antichess.id :+ chess.variant.Atomic.id :+ chess.variant.Horde.id val variantsWithFenAndVariants = diff --git a/modules/setup/src/main/FormFactory.scala b/modules/setup/src/main/FormFactory.scala index 31ff664b59..a0fc1a8ec4 100644 --- a/modules/setup/src/main/FormFactory.scala +++ b/modules/setup/src/main/FormFactory.scala @@ -35,7 +35,7 @@ private[setup] final class FormFactory(casualOnly: Boolean) { def ai(ctx: UserContext) = Form( mapping( - "variant" -> variantWithFenAndKingOfTheHill, + "variant" -> aiVariants, "timeMode" -> timeMode, "time" -> time, "increment" -> increment, diff --git a/modules/setup/src/main/Mappings.scala b/modules/setup/src/main/Mappings.scala index 039fb1c9c2..b002257c76 100644 --- a/modules/setup/src/main/Mappings.scala +++ b/modules/setup/src/main/Mappings.scala @@ -11,7 +11,7 @@ object Mappings { val variant = number.verifying(Config.variants contains _) val variantWithFen = number.verifying(Config.variantsWithFen contains _) - val variantWithFenAndKingOfTheHill = number.verifying(Config.variantsWithFenAndKingOfTheHill contains _) + val aiVariants = number.verifying(Config.aiVariants contains _) val variantWithVariants = number.verifying(Config.variantsWithVariants contains _) val variantWithFenAndVariants = number.verifying(Config.variantsWithFenAndVariants contains _) val time = number.verifying(HookConfig validateTime _)