ThreeD_Ed_Game / index.html
eaglelandsonce's picture
Update index.html
56cc615 verified
Raw
History Blame Contribute Delete
17 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>AI+ L&D 2D Escape Quiz</title>
<style>
:root { color-scheme: dark; }
html, body { height:100%; margin:0; background:#0b1020; font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; }
canvas { display:block; margin:0 auto; background: radial-gradient(1200px 800px at 50% 40%, #0a0f28 0%, #070d22 60%, #050a1c 100%); image-rendering: pixelated; }
#hud, #hint, #overlay, #quiz, #win { position: fixed; left:0; right:0; margin:0 auto; color:#eaf2ff; }
#hud { top:12px; text-align:center; font-weight:700; text-shadow:0 2px 10px rgba(0,0,0,.6); pointer-events:none; }
#hint { bottom: 12px; text-align:center; color:#bcd3ff; text-shadow:0 2px 10px rgba(0,0,0,.6); }
#hint.pulse { animation: pulse 1.4s ease-in-out infinite; }
@keyframes pulse { 0%{opacity:.6} 50%{opacity:1} 100%{opacity:.6} }
.panel-wrap { display:flex; align-items:center; justify-content:center; top:0; bottom:0; }
.panel { max-width: 840px; margin: 16px; padding: 26px; background: rgba(18,24,52,.72); border:1px solid rgba(180,200,255,.25); border-radius:18px; backdrop-filter: blur(6px); box-shadow: 0 20px 60px rgba(0,0,0,.45); }
h1 { margin:0 0 .25rem; font-size: 1.9rem; letter-spacing:.3px; }
h2 { margin:.25rem 0 .75rem; font-size: 1.25rem; color:#cfe3ff; }
p, li { line-height:1.55; }
.btn { display:inline-block; margin-top: 12px; padding: 12px 18px; border-radius: 12px; background: linear-gradient(135deg,#6aa4ff,#7ef0ff); color:#071225; font-weight:700; border:none; cursor:pointer; }
.btn.secondary { background: transparent; border:1px solid rgba(180,200,255,.35); color:#eaf2ff; }
#quiz.hidden, #overlay.hidden, #win.hidden { display:none; }
.choices { display:grid; gap:10px; margin-top:10px; }
.choice { text-align:left; padding:10px 12px; border-radius:12px; border:1px solid rgba(180,200,255,.3); background:rgba(255,255,255,.05); cursor:pointer; }
.choice.correct { border-color:#36e29f; background: rgba(54,226,159,.16); }
.choice.wrong { border-color:#ff6b6b; background: rgba(255,107,107,.16); }
#footerLink { margin-top:10px; font-size:.9rem; opacity:.9; }
a { color:#8ad4ff; }
</style>
</head>
<body>
<canvas id="game" width="1152" height="648" aria-label="AI+ L&D 2D Escape Quiz"></canvas>
<div id="hud">Unlock terminals: <span id="progress">0</span>/<span id="total">0</span></div>
<div id="hint">Press <b>Enter the Maze</b> to begin</div>
<!-- Start Panel -->
<div id="overlay" class="panel-wrap">
<div class="panel">
<h1>AI+ L&amp;D 2D Escape Quiz</h1>
<p>Navigate a neon maze inspired by the white paper: <em>AI-driven personalization, predictive analytics, fair teacher evaluation, and ethical data practices</em>.<br/>Unlock glowing terminals by answering questions. Open the exit by completing them all.</p>
<h2>How to Play</h2>
<ul style="text-align:left; margin:0 auto; max-width:640px">
<li><b>W / A / S / D</b> or <b>Arrow Keys</b> to move.</li>
<li>Walk onto a glowing terminal and press <b>E</b> to answer.</li>
<li>Unlock all terminals to open the exit, then step through it.</li>
</ul>
<div id="footerLink">
Source: <a target="_blank" href="https://www.linkedin.com/pulse/artificial-intelligence-learning-development-michael-lively-welze/">
Artificial Intelligence in Learning &amp; Development – Michael Lively</a>
</div>
<button id="startBtn" class="btn">Enter the Maze</button>
</div>
</div>
<!-- Quiz Panel -->
<div id="quiz" class="panel-wrap hidden">
<div class="panel">
<h2 id="qTitle">Challenge</h2>
<div id="qText"></div>
<div class="choices" id="choices"></div>
<div id="explain" class="meta"></div>
<div style="display:flex; gap:10px; justify-content:flex-end; margin-top:12px;">
<button id="closeQuiz" class="btn secondary">Close</button>
</div>
</div>
</div>
<!-- Win Panel -->
<div id="win" class="panel-wrap hidden">
<div class="panel">
<h1>Exit Unlocked</h1>
<p>You’ve mastered personalization, predictive analytics, teacher growth, and ethical data. Step through the exit portal!</p>
<button id="resume" class="btn">Resume</button>
</div>
</div>
<script>
/* ======================== GAME DATA ======================== */
const QUESTIONS = [
{
key:'personalization',
title:'Personalization at the Core',
text:'What best describes AI-driven personalization in the classroom?',
choices:[
'Delivering the same lesson faster to all students',
'Adjusting pace, difficulty, and materials to each learner in real time',
'Replacing teachers with chatbots for all instruction',
'Focusing solely on standardized tests for progress'
],
correct:1,
explain:'Personalization adapts pace, difficulty, and content to each learner, ensuring challenge for advanced students and support for those who need it.'
},
{
key:'predictive',
title:'Predictive Analytics & Foresight',
text:'How do predictive analytics most usefully assist teachers?',
choices:[
'By guessing final grades at the end of term only',
'By forecasting disengagement and surfacing timely, targeted interventions',
'By removing the need for any formative assessment',
'By replacing lesson planning entirely'
],
correct:1,
explain:'Predictive models surface early warning signs (e.g., declining engagement) and suggest targeted interventions so teachers can act in time.'
},
{
key:'teacherPD',
title:'Personalized Programs for Teachers',
text:'Which is a key benefit of AI for teacher professional development?',
choices:[
'One-size-fits-all workshops for consistency',
'Real-time coaching and adaptive learning pathways tied to classroom practice',
'Eliminating peer collaboration',
'Annual compliance videos only'
],
correct:1,
explain:'AI enables adaptive PD—simulations, micro-lessons, and feedback aligned to each teacher’s goals and classroom data.'
},
{
key:'evaluation',
title:'Fixing Teacher Evaluation',
text:'Why can AI-supported evaluation be fairer than traditional systems?',
choices:[
'It relies on a single standardized measure',
'It hides how decisions are made',
'It integrates many signals and emphasizes growth rather than punitive ratings',
'It replaces human judgment with automation'
],
correct:2,
explain:'A holistic, data-rich model highlights strengths, identifies needs, and supports growth instead of punishing with one score.'
},
{
key:'ethics',
title:'Ethics & Data Protection',
text:'Which approach best captures ethical use of learner/teacher data?',
choices:[
'Collect everything without telling users',
'Use encryption, minimize collection, ensure transparency, allow human override',
'Share raw data with vendors by default',
'Keep models secret and un-audited'
],
correct:1,
explain:'Ethical practice requires transparency, consent/minimization, encryption, access controls, bias checks, and humans in the loop.'
}
];
// Level legend: # = wall, . = floor, S = spawn, G = terminal, E = exit (door)
const LEVEL = [
"#####################",
"#S .# . G .# ..#",
"#.###.###.###.###E.#",
"#.. G .# .. # ...##",
"###.###.#.###.#.#..#",
"#.... #.#...#.#.#..#",
"#.### #.### #.#.#..#",
"#.. # #...# #.. #..#",
"### # ### # #####..#",
"#.. # .# # . #..#",
"# ### # # ### # ####",
"# # .# .# .#",
"#####################"
];
const TILE = 48; // pixels per tile
const ROWS = LEVEL.length;
const COLS = LEVEL[0].length;
const W = COLS * TILE;
const H = ROWS * TILE;
// Canvas + context
const canvas = document.getElementById('game');
canvas.width = W; canvas.height = H;
const ctx = canvas.getContext('2d');
// HUD & panels
const overlay = document.getElementById('overlay');
const startBtn = document.getElementById('startBtn');
const hintEl = document.getElementById('hint');
const hudProgress = document.getElementById('progress');
const hudTotal = document.getElementById('total');
const quizWrap = document.getElementById('quiz');
const qTitle = document.getElementById('qTitle');
const qText = document.getElementById('qText');
const choicesEl = document.getElementById('choices');
const explainEl = document.getElementById('explain');
const closeQuiz = document.getElementById('closeQuiz');
const winWrap = document.getElementById('win');
const resumeBtn = document.getElementById('resume');
const state = {
started:false,
solved:new Set(),
exitOpen:false,
player:{x:0,y:0,px:0,py:0, speed:180}, // px/py in pixels
terminals:[], // {x,y,key,index,active}
exit:{x:0,y:0,open:false}
};
// Parse level
for (let r=0;r<ROWS;r++){
for (let c=0;c<COLS;c++){
const ch = LEVEL[r][c];
if (ch==='S'){ state.player.x=c; state.player.y=r; }
if (ch==='G'){ state.terminals.push({x:c,y:r,key:null,index:0,active:true}); }
if (ch==='E'){ state.exit.x=c; state.exit.y=r; }
}
}
state.player.px = state.player.x*TILE + TILE/2;
state.player.py = state.player.y*TILE + TILE/2;
// Assign questions round-robin
state.terminals.forEach((t,i)=>{
t.key = QUESTIONS[i % QUESTIONS.length].key;
t.index = i % QUESTIONS.length;
});
hudTotal.textContent = String(state.terminals.length);
// Input
const keys = {};
window.addEventListener('keydown', e=>{ keys[e.code]=true; if(e.code==='KeyE') tryInteract();});
window.addEventListener('keyup', e=> keys[e.code]=false);
// Start
startBtn.addEventListener('click', ()=>{
overlay.classList.add('hidden');
state.started = true;
hint("Use WASD/Arrows to move • E to interact at terminals", true);
});
// Quiz handling
function openQuiz(terminal){
const q = QUESTIONS.find(x=>x.key===terminal.key) || QUESTIONS[terminal.index];
qTitle.textContent = q.title;
qText.textContent = q.text;
explainEl.textContent = '';
choicesEl.innerHTML = '';
q.choices.forEach((c,i)=>{
const btn = document.createElement('button');
btn.className = 'choice';
btn.textContent = c;
btn.onclick = ()=>{
if(i===q.correct){
btn.classList.add('correct');
explainEl.textContent = q.explain;
terminal.active = false;
state.solved.add(q.key);
hudProgress.textContent = String(state.solved.size);
setTimeout(()=>{ quizWrap.classList.add('hidden'); checkWin(); }, 850);
} else {
btn.classList.add('wrong');
explainEl.textContent = 'Try again: ' + q.explain;
}
};
choicesEl.appendChild(btn);
});
quizWrap.classList.remove('hidden');
}
closeQuiz.addEventListener('click', ()=> quizWrap.classList.add('hidden'));
resumeBtn.addEventListener('click', ()=> winWrap.classList.add('hidden'));
function tryInteract(){
if(!state.started) return;
// If on a terminal tile, open quiz
const {cx,cy} = playerCell();
const term = state.terminals.find(t=>t.active && t.x===cx && t.y===cy);
if (term){
openQuiz(term);
return;
}
// If on exit tile and open, show win
if (state.exitOpen && cx===state.exit.x && cy===state.exit.y){
winWrap.classList.remove('hidden');
}
}
function checkWin(){
const remaining = state.terminals.filter(t=>t.active).length;
if (remaining===0){
state.exitOpen = true;
hint("Exit is open! Find the green portal.", true);
}
}
/* ======================== RENDERING ======================== */
function draw(){
ctx.clearRect(0,0,W,H);
// Background grid glow
drawGridGlow();
// Draw level tiles
for (let r=0;r<ROWS;r++){
for (let c=0;c<COLS;c++){
const ch = LEVEL[r][c];
const x = c*TILE, y = r*TILE;
if (ch === '#') {
drawWall(x,y);
} else {
drawFloor(x,y);
}
}
}
// Draw terminals
state.terminals.forEach(t=>{
if (!t.active) { drawInactiveTerminal(t.x,t.y); }
else { drawActiveTerminal(t.x,t.y); }
});
// Draw exit
drawExit(state.exit.x, state.exit.y, state.exitOpen);
// Draw player
drawPlayer(state.player.px, state.player.py);
// Debug / hints
if (!state.started){
hint("Press Enter the Maze to begin", true);
}
}
function drawGridGlow(){
const step = TILE;
ctx.save();
ctx.globalAlpha = .06;
ctx.strokeStyle = '#6ba3ff';
ctx.lineWidth = 1;
for (let x=0; x<=W; x+=step){
ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,H); ctx.stroke();
}
for (let y=0; y<=H; y+=step){
ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(W,y); ctx.stroke();
}
ctx.restore();
}
function drawWall(x,y){
const g = ctx.createLinearGradient(x,y,x,y+TILE);
g.addColorStop(0, '#1d2a5a');
g.addColorStop(1, '#111c43');
ctx.fillStyle = g;
ctx.fillRect(x,y,TILE,TILE);
// edge
ctx.strokeStyle = 'rgba(255,255,255,.06)';
ctx.strokeRect(x+.5,y+.5,TILE-1,TILE-1);
}
function drawFloor(x,y){
const g = ctx.createLinearGradient(x,y,x+TILE,y+TILE);
g.addColorStop(0, '#0e1738');
g.addColorStop(1, '#0b122f');
ctx.fillStyle = g;
ctx.fillRect(x,y,TILE,TILE);
// soft bloom dots
ctx.fillStyle = 'rgba(80,120,255,.08)';
ctx.beginPath(); ctx.arc(x+TILE*0.3, y+TILE*0.3, 3, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(x+TILE*0.75, y+TILE*0.7, 2.5, 0, Math.PI*2); ctx.fill();
}
function drawActiveTerminal(cx,cy){
const x = cx*TILE + TILE/2, y = cy*TILE + TILE/2;
// glow
ctx.save();
ctx.shadowBlur = 18; ctx.shadowColor = '#63ccff';
ctx.fillStyle = '#2df0ff';
ctx.beginPath(); ctx.arc(x,y,9,0,Math.PI*2); ctx.fill();
ctx.restore();
// ring
ctx.strokeStyle = 'rgba(99,204,255,.6)';
ctx.lineWidth = 2;
ctx.beginPath(); ctx.arc(x,y,14,0,Math.PI*2); ctx.stroke();
}
function drawInactiveTerminal(cx,cy){
const x = cx*TILE + TILE/2, y = cy*TILE + TILE/2;
ctx.fillStyle = '#3c8b92';
ctx.beginPath(); ctx.arc(x,y,8,0,Math.PI*2); ctx.fill();
ctx.strokeStyle = 'rgba(60,139,146,.6)'; ctx.lineWidth=2;
ctx.beginPath(); ctx.arc(x,y,13,0,Math.PI*2); ctx.stroke();
}
function drawExit(cx,cy,open){
const x = cx*TILE, y = cy*TILE;
const g = ctx.createLinearGradient(x,y,x+TILE,y+TILE);
if (open) { g.addColorStop(0, '#21d08a'); g.addColorStop(1, '#0c8f60'); }
else { g.addColorStop(0, '#165d42'); g.addColorStop(1, '#0a3b29'); }
ctx.fillStyle = g; ctx.fillRect(x,y,TILE,TILE);
ctx.strokeStyle = open ? 'rgba(40,255,160,.8)' : 'rgba(80,180,140,.6)';
ctx.lineWidth = 3;
ctx.strokeRect(x+4, y+4, TILE-8, TILE-8);
}
function drawPlayer(px,py){
// shadow
ctx.fillStyle = 'rgba(0,0,0,.35)';
ctx.beginPath(); ctx.ellipse(px, py+6, 8, 4, 0, 0, Math.PI*2); ctx.fill();
// body
const g = ctx.createRadialGradient(px-4,py-4,2, px,py,14);
g.addColorStop(0, '#8ad4ff');
g.addColorStop(1, '#3b79ff');
ctx.fillStyle = g;
ctx.beginPath(); ctx.arc(px,py,10,0,Math.PI*2); ctx.fill();
// face-line
ctx.strokeStyle = 'rgba(255,255,255,.8)';
ctx.lineWidth = 2;
ctx.beginPath(); ctx.arc(px,py,6, Math.PI*.05, Math.PI*.95); ctx.stroke();
}
/* ======================== GAME LOOP ======================== */
let last = performance.now();
function loop(now){
const dt = Math.min((now-last)/1000, 0.033);
last = now;
update(dt);
draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
function update(dt){
if (!state.started) return;
const p = state.player;
const speed = (keys['ShiftLeft'] || keys['ShiftRight']) ? p.speed*1.5 : p.speed;
let dx=0, dy=0;
if (keys['KeyW'] || keys['ArrowUp']) dy -= speed*dt;
if (keys['KeyS'] || keys['ArrowDown']) dy += speed*dt;
if (keys['KeyA'] || keys['ArrowLeft']) dx -= speed*dt;
if (keys['KeyD'] || keys['ArrowRight']) dx += speed*dt;
// attempt move with collision
tryMove(dx, 0);
tryMove(0, dy);
}
function isWallAt(px,py){
const c = Math.floor(px / TILE);
const r = Math.floor(py / TILE);
if (r<0||r>=ROWS||c<0||c>=COLS) return true;
return LEVEL[r][c] === '#';
}
function tryMove(dx,dy){
const p = state.player;
const nx = p.px + dx;
const ny = p.py + dy;
// collision radius
const rad = 12;
// test 4 corners
const testPoints = [
[nx - rad, ny - rad],
[nx + rad, ny - rad],
[nx - rad, ny + rad],
[nx + rad, ny + rad]
];
for (const [tx,ty] of testPoints){
if (isWallAt(tx,ty)) return; // abort move on collision
}
p.px = nx; p.py = ny;
}
function playerCell(){
return { cx: Math.floor(state.player.px / TILE),
cy: Math.floor(state.player.py / TILE) };
}
/* ======================== UX HELPERS ======================== */
function hint(text, pulse=false){
hintEl.textContent = text || '';
hintEl.classList.toggle('pulse', !!pulse);
}
/* ======================== INTERACTION ======================== */
function near(a,b){ return a.cx===b.x && a.cy===b.y; }
</script>
</body>
</html>