remove puzzle spaced repetition - closes #3112
parent
3ff4238508
commit
badb7abf9d
|
@ -125,7 +125,6 @@ puzzle {
|
|||
collection {
|
||||
puzzle = puzzle
|
||||
round = puzzle_round
|
||||
learning = puzzle_learning
|
||||
vote = puzzle_vote
|
||||
head = puzzle_head
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ final class Env(
|
|||
private val settings = new {
|
||||
val CollectionPuzzle = config getString "collection.puzzle"
|
||||
val CollectionRound = config getString "collection.round"
|
||||
val CollectionLearning = config getString "collection.learning"
|
||||
val CollectionVote = config getString "collection.vote"
|
||||
val CollectionHead = config getString "collection.head"
|
||||
val ApiToken = config getString "api.token"
|
||||
|
@ -38,7 +37,6 @@ final class Env(
|
|||
lazy val api = new PuzzleApi(
|
||||
puzzleColl = puzzleColl,
|
||||
roundColl = roundColl,
|
||||
learningColl = learningColl,
|
||||
voteColl = voteColl,
|
||||
headColl = headColl,
|
||||
puzzleIdMin = PuzzleIdMin,
|
||||
|
@ -78,7 +76,6 @@ final class Env(
|
|||
|
||||
private[puzzle] lazy val puzzleColl = db(CollectionPuzzle)
|
||||
private[puzzle] lazy val roundColl = db(CollectionRound)
|
||||
private[puzzle] lazy val learningColl = db(CollectionLearning)
|
||||
private[puzzle] lazy val voteColl = db(CollectionVote)
|
||||
private[puzzle] lazy val headColl = db(CollectionHead)
|
||||
}
|
||||
|
|
|
@ -16,32 +16,30 @@ private[puzzle] final class Finisher(
|
|||
def apply(puzzle: Puzzle, user: User, result: Result): Fu[(Round, Mode)] =
|
||||
api.head.find(user) flatMap {
|
||||
case Some(PuzzleHead(_, Some(c), _)) if c == puzzle.id =>
|
||||
api.head.solved(user, puzzle.id) >>
|
||||
api.learning.update(user, puzzle, result).flatMap { isLearning =>
|
||||
val userRating = user.perfs.puzzle.toRating
|
||||
val puzzleRating = puzzle.perf.toRating
|
||||
updateRatings(userRating, puzzleRating,
|
||||
result = result.win.fold(Glicko.Result.Win, Glicko.Result.Loss),
|
||||
isLearning = isLearning)
|
||||
val date = DateTime.now
|
||||
val puzzlePerf = puzzle.perf.addOrReset(_.puzzle.crazyGlicko, s"puzzle ${puzzle.id} user")(puzzleRating)
|
||||
val userPerf = user.perfs.puzzle.addOrReset(_.puzzle.crazyGlicko, s"puzzle ${puzzle.id}")(userRating, date)
|
||||
val a = new Round(
|
||||
puzzleId = puzzle.id,
|
||||
userId = user.id,
|
||||
date = date,
|
||||
result = result,
|
||||
rating = user.perfs.puzzle.intRating,
|
||||
ratingDiff = userPerf.intRating - user.perfs.puzzle.intRating
|
||||
)
|
||||
(api.round add a) >> {
|
||||
puzzleColl.update(
|
||||
$id(puzzle.id),
|
||||
$inc(Puzzle.BSONFields.attempts -> $int(1)) ++
|
||||
$set(Puzzle.BSONFields.perf -> PuzzlePerf.puzzlePerfBSONHandler.write(puzzlePerf))
|
||||
) zip UserRepo.setPerf(user.id, PerfType.Puzzle, userPerf)
|
||||
} inject (a -> Mode.Rated)
|
||||
}
|
||||
api.head.solved(user, puzzle.id) >> {
|
||||
val userRating = user.perfs.puzzle.toRating
|
||||
val puzzleRating = puzzle.perf.toRating
|
||||
updateRatings(userRating, puzzleRating,
|
||||
result = result.win.fold(Glicko.Result.Win, Glicko.Result.Loss))
|
||||
val date = DateTime.now
|
||||
val puzzlePerf = puzzle.perf.addOrReset(_.puzzle.crazyGlicko, s"puzzle ${puzzle.id} user")(puzzleRating)
|
||||
val userPerf = user.perfs.puzzle.addOrReset(_.puzzle.crazyGlicko, s"puzzle ${puzzle.id}")(userRating, date)
|
||||
val a = new Round(
|
||||
puzzleId = puzzle.id,
|
||||
userId = user.id,
|
||||
date = date,
|
||||
result = result,
|
||||
rating = user.perfs.puzzle.intRating,
|
||||
ratingDiff = userPerf.intRating - user.perfs.puzzle.intRating
|
||||
)
|
||||
(api.round add a) >> {
|
||||
puzzleColl.update(
|
||||
$id(puzzle.id),
|
||||
$inc(Puzzle.BSONFields.attempts -> $int(1)) ++
|
||||
$set(Puzzle.BSONFields.perf -> PuzzlePerf.puzzlePerfBSONHandler.write(puzzlePerf))
|
||||
) zip UserRepo.setPerf(user.id, PerfType.Puzzle, userPerf)
|
||||
} inject (a -> Mode.Rated)
|
||||
}
|
||||
case _ =>
|
||||
incPuzzleAttempts(puzzle)
|
||||
val a = new Round(
|
||||
|
@ -62,7 +60,7 @@ private[puzzle] final class Finisher(
|
|||
def incPuzzleAttempts(puzzle: Puzzle) =
|
||||
puzzleColl.incFieldUnchecked($id(puzzle.id), Puzzle.BSONFields.attempts)
|
||||
|
||||
private def updateRatings(u1: Rating, u2: Rating, result: Glicko.Result, isLearning: Boolean) {
|
||||
private def updateRatings(u1: Rating, u2: Rating, result: Glicko.Result) {
|
||||
val results = new RatingPeriodResults()
|
||||
result match {
|
||||
case Glicko.Result.Draw => results.addDraw(u1, u2)
|
||||
|
@ -72,11 +70,6 @@ private[puzzle] final class Finisher(
|
|||
try {
|
||||
val (r1, r2) = (u1.getRating, u2.getRating)
|
||||
system.updateRatings(results)
|
||||
if (isLearning) {
|
||||
def mitigate(prev: Double, next: Rating) = next.setRating((next.getRating + prev) / 2)
|
||||
mitigate(r1, u1)
|
||||
mitigate(r2, u2)
|
||||
}
|
||||
// never take away more than 30 rating points - it just causes upsets
|
||||
List(r1 -> u1, r2 -> u2).foreach {
|
||||
case (prev, next) if next.getRating - prev < -30 => next.setRating(prev - 30)
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package lila.puzzle
|
||||
|
||||
case class Learning(
|
||||
_id: String, // userId
|
||||
stackA: List[PuzzleId],
|
||||
stackB: List[PuzzleId]
|
||||
) { // puzzleIds being learnt
|
||||
|
||||
def id = _id
|
||||
|
||||
def nextPuzzleId = (stackA.lastOption, stackB.lastOption, scala.util.Random.nextInt(5)) match {
|
||||
case (Some(a), _, r) if r != 0 => a.some
|
||||
case (_, Some(b), r) if r == 0 => b.some
|
||||
case (Some(a), _, _) => a.some
|
||||
case (_, Some(b), _) => b.some
|
||||
case _ => none
|
||||
}
|
||||
|
||||
def failed(puzzleId: PuzzleId): Learning = copy(
|
||||
stackA = (puzzleId :: stackA.filter(puzzleId !=)) take 50,
|
||||
stackB = stackB.filter(puzzleId !=)
|
||||
)
|
||||
|
||||
def solved(puzzleId: PuzzleId): Learning =
|
||||
if (stackA has puzzleId) copy(
|
||||
stackA = stackA.filter(puzzleId !=),
|
||||
stackB = (puzzleId :: stackB) take 50
|
||||
)
|
||||
else if (stackB has puzzleId)
|
||||
copy(stackB = stackB.filter(puzzleId !=))
|
||||
else this
|
||||
|
||||
def contains(puzzleId: PuzzleId) =
|
||||
stackA.has(puzzleId) || stackB.has(puzzleId)
|
||||
}
|
|
@ -11,7 +11,6 @@ import Puzzle.{ BSONFields => F }
|
|||
private[puzzle] final class PuzzleApi(
|
||||
puzzleColl: Coll,
|
||||
roundColl: Coll,
|
||||
learningColl: Coll,
|
||||
voteColl: Coll,
|
||||
headColl: Coll,
|
||||
puzzleIdMin: PuzzleId,
|
||||
|
@ -78,36 +77,6 @@ private[puzzle] final class PuzzleApi(
|
|||
def add(a: Round) = roundColl insert a void
|
||||
}
|
||||
|
||||
object learning {
|
||||
|
||||
private implicit val learningBSONHandler = reactivemongo.bson.Macros.handler[Learning]
|
||||
|
||||
def find(user: User): Fu[Option[Learning]] = learningColl.byId[Learning](user.id)
|
||||
|
||||
def add(l: Learning) = learningColl insert l void
|
||||
|
||||
def update(user: User, puzzle: Puzzle, result: Result): Fu[Boolean] =
|
||||
if (result.win) solved(user, puzzle.id)
|
||||
else failed(user, puzzle.id)
|
||||
|
||||
def solved(user: User, puzzleId: PuzzleId): Fu[Boolean] = learning find user flatMap {
|
||||
case None => fuccess(false)
|
||||
case Some(l) =>
|
||||
learningColl.update($id(l.id), l solved puzzleId) inject l.contains(puzzleId)
|
||||
}
|
||||
|
||||
def failed(user: User, puzzleId: PuzzleId): Fu[Boolean] = learning find user flatMap {
|
||||
case None => learning add Learning(user.id, List(puzzleId), List()) inject false
|
||||
case Some(l) =>
|
||||
learningColl.update($id(l.id), l failed puzzleId) inject l.contains(puzzleId)
|
||||
}
|
||||
|
||||
def nextPuzzle(user: User): Fu[Option[Puzzle]] = learning find user flatMap {
|
||||
case None => fuccess(none)
|
||||
case Some(l) => l.nextPuzzleId ?? puzzle.find
|
||||
}
|
||||
}
|
||||
|
||||
object vote {
|
||||
|
||||
def value(id: PuzzleId, user: User): Fu[Option[Boolean]] =
|
||||
|
@ -146,17 +115,7 @@ private[puzzle] final class PuzzleApi(
|
|||
|
||||
def find(user: User): Fu[Option[PuzzleHead]] = headColl.byId[PuzzleHead](user.id)
|
||||
|
||||
def add(h: PuzzleHead) = headColl update (
|
||||
$id(h.id),
|
||||
h,
|
||||
upsert = true
|
||||
) void
|
||||
|
||||
def addLearning(user: User, puzzleId: PuzzleId) = headColl update (
|
||||
$id(user.id),
|
||||
$set(PuzzleHead.BSONFields.current -> puzzleId.some),
|
||||
upsert = true
|
||||
) void
|
||||
def add(h: PuzzleHead) = headColl.update($id(h.id), h, upsert = true) void
|
||||
|
||||
def addNew(user: User, puzzleId: PuzzleId) = add(PuzzleHead(user.id, puzzleId.some, puzzleId))
|
||||
|
||||
|
|
|
@ -29,17 +29,9 @@ private[puzzle] final class Selector(
|
|||
api.head.find(user) flatMap {
|
||||
case Some(PuzzleHead(_, Some(c), _)) => api.puzzle.find(c)
|
||||
case _ =>
|
||||
val isLearn = scala.util.Random.nextInt(7) == 0
|
||||
val next = if (isLearn) api.learning.nextPuzzle(user) flatMap {
|
||||
case None => newPuzzleForUser(user)
|
||||
case p => fuccess(p)
|
||||
newPuzzleForUser(user) flatMap { next =>
|
||||
next.?? { p => api.head.addNew(user, p.id) } inject next
|
||||
}
|
||||
else newPuzzleForUser(user)
|
||||
(next flatMap {
|
||||
case Some(p) if isLearn => api.head.addLearning(user, p.id)
|
||||
case Some(p) => api.head.addNew(user, p.id)
|
||||
case _ => fuccess(none)
|
||||
}) >> next
|
||||
}
|
||||
}
|
||||
}.mon(_.puzzle.selector.time) flatten "No puzzles available" addEffect { puzzle =>
|
||||
|
|
Loading…
Reference in New Issue