lila/modules/bot/src/main/BotPlayer.scala

103 lines
3.3 KiB
Scala

package lila.bot
import akka.actor._
import scala.concurrent.duration._
import scala.concurrent.Promise
import chess.format.Uci
import lila.game.{ Game, GameRepo, Pov }
import lila.hub.actorApi.map.Tell
import lila.game.Game.{ PlayerId, FullId }
import lila.hub.actorApi.round.{ Abort, BotPlay, RematchNo, RematchYes, Resign }
import lila.round.actorApi.round.{ DrawNo, DrawYes }
import lila.user.User
final class BotPlayer(
chatApi: lila.chat.ChatApi,
isOfferingRematch: Pov => Boolean
)(implicit system: ActorSystem) {
def apply(pov: Pov, me: User, uciStr: String, offeringDraw: Option[Boolean]): Funit =
lila.common.Future.delay((pov.game.hasAi ?? 500) millis) {
Uci(uciStr).fold(fufail[Unit](s"Invalid UCI: $uciStr")) { uci =>
lila.mon.bot.moves(me.username)()
if (!pov.isMyTurn) fufail("Not your turn, or game already over")
else {
val promise = Promise[Unit]
if (pov.player.isOfferingDraw && (offeringDraw contains false)) declineDraw(pov)
else if (!pov.player.isOfferingDraw && (offeringDraw contains true)) offerDraw(pov)
system.lilaBus.publish(
Tell(pov.gameId, BotPlay(pov.playerId, uci, promise.some)),
'roundMapTell
)
promise.future
}
}
}
def chat(gameId: Game.ID, me: User, d: BotForm.ChatData) = fuccess {
lila.mon.bot.chats(me.username)()
val chatId = lila.chat.Chat.Id {
if (d.room == "player") gameId else s"$gameId/w"
}
val source = d.room == "spectator" option {
lila.hub.actorApi.shutup.PublicSource.Watcher(gameId)
}
chatApi.userChat.write(chatId, me.id, d.text, publicSource = source)
}
def rematchAccept(id: Game.ID, me: User): Fu[Boolean] = rematch(id, me, true)
def rematchDecline(id: Game.ID, me: User): Fu[Boolean] = rematch(id, me, false)
private def rematch(id: Game.ID, me: User, accept: Boolean): Fu[Boolean] =
GameRepo game id map {
_.flatMap(Pov(_, me)).filter(p => isOfferingRematch(!p)) ?? { pov =>
// delay so it feels more natural
lila.common.Future.delay(if (accept) 100.millis else 2.seconds) {
fuccess {
system.lilaBus.publish(
Tell(pov.gameId, (if (accept) RematchYes else RematchNo)(pov.playerId)),
'roundMapTell
)
}
}(system)
true
}
}
def abort(pov: Pov): Funit =
if (!pov.game.abortable) fufail("This game can no longer be aborted")
else fuccess {
system.lilaBus.publish(
Tell(pov.gameId, Abort(pov.playerId)),
'roundMapTell
)
}
def resign(pov: Pov): Funit =
if (pov.game.abortable) abort(pov)
else if (pov.game.resignable) fuccess {
system.lilaBus.publish(
Tell(pov.gameId, Resign(pov.playerId)),
'roundMapTell
)
}
else fufail("This game cannot be resigned")
def declineDraw(pov: Pov): Unit =
if (pov.game.drawable && pov.opponent.isOfferingDraw)
system.lilaBus.publish(
Tell(pov.gameId, DrawNo(PlayerId(pov.playerId))),
'roundMapTell
)
def offerDraw(pov: Pov): Unit =
if (pov.game.drawable && pov.game.playerCanOfferDraw(pov.color) && pov.isMyTurn)
system.lilaBus.publish(
Tell(pov.gameId, DrawYes(PlayerId(pov.playerId))),
'roundMapTell
)
}