Stockfish for King of the Hill WIP

This commit is contained in:
Thibault Duplessis 2014-08-05 21:35:05 +02:00
parent 5d0835c4d5
commit 9c43c7fe04
17 changed files with 44 additions and 27 deletions

View file

@ -14,7 +14,8 @@ object Ai extends LilaController {
Env.ai.server.move(
uciMoves = get("uciMoves", req) ?? (_.split(' ').toList),
initialFen = get("initialFen", req),
level = getInt("level", req) | 1
level = getInt("level", req) | 1,
kingOfTheHill = getBool("kingOfTheHill", req)
) fold (
err => {
logwarn("[ai] stockfish server play: " + err)
@ -29,7 +30,8 @@ object Ai extends LilaController {
Env.ai.server.analyse(
uciMoves = get("uciMoves", req) ?? (_.split(' ').toList),
initialFen = get("initialFen", req),
requestedByHuman = getBool("human", req)
requestedByHuman = getBool("human", req),
kingOfTheHill = getBool("kingOfTheHill", req)
).effectFold(
err => WS.url(replyToUrl).post(err.toString),
infos => WS.url(replyToUrl).post(lila.analyse.Info encodeList infos)

View file

@ -34,6 +34,10 @@ trait SetupHelper { self: I18nHelper =>
translatedVariantChoices(ctx) :+
variantTuple(Variant.FromPosition)
def translatedVariantChoicesWithFenAndKingOfTheHill(implicit ctx: Context) =
translatedVariantChoicesWithFen(ctx) :+
variantTuple(Variant.KingOfTheHill)
def translatedVariantChoicesWithVariantsAndFen(implicit ctx: Context) =
translatedVariantChoicesWithVariants :+
variantTuple(Variant.FromPosition)

View file

@ -2,7 +2,7 @@
@fields = {
<div class="variants">
@trans.variant() @setup.select(form("variant"), translatedVariantChoicesWithFen)
@trans.variant() @setup.select(form("variant"), translatedVariantChoicesWithFenAndKingOfTheHill)
</div>
@fenInput(form("fen"), true)
@setup.clock(form, lila.setup.AiConfig)

View file

@ -26,7 +26,7 @@ final class Client(
for {
fen game.variant.exotic ?? { GameRepo initialFen game.id }
uciMoves uciMemo get game
moveResult move(uciMoves.toList, fen, level)
moveResult move(uciMoves.toList, fen, level, game.variant.kingOfTheHill)
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,22 +37,24 @@ final class Client(
private val networkLatency = 1 second
def move(uciMoves: List[String], initialFen: Option[String], level: Int): Fu[MoveResult] = {
def move(uciMoves: List[String], initialFen: Option[String], level: Int, kingOfTheHill: Boolean): 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)
"level" -> level.toString,
"kingOfTheHill" -> (kingOfTheHill ?? "1"))
} map MoveResult.apply
}
def analyse(gameId: String, uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean) {
def analyse(gameId: String, uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean, kingOfTheHill: Boolean) {
WS.url(s"$endpoint/analyse").withQueryString(
"replyUrl" -> callbackUrl.replace("%", gameId),
"uciMoves" -> uciMoves.mkString(" "),
"initialFen" -> ~initialFen,
"human" -> requestedByHuman.fold("1", "0")).post("go")
"human" -> requestedByHuman.fold("1", "0"),
"kingOfTheHill" -> (kingOfTheHill ?? "1")).post("go")
}
private def sendRequest(retriable: Boolean)(req: WSRequestHolder): Fu[String] =

View file

@ -44,11 +44,13 @@ private[ai] case class Config(
setoption("Uci_AnalyseMode", false),
setoption("Skill Level", skill(r.level)),
setoption("UCI_Chess960", r.chess960),
setoption("UCI_KingOfTheHill", r.kingOfTheHill),
setoption("OwnBook", ownBook(r.level)))
case r: AnalReq => List(
setoption("Uci_AnalyseMode", true),
setoption("Skill Level", skillMax),
setoption("UCI_Chess960", r.chess960),
setoption("UCI_KingOfTheHill", r.kingOfTheHill),
setoption("OwnBook", true))
}

View file

@ -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) =>
client.analyse(gameId, uciMoves, fen, requestedByHuman)
case lila.hub.actorApi.ai.Analyse(gameId, uciMoves, fen, requestedByHuman, kingOfTheHill) =>
client.analyse(gameId, uciMoves, fen, requestedByHuman, kingOfTheHill)
}
}), name = ActorName)

View file

@ -32,7 +32,7 @@ private[ai] final class Queue(config: Config) extends Actor {
private val tasks = new scala.collection.mutable.PriorityQueue[Task]
private val maxTasks = 500 * config.nbInstances
private val maxTasks = 600 * config.nbInstances
def receive = {
@ -58,14 +58,14 @@ private[ai] final class Queue(config: Config) extends Actor {
tasks += Task(req, sender, timeout)
}
case FullAnalReq(moves, fen, requestedByHuman) if (requestedByHuman || tasks.size < maxTasks) =>
case FullAnalReq(moves, fen, requestedByHuman, kingOfTheHill) 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) mapTo manifest[Option[Evaluation]]
self ? AnalReq(serie, fen, size, requestedByHuman, kingOfTheHill) mapTo manifest[Option[Evaluation]]
}
Future.fold(futures)(Vector[Option[Evaluation]]())(_ :+ _) addFailureEffect {
case e => mrSender ! Status.Failure(e)
@ -73,7 +73,7 @@ private[ai] final class Queue(config: Config) extends Actor {
mrSender ! Evaluation.toInfos(results.toList.map(_ | Evaluation.empty), moves)
}
case FullAnalReq(moves, fen, requestedByHuman) =>
case r: FullAnalReq =>
sender ! Status.Failure(new Exception("analysis queue is full"))
}
}

View file

@ -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): Fu[MoveResult] = {
def move(uciMoves: List[String], initialFen: Option[String], level: Int, kingOfTheHill: Boolean): Fu[MoveResult] = {
implicit val timeout = makeTimeout(config.playTimeout)
queue ? PlayReq(uciMoves, initialFen map chess960Fen, level) mapTo
queue ? PlayReq(uciMoves, initialFen map chess960Fen, level, kingOfTheHill) mapTo
manifest[Option[String]] flatten "[stockfish] play failed" map MoveResult.apply
}
def analyse(uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean): Fu[List[Info]] = {
def analyse(uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean, kingOfTheHill: Boolean): Fu[List[Info]] = {
implicit val timeout = makeTimeout {
if (requestedByHuman) 1.hour else 24.hours
}
(queue ? FullAnalReq(uciMoves take config.analyseMaxPlies, initialFen map chess960Fen, requestedByHuman)) mapTo manifest[List[Info]]
(queue ? FullAnalReq(uciMoves take config.analyseMaxPlies, initialFen map chess960Fen, requestedByHuman, kingOfTheHill)) mapTo manifest[List[Info]]
}
private def chess960Fen(fen: String) = (Forsyth << fen).fold(fen) { situation =>

View file

@ -24,6 +24,7 @@ sealed trait Req {
def analyse: Boolean
def requestedByHuman: Boolean
def priority: Int
def kingOfTheHill: Boolean
def chess960 = fen.isDefined
}
@ -31,7 +32,8 @@ sealed trait Req {
case class PlayReq(
moves: List[String],
fen: Option[String],
level: Int) extends Req {
level: Int,
kingOfTheHill: Boolean) extends Req {
val analyse = false
val requestedByHuman = true
@ -43,7 +45,8 @@ case class AnalReq(
moves: List[String],
fen: Option[String],
totalSize: Int,
requestedByHuman: Boolean) extends Req {
requestedByHuman: Boolean,
kingOfTheHill: Boolean) extends Req {
val priority =
if (requestedByHuman) -totalSize
@ -57,7 +60,8 @@ case class AnalReq(
case class FullAnalReq(
moves: List[String],
fen: Option[String],
requestedByHuman: Boolean)
requestedByHuman: Boolean,
kingOfTheHill: Boolean)
case class Job(req: Req, sender: akka.actor.ActorRef, buffer: List[String]) {

View file

@ -51,7 +51,7 @@ final class Analyser(
Replay(game.pgnMoves mkString " ", initialFen, game.variant).fold(
fufail(_),
replay => {
ai ! lila.hub.actorApi.ai.Analyse(game.id, UciDump(replay), initialFen, requestedByHuman = !auto)
ai ! lila.hub.actorApi.ai.Analyse(game.id, UciDump(replay), initialFen, requestedByHuman = !auto, game.variant.kingOfTheHill)
AnalysisRepo byId id flatten "Missing analysis"
}
)

View file

@ -31,7 +31,8 @@ private[api] final class GameApi(
G.status -> $gte(chess.Status.Mate.id),
G.playerUids -> username,
G.rated -> rated.map(_.fold(JsBoolean(true), $exists(false))),
G.analysed -> analysed.map(_.fold(JsBoolean(true), $exists(false)))
G.analysed -> analysed.map(_.fold(JsBoolean(true), $exists(false))),
G.variant -> checkToken.option(JsArray(Variant.Standard, Variant.Chess960))
).noNull) sort lila.game.Query.sortCreated, makeNb(token, nb)) flatMap
gamesJson(withAnalysis, token) map { games =>
Json.obj("list" -> games)

View file

@ -84,7 +84,7 @@ private[chat] final class ChatApi(
def cut(text: String) = Some(text.trim take 140) filter (_.nonEmpty)
val delocalize = new lila.common.String.Delocalizer(netDomain)
val domainRegex = netDomain.replace(".", """\.""")
val gameUrlRegex = (domainRegex + """/([\w-]{8})[\w-]{4}""").r
val gameUrlRegex = (domainRegex + """\b/([\w]{8})[\w]{4}\b""").r
def noPrivateUrl(str: String): String =
gameUrlRegex.replaceAllIn(str, m => quoteReplacement(netDomain + "/" + (m group 1)))
}

View file

@ -291,7 +291,7 @@ case class Game(
def replayable = imported || finished
private val analysableVariants: Set[Variant] = Set(Variant.Standard, Variant.Chess960)
private val analysableVariants: Set[Variant] = Set(Variant.Standard, Variant.Chess960, Variant.KingOfTheHill)
def analysable = replayable && turns > 4 && analysableVariants(variant)
def fromPosition = source ?? (Source.Position==)

View file

@ -141,7 +141,7 @@ case class MakeTeam(id: String, name: String)
}
package ai {
case class Analyse(gameId: String, uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean)
case class Analyse(gameId: String, uciMoves: List[String], initialFen: Option[String], requestedByHuman: Boolean, kingOfTheHill: Boolean)
case class AutoAnalyse(gameId: String)
}

View file

@ -89,6 +89,7 @@ trait BaseConfig {
val variantDefault = Variant.Standard
val variantsWithFen = variants :+ Variant.FromPosition.id
val variantsWithFenAndKingOfTheHill = variantsWithFen :+ Variant.KingOfTheHill.id
val variantsWithVariants = variants :+ Variant.KingOfTheHill.id :+ Variant.ThreeCheck.id
val variantsWithFenAndVariants = variantsWithFen :+ Variant.KingOfTheHill.id :+ Variant.ThreeCheck.id

View file

@ -36,7 +36,7 @@ private[setup] final class FormFactory(casualOnly: Boolean) {
def ai(ctx: UserContext) = Form(
mapping(
"variant" -> variantWithFen,
"variant" -> variantWithFenAndKingOfTheHill,
"clock" -> boolean,
"time" -> time,
"increment" -> increment,

View file

@ -11,6 +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 variantWithVariants = number.verifying(Config.variantsWithVariants contains _)
val variantWithFenAndVariants = number.verifying(Config.variantsWithFenAndVariants contains _)
val time = number.verifying(HookConfig validateTime _)