Feature: 100-point scoring, question memory & spaced repetition, random wizard name generator, cache-busting v2.2, gold footer pills

This commit is contained in:
kobold-ai
2026-09-21 13:53:33 +02:00
parent 26ff0660d3
commit a938123271
10 changed files with 257 additions and 33 deletions
+79 -3
View File
@@ -159,6 +159,58 @@ function applyQuizTranslations(lang) {
}
}
let sessionQuestionIds = [];
function getRecentQuestionIds() {
try {
const stored = localStorage.getItem("quiz_recent_qids");
return stored ? JSON.parse(stored) : [];
} catch(e) {
return [];
}
}
function getWrongQuestionIds() {
try {
const stored = localStorage.getItem("quiz_wrong_qids");
return stored ? JSON.parse(stored) : [];
} catch(e) {
return [];
}
}
function recordQuestionWrong(qid) {
if (!qid) return;
try {
const wrong = getWrongQuestionIds();
if (!wrong.includes(qid)) {
wrong.push(qid);
if (wrong.length > 30) wrong.shift();
localStorage.setItem("quiz_wrong_qids", JSON.stringify(wrong));
}
} catch(e) {}
}
function recordQuestionMastered(qid) {
if (!qid) return;
try {
let wrong = getWrongQuestionIds();
if (wrong.includes(qid)) {
wrong = wrong.filter(id => id !== qid);
localStorage.setItem("quiz_wrong_qids", JSON.stringify(wrong));
}
} catch(e) {}
}
function saveRecentQuestions(ids) {
if (!ids || ids.length === 0) return;
try {
let recent = getRecentQuestionIds();
recent = Array.from(new Set([...ids, ...recent])).slice(0, 20);
localStorage.setItem("quiz_recent_qids", JSON.stringify(recent));
} catch(e) {}
}
async function startGame() {
const envelope = document.getElementById("envelope");
const startBtn = document.getElementById("startBtn");
@@ -167,6 +219,7 @@ async function startGame() {
startBtn.disabled = true;
startBtn.innerText = t.startBtnCasting;
envelope.classList.add("envelope-opened");
sessionQuestionIds = [];
// Start background music if enabled or first click
const bgm = document.getElementById("bgm");
@@ -183,12 +236,17 @@ async function startGame() {
}
try {
const recentIds = getRecentQuestionIds();
const wrongIds = getWrongQuestionIds();
const res = await fetch("/api/game/start", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
lang: currentLang,
difficulty: selectedDifficulty
difficulty: selectedDifficulty,
recent_ids: recentIds,
wrong_ids: wrongIds
})
});
@@ -239,6 +297,9 @@ function updateTimer() {
function renderQuestion(qData) {
lastQuestionData = qData;
if (qData.id && !sessionQuestionIds.includes(qData.id)) {
sessionQuestionIds.push(qData.id);
}
const t = UI_TEXT[currentLang] || UI_TEXT.de;
document.getElementById("questionCounter").innerText = `${t.questionLabel} ${qData.question_number} ${t.ofLabel} ${totalQuestions}`;
@@ -364,7 +425,17 @@ async function submitAnswer(answer, clickedBtn = null) {
clickedBtn.classList.add("choice-correct");
}
frageBox.classList.add("sparkle-effect");
feedbackBox.innerHTML = `<span class="feedback-correct">${t.correctFeedback}</span>`;
// Mark question as mastered in history
if (result.question_id) {
recordQuestionMastered(result.question_id);
}
const pts = result.points_earned || 100;
const ptsText = (currentLang === "en")
? `✨ Correct! +${pts} House Points! ✨`
: `✨ Richtig! +${pts} Hauspunkte für dein Haus! ✨`;
feedbackBox.innerHTML = `<span class="feedback-correct">${ptsText}</span>`;
setTimeout(() => {
frageBox.classList.remove("sparkle-effect");
@@ -391,7 +462,11 @@ async function submitAnswer(answer, clickedBtn = null) {
document.getElementById("attemptBadge").innerText = `${t.attemptLabel} ${Math.min(attemptsUsed + 1, maxAttempts)} ${t.ofLabel} ${maxAttempts}`;
if (result.attempts_left === 0) {
// Exhausted all attempts
// Exhausted all attempts -> record for spaced repetition learning
if (result.question_id) {
recordQuestionWrong(result.question_id);
}
feedbackBox.innerHTML = `
<div class="feedback-wrong">${t.exhaustedWrong}</div>
<div class="feedback-revealed">${t.solutionLabel} <strong>${result.revealed_answer}</strong></div>
@@ -448,6 +523,7 @@ async function requestHint() {
function finishGame() {
if (timerInterval) clearInterval(timerInterval);
saveRecentQuestions(sessionQuestionIds);
document.getElementById("progressBar").style.width = "100%";
window.location.href = `/finale?score=${currentScore}&total=${totalQuestions}&time=${elapsedSeconds}&lang=${currentLang}`;
}