2014-02-03 11:53:10 -07:00
|
|
|
package lila.puzzle
|
|
|
|
|
2020-11-10 00:41:40 -07:00
|
|
|
import cats.data.NonEmptyList
|
2020-08-16 10:20:56 -06:00
|
|
|
import chess.format.{ FEN, Forsyth, Uci }
|
2014-02-03 11:53:10 -07:00
|
|
|
|
2020-11-10 00:41:40 -07:00
|
|
|
import lila.rating.Glicko
|
|
|
|
|
2014-02-03 11:53:10 -07:00
|
|
|
case class Puzzle(
|
2020-11-10 00:41:40 -07:00
|
|
|
id: Puzzle.Id,
|
|
|
|
gameId: lila.game.Game.ID,
|
2020-10-18 12:21:34 -06:00
|
|
|
fen: FEN,
|
2020-11-10 00:41:40 -07:00
|
|
|
line: NonEmptyList[Uci.Move],
|
|
|
|
glicko: Glicko,
|
2020-12-03 09:51:17 -07:00
|
|
|
plays: Int,
|
2020-12-23 03:55:41 -07:00
|
|
|
vote: Float, // denormalized ratio of voteUp/voteDown
|
2020-12-03 09:51:17 -07:00
|
|
|
themes: Set[PuzzleTheme.Key]
|
2017-02-14 08:34:07 -07:00
|
|
|
) {
|
2016-12-07 16:17:45 -07:00
|
|
|
// ply after "initial move" when we start solving
|
2020-10-18 12:21:34 -06:00
|
|
|
def initialPly: Int =
|
|
|
|
fen.fullMove ?? { fm =>
|
2020-12-08 04:57:39 -07:00
|
|
|
fm * 2 - color.fold(1, 2)
|
2016-12-06 06:46:24 -07:00
|
|
|
}
|
2014-02-25 13:44:02 -07:00
|
|
|
|
2020-12-08 03:33:59 -07:00
|
|
|
lazy val fenAfterInitialMove: FEN = {
|
2015-03-17 16:32:45 -06:00
|
|
|
for {
|
|
|
|
sit1 <- Forsyth << fen
|
2020-11-10 00:41:40 -07:00
|
|
|
sit2 <- sit1.move(line.head).toOption.map(_.situationAfter)
|
2020-10-18 12:21:34 -06:00
|
|
|
} yield Forsyth >> sit2
|
2020-11-10 00:41:40 -07:00
|
|
|
} err s"Can't apply puzzle $id first move"
|
2020-12-08 03:33:59 -07:00
|
|
|
|
2020-12-15 03:02:46 -07:00
|
|
|
def color = fen.color.fold[chess.Color](chess.White)(!_)
|
2021-11-07 01:21:29 -07:00
|
|
|
|
|
|
|
def hasTheme(theme: PuzzleTheme) = themes(theme.key)
|
2014-02-03 14:04:43 -07:00
|
|
|
}
|
2014-02-03 11:53:10 -07:00
|
|
|
|
|
|
|
object Puzzle {
|
|
|
|
|
2020-12-08 04:57:49 -07:00
|
|
|
val idSize = 5
|
|
|
|
|
2020-11-10 00:41:40 -07:00
|
|
|
case class Id(value: String) extends AnyVal with StringValue
|
|
|
|
|
2020-12-29 14:29:47 -07:00
|
|
|
def toId(id: String) = id.size == idSize option Id(id)
|
|
|
|
|
2020-12-08 04:57:49 -07:00
|
|
|
/* The mobile app requires numerical IDs.
|
|
|
|
* We convert string ids from and to Longs using base 62
|
|
|
|
*/
|
|
|
|
object numericalId {
|
|
|
|
|
|
|
|
private val powers: List[Long] =
|
|
|
|
(0 until idSize).toList.map(m => Math.pow(62, m).toLong)
|
|
|
|
|
|
|
|
def apply(id: Id): Long = id.value.toList
|
|
|
|
.zip(powers)
|
|
|
|
.foldLeft(0L) { case (l, (char, pow)) =>
|
|
|
|
l + charToInt(char) * pow
|
|
|
|
}
|
|
|
|
|
2020-12-30 00:17:48 -07:00
|
|
|
def apply(l: Long): Option[Id] = (l > 130_000) ?? {
|
2020-12-24 04:38:48 -07:00
|
|
|
val str = powers.reverse
|
2020-12-08 04:57:49 -07:00
|
|
|
.foldLeft(("", l)) { case ((id, rest), pow) =>
|
|
|
|
val frac = rest / pow
|
|
|
|
(s"${intToChar(frac.toInt)}$id", rest - frac * pow)
|
|
|
|
}
|
|
|
|
._1
|
2020-12-24 04:38:48 -07:00
|
|
|
(str.size == idSize) option Id(str)
|
2020-12-08 04:57:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private def charToInt(c: Char) = {
|
|
|
|
val i = c.toInt
|
|
|
|
if (i > 96) i - 71
|
|
|
|
else if (i > 64) i - 65
|
|
|
|
else i + 4
|
|
|
|
}
|
|
|
|
|
|
|
|
private def intToChar(i: Int): Char = {
|
|
|
|
if (i < 26) i + 65
|
|
|
|
else if (i < 52) i + 71
|
|
|
|
else i - 4
|
|
|
|
}.toChar
|
|
|
|
}
|
|
|
|
|
2017-07-18 07:44:21 -06:00
|
|
|
case class UserResult(
|
2020-11-10 00:41:40 -07:00
|
|
|
puzzleId: Id,
|
2017-08-23 17:56:39 -06:00
|
|
|
userId: lila.user.User.ID,
|
|
|
|
result: Result,
|
|
|
|
rating: (Int, Int)
|
2017-07-18 07:44:21 -06:00
|
|
|
)
|
2017-07-18 07:09:16 -06:00
|
|
|
|
2014-02-03 11:53:10 -07:00
|
|
|
object BSONFields {
|
2020-12-23 03:55:41 -07:00
|
|
|
val id = "_id"
|
|
|
|
val gameId = "gameId"
|
|
|
|
val fen = "fen"
|
|
|
|
val line = "line"
|
|
|
|
val glicko = "glicko"
|
|
|
|
val vote = "vote"
|
|
|
|
val voteUp = "vu"
|
|
|
|
val voteDown = "vd"
|
|
|
|
val plays = "plays"
|
|
|
|
val themes = "themes"
|
|
|
|
val day = "day"
|
|
|
|
val dirty = "dirty" // themes need to be denormalized
|
2014-02-03 11:53:10 -07:00
|
|
|
}
|
|
|
|
|
2020-12-05 11:09:27 -07:00
|
|
|
implicit val idIso = lila.common.Iso.string[Id](Id.apply, _.value)
|
2014-02-03 11:53:10 -07:00
|
|
|
}
|