mirror of
https://github.com/shadoll/sLogos.git
synced 2025-12-20 04:27:59 +00:00
Add tracking for correct and incorrect answers in FlagQuiz and implement adaptive learning settings
This commit is contained in:
5
public/icons/settings.svg
Normal file
5
public/icons/settings.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="1.5"/>
|
||||||
|
<path d="M3.66122 10.6392C4.13377 10.9361 4.43782 11.4419 4.43782 11.9999C4.43781 12.558 4.13376 13.0638 3.66122 13.3607C3.33966 13.5627 3.13248 13.7242 2.98508 13.9163C2.66217 14.3372 2.51966 14.869 2.5889 15.3949C2.64082 15.7893 2.87379 16.1928 3.33973 16.9999C3.80568 17.8069 4.03865 18.2104 4.35426 18.4526C4.77508 18.7755 5.30694 18.918 5.83284 18.8488C6.07287 18.8172 6.31628 18.7185 6.65196 18.5411C7.14544 18.2803 7.73558 18.2699 8.21895 18.549C8.70227 18.8281 8.98827 19.3443 9.00912 19.902C9.02332 20.2815 9.05958 20.5417 9.15224 20.7654C9.35523 21.2554 9.74458 21.6448 10.2346 21.8478C10.6022 22 11.0681 22 12 22C12.9319 22 13.3978 22 13.7654 21.8478C14.2554 21.6448 14.6448 21.2554 14.8478 20.7654C14.9404 20.5417 14.9767 20.2815 14.9909 19.9021C15.0117 19.3443 15.2977 18.8281 15.7811 18.549C16.2644 18.27 16.8545 18.2804 17.3479 18.5412C17.6837 18.7186 17.9271 18.8173 18.1671 18.8489C18.693 18.9182 19.2249 18.7756 19.6457 18.4527C19.9613 18.2106 20.1943 17.807 20.6603 17C20.8677 16.6407 21.029 16.3614 21.1486 16.1272M20.3387 13.3608C19.8662 13.0639 19.5622 12.5581 19.5621 12.0001C19.5621 11.442 19.8662 10.9361 20.3387 10.6392C20.6603 10.4372 20.8674 10.2757 21.0148 10.0836C21.3377 9.66278 21.4802 9.13092 21.411 8.60502C21.3591 8.2106 21.1261 7.80708 20.6601 7.00005C20.1942 6.19301 19.9612 5.7895 19.6456 5.54732C19.2248 5.22441 18.6929 5.0819 18.167 5.15113C17.927 5.18274 17.6836 5.2814 17.3479 5.45883C16.8544 5.71964 16.2643 5.73004 15.781 5.45096C15.2977 5.1719 15.0117 4.6557 14.9909 4.09803C14.9767 3.71852 14.9404 3.45835 14.8478 3.23463C14.6448 2.74458 14.2554 2.35523 13.7654 2.15224C13.3978 2 12.9319 2 12 2C11.0681 2 10.6022 2 10.2346 2.15224C9.74458 2.35523 9.35523 2.74458 9.15224 3.23463C9.05958 3.45833 9.02332 3.71848 9.00912 4.09794C8.98826 4.65566 8.70225 5.17191 8.21891 5.45096C7.73557 5.73002 7.14548 5.71959 6.65205 5.4588C6.31633 5.28136 6.0729 5.18269 5.83285 5.15108C5.30695 5.08185 4.77509 5.22436 4.35427 5.54727C4.03866 5.78945 3.80569 6.19297 3.33974 7C3.13231 7.35929 2.97105 7.63859 2.85138 7.87273" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -29,6 +29,8 @@
|
|||||||
// Scoring
|
// Scoring
|
||||||
let score = { correct: 0, total: 0, skipped: 0 };
|
let score = { correct: 0, total: 0, skipped: 0 };
|
||||||
let gameStats = { correct: 0, wrong: 0, total: 0, skipped: 0 };
|
let gameStats = { correct: 0, wrong: 0, total: 0, skipped: 0 };
|
||||||
|
let wrongAnswers = new Map(); // Track flags answered incorrectly: flag.name -> count
|
||||||
|
let correctAnswers = new Map(); // Track flags answered correctly: flag.name -> count
|
||||||
|
|
||||||
// Achievement System
|
// Achievement System
|
||||||
let currentStreak = 0;
|
let currentStreak = 0;
|
||||||
@@ -41,6 +43,8 @@
|
|||||||
let showSettings = false;
|
let showSettings = false;
|
||||||
let settingsLoaded = false;
|
let settingsLoaded = false;
|
||||||
let showResetConfirmation = false;
|
let showResetConfirmation = false;
|
||||||
|
let focusWrongAnswers = false;
|
||||||
|
let reduceCorrectAnswers = false;
|
||||||
|
|
||||||
// Theme
|
// Theme
|
||||||
let theme = 'system';
|
let theme = 'system';
|
||||||
@@ -51,16 +55,16 @@
|
|||||||
theme = t;
|
theme = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save settings when they change (after initial load)
|
|
||||||
$: if (settingsLoaded) {
|
|
||||||
localStorage.setItem('flagQuizSettings', JSON.stringify({ autoAdvance }));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update achievement count when achievements component is available
|
// Update achievement count when achievements component is available
|
||||||
$: if (achievementsComponent) {
|
$: if (achievementsComponent) {
|
||||||
updateAchievementCount();
|
updateAchievementCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save settings when they change (after initial load)
|
||||||
|
$: if (settingsLoaded && typeof reduceCorrectAnswers !== 'undefined') {
|
||||||
|
localStorage.setItem('flagQuizSettings', JSON.stringify({ autoAdvance, focusWrongAnswers, reduceCorrectAnswers }));
|
||||||
|
}
|
||||||
|
|
||||||
// Load game stats from localStorage
|
// Load game stats from localStorage
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// Initialize theme
|
// Initialize theme
|
||||||
@@ -94,12 +98,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load wrong answers tracking
|
||||||
|
const savedWrongAnswers = localStorage.getItem('flagQuizWrongAnswers');
|
||||||
|
if (savedWrongAnswers) {
|
||||||
|
try {
|
||||||
|
const loadedWrongAnswers = JSON.parse(savedWrongAnswers);
|
||||||
|
wrongAnswers = new Map(Object.entries(loadedWrongAnswers));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error loading wrong answers:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load correct answers tracking
|
||||||
|
const savedCorrectAnswers = localStorage.getItem('flagQuizCorrectAnswers');
|
||||||
|
if (savedCorrectAnswers) {
|
||||||
|
try {
|
||||||
|
const loadedCorrectAnswers = JSON.parse(savedCorrectAnswers);
|
||||||
|
correctAnswers = new Map(Object.entries(loadedCorrectAnswers));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error loading correct answers:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load settings
|
// Load settings
|
||||||
const savedSettings = localStorage.getItem('flagQuizSettings');
|
const savedSettings = localStorage.getItem('flagQuizSettings');
|
||||||
if (savedSettings) {
|
if (savedSettings) {
|
||||||
try {
|
try {
|
||||||
const settings = JSON.parse(savedSettings);
|
const settings = JSON.parse(savedSettings);
|
||||||
autoAdvance = settings.autoAdvance !== undefined ? settings.autoAdvance : true;
|
autoAdvance = settings.autoAdvance !== undefined ? settings.autoAdvance : true;
|
||||||
|
focusWrongAnswers = settings.focusWrongAnswers !== undefined ? settings.focusWrongAnswers : false;
|
||||||
|
reduceCorrectAnswers = settings.reduceCorrectAnswers !== undefined ? settings.reduceCorrectAnswers : false;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error loading settings:', e);
|
console.error('Error loading settings:', e);
|
||||||
}
|
}
|
||||||
@@ -107,8 +135,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
await loadFlags();
|
await loadFlags();
|
||||||
generateQuestion();
|
|
||||||
settingsLoaded = true;
|
settingsLoaded = true;
|
||||||
|
generateQuestion();
|
||||||
});
|
});
|
||||||
|
|
||||||
function applyTheme(theme) {
|
function applyTheme(theme) {
|
||||||
@@ -169,38 +197,73 @@
|
|||||||
// Randomly choose question type
|
// Randomly choose question type
|
||||||
questionType = Math.random() < 0.5 ? 'flag-to-country' : 'country-to-flag';
|
questionType = Math.random() < 0.5 ? 'flag-to-country' : 'country-to-flag';
|
||||||
|
|
||||||
// Pick a random correct answer
|
// Pick correct answer with adaptive learning settings
|
||||||
const correctFlag = flags[Math.floor(Math.random() * flags.length)];
|
let correctFlag;
|
||||||
|
|
||||||
|
// Simple fallback to avoid uninitialized variable errors
|
||||||
|
if (settingsLoaded && (focusWrongAnswers || reduceCorrectAnswers)) { // Re-enable adaptive learning
|
||||||
|
// Create weighted array based on learning settings
|
||||||
|
const weightedFlags = [];
|
||||||
|
for (const flag of flags) {
|
||||||
|
const wrongCount = wrongAnswers.get(flag.name) || 0;
|
||||||
|
const correctCount = correctAnswers.get(flag.name) || 0;
|
||||||
|
|
||||||
|
let weight = 1; // Base weight
|
||||||
|
|
||||||
|
// Increase weight for flags with wrong answers (if setting enabled)
|
||||||
|
if (focusWrongAnswers && wrongCount > 0) {
|
||||||
|
weight = Math.min(wrongCount + 1, 4); // Max 4x weight for wrong answers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrease weight for flags with correct answers (if setting enabled)
|
||||||
|
if (reduceCorrectAnswers && correctCount > 0) {
|
||||||
|
weight = weight / Math.min(correctCount + 1, 4); // Reduce weight, min 0.25x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add flag to weighted array based on calculated weight
|
||||||
|
const finalWeight = Math.max(0.25, weight); // Minimum weight to ensure variety
|
||||||
|
const timesToAdd = Math.ceil(finalWeight);
|
||||||
|
for (let i = 0; i < timesToAdd; i++) {
|
||||||
|
weightedFlags.push(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weightedFlags.length > 0) {
|
||||||
|
correctFlag = weightedFlags[Math.floor(Math.random() * weightedFlags.length)];
|
||||||
|
} else {
|
||||||
|
correctFlag = flags[Math.floor(Math.random() * flags.length)];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normal random selection
|
||||||
|
correctFlag = flags[Math.floor(Math.random() * flags.length)];
|
||||||
|
}
|
||||||
|
|
||||||
const correctCountry = getCountryName(correctFlag).toLowerCase();
|
const correctCountry = getCountryName(correctFlag).toLowerCase();
|
||||||
|
|
||||||
// Generate 3 wrong answers ensuring no duplicate country names
|
// Generate 3 wrong answers ensuring no duplicate country names
|
||||||
const wrongAnswers = [];
|
const wrongOptions = [];
|
||||||
const usedCountries = new Set([correctCountry]);
|
const usedCountries = new Set([correctCountry]);
|
||||||
|
|
||||||
while (wrongAnswers.length < 3 && wrongAnswers.length < flags.length - 1) {
|
while (wrongOptions.length < 3 && wrongOptions.length < flags.length - 1) {
|
||||||
const randomFlag = flags[Math.floor(Math.random() * flags.length)];
|
const randomFlag = flags[Math.floor(Math.random() * flags.length)];
|
||||||
const randomCountry = getCountryName(randomFlag).toLowerCase();
|
const randomCountry = getCountryName(randomFlag).toLowerCase();
|
||||||
|
|
||||||
if (randomFlag !== correctFlag &&
|
if (!usedCountries.has(randomCountry)) {
|
||||||
!wrongAnswers.includes(randomFlag) &&
|
wrongOptions.push(randomFlag);
|
||||||
!usedCountries.has(randomCountry)) {
|
|
||||||
wrongAnswers.push(randomFlag);
|
|
||||||
usedCountries.add(randomCountry);
|
usedCountries.add(randomCountry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have enough options
|
// If we couldn't find 3 unique countries, fill with random flags
|
||||||
if (wrongAnswers.length < 3) {
|
while (wrongOptions.length < 3) {
|
||||||
console.warn(`Only found ${wrongAnswers.length + 1} unique countries, regenerating...`);
|
const randomFlag = flags[Math.floor(Math.random() * flags.length)];
|
||||||
// Try again
|
if (randomFlag !== correctFlag && !wrongOptions.includes(randomFlag)) {
|
||||||
generateQuestion();
|
wrongOptions.push(randomFlag);
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shuffle all options
|
// Combine correct and wrong answers
|
||||||
const allOptions = [correctFlag, ...wrongAnswers].sort(() => Math.random() - 0.5);
|
const allOptions = [correctFlag, ...wrongOptions].sort(() => Math.random() - 0.5); currentQuestion = {
|
||||||
|
|
||||||
currentQuestion = {
|
|
||||||
type: questionType,
|
type: questionType,
|
||||||
correct: correctFlag,
|
correct: correctFlag,
|
||||||
options: allOptions,
|
options: allOptions,
|
||||||
@@ -225,6 +288,14 @@
|
|||||||
gameStats.correct++;
|
gameStats.correct++;
|
||||||
currentStreak++;
|
currentStreak++;
|
||||||
|
|
||||||
|
// Track correct answer for this flag
|
||||||
|
if (currentQuestion.correct?.name) {
|
||||||
|
const flagName = currentQuestion.correct.name;
|
||||||
|
correctAnswers.set(flagName, (correctAnswers.get(flagName) || 0) + 1);
|
||||||
|
// Save correct answers to localStorage
|
||||||
|
localStorage.setItem('flagQuizCorrectAnswers', JSON.stringify(Object.fromEntries(correctAnswers)));
|
||||||
|
}
|
||||||
|
|
||||||
// Track continent progress for correct answers
|
// Track continent progress for correct answers
|
||||||
if (achievementsComponent && currentQuestion.correct?.tags) {
|
if (achievementsComponent && currentQuestion.correct?.tags) {
|
||||||
const continent = currentQuestion.correct.tags.find(tag =>
|
const continent = currentQuestion.correct.tags.find(tag =>
|
||||||
@@ -242,6 +313,15 @@
|
|||||||
} else {
|
} else {
|
||||||
gameStats.wrong++;
|
gameStats.wrong++;
|
||||||
currentStreak = 0; // Reset streak on wrong answer
|
currentStreak = 0; // Reset streak on wrong answer
|
||||||
|
|
||||||
|
// Track wrong answer for this flag
|
||||||
|
if (currentQuestion.correct?.name) {
|
||||||
|
const flagName = currentQuestion.correct.name;
|
||||||
|
wrongAnswers.set(flagName, (wrongAnswers.get(flagName) || 0) + 1);
|
||||||
|
// Save wrong answers to localStorage
|
||||||
|
localStorage.setItem('flagQuizWrongAnswers', JSON.stringify(Object.fromEntries(wrongAnswers)));
|
||||||
|
}
|
||||||
|
|
||||||
if (achievementsComponent) {
|
if (achievementsComponent) {
|
||||||
achievementsComponent.resetConsecutiveSkips();
|
achievementsComponent.resetConsecutiveSkips();
|
||||||
}
|
}
|
||||||
@@ -330,6 +410,14 @@
|
|||||||
currentStreak = 0;
|
currentStreak = 0;
|
||||||
localStorage.setItem('flagQuizStats', JSON.stringify(gameStats));
|
localStorage.setItem('flagQuizStats', JSON.stringify(gameStats));
|
||||||
|
|
||||||
|
// Reset wrong answers tracking
|
||||||
|
wrongAnswers = new Map();
|
||||||
|
localStorage.removeItem('flagQuizWrongAnswers');
|
||||||
|
|
||||||
|
// Reset correct answers tracking
|
||||||
|
correctAnswers = new Map();
|
||||||
|
localStorage.removeItem('flagQuizCorrectAnswers');
|
||||||
|
|
||||||
// Reset achievements
|
// Reset achievements
|
||||||
if (achievementsComponent) {
|
if (achievementsComponent) {
|
||||||
localStorage.removeItem('flagQuizAchievements');
|
localStorage.removeItem('flagQuizAchievements');
|
||||||
@@ -369,6 +457,10 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Flag Quiz</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<Header
|
<Header
|
||||||
{theme}
|
{theme}
|
||||||
{setTheme}
|
{setTheme}
|
||||||
@@ -399,7 +491,8 @@
|
|||||||
aria-labelledby="settings-title"
|
aria-labelledby="settings-title"
|
||||||
>
|
>
|
||||||
<div class="settings-header">
|
<div class="settings-header">
|
||||||
<h2 id="settings-title">⚙️ Game Settings</h2>
|
<InlineSvg path="/icons/settings.svg" alt="Settings" />
|
||||||
|
<h2 id="settings-title">Game Settings</h2>
|
||||||
<button class="close-btn" on:click={toggleSettings}>✕</button>
|
<button class="close-btn" on:click={toggleSettings}>✕</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -414,6 +507,26 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={focusWrongAnswers}
|
||||||
|
/>
|
||||||
|
Focus on previously answered incorrectly flags
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={reduceCorrectAnswers}
|
||||||
|
/>
|
||||||
|
Show correctly answered flags less frequently
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting-actions">
|
<div class="setting-actions">
|
||||||
<button class="reset-stats-btn" on:click={resetAllStats}>
|
<button class="reset-stats-btn" on:click={resetAllStats}>
|
||||||
Reset All Statistics
|
Reset All Statistics
|
||||||
@@ -449,6 +562,7 @@
|
|||||||
<li>✗ Current session score</li>
|
<li>✗ Current session score</li>
|
||||||
<li>✗ All unlocked achievements</li>
|
<li>✗ All unlocked achievements</li>
|
||||||
<li>✗ Achievement progress</li>
|
<li>✗ Achievement progress</li>
|
||||||
|
<li>✗ Wrong answer tracking data</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><strong>This cannot be undone!</strong></p>
|
<p><strong>This cannot be undone!</strong></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -637,11 +751,17 @@
|
|||||||
border-bottom: 2px solid var(--color-border);
|
border-bottom: 2px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-header :global(.svg-wrapper) {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-header h2 {
|
.settings-header h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-btn {
|
.close-btn {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
Reference in New Issue
Block a user