Stockfish for King of the Hill WIP
This commit is contained in:
parent
5d0835c4d5
commit
9c43c7fe04
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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] =
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 =>
|
||||
|
|
|
@ -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]) {
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
|
|
|
@ -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==)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 _)
|
||||
|
|
Loading…
Reference in a new issue