edu-boardgame-generator/minigames/quiz.js
Spiel-Generator Workshop 016be6ea90 feat: initial project structure with modular mini-game system
- editor.html: 5-step game editor with live Python code split-screen
- game.html: playable board game engine
- codegen.js: Python live-code generator (extracted from editor)
- logo.png: PH Weingarten logo (extracted from Base64)
- minigames/_api.js: shared Mini-Game API (makeCanvas, onResult, helpers)
- 12 Mini-Games as individual modules (launch + preview API):
  snake, flappy, memory, quiz, reaction,
  basketball, catch, maze, simon, typing, puzzle, spotdiff
- README.md with architecture docs and deployment guide

Refactoring highlights:
  - editor.html: 251 KB → 102 KB (-59%)
  - Mini-games fully decoupled, each ~100-200 lines
  - All 12 games now have working launch() + preview()
  - Maze uses recursive backtracker algorithm
  - spotdiff uses canvas-drawn scene with 5 differences
2026-03-14 22:12:25 +00:00

111 lines
4.3 KiB
JavaScript

/**
* minigames/quiz.js
* ❓ Quiz — Beantworte die Frage richtig!
*/
window.MG_quiz = (function() {
const ID = 'quiz';
const EMOJI = '❓';
const NAME = 'Quiz';
const DESC = 'Beantworte die Frage richtig!';
const CONTROLS = 'Mausklick / Tippen';
const MULTI = 99; // unbegrenzt, je Quiz-Feld eine Frage
function run(wrap, W, H, cfg, onDone) {
const theme = cfg.theme || { primary: '#f59e0b' };
const quizData = cfg.quizData || [];
const fieldIdx = cfg.fieldIndex ?? null;
// Passende Frage suchen
let q = null;
if (fieldIdx !== null) q = quizData.find(d => d.fieldIndex === fieldIdx);
if (!q && quizData.length > 0) q = quizData[Math.floor(Math.random() * quizData.length)];
// Fallback wenn keine Frage hinterlegt
if (!q || !q.question) {
wrap.innerHTML = `
<div style="text-align:center;padding:40px 20px;font-family:'Nunito',sans-serif">
<div style="font-size:3rem;margin-bottom:12px">❓</div>
<div style="color:#a7a3c2;font-size:14px;margin-bottom:20px">Keine Quiz-Frage für dieses Feld hinterlegt.</div>
<button onclick="MGAPI.onResult(true)"
style="background:linear-gradient(135deg,#7c3aed,#f5a623);color:#fff;font-family:'Fredoka One',cursive;
font-size:1rem;border:none;border-radius:10px;padding:12px 28px;cursor:pointer">
✅ Trotzdem bestanden!
</button>
</div>`;
return { stop() {} };
}
// HTML-Quiz (kein Canvas — besser lesbar)
const answers = q.answers || [];
const correct = q.correct ?? 0;
const COLORS = ['#7c3aed','#2563eb','#059669','#d97706'];
const LABELS = ['A','B','C','D'];
const container = document.createElement('div');
container.style.cssText = `
display:flex;flex-direction:column;gap:10px;padding:16px;
font-family:'Nunito',sans-serif;width:100%;box-sizing:border-box;
`;
// Frage
const qEl = document.createElement('div');
qEl.style.cssText = `
background:rgba(255,255,255,0.05);border-radius:12px;padding:16px;
color:#fff;font-size:15px;font-weight:700;line-height:1.5;text-align:center;
`;
qEl.textContent = q.question;
container.appendChild(qEl);
// Antworten
let answered = false;
answers.forEach((a, i) => {
if (!a) return;
const btn = document.createElement('button');
btn.style.cssText = `
background:${COLORS[i] || '#333'}22;border:2px solid ${COLORS[i] || '#333'}88;
color:#fff;border-radius:10px;padding:12px 16px;cursor:pointer;
font-family:'Nunito',sans-serif;font-size:13px;font-weight:700;
text-align:left;transition:all 0.2s;display:flex;gap:10px;align-items:center;
`;
btn.innerHTML = `<span style="background:${COLORS[i]};border-radius:6px;padding:2px 8px;font-size:12px">${LABELS[i]}</span> ${a}`;
btn.addEventListener('click', () => {
if (answered) return;
answered = true;
const won = (i === correct);
// Feedback-Farben
answers.forEach((_, j) => {
const b = container.querySelectorAll('button')[j];
if (!b) return;
if (j === correct) {
b.style.background = 'rgba(16,185,129,0.3)';
b.style.borderColor = '#10b981';
} else if (j === i && !won) {
b.style.background = 'rgba(239,68,68,0.3)';
b.style.borderColor = '#ef4444';
}
b.style.cursor = 'default';
});
// Ergebnis-Text
const result = document.createElement('div');
result.style.cssText = `
text-align:center;font-family:'Fredoka One',cursive;font-size:1.2rem;
padding:10px;color:${won ? '#10b981' : '#ef4444'};
`;
result.textContent = won ? '🎉 Richtig!' : `❌ Falsch! Richtig wäre: ${answers[correct]}`;
container.appendChild(result);
setTimeout(() => onDone(won), 1400);
});
container.appendChild(btn);
});
wrap.appendChild(container);
return { stop() { wrap.innerHTML = ''; } };
}
function launch(wrap, W, H, cfg) { return run(wrap, W, H, cfg, won => MGAPI.onResult(won)); }
function preview(wrap, W, H, cfg) { return run(wrap, W, H, cfg, won => MGAPI.onResult(won)); }
return { id: ID, emoji: EMOJI, name: NAME, desc: DESC, controls: CONTROLS, multi: MULTI, launch, preview };
})();