rewrite Ai interface and implementations
This commit is contained in:
parent
7e4cd6759c
commit
3087cd05d4
|
@ -36,7 +36,7 @@ private[app] final class AiStresser(env: lila.ai.Env, system: ActorSystem) {
|
|||
if (loop) newGame pipeTo self
|
||||
}
|
||||
case Game(moves, it) ⇒
|
||||
ai.getMove(moves take it mkString " ", none, level).effectFold(e ⇒ {
|
||||
ai.move(moves take it mkString " ", none, level).effectFold(e ⇒ {
|
||||
logwarn("[ai] play: " + e)
|
||||
newGame pipeTo self
|
||||
}, { _ ⇒
|
||||
|
|
|
@ -12,7 +12,7 @@ object Ai extends LilaController {
|
|||
|
||||
def playStockfish = Action.async { req ⇒
|
||||
IfServer {
|
||||
stockfishServer.play(
|
||||
stockfishServer.move(
|
||||
pgn = ~get("pgn", req),
|
||||
initialFen = get("initialFen", req),
|
||||
level = getInt("level", req) | 1
|
||||
|
|
|
@ -1,11 +1,33 @@
|
|||
package lila.ai
|
||||
|
||||
import chess.{ Game, Move }
|
||||
import chess.format.UciMove
|
||||
import chess.Move
|
||||
|
||||
import lila.analyse.AnalysisMaker
|
||||
import lila.game.{ Game, Progress, GameRepo, PgnRepo }
|
||||
|
||||
trait Ai {
|
||||
|
||||
def play(game: Game, pgn: String, initialFen: Option[String], level: Int): Fu[(Game, Move)]
|
||||
def play(game: Game, level: Int): Fu[Progress] = withValidSituation(game) {
|
||||
for {
|
||||
fen ← game.variant.exotic ?? { GameRepo initialFen game.id }
|
||||
pgn ← PgnRepo get game.id
|
||||
moveStr ← move(pgn, fen, level)
|
||||
result ← (for {
|
||||
uciMove ← UciMove(moveStr) toValid "Wrong bestmove: " + moveStr
|
||||
result ← (game.toChess withPgnMoves pgn)(uciMove.orig, uciMove.dest)
|
||||
} yield result).future
|
||||
(c, m) = result
|
||||
(progress, pgn2) = game.update(c, m)
|
||||
_ ← (GameRepo save progress) >> PgnRepo.save(game.id, pgn2)
|
||||
} yield progress
|
||||
}
|
||||
|
||||
def move(pgn: String, initialFen: Option[String], level: Int): Fu[String]
|
||||
|
||||
def analyse(pgn: String, initialFen: Option[String]): Fu[AnalysisMaker]
|
||||
|
||||
private def withValidSituation[A](game: Game)(op: ⇒ Fu[A]): Fu[A] =
|
||||
if (game.toChess.situation playable true) op
|
||||
else fufail("[ai stockfish] invalid game situation: " + game.toChess.situation)
|
||||
}
|
||||
|
|
|
@ -40,8 +40,8 @@ final class Env(
|
|||
|
||||
lazy val ai: Ai = (EngineName, IsClient) match {
|
||||
case ("stockfish", true) ⇒ stockfishClient
|
||||
case ("stockfish", false) ⇒ stockfishAi
|
||||
case _ ⇒ stupidAi
|
||||
case ("stockfish", false) ⇒ stockfishServer
|
||||
case _ ⇒ throw new Exception(s"Unsupported AI: $EngineName")
|
||||
}
|
||||
|
||||
def isServer = IsServer
|
||||
|
@ -58,8 +58,6 @@ final class Env(
|
|||
}
|
||||
}), name = ActorName)
|
||||
|
||||
private lazy val stockfishAi = new stockfish.Ai(stockfishServer)
|
||||
|
||||
lazy val stockfishClient = new stockfish.Client(
|
||||
dispatcher = system.actorOf(
|
||||
Props(new stockfish.remote.Dispatcher(
|
||||
|
@ -71,7 +69,7 @@ final class Env(
|
|||
loadRoute = StockfishLoadRoute) _,
|
||||
scheduler = system.scheduler
|
||||
)), name = "stockfish-dispatcher"),
|
||||
fallback = stockfishAi,
|
||||
fallback = stockfishServer,
|
||||
config = stockfishConfig)
|
||||
|
||||
lazy val stockfishServer = new stockfish.Server(
|
||||
|
@ -84,8 +82,6 @@ final class Env(
|
|||
new stockfish.Queue(stockfishConfig, system)
|
||||
) withDispatcher StockfishQueueDispatcher, name = StockfishQueueName)
|
||||
|
||||
private lazy val stupidAi = new StupidAi
|
||||
|
||||
private lazy val client = (EngineName, IsClient) match {
|
||||
case ("stockfish", true) ⇒ stockfishClient.some
|
||||
case _ ⇒ none
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
package lila.ai
|
||||
|
||||
import chess.{ Game, Move }
|
||||
|
||||
private[ai] final class StupidAi extends Ai {
|
||||
|
||||
def play(game: Game, pgn: String, initialFen: Option[String], level: Int): Fu[(Game, Move)] = (for {
|
||||
destination ← game.situation.destinations.headOption toValid "Game is finished"
|
||||
(orig, dests) = destination
|
||||
dest ← dests.headOption toValid "No moves from " + orig
|
||||
newChessGameAndMove ← game(orig, dest)
|
||||
} yield newChessGameAndMove).future
|
||||
|
||||
def analyse(pgn: String, initialFen: Option[String]) =
|
||||
throw new RuntimeException("Stupid analysis is not implemented")
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package lila.ai
|
||||
package stockfish
|
||||
|
||||
import chess.{ Game, Move }
|
||||
import lila.analyse.AnalysisMaker
|
||||
|
||||
final class Ai(server: Server) extends lila.ai.Ai {
|
||||
|
||||
def play(game: Game, pgn: String, initialFen: Option[String], level: Int): Fu[(Game, Move)] =
|
||||
withValidSituation(game) {
|
||||
server.play(pgn, initialFen, level) flatMap {
|
||||
Stockfish.applyMove(game, pgn, _)
|
||||
}
|
||||
}
|
||||
|
||||
def analyse(pgn: String, initialFen: Option[String]): Fu[AnalysisMaker] =
|
||||
server.analyse(pgn, initialFen)
|
||||
|
||||
private def withValidSituation[A](game: Game)(op: ⇒ Fu[A]): Fu[A] =
|
||||
if (game.situation playable true) op
|
||||
else fufail("[ai stockfish] invalid game situation: " + game.situation)
|
||||
}
|
|
@ -19,18 +19,14 @@ final class Client(
|
|||
fallback: lila.ai.Ai,
|
||||
config: Config) extends lila.ai.Ai {
|
||||
|
||||
def play(game: Game, pgn: String, initialFen: Option[String], level: Int): Fu[(Game, Move)] = {
|
||||
getMove(pgn, initialFen, level) flatMap {
|
||||
Stockfish.applyMove(game, pgn, _)
|
||||
} recoverWith {
|
||||
case e: Exception ⇒ fallback.play(game, pgn, initialFen, level)
|
||||
}
|
||||
}
|
||||
def getMove(pgn: String, initialFen: Option[String], level: Int): Fu[String] = {
|
||||
def move(pgn: String, initialFen: Option[String], level: Int): Fu[String] = {
|
||||
implicit val timeout = makeTimeout(config.playTimeout)
|
||||
dispatcher ? Play(pgn, ~initialFen, level) mapTo manifest[String]
|
||||
} recoverWith {
|
||||
case e: Exception ⇒ fallback.move( pgn, initialFen, level)
|
||||
}
|
||||
|
||||
|
||||
def analyse(pgn: String, initialFen: Option[String]): Fu[AnalysisMaker] = {
|
||||
implicit val timeout = makeTimeout(config.analyseTimeout)
|
||||
dispatcher ? Analyse(pgn, ~initialFen) mapTo manifest[String] flatMap { str ⇒
|
||||
|
|
|
@ -15,9 +15,9 @@ import chess.Variant.Chess960
|
|||
import lila.analyse.AnalysisMaker
|
||||
import lila.hub.actorApi.ai.GetLoad
|
||||
|
||||
private[ai] final class Server(queue: ActorRef, config: Config) {
|
||||
private[ai] final class Server(queue: ActorRef, config: Config) extends lila.ai.Ai {
|
||||
|
||||
def play(pgn: String, initialFen: Option[String], level: Int): Fu[String] = {
|
||||
def move(pgn: String, initialFen: Option[String], level: Int): Fu[String] = {
|
||||
implicit val timeout = makeTimeout(config.playTimeout)
|
||||
UciDump(pgn, initialFen, initialFen.isDefined option Chess960).future flatMap { moves ⇒
|
||||
queue ? PlayReq(moves, initialFen map chess960Fen, level) mapTo
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package lila.round
|
||||
|
||||
import actorApi.round.{ HumanPlay, AiPlay, DrawNo, TakebackNo, PlayResult, Cheat }
|
||||
import chess.format.Forsyth
|
||||
import chess.Pos.posAt
|
||||
import chess.{ Status, Role, Color }
|
||||
|
||||
import actorApi.round.{ HumanPlay, AiPlay, DrawNo, TakebackNo, PlayResult, Cheat }
|
||||
import lila.ai.Ai
|
||||
import lila.game.{ Game, GameRepo, PgnRepo, Pov, Progress }
|
||||
import lila.hub.actorApi.map.Tell
|
||||
|
@ -51,19 +51,10 @@ private[round] final class Player(
|
|||
|
||||
def ai(play: AiPlay)(game: Game): Fu[Events] =
|
||||
(game.playable && game.player.isAi).fold(
|
||||
(game.variant.exotic ?? { GameRepo initialFen game.id }) zip
|
||||
(PgnRepo get game.id) flatMap {
|
||||
case (fen, pgn) ⇒
|
||||
engine.play(game.toChess, pgn, fen, ~game.aiLevel) flatMap {
|
||||
case (newChessGame, move) ⇒ {
|
||||
val (progress, pgn2) = game.update(newChessGame, move)
|
||||
(GameRepo save progress) >>
|
||||
PgnRepo.save(game.id, pgn2) >>-
|
||||
notifyProgress(progress) >>
|
||||
moveFinish(progress.game, game.turnColor) map { progress.events ::: _ }
|
||||
}
|
||||
}
|
||||
} addFailureEffect play.onFailure,
|
||||
engine.play(game, game.aiLevel | 1) flatMap { progress ⇒
|
||||
notifyProgress(progress)
|
||||
moveFinish(progress.game, game.turnColor) map { progress.events ::: _ }
|
||||
} addFailureEffect play.onFailure,
|
||||
fufail("not AI turn")
|
||||
) logFailureErr "[ai play] game %s turn %d".format(game.id, game.turns)
|
||||
|
||||
|
|
|
@ -33,12 +33,7 @@ private[setup] final class Processor(
|
|||
(GameRepo insertDenormalized game) >>-
|
||||
(timeline ! game) >>
|
||||
game.player.isHuman.fold(fuccess(pov), for {
|
||||
initialFen ← game.variant.exotic ?? (GameRepo initialFen game.id)
|
||||
pgnString ← PgnRepo get game.id
|
||||
aiResult ← engine.play(game.toChess, pgnString, initialFen, ~game.aiLevel)
|
||||
(newChessGame, move) = aiResult
|
||||
(progress, pgn) = game.update(newChessGame, move)
|
||||
_ ← (GameRepo save progress) >> PgnRepo.save(game.id, pgn)
|
||||
progress ← engine.play(game, game.aiLevel | 1)
|
||||
} yield pov withGame progress.game)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue