2014-02-03 11:53:10 -07:00
|
|
|
package lila.puzzle
|
|
|
|
|
2014-02-05 01:57:50 -07:00
|
|
|
import chess.Color
|
2014-02-03 11:53:10 -07:00
|
|
|
import org.joda.time.DateTime
|
|
|
|
import scalaz.NonEmptyList
|
|
|
|
|
2014-02-06 11:22:28 -07:00
|
|
|
import lila.rating.Perf
|
2014-02-03 11:53:10 -07:00
|
|
|
|
|
|
|
case class Puzzle(
|
2014-02-03 14:04:43 -07:00
|
|
|
id: PuzzleId,
|
|
|
|
gameId: Option[String],
|
|
|
|
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,
|
2014-02-06 11:22:28 -07:00
|
|
|
perf: Perf,
|
2014-02-06 13:36:31 -07:00
|
|
|
vote: Vote,
|
2014-02-05 01:57:50 -07:00
|
|
|
attempts: Int,
|
|
|
|
wins: Int,
|
|
|
|
time: Int) {
|
2014-02-03 14:04:43 -07:00
|
|
|
|
2014-02-25 13:44:02 -07:00
|
|
|
def initialPly: Option[Int] = fen.split(' ').lastOption flatMap parseIntOption map { move =>
|
|
|
|
move * 2 + color.fold(0, 1)
|
|
|
|
}
|
|
|
|
|
2014-02-17 02:12:19 -07:00
|
|
|
def withVote(f: Vote => Vote) = copy(vote = f(vote))
|
2014-02-06 13:36:31 -07:00
|
|
|
|
2014-02-05 13:18:15 -07:00
|
|
|
def winPercent = if (attempts == 0) 0 else wins * 100 / attempts
|
|
|
|
|
2014-02-03 14:04:43 -07:00
|
|
|
def initialMove = history.last
|
2014-02-09 16:17:33 -07:00
|
|
|
|
|
|
|
def enabled = vote.sum > -9000
|
2014-02-03 14:04:43 -07:00
|
|
|
}
|
2014-02-03 11:53:10 -07:00
|
|
|
|
|
|
|
object Puzzle {
|
|
|
|
|
|
|
|
def make(
|
|
|
|
gameId: Option[String],
|
|
|
|
history: List[String],
|
|
|
|
fen: String,
|
2014-02-05 13:54:19 -07:00
|
|
|
lines: Lines)(id: PuzzleId) = new Puzzle(
|
|
|
|
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,
|
2014-02-05 01:57:50 -07:00
|
|
|
color = Color(history.size % 2 == 0),
|
2014-02-03 11:53:10 -07:00
|
|
|
date = DateTime.now,
|
2014-02-06 11:22:28 -07:00
|
|
|
perf = Perf.default,
|
2014-02-06 13:36:31 -07:00
|
|
|
vote = Vote(0, 0, 0),
|
2014-02-05 01:57:50 -07:00
|
|
|
attempts = 0,
|
|
|
|
wins = 0,
|
|
|
|
time = 0)
|
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 {
|
2014-02-17 02:12:19 -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
|
|
|
}
|
2014-02-03 11:53:10 -07:00
|
|
|
def read(doc: BSONDocument): Lines = doc.elements.toList map {
|
2014-02-17 02:12:19 -07:00
|
|
|
case (move, BSONBoolean(true)) => Win(readMove(move))
|
|
|
|
case (move, BSONBoolean(false)) => Retry(readMove(move))
|
|
|
|
case (move, more: BSONDocument) => Node(readMove(move), read(more))
|
|
|
|
case (move, value) => throw new Exception(s"Can't read value of $move: $value")
|
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 {
|
2014-02-17 02:12:19 -07:00
|
|
|
case Win(move) => writeMove(move) -> BSONBoolean(true)
|
|
|
|
case Retry(move) => writeMove(move) -> BSONBoolean(false)
|
|
|
|
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"
|
2014-02-08 04:28:36 -07:00
|
|
|
val voteSum = s"$vote.sum"
|
2014-02-03 11:53:10 -07:00
|
|
|
val attempts = "attempts"
|
2014-02-05 01:57:50 -07:00
|
|
|
val wins = "wins"
|
|
|
|
val time = "time"
|
2014-02-03 11:53:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
implicit val puzzleBSONHandler = new BSON[Puzzle] {
|
|
|
|
|
|
|
|
import BSONFields._
|
2014-02-06 11:22:28 -07:00
|
|
|
import Perf.perfBSONHandler
|
2014-02-06 13:36:31 -07:00
|
|
|
import Vote.voteBSONHandler
|
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,
|
2014-02-03 11:53:10 -07:00
|
|
|
gameId = r strO gameId,
|
|
|
|
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,
|
2014-02-06 11:22:28 -07:00
|
|
|
perf = r.get[Perf](perf),
|
2014-02-06 13:36:31 -07:00
|
|
|
vote = r.get[Vote](vote),
|
2014-02-06 11:22:28 -07:00
|
|
|
attempts = r int attempts,
|
|
|
|
wins = r int wins,
|
|
|
|
time = r int time)
|
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,
|
2014-02-05 17:34:22 -07:00
|
|
|
wins -> o.wins,
|
|
|
|
time -> o.time)
|
2014-02-03 11:53:10 -07:00
|
|
|
}
|
|
|
|
}
|