lila/modules/puzzle/src/main/Puzzle.scala

163 lines
4.2 KiB
Scala
Raw Normal View History

2014-02-03 11:53:10 -07:00
package lila.puzzle
2017-03-28 09:18:51 -06:00
import scala.collection.breakOut
2014-02-05 01:57:50 -07:00
import chess.Color
import chess.format.{ Uci, Forsyth }
2014-02-03 11:53:10 -07:00
import org.joda.time.DateTime
case class Puzzle(
2014-02-03 14:04:43 -07:00
id: PuzzleId,
2016-11-26 07:49:25 -07:00
gameId: String,
2014-02-03 14:04:43 -07:00
history: List[String],
fen: String,
lines: List[Line],
2014-02-05 01:57:50 -07:00
depth: Int,
color: Color,
2014-02-03 14:04:43 -07:00
date: DateTime,
2016-12-12 01:47:21 -07:00
perf: PuzzlePerf,
vote: AggregateVote,
2014-02-05 01:57:50 -07:00
attempts: Int,
mate: Boolean
) {
2014-02-03 14:04:43 -07:00
2016-12-07 16:17:45 -07:00
// ply after "initial move" when we start solving
def initialPly: Int = {
fen.split(' ').lastOption flatMap parseIntOption map { move =>
2016-12-07 16:17:45 -07:00
move * 2 - color.fold(0, 1)
}
} | 0
2016-12-11 11:48:43 -07:00
// (1 - 3)/(1 + 3) = -0.5
def enabled = vote.ratio > AggregateVote.minRatio || vote.nb < AggregateVote.minVotes
2016-12-11 11:48:43 -07:00
def withVote(f: AggregateVote => AggregateVote) = copy(vote = f(vote))
2014-02-06 13:36:31 -07:00
2016-11-29 06:15:46 -07:00
def initialMove: Uci.Move = history.lastOption flatMap Uci.Move.apply err s"Bad initial move $this"
2014-02-09 16:17:33 -07:00
def fenAfterInitialMove: Option[String] = {
for {
sit1 <- Forsyth << fen
2016-11-29 06:15:46 -07:00
sit2 <- sit1.move(initialMove).toOption.map(_.situationAfter)
} yield Forsyth >> sit2
}
2014-02-03 14:04:43 -07:00
}
2014-02-03 11:53:10 -07:00
object Puzzle {
2017-07-18 07:44:21 -06:00
case class UserResult(
puzzleId: PuzzleId,
userId: lila.user.User.ID,
result: Result,
rating: (Int, Int)
)
2017-07-18 07:09:16 -06:00
2014-02-03 11:53:10 -07:00
def make(
2016-11-26 07:49:25 -07:00
gameId: String,
2014-02-03 11:53:10 -07:00
history: List[String],
fen: String,
2016-07-29 06:11:56 -06:00
color: Color,
lines: Lines,
mate: Boolean
)(id: PuzzleId) = new Puzzle(
2014-02-05 13:54:19 -07:00
id = id,
2014-02-03 11:53:10 -07:00
gameId = gameId,
history = history,
fen = fen,
lines = lines,
2014-02-05 16:18:11 -07:00
depth = Line minDepth lines,
2016-07-29 06:11:56 -06:00
color = color,
2014-02-03 11:53:10 -07:00
date = DateTime.now,
2016-12-12 01:47:21 -07:00
perf = PuzzlePerf.default,
2016-12-11 12:53:12 -07:00
vote = AggregateVote.default,
2014-02-05 01:57:50 -07:00
attempts = 0,
mate = mate
)
2014-02-03 11:53:10 -07:00
import reactivemongo.bson._
import lila.db.BSON
import BSON.BSONJodaDateTimeHandler
private implicit val lineBSONHandler = new BSONHandler[BSONDocument, Lines] {
2014-02-05 01:57:50 -07:00
private def readMove(move: String) = chess.Pos.doublePiotrToKey(move take 2) match {
2016-12-09 02:31:41 -07:00
case Some(m) => s"$m${move drop 2}"
case _ => sys error s"Invalid piotr move notation: $move"
2014-02-05 01:57:50 -07:00
}
2017-03-28 09:18:51 -06:00
def read(doc: BSONDocument): Lines = doc.elements.map {
case BSONElement(move, BSONBoolean(true)) => Win(readMove(move))
case BSONElement(move, BSONBoolean(false)) => Retry(readMove(move))
case BSONElement(move, more: BSONDocument) =>
Node(readMove(move), read(more))
case BSONElement(move, value) =>
throw new Exception(s"Can't read value of $move: $value")
2017-03-28 09:18:51 -06:00
}(breakOut)
2014-02-05 01:57:50 -07:00
private def writeMove(move: String) = chess.Pos.doubleKeyToPiotr(move take 4) match {
2014-02-17 02:12:19 -07:00
case Some(m) => s"$m${move drop 4}"
case _ => sys error s"Invalid move notation: $move"
2014-02-03 11:53:10 -07:00
}
def write(lines: Lines): BSONDocument = BSONDocument(lines map {
case Win(move) => writeMove(move) -> BSONBoolean(true)
case Retry(move) => writeMove(move) -> BSONBoolean(false)
2014-02-17 02:12:19 -07:00
case Node(move, lines) => writeMove(move) -> write(lines)
2014-02-03 11:53:10 -07:00
})
}
object BSONFields {
val id = "_id"
val gameId = "gameId"
val history = "history"
val fen = "fen"
val lines = "lines"
2014-02-05 01:57:50 -07:00
val depth = "depth"
val white = "white"
2014-02-03 11:53:10 -07:00
val date = "date"
2014-02-06 11:22:28 -07:00
val perf = "perf"
2014-02-08 04:28:36 -07:00
val rating = s"$perf.gl.r"
2014-02-03 11:53:10 -07:00
val vote = "vote"
2016-12-11 11:48:43 -07:00
val voteNb = s"$vote.nb"
val voteRatio = s"$vote.ratio"
2016-12-10 16:26:44 -07:00
val day = "day"
2014-02-03 11:53:10 -07:00
val attempts = "attempts"
val mate = "mate"
2014-02-03 11:53:10 -07:00
}
implicit val puzzleBSONHandler = new BSON[Puzzle] {
import BSONFields._
2016-12-12 01:47:21 -07:00
import PuzzlePerf.puzzlePerfBSONHandler
import AggregateVote.aggregatevoteBSONHandler
2014-02-03 11:53:10 -07:00
def reads(r: BSON.Reader): Puzzle = Puzzle(
2014-02-05 13:54:19 -07:00
id = r int id,
2016-11-26 07:49:25 -07:00
gameId = r str gameId,
2014-02-03 11:53:10 -07:00
history = r str history split ' ' toList,
fen = r str fen,
lines = r.get[Lines](lines),
2014-02-05 01:57:50 -07:00
depth = r int depth,
color = Color(r bool white),
2014-02-03 11:53:10 -07:00
date = r date date,
2016-12-12 01:47:21 -07:00
perf = r.get[PuzzlePerf](perf),
vote = r.get[AggregateVote](vote),
2014-02-06 11:22:28 -07:00
attempts = r int attempts,
mate = r bool mate
)
2014-02-03 11:53:10 -07:00
def writes(w: BSON.Writer, o: Puzzle) = BSONDocument(
id -> o.id,
gameId -> o.gameId,
history -> o.history.mkString(" "),
fen -> o.fen,
lines -> o.lines,
2014-02-05 01:57:50 -07:00
depth -> o.depth,
white -> o.color.white,
2014-02-03 11:53:10 -07:00
date -> o.date,
2014-02-06 11:22:28 -07:00
perf -> o.perf,
2014-02-03 11:53:10 -07:00
vote -> o.vote,
2014-02-05 01:57:50 -07:00
attempts -> o.attempts,
mate -> o.mate
)
2014-02-03 11:53:10 -07:00
}
}