From 356fc15cec793f615c8ca184fca1c28bc08088b2 Mon Sep 17 00:00:00 2001 From: Sreesh Maheshwar Date: Mon, 18 Mar 2024 03:29:18 +0000 Subject: [PATCH 1/5] Removed unnecessary Leitner casework --- app/api/puzzle/nextPuzzle/nextFor.ts | 5 +-- app/api/puzzle/submit/route.ts | 9 ++--- models/LeitnerColl.ts | 14 ------- src/LeitnerIntance.ts | 56 ++-------------------------- 4 files changed, 7 insertions(+), 77 deletions(-) delete mode 100644 models/LeitnerColl.ts diff --git a/app/api/puzzle/nextPuzzle/nextFor.ts b/app/api/puzzle/nextPuzzle/nextFor.ts index 614d5e2..4c19dd2 100644 --- a/app/api/puzzle/nextPuzzle/nextFor.ts +++ b/app/api/puzzle/nextPuzzle/nextFor.ts @@ -13,7 +13,6 @@ import { ActivePuzzleColl } from '@/models/ActivePuzzle'; import { booleanWithProbability, toGroupId } from '@/lib/utils'; import { nextLeitnerReview, - nextThemedLeitnerReview, } from '@/src/LeitnerIntance'; import { similarBatchForCompromised } from '../similarBatch/similarBatchFor'; import { assert } from 'console'; @@ -208,9 +207,7 @@ const nextPuzzleFor = async ( if (!woodpecker && booleanWithProbability(LEITNER_PROBABILITY)) { console.log('Trying to use Leitner...'); - const puzzleToReview = group - ? await nextThemedLeitnerReview(user, group) - : await nextLeitnerReview(user); + const puzzleToReview = await nextLeitnerReview(user, group || "") if (puzzleToReview) { console.log( `Worked! Puzzle Id: ${puzzleToReview.PuzzleId} from Leitner, tags: ${puzzleToReview.hierarchy_tags}` diff --git a/app/api/puzzle/submit/route.ts b/app/api/puzzle/submit/route.ts index 4eb5796..d45d84e 100644 --- a/app/api/puzzle/submit/route.ts +++ b/app/api/puzzle/submit/route.ts @@ -13,7 +13,7 @@ import { UserThemeColl } from '@/models/UserThemeColl'; import addRound from './addRound'; import { RatingHistory } from '@/models/RatingHistory'; import { ActivePuzzleColl } from '@/models/ActivePuzzle'; -import { updateLeitner, updateThemedLeitner } from '@/src/LeitnerIntance'; +import { updateLeitner } from '@/src/LeitnerIntance'; import { toGroupId } from '@/lib/utils'; import { TimeThemeColl } from '@/models/TimeThemeColl'; import updateAndScaleRatings from '@/src/rating/RatingCalculator'; @@ -44,6 +44,7 @@ export async function POST(req: NextRequest) { const userRating = await getExistingUserRating(user); const moves = puzzle.Moves.split(' ').length / 2; const timePerMove = totalTime / moves; + console.log(`Group: ${group}`); console.log(`Time per move : ${timePerMove}`); updateAndScaleRatings(userRating, puzzle, success, activePuzzle.isReview); @@ -62,11 +63,7 @@ export async function POST(req: NextRequest) { ? (JSON.parse(activePuzzle.reviewee) as Puzzle) : puzzle; - if (group) { - await updateThemedLeitner(user, reviewee, success, group, timePerMove); - } else { - await updateLeitner(user, reviewee, success, timePerMove); - } + await updateLeitner(user, reviewee, success, group || "", timePerMove); // NB: We don't filter out irrelevant themes here. Even if theme is irrelevant, we compute ratings and // persist in the DB, as this information is useful for dashboard analysitcs. diff --git a/models/LeitnerColl.ts b/models/LeitnerColl.ts deleted file mode 100644 index f246ece..0000000 --- a/models/LeitnerColl.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Puzzle } from '@/types/lichess-api'; -import mongoose, { Schema } from 'mongoose'; - -const leitnerSchema = new Schema({ - username: { type: String, required: true }, - boxA: Array, - boxB: Array, -}); - -// Index on username for fast lookups. -leitnerSchema.index({ username: 1 }, { unique: true }); - -export const LeitnerColl = - mongoose.models.Leitner || mongoose.model('Leitner', leitnerSchema); diff --git a/src/LeitnerIntance.ts b/src/LeitnerIntance.ts index 126dc05..1a284a9 100644 --- a/src/LeitnerIntance.ts +++ b/src/LeitnerIntance.ts @@ -1,5 +1,4 @@ import { booleanWithProbability } from '@/lib/utils'; -import { LeitnerColl } from '@/models/LeitnerColl'; import { ThemedLeitnerColl } from '@/models/ThemedLeitnerColl'; import { Puzzle } from '@/types/lichess-api'; import { User } from 'lucia'; @@ -16,7 +15,7 @@ interface LeitnerInstance { boxB: Array; } -const findThemedLeitner = async ( +const findLeitner = async ( user: User, groupID: string ): Promise => { @@ -30,13 +29,6 @@ const findThemedLeitner = async ( return { boxA: leitner.boxA, boxB: leitner.boxB }; }; -const findLeitner = async ( - user: User -): Promise => { - const leitner = await LeitnerColl.findOne({ username: user.username }); - return leitner ? { boxA: leitner.boxA, boxB: leitner.boxB } : undefined; -}; - const nextPuzzle = ({ boxA, boxB }: LeitnerInstance): Puzzle | undefined => { console.log( `boxA = ${boxA.map((puzzle) => puzzle.PuzzleId)}, boxB = ${boxB.map((puzzle) => puzzle.PuzzleId)}` @@ -90,20 +82,10 @@ const updateCorrect = ( }; export const nextLeitnerReview = async ( - user: User -): Promise => { - const leitner = await findLeitner(user); - if (!leitner) { - return undefined; - } - return nextPuzzle(leitner); -}; - -export const nextThemedLeitnerReview = async ( user: User, groupID: string ): Promise => { - const leitner = await findThemedLeitner(user, groupID); + const leitner = await findLeitner(user, groupID); if (!leitner) { return undefined; } @@ -111,45 +93,13 @@ export const nextThemedLeitnerReview = async ( }; export const updateLeitner = async ( - user: User, - puzzle: Puzzle, - correct: boolean, - time: number -): Promise => { - const leitner = await findLeitner(user); - if (!leitner) { - if (!correct) { - // We should initialise a default Leitner instance with this puzzle in Box A. - await LeitnerColl.create({ - username: user.username, - boxA: [puzzle], - boxB: [], - }); - } - return; - } - console.log( - `Before update: boxA = ${leitner.boxA.map((puzzle) => puzzle.PuzzleId)}, boxB = ${leitner.boxB.map((puzzle) => puzzle.PuzzleId)}` - ); - if (correct) { - updateCorrect(leitner, puzzle, time); - } else { - updateIncorrect(leitner, puzzle); - } - console.log( - `After update: boxA = ${leitner.boxA.map((puzzle) => puzzle.PuzzleId)}, boxB = ${leitner.boxB.map((puzzle) => puzzle.PuzzleId)}` - ); - await LeitnerColl.updateOne({ username: user.username }, leitner); -}; - -export const updateThemedLeitner = async ( user: User, puzzle: Puzzle, correct: boolean, groupID: string, time: number ): Promise => { - const leitner = await findThemedLeitner(user, groupID); + const leitner = await findLeitner(user, groupID); if (!leitner) { if (!correct) { // We should initialise a default Leitner instance with this puzzle in Box A. From 48e7857a95d36e24b02f5c13bb3fbf1180ce8496 Mon Sep 17 00:00:00 2001 From: Sreesh Maheshwar Date: Mon, 18 Mar 2024 04:22:44 +0000 Subject: [PATCH 2/5] Use empty string for empty group ID throughout --- app/api/puzzle/nextPuzzle/nextFor.ts | 173 +++++++++------------------ app/api/puzzle/submit/route.ts | 13 +- models/ActivePuzzle.ts | 9 +- 3 files changed, 67 insertions(+), 128 deletions(-) diff --git a/app/api/puzzle/nextPuzzle/nextFor.ts b/app/api/puzzle/nextPuzzle/nextFor.ts index 4c19dd2..293e9db 100644 --- a/app/api/puzzle/nextPuzzle/nextFor.ts +++ b/app/api/puzzle/nextPuzzle/nextFor.ts @@ -11,11 +11,8 @@ import sm2RandomThemeFromRatingMap from '../../../../src/sm2'; import frequentiallyRandomTheme, { isIrrelevant } from './themeGenerator'; import { ActivePuzzleColl } from '@/models/ActivePuzzle'; import { booleanWithProbability, toGroupId } from '@/lib/utils'; -import { - nextLeitnerReview, -} from '@/src/LeitnerIntance'; +import { nextLeitnerReview } from '@/src/LeitnerIntance'; import { similarBatchForCompromised } from '../similarBatch/similarBatchFor'; -import { assert } from 'console'; const MAX_REPS: number = 12; const MAX_COMPROMISE: number = 3; @@ -169,158 +166,102 @@ const nextPuzzleFor = async ( themeGroup: string[] = [] ): Promise => getExistingUserRating(user).then(async (rating) => { - const group = themeGroup.length > 0 ? toGroupId(themeGroup) : undefined; + const themeTrainer = themeGroup.length > 0; + const groupId = toGroupId(themeGroup); if (!woodpecker) { const activePuzzle = await ActivePuzzleColl.findOne({ username: user.username, }); if (activePuzzle) { - if ( - (group && activePuzzle.groupID !== group) || - (!group && activePuzzle.groupID) - ) { + if (groupId !== activePuzzle.groupID) { // TODO: Maybe preserve previous active puzzle? But we need to be // careful in case this mode solves that puzzle, then back in // normal mode user solves puzzle again - then errors! console.log('Deleting different active puzzle'); await ActivePuzzleColl.deleteOne({ username: user.username }); } else { - let similar: Puzzle[] = []; - if (activePuzzle.isReview) { - const reviewee = JSON.parse(activePuzzle.reviewee) as Puzzle; - similar = [reviewee]; - } - console.log(similar); console.log('Found active puzzle'); return { puzzle: JSON.parse(activePuzzle.puzzle) as Puzzle, rating: rating, - similar: similar, + similar: activePuzzle.reviewee + ? [JSON.parse(activePuzzle.reviewee) as Puzzle] + : [], }; } } } - // TODO: Iterate to better handle repeat avoidance. - const exceptions: string[] = await getUserSolvedPuzzleIDs(user); - - if (!woodpecker && booleanWithProbability(LEITNER_PROBABILITY)) { - console.log('Trying to use Leitner...'); - const puzzleToReview = await nextLeitnerReview(user, group || "") - if (puzzleToReview) { - console.log( - `Worked! Puzzle Id: ${puzzleToReview.PuzzleId} from Leitner, tags: ${puzzleToReview.hierarchy_tags}` - ); - const [similarPuzzle] = await similarBatchForCompromised( - user, - [puzzleToReview], - clampRating(rating.rating), - exceptions, - MIN_CANDIDATES, // TODO: Increase this, or maybe start compromise at 3 instead, to use wider similarity radius? Unsure. - false - ); - - console.log( - `Got similar puzzle with tags ${similarPuzzle.hierarchy_tags} and line ${similarPuzzle.Moves}` - ); - - if (group) { - await ActivePuzzleColl.updateOne( - { username: user.username }, - { - username: user.username, - puzzle: JSON.stringify(similarPuzzle), - isReview: true, - reviewee: JSON.stringify(puzzleToReview), - groupID: group, - }, - { upsert: true } + const result = await (async () => { + // TODO: Iterate to better handle repeat avoidance. + const exceptions: string[] = await getUserSolvedPuzzleIDs(user); + if (!woodpecker && booleanWithProbability(LEITNER_PROBABILITY)) { + console.log('Trying to use Leitner...'); + const puzzleToReview = await nextLeitnerReview(user, groupId); + if (puzzleToReview) { + console.log( + `Worked! Puzzle Id: ${puzzleToReview.PuzzleId} from Leitner, tags: ${puzzleToReview.hierarchy_tags}` ); - } else { - await ActivePuzzleColl.updateOne( - { username: user.username }, - { - username: user.username, - puzzle: JSON.stringify(similarPuzzle), - isReview: true, - reviewee: JSON.stringify(puzzleToReview), - }, - { upsert: true } + const [similarPuzzle] = await similarBatchForCompromised( + user, + [puzzleToReview], + clampRating(rating.rating), + exceptions, + MIN_CANDIDATES, + false ); + console.log( + `Got similar puzzle with tags ${similarPuzzle.hierarchy_tags} and line ${similarPuzzle.Moves}` + ); + return { + puzzle: similarPuzzle, + similar: [puzzleToReview], + }; } - return { - puzzle: similarPuzzle, - similar: [puzzleToReview], - rating: rating, - }; - } - } - - // NB: The persisted rating map may contain irrelevant themes, but we don't - // want to include these for nextPuzzle / SM2, so we filter them out below. - const ratingMap = await getThemeRatings(user, true); - - if (group) { - const puzzle = await nextThemedPuzzlesForRepetitions( - rating.rating, - ratingMap, - 0, - themeGroup, - exceptions - ); - if (puzzle) { - console.log( - `Got puzzle with themes ${puzzle.Themes} and rating ${puzzle.Rating} and line ${puzzle.Moves}` - ); - assert(!woodpecker); - await ActivePuzzleColl.updateOne( - { username: user.username }, - { - username: user.username, - puzzle: JSON.stringify(puzzle), - isReview: false, - groupID: group, - }, - { upsert: true } - ); } + // NB: The persisted rating map may contain irrelevant themes, but we don't + // want to include these for nextPuzzle / SM2, so we filter them out below. + const ratingMap = await getThemeRatings(user, true); return { - puzzle: puzzle, - rating: rating, + puzzle: themeTrainer + ? await nextThemedPuzzlesForRepetitions( + rating.rating, + ratingMap, + 0, + themeGroup, + exceptions + ) + : await nextPuzzleRepetitions( + user.username, + rating.rating, + 0, + ratingMap, + exceptions + ), }; - } + })(); - const puzzle = await nextPuzzleRepetitions( - user.username, - rating.rating, - 0, - ratingMap, - exceptions - ); - - if (puzzle) { + if (result.puzzle) { console.log( - `Got puzzle with themes ${puzzle.Themes} and rating ${puzzle.Rating} and line ${puzzle.Moves}` + `Got puzzle with themes ${result.puzzle.Themes} and rating ${result.puzzle.Rating} and line ${result.puzzle.Moves}` ); - if (!woodpecker) { await ActivePuzzleColl.updateOne( { username: user.username }, { username: user.username, - puzzle: JSON.stringify(puzzle), - isReview: false, + puzzle: JSON.stringify(result.puzzle), + reviewee: result.similar + ? JSON.stringify(result.similar[0]) + : undefined, + groupID: groupId, }, { upsert: true } ); } } - - return { - puzzle: puzzle, - rating: rating, - }; + return { ...result, rating }; }); export default nextPuzzleFor; diff --git a/app/api/puzzle/submit/route.ts b/app/api/puzzle/submit/route.ts index d45d84e..0f10459 100644 --- a/app/api/puzzle/submit/route.ts +++ b/app/api/puzzle/submit/route.ts @@ -39,15 +39,14 @@ export async function POST(req: NextRequest) { const puzzle = JSON.parse(activePuzzle.puzzle) as Puzzle; const success = successStr as boolean; const themeGroup = themeGroupStr as string[]; - const group = themeGroup.length > 0 ? toGroupId(themeGroup) : undefined; + const group = toGroupId(themeGroup); const totalTime = (timeStr as number) / MILLISECONDS_IN_SECOND; const userRating = await getExistingUserRating(user); const moves = puzzle.Moves.split(' ').length / 2; const timePerMove = totalTime / moves; - console.log(`Group: ${group}`); - console.log(`Time per move : ${timePerMove}`); + const isReview = !!activePuzzle.reviewee; - updateAndScaleRatings(userRating, puzzle, success, activePuzzle.isReview); + updateAndScaleRatings(userRating, puzzle, success, isReview); // Update user's rating. await RatingColl.updateOne({ username: user.username }, { $set: userRating }); @@ -59,11 +58,11 @@ export async function POST(req: NextRequest) { await addRound(user, puzzle); - const reviewee = activePuzzle.isReview + const reviewee = isReview ? (JSON.parse(activePuzzle.reviewee) as Puzzle) : puzzle; - await updateLeitner(user, reviewee, success, group || "", timePerMove); + await updateLeitner(user, reviewee, success, group, timePerMove); // NB: We don't filter out irrelevant themes here. Even if theme is irrelevant, we compute ratings and // persist in the DB, as this information is useful for dashboard analysitcs. @@ -73,7 +72,7 @@ export async function POST(req: NextRequest) { const themes = puzzle.Themes.split(' '); themes.forEach(async (theme) => { const themeRating: Rating = ratingMap.get(theme) || DEFAULT_RATING; - updateAndScaleRatings(themeRating, puzzle, success, activePuzzle.isReview); + updateAndScaleRatings(themeRating, puzzle, success, isReview); await UserThemeColl.updateOne( { username: user.username, theme: theme }, { $set: themeRating }, diff --git a/models/ActivePuzzle.ts b/models/ActivePuzzle.ts index e4a15e0..7088f40 100644 --- a/models/ActivePuzzle.ts +++ b/models/ActivePuzzle.ts @@ -1,11 +1,10 @@ import mongoose, { Schema } from 'mongoose'; const activePuzzleSchema = new Schema({ - username: String, - puzzle: String, - isReview: Boolean, - reviewee: String, // Original puzzle, if it's a similar one. TODO: Redundancy with above. - groupID: String, + username: { type: String, required: true }, + puzzle: { type: String, required: true }, + reviewee: String, // Original puzzle, if it's a similar one. + groupID: { type: String, required: true }, }); // Index on username for fast lookups. From 4d6125df0747502cd7ac2208b63d13f99aff3b8e Mon Sep 17 00:00:00 2001 From: Sreesh Maheshwar Date: Mon, 18 Mar 2024 04:37:31 +0000 Subject: [PATCH 3/5] Add logging for max reps reached --- app/api/puzzle/nextPuzzle/nextFor.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/api/puzzle/nextPuzzle/nextFor.ts b/app/api/puzzle/nextPuzzle/nextFor.ts index 293e9db..e874eff 100644 --- a/app/api/puzzle/nextPuzzle/nextFor.ts +++ b/app/api/puzzle/nextPuzzle/nextFor.ts @@ -94,7 +94,7 @@ const nextPuzzleRepetitions = async ( exceptions: any ): Promise => { if (reps == MAX_REPS) { - // throw new Error('Maximum repetitions reached during puzzle selection'); + console.log('Maximum repetitions reached during puzzle selection'); return undefined; } let rating = userRating; @@ -136,16 +136,14 @@ const nextThemedPuzzlesForRepetitions = async ( expceptions: any ): Promise => { if (reps == MAX_REPS) { - // throw new Error('Maximum repetitions reached during puzzle selection'); + console.log('Maximum repetitions reached during puzzle selection'); return undefined; } - const theme = themeGroup[Math.floor(Math.random() * themeGroup.length)]; // If theme is present in rating map, use its rating for adaptive difficulty // selection. Otherwise, use user's rating. const rating = ratingMap.get(theme)?.rating || userRating; console.log(`Using rating: ${rating} for theme: ${theme}`); - const p = await nextPuzzleForThemeAndRating(theme, rating, expceptions); if (p) { console.log(`Found grouped theme ${theme} after ${reps} reps.`); From fc0d85d6914ad017bbdfbb7665ce5597bf82ae53 Mon Sep 17 00:00:00 2001 From: Sreesh Maheshwar Date: Mon, 18 Mar 2024 10:04:58 +0000 Subject: [PATCH 4/5] Minor style improvements throughout --- app/api/puzzle/nextPuzzle/nextFor.ts | 4 +- app/api/puzzle/submit/route.ts | 41 +++++++++---------- components/puzzle-ui/controls/puzzle-hint.tsx | 1 - components/puzzle-ui/puzzle-mode.tsx | 1 + 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/app/api/puzzle/nextPuzzle/nextFor.ts b/app/api/puzzle/nextPuzzle/nextFor.ts index e874eff..030d6d8 100644 --- a/app/api/puzzle/nextPuzzle/nextFor.ts +++ b/app/api/puzzle/nextPuzzle/nextFor.ts @@ -164,7 +164,6 @@ const nextPuzzleFor = async ( themeGroup: string[] = [] ): Promise => getExistingUserRating(user).then(async (rating) => { - const themeTrainer = themeGroup.length > 0; const groupId = toGroupId(themeGroup); if (!woodpecker) { @@ -222,7 +221,7 @@ const nextPuzzleFor = async ( // want to include these for nextPuzzle / SM2, so we filter them out below. const ratingMap = await getThemeRatings(user, true); return { - puzzle: themeTrainer + puzzle: groupId ? await nextThemedPuzzlesForRepetitions( rating.rating, ratingMap, @@ -244,6 +243,7 @@ const nextPuzzleFor = async ( console.log( `Got puzzle with themes ${result.puzzle.Themes} and rating ${result.puzzle.Rating} and line ${result.puzzle.Moves}` ); + // Persist selection as active puzzle. if (!woodpecker) { await ActivePuzzleColl.updateOne( { username: user.username }, diff --git a/app/api/puzzle/submit/route.ts b/app/api/puzzle/submit/route.ts index 0f10459..9900d70 100644 --- a/app/api/puzzle/submit/route.ts +++ b/app/api/puzzle/submit/route.ts @@ -35,58 +35,55 @@ export async function POST(req: NextRequest) { throw new Error('No active puzzle found - something is wrong!'); } + const userRating = await getExistingUserRating(user); const { successStr, themeGroupStr, timeStr } = await req.json(); const puzzle = JSON.parse(activePuzzle.puzzle) as Puzzle; const success = successStr as boolean; - const themeGroup = themeGroupStr as string[]; - const group = toGroupId(themeGroup); - const totalTime = (timeStr as number) / MILLISECONDS_IN_SECOND; - const userRating = await getExistingUserRating(user); const moves = puzzle.Moves.split(' ').length / 2; - const timePerMove = totalTime / moves; + const timePerMove = (timeStr as number) / MILLISECONDS_IN_SECOND / moves; const isReview = !!activePuzzle.reviewee; + // Update and persist user's rating. updateAndScaleRatings(userRating, puzzle, success, isReview); - - // Update user's rating. await RatingColl.updateOne({ username: user.username }, { $set: userRating }); await RatingHistory.create({ username: user.username, theme: 'overall', rating: userRating.rating, }); - + // Mark puzzle as solved by user. await addRound(user, puzzle); - - const reviewee = isReview - ? (JSON.parse(activePuzzle.reviewee) as Puzzle) - : puzzle; - - await updateLeitner(user, reviewee, success, group, timePerMove); + // Update Leitner instance (of original puzzle if review). + await updateLeitner( + user, + isReview ? (JSON.parse(activePuzzle.reviewee) as Puzzle) : puzzle, + success, + toGroupId(themeGroupStr as string[]), + timePerMove + ); // NB: We don't filter out irrelevant themes here. Even if theme is irrelevant, we compute ratings and - // persist in the DB, as this information is useful for dashboard analysitcs. + // persist in the DB, as this information may be useful for dashboard analyitcs. const ratingMap = await getThemeRatings(user, false); - // Update theme ratings. - const themes = puzzle.Themes.split(' '); - themes.forEach(async (theme) => { + puzzle.Themes.split(' ').forEach(async (theme) => { const themeRating: Rating = ratingMap.get(theme) || DEFAULT_RATING; + // Update and persist per-theme ratings. updateAndScaleRatings(themeRating, puzzle, success, isReview); await UserThemeColl.updateOne( { username: user.username, theme: theme }, { $set: themeRating }, - { upsert: true } // Insert if not found. + { upsert: true } ); + // Persist LAST time taken for each theme. if (success) { await TimeThemeColl.updateOne( { username: user.username, theme: theme }, - { - $set: { time: timePerMove }, - }, + { $set: { time: timePerMove } }, { upsert: true } ); } + // Add entry in theme's rating history. await RatingHistory.create({ username: user.username, theme: theme, diff --git a/components/puzzle-ui/controls/puzzle-hint.tsx b/components/puzzle-ui/controls/puzzle-hint.tsx index 69c88e1..12dd837 100644 --- a/components/puzzle-ui/controls/puzzle-hint.tsx +++ b/components/puzzle-ui/controls/puzzle-hint.tsx @@ -5,7 +5,6 @@ import StaticBoard from '../static-board'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { capitalize } from '@/lib/utils'; -import { isRelevant } from '@/app/api/puzzle/nextPuzzle/themeGenerator'; const hintMode = (similar: Puzzle[] | undefined, themes: string[]) => { return ( diff --git a/components/puzzle-ui/puzzle-mode.tsx b/components/puzzle-ui/puzzle-mode.tsx index b6014f8..d0ff84f 100644 --- a/components/puzzle-ui/puzzle-mode.tsx +++ b/components/puzzle-ui/puzzle-mode.tsx @@ -1,4 +1,5 @@ 'use client'; + import { Puzzle } from '@/types/lichess-api'; import React, { useState, useEffect } from 'react'; import PuzzleBoard from './puzzle-board'; From 0e5f347f7223eb7402e16c639dd1741ef3440b74 Mon Sep 17 00:00:00 2001 From: Sreesh Maheshwar Date: Mon, 18 Mar 2024 10:13:09 +0000 Subject: [PATCH 5/5] Remove some logging and a TODO --- app/api/puzzle/nextPuzzle/nextFor.ts | 8 +++----- src/sm2.ts | 6 +----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/api/puzzle/nextPuzzle/nextFor.ts b/app/api/puzzle/nextPuzzle/nextFor.ts index 030d6d8..88c440d 100644 --- a/app/api/puzzle/nextPuzzle/nextFor.ts +++ b/app/api/puzzle/nextPuzzle/nextFor.ts @@ -18,7 +18,7 @@ const MAX_REPS: number = 12; const MAX_COMPROMISE: number = 3; const LEITNER_PROBABILITY: number = 0.2; -const MIN_CANDIDATES: number = 10; // TODO: Increase this. +const MIN_CANDIDATES: number = 10; export type PuzzleWithUserRating = { puzzle: Puzzle | undefined; @@ -172,10 +172,8 @@ const nextPuzzleFor = async ( }); if (activePuzzle) { if (groupId !== activePuzzle.groupID) { - // TODO: Maybe preserve previous active puzzle? But we need to be - // careful in case this mode solves that puzzle, then back in - // normal mode user solves puzzle again - then errors! - console.log('Deleting different active puzzle'); + // NB: Preserving active puzzles per-modes is tricky. We can't permit this mode solving that puzzle, + // then back in previous mode user solving it again. await ActivePuzzleColl.deleteOne({ username: user.username }); } else { console.log('Found active puzzle'); diff --git a/src/sm2.ts b/src/sm2.ts index 015df03..bc1560a 100644 --- a/src/sm2.ts +++ b/src/sm2.ts @@ -102,14 +102,10 @@ const sm2RandomThemeFromRatingMap = async ( theme: theme, }); if (entry) { - const t = Math.min(entry.time, MAX_CORRECT_TIME); - console.log(`Unscaled rating: ${v.rating} with time ${t}`); - v.rating = scaleGlickoByTime(v.rating, t); - console.log(`Scaled rating: ${v.rating}`); + v.rating = scaleGlickoByTime(v.rating, Math.min(entry.time, MAX_CORRECT_TIME)); } result.set(theme, v); } - console.log(result); return proportionallyRandomTheme(applySm2(result)); };