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

115 lines
3.3 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
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
import scalaz.Validation.FlatMap._
import scalaz.NonEmptyList
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 = {
case AnyCaptcha => sender ! Impl.current
2013-03-30 15:30:47 -06:00
2014-04-20 15:13:02 -06:00
case GetCaptcha(id: String) => Impl get id pipeTo sender
2013-03-30 15:30:47 -06:00
case actorApi.NewCaptcha => Impl.refresh
2013-05-04 17:12:53 -06:00
2014-02-17 02:12:19 -07:00
case ValidCaptcha(id: String, solution: String) =>
2013-05-04 17:12:53 -06:00
Impl get id map (_ valid solution) pipeTo sender
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
def get(id: String): Fu[Captcha] = find(id) match {
2019-12-13 07:30:20 -07:00
case None => getFromDb(id) map (c => (c | Captcha.default) ~ add)
2014-02-17 02:12:19 -07:00
case Some(c) => fuccess(c)
2013-03-30 15:30:47 -06:00
}
def current = challenges.head
2019-11-30 11:06:50 -07:00
def refresh = createFromDb andThen {
case Success(Some(captcha)) => add(captcha)
}
2013-03-30 15:30:47 -06:00
// Private stuff
2019-12-13 07:30:20 -07:00
private val capacity = 256
2013-09-19 03:20:10 -06:00
private var challenges: NonEmptyList[Captcha] = NonEmptyList(Captcha.default)
2013-03-30 15:30:47 -06:00
2017-10-21 14:06:14 -06:00
private def add(c: Captcha): Unit = {
2013-03-30 15:30:47 -06:00
find(c.gameId) ifNone {
2013-09-19 03:20:10 -06:00
challenges = NonEmptyList.nel(c, challenges.list take capacity)
2013-03-30 15:30:47 -06:00
}
}
private def find(id: String): Option[Captcha] =
challenges.list.find(_.gameId == id)
private def createFromDb: Fu[Option[Captcha]] =
findCheckmateInDb(10) flatMap {
2014-02-17 02:12:19 -07:00
_.fold(findCheckmateInDb(1))(g => fuccess(g.some))
} 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)
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
.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
private def safeInit[A](list: List[A]): List[A] = list match {
2019-12-08 01:02:12 -07:00
case _ :: Nil => Nil
2019-12-13 07:30:20 -07:00
case x :: xs => x :: safeInit(xs)
case _ => Nil
2013-09-20 08:47:03 -06:00
}
2013-03-30 15:30:47 -06:00
private def fen(game: ChessGame): String = Forsyth >> game takeWhile (_ != ' ')
}
}