puzzle streak improvements

streak
Thibault Duplessis 2021-03-28 22:05:03 +02:00
parent 1f70b71033
commit 34905daa89
7 changed files with 76 additions and 64 deletions

View File

@ -206,7 +206,7 @@ final class Puzzle(
env.puzzle.streak.apply flatMap {
_ ?? { case (streak, puzzle) =>
env.puzzle.jsonView(puzzle = puzzle, PuzzleTheme.mix.some, none, user = ctx.me) map { preJson =>
val json = preJson ++ Json.obj("streak" -> streak.ids.map(_.value))
val json = preJson ++ Json.obj("streak" -> streak.ids.mkString(" "))
EnableSharedArrayBuffer(
Ok(
views.html.puzzle

View File

@ -17,10 +17,12 @@ object bits {
def daily(p: lila.puzzle.Puzzle, fen: chess.format.FEN, lastMove: String) =
views.html.board.bits.mini(fen, p.color, lastMove)(span)
def jsI18n(streak: Boolean)(implicit lang: Lang) = i18nJsObject {
i18nKeys ++ streak.??(streakI18nKeys)
} +
(PuzzleTheme.enPassant.key.value -> JsString(PuzzleTheme.enPassant.name.txt()(lila.i18n.defaultLang)))
def jsI18n(streak: Boolean)(implicit lang: Lang) =
if (streak) i18nJsObject(streakI18nKeys)
else
i18nJsObject(trainingI18nKeys) + (PuzzleTheme.enPassant.key.value -> JsString(
PuzzleTheme.enPassant.name.txt()(lila.i18n.defaultLang)
))
lazy val jsonThemes = PuzzleTheme.all
.collect {
@ -59,9 +61,8 @@ object bits {
)
)
private val i18nKeys: List[MessageKey] = {
private val baseI18nKeys: List[MessageKey] =
List(
trans.puzzle.yourPuzzleRatingX,
trans.puzzle.bestMove,
trans.puzzle.keepGoing,
trans.puzzle.notTheMove,
@ -75,17 +76,10 @@ object bits {
trans.puzzle.hidden,
trans.puzzle.jumpToNextPuzzleImmediately,
trans.puzzle.fromGameLink,
trans.puzzle.didYouLikeThisPuzzle,
trans.puzzle.voteToLoadNextOne,
trans.puzzle.puzzleId,
trans.puzzle.ratingX,
trans.puzzle.playedXTimes,
trans.puzzle.continueTraining,
trans.puzzle.difficultyLevel,
trans.puzzle.example,
trans.puzzle.toGetPersonalizedPuzzles,
trans.puzzle.addAnotherTheme,
trans.signUp,
trans.analysis,
trans.playWithTheMachine,
// ceval
@ -98,13 +92,25 @@ object bits {
trans.gameOver,
trans.inLocalBrowser,
trans.toggleLocalEvaluation
) ::: PuzzleTheme.all.map(_.name) :::
PuzzleTheme.all.map(_.description) :::
PuzzleDifficulty.all.map(_.name)
}.map(_.key)
).map(_.key)
private val trainingI18nKeys: List[MessageKey] =
baseI18nKeys ::: List(
trans.puzzle.example,
trans.puzzle.addAnotherTheme,
trans.puzzle.yourPuzzleRatingX,
trans.puzzle.difficultyLevel,
trans.puzzle.didYouLikeThisPuzzle,
trans.puzzle.voteToLoadNextOne,
trans.signUp,
trans.puzzle.toGetPersonalizedPuzzles
).map(_.key) :::
PuzzleTheme.all.map(_.name.key) :::
PuzzleTheme.all.map(_.description.key) :::
PuzzleDifficulty.all.map(_.name.key)
private val streakI18nKeys: List[MessageKey] =
List(
baseI18nKeys ::: List(
trans.storm.skip,
trans.puzzle.streakDescription,
trans.puzzle.yourStreakX,

View File

@ -43,10 +43,8 @@ final class PuzzleStreakApi(colls: PuzzleColls, cacheApi: CacheApi)(implicit ec:
2799 -> 23,
highestBoundary -> 25
)
private val poolSize = buckets.map(_._2).sum
private val theme = lila.puzzle.PuzzleTheme.mix.key.value
private val tier = lila.puzzle.PuzzleTier.Good.key
private val maxDeviation = 110
private val poolSize = buckets.map(_._2).sum
private val theme = lila.puzzle.PuzzleTheme.mix.key.value
private val current = cacheApi.unit[List[Puzzle.Id]] {
_.refreshAfterWrite(30 seconds)
@ -57,6 +55,8 @@ final class PuzzleStreakApi(colls: PuzzleColls, cacheApi: CacheApi)(implicit ec:
import framework._
Facet(
buckets.map { case (rating, nbPuzzles) =>
val (tier, samples, deviation) =
if (rating > 2300) (PuzzleTier.Good, 5, 110) else (PuzzleTier.Top, 1, 85)
rating.toString -> List(
Match(
$doc(
@ -64,7 +64,7 @@ final class PuzzleStreakApi(colls: PuzzleColls, cacheApi: CacheApi)(implicit ec:
"max" $gte f"${theme}_${tier}_${if (rating == highestBoundary) 9999 else rating}%04d"
)
),
Sample(if (rating > 2300) 5 else 1),
Sample(samples),
Project($doc("_id" -> false, "ids" -> true)),
UnwindField("ids"),
// ensure we have enough after filtering deviation
@ -81,7 +81,7 @@ final class PuzzleStreakApi(colls: PuzzleColls, cacheApi: CacheApi)(implicit ec:
"$expr" -> $doc(
"$and" -> $arr(
$doc("$eq" -> $arr("$_id", "$$id")),
$doc("$lte" -> $arr("$glicko.d", maxDeviation))
$doc("$lte" -> $arr("$glicko.d", deviation))
)
)
)

View File

@ -33,7 +33,7 @@ export default function (opts: PuzzleOpts, redraw: Redraw): Controller {
const ground = prop<CgApi | undefined>(undefined) as Prop<CgApi>;
const threatMode = prop(false);
const session = new PuzzleSession(opts.data.theme.key, opts.data.user?.id, hasStreak);
const streak = opts.data.streak && new PuzzleStreak(opts.data.streak);
const streak = opts.data.streak ? new PuzzleStreak(opts.data.streak.split(' ')) : undefined;
// required by ceval
vm.showComputer = () => vm.mode === 'view';

View File

@ -133,7 +133,7 @@ export interface PuzzleData {
game: PuzzleGame;
user: PuzzleUser | undefined;
replay?: PuzzleReplay;
streak?: PuzzleId[];
streak?: string;
}
export interface PuzzleReplay {

View File

@ -102,7 +102,7 @@ export default function (ctrl: Controller): VNode {
h('aside.puzzle__side', [
side.replay(ctrl),
side.puzzleBox(ctrl),
side.userBox(ctrl),
ctrl.streak ? side.streakBox(ctrl) : side.userBox(ctrl),
side.config(ctrl),
theme(ctrl),
]),
@ -155,12 +155,14 @@ function session(ctrl: Controller) {
);
}),
rounds.find(r => r.id == current)
? h('a.session-new', {
key: 'new',
attrs: {
href: `/training/${ctrl.session.theme}`,
},
})
? ctrl.streak
? null
: h('a.session-new', {
key: 'new',
attrs: {
href: `/training/${ctrl.session.theme}`,
},
})
: h('a.result-cursor.current', {
key: current,
attrs: ctrl.streak

View File

@ -18,21 +18,24 @@ function puzzleInfos(ctrl: Controller, puzzle: Puzzle): VNode {
},
[
h('div', [
h('p', [
...ctrl.trans.vdom(
'puzzleId',
h(
'a',
{
attrs: {
href: `/training/${puzzle.id}`,
...(ctrl.streak ? { target: '_blank' } : {}),
},
},
'#' + puzzle.id
)
),
]),
ctrl.streak
? null
: h(
'p',
ctrl.trans.vdom(
'puzzleId',
h(
'a',
{
attrs: {
href: `/training/${puzzle.id}`,
...(ctrl.streak ? { target: '_blank' } : {}),
},
},
'#' + puzzle.id
)
)
),
h(
'p',
ctrl.trans.vdom(
@ -117,7 +120,7 @@ const renderStreak = (streak: PuzzleStreak, noarg: TransNoArg) =>
)
);
export function userBox(ctrl: Controller): VNode {
export const userBox = (ctrl: Controller): VNode => {
const data = ctrl.getData();
if (!data.user)
return h('div.puzzle__side__user', [
@ -126,21 +129,22 @@ export function userBox(ctrl: Controller): VNode {
]);
const diff = ctrl.vm.round?.ratingDiff;
return h('div.puzzle__side__user', [
ctrl.streak
? renderStreak(ctrl.streak, ctrl.trans.noarg)
: h(
'div.puzzle__side__user__rating',
ctrl.trans.vdom(
'yourPuzzleRatingX',
h('strong', [
data.user.rating - (diff || 0),
...(diff && diff > 0 ? [' ', h('good.rp', '+' + diff)] : []),
...(diff && diff < 0 ? [' ', h('bad.rp', '' + -diff)] : []),
])
)
),
h(
'div.puzzle__side__user__rating',
ctrl.trans.vdom(
'yourPuzzleRatingX',
h('strong', [
data.user.rating - (diff || 0),
...(diff && diff > 0 ? [' ', h('good.rp', '+' + diff)] : []),
...(diff && diff < 0 ? [' ', h('bad.rp', '' + -diff)] : []),
])
)
),
]);
}
};
export const streakBox = (ctrl: Controller) =>
h('div.puzzle__side__user', renderStreak(ctrl.streak!, ctrl.trans.noarg));
const difficulties: PuzzleDifficulty[] = ['easiest', 'easier', 'normal', 'harder', 'hardest'];