lila/modules/fishnet/src/main/FishnetPlayer.scala

86 lines
2.8 KiB
Scala
Raw Normal View History

2016-03-11 20:32:21 -07:00
package lila.fishnet
import chess.format.Uci
2019-12-13 07:30:20 -07:00
import chess.{ Black, Clock, White }
import scala.concurrent.duration._
2016-03-11 20:32:21 -07:00
import lila.common.{ Bus, Future, ThreadLocalRandom }
2016-03-11 20:32:21 -07:00
import lila.game.{ Game, GameRepo, UciMemo }
import lila.hub.actorApi.map.Tell
import lila.hub.actorApi.round.FishnetPlay
2016-03-11 20:32:21 -07:00
2021-04-13 02:12:51 -06:00
final class FishnetPlayer(
redis: FishnetRedis,
openingBook: FishnetOpeningBook,
2019-12-01 19:04:35 -07:00
gameRepo: GameRepo,
2016-09-05 08:15:24 -06:00
uciMemo: UciMemo,
val maxPlies: Int
2020-06-24 03:37:18 -06:00
)(implicit
ec: scala.concurrent.ExecutionContext,
system: akka.actor.ActorSystem
) {
2016-03-12 10:00:27 -07:00
2019-12-13 07:30:20 -07:00
def apply(game: Game): Funit =
game.aiLevel ?? { level =>
Future.delay(delayFor(game) | 0.millis) {
openingBook(game, level) flatMap {
case Some(move) =>
fuccess {
Bus.publish(Tell(game.id, FishnetPlay(move, game.playedTurns)), "roundSocket")
}
case None => makeWork(game, level) addEffect redis.request void
}
2019-12-13 07:30:20 -07:00
}
2020-09-21 01:28:28 -06:00
} recover { case e: Exception =>
logger.info(e.getMessage)
2018-05-08 14:53:05 -06:00
}
2016-03-11 20:32:21 -07:00
2019-12-13 07:30:20 -07:00
private val delayFactor = 0.011f
2018-05-08 14:53:05 -06:00
private val defaultClock = Clock(300, 0)
2018-12-26 19:56:14 -07:00
private def delayFor(g: Game): Option[FiniteDuration] =
2019-11-26 20:02:46 -07:00
if (!g.bothPlayersHaveMoved) 2.seconds.some
2019-12-13 07:30:20 -07:00
else
for {
pov <- g.aiPov
clock = g.clock | defaultClock
totalTime = clock.estimateTotalTime.centis
if totalTime > 20 * 100
delay = (clock.remainingTime(pov.color).centis atMost totalTime) * delayFactor
accel = 1 - ((g.turns - 20) atLeast 0 atMost 100) / 150f
sleep = (delay * accel) atMost 500
if sleep > 25
millis = sleep * 10
randomized = millis + millis * (ThreadLocalRandom.nextDouble() - 0.5)
2019-12-13 07:30:20 -07:00
divided = randomized / (if (g.turns > 9) 1 else 2)
} yield divided.millis
2018-05-08 14:53:05 -06:00
private def makeWork(game: Game, level: Int): Fu[Work.Move] =
if (game.situation playable true)
2019-12-01 19:04:35 -07:00
if (game.turns <= maxPlies) gameRepo.initialFen(game) zip uciMemo.get(game) map {
2019-12-13 07:30:20 -07:00
case (initialFen, moves) =>
Work.Move(
_id = Work.makeId,
game = Work.Game(
id = game.id,
initialFen = initialFen,
studyId = none,
variant = game.variant,
moves = moves mkString " "
),
level =
if (level < 3 && game.clock.exists(_.config.limit.toSeconds < 60)) 3
else level,
clock = game.clock.map { clk =>
Work.Clock(
wtime = clk.remainingTime(White).centis,
btime = clk.remainingTime(Black).centis,
inc = clk.incrementSeconds
)
}
)
}
2016-03-12 10:00:27 -07:00
else fufail(s"[fishnet] Too many moves (${game.turns}), won't play ${game.id}")
else fufail(s"[fishnet] invalid position on ${game.id}")
2016-03-11 20:32:21 -07:00
}