lila/modules/game/src/main/Captcher.scala

111 lines
3.2 KiB
Scala
Raw Normal View History

2013-03-30 15:30:47 -06:00
package lila.game
import akka.actor._
2017-01-15 05:26:08 -07:00
import akka.pattern.pipe
import cats.data.NonEmptyList
2019-12-13 07:30:20 -07:00
import chess.format.pgn.{ Sans, Tags }
import chess.format.{ pgn, Forsyth }
2017-01-15 05:26:08 -07:00
import chess.{ Game => ChessGame }
2019-12-08 01:02:12 -07:00
import scala.util.Success
2013-05-24 11:04:49 -06:00
2017-01-15 05:26:08 -07:00
import lila.common.Captcha
2013-05-24 11:04:49 -06:00
import lila.hub.actorApi.captcha._
2013-03-30 15:30:47 -06:00
// only works with standard chess (not chess960)
final private class Captcher(gameRepo: GameRepo)(implicit ec: scala.concurrent.ExecutionContext)
extends Actor {
2013-03-30 15:30:47 -06:00
def receive = {
2020-07-07 02:34:48 -06:00
case AnyCaptcha => sender() ! Impl.current
2013-03-30 15:30:47 -06:00
case GetCaptcha(id: String) => Impl.get(id).pipeTo(sender()).unit
2013-03-30 15:30:47 -06:00
case actorApi.NewCaptcha => Impl.refresh.unit
2013-05-04 17:12:53 -06:00
2014-02-17 02:12:19 -07:00
case ValidCaptcha(id: String, solution: String) =>
Impl.get(id).map(_ valid solution).pipeTo(sender()).unit
2013-03-30 15:30:47 -06:00
}
2013-09-19 03:20:10 -06:00
private object Impl {
2013-03-30 15:30:47 -06:00
2020-05-05 22:11:15 -06:00
def get(id: String): Fu[Captcha] =
find(id) match {
case None => getFromDb(id) map (c => (c | Captcha.default) ~ add)
case Some(c) => fuccess(c)
}
2013-03-30 15:30:47 -06:00
def current = challenges.head
2020-05-05 22:11:15 -06:00
def refresh =
2020-09-21 01:28:28 -06:00
createFromDb andThen { case Success(Some(captcha)) =>
add(captcha)
2020-05-05 22:11:15 -06:00
}
2013-03-30 15:30:47 -06:00
// Private stuff
2020-08-11 17:25:04 -06:00
private val capacity = 256
private var challenges = NonEmptyList.one(Captcha.default)
2013-03-30 15:30:47 -06:00
2021-11-29 03:39:28 -07:00
private def add(c: Captcha): Unit =
if (find(c.gameId).isEmpty) {
2020-08-11 17:25:04 -06:00
challenges = NonEmptyList(c, challenges.toList take capacity)
2013-03-30 15:30:47 -06:00
}
private def find(id: String): Option[Captcha] =
2020-08-11 17:25:04 -06:00
challenges.find(_.gameId == id)
2013-03-30 15:30:47 -06:00
private def createFromDb: Fu[Option[Captcha]] =
2021-11-29 03:33:28 -07:00
findCheckmateInDb(10) orElse findCheckmateInDb(1) flatMap { _ ?? fromGame }
2013-03-30 15:30:47 -06:00
private def findCheckmateInDb(distribution: Int): Fu[Option[Game]] =
2019-11-30 11:06:50 -07:00
gameRepo findRandomStandardCheckmate distribution
2013-03-30 15:30:47 -06:00
2013-05-04 17:12:53 -06:00
private def getFromDb(id: String): Fu[Option[Captcha]] =
gameRepo game id flatMap { _ ?? fromGame }
private def fromGame(game: Game): Fu[Option[Captcha]] =
gameRepo getOptionPgn game.id map {
_ flatMap { makeCaptcha(game, _) }
}
private def makeCaptcha(game: Game, moves: PgnMoves): Option[Captcha] =
for {
rewinded <- rewind(moves)
solutions <- solve(rewinded)
2020-09-21 01:28:28 -06:00
moves = rewinded.situation.destinations map { case (from, dests) =>
from.key -> dests.mkString
}
} yield Captcha(game.id, fen(rewinded), rewinded.player.white, solutions, moves = moves)
2013-03-30 15:30:47 -06:00
private def solve(game: ChessGame): Option[Captcha.Solutions] =
2019-12-13 07:30:20 -07:00
game.situation.moves.view
2020-09-21 01:28:28 -06:00
.flatMap { case (_, moves) =>
moves filter { move =>
(move.after situationOf !game.player).checkMate
}
2013-03-30 15:30:47 -06:00
}
2019-12-13 07:30:20 -07:00
.to(List) map { move =>
s"${move.orig} ${move.dest}"
} toNel
2013-03-30 15:30:47 -06:00
2019-12-08 01:02:12 -07:00
private def rewind(moves: PgnMoves): Option[ChessGame] =
2019-12-13 07:30:20 -07:00
pgn.Reader
.movesWithSans(
moves,
sans => Sans(safeInit(sans.value)),
tags = Tags.empty
)
.flatMap(_.valid) map (_.state) toOption
2013-09-20 08:47:03 -06:00
2020-05-05 22:11:15 -06:00
private def safeInit[A](list: List[A]): List[A] =
list match {
case _ :: Nil => Nil
case x :: xs => x :: safeInit(xs)
case _ => Nil
}
2013-03-30 15:30:47 -06:00
private def fen(game: ChessGame): String = Forsyth exportBoard game.board
2013-03-30 15:30:47 -06:00
}
}