Skill: integers
Integer Sprint
90 seconds, 3 lives. Race through integer operations and negative-number order of operations.
Integer Sprint
90 seconds, 3 lives. Race through integer operations and negative-number order of operations.
- 1You have 90 seconds and 3 lives.
- 2Each correct answer scores 10 points, plus a streak bonus up to +50.
- 3A wrong answer costs one life and resets your streak.
- 4The sprint ends when time runs out or all lives are gone.
-2. Use the hyphen-minus key.// __QCORE_BEGIN__
var QGEN = (function(){
'use strict';
function ri(rng, lo, hi){ return lo + Math.floor(rng() * (hi - lo + 1)); }
function pick(rng, a){ return a[Math.floor(rng() * a.length)]; }
function sgn(n){ return n < 0 ? '(' + n + ')' : String(n); }
function mhtml(s){ return '' + s + ''; }
/* recursive-descent evaluator for integer expressions with ^ and unary minus */
function makeParser(toks){
var pos = 0;
function peek(){ return pos < toks.length ? toks[pos] : null; }
function next(){ return toks[pos++]; }
function parseE(){
var v = parseT();
while (peek() === '+' || peek() === '-'){ var op = next(); var r = parseT(); v = op === '+' ? v + r : v - r; }
return v;
}
function parseT(){
var v = parseF();
while (peek() === '*' || peek() === '/'){ var op = next(); var r = parseF(); v = op === '*' ? v * r : v / r; }
return v;
}
function parseF(){
if (peek() === '-' || peek() === '+'){ var op = next(); var v = parseF(); return op === '-' ? -v : v; }
return parseP();
}
function parseP(){
var base = parseA();
if (peek() === '^'){ next(); var e = parseF(); return Math.pow(base, e); }
return base;
}
function parseA(){
var t = next();
if (t === '('){ var v = parseE(); next(); return v; }
return parseFloat(t);
}
return {parse:function(){ var v = parseE(); return v; }};
}
function tokenize(s){
var toks = [], i = 0;
while (i < s.length){
var c = s[i];
if (c === ' '){ i++; continue; }
if ('+-*/^()'.indexOf(c) >= 0){ toks.push(c); i++; continue; }
var j = i;
while (j < s.length){
if (!/[0-9.]/.test(s[j])) break;
j++;
}
toks.push(s.slice(i, j)); i = j;
}
return toks;
}
function evalExpr(s){ return makeParser(tokenize(s)).parse(); }
var T = [];
function nz(rng, lo, hi){ var v = 0; while (v === 0) v = ri(rng, lo, hi); return v; }
/* easy: add integers */
T.push({id:'add', name:'Add integers', level:'easy', gen:function(rng){
var a = nz(rng, -9, 9), b = nz(rng, -9, 9);
var ans = a + b;
return {q:'Compute: ' + mhtml(sgn(a) + ' + ' + sgn(b)), a:String(ans), av:ans, alt:[],
data:{kind:'add', a:a, b:b},
hint:'Same sign: add and keep it. Different signs: subtract and keep the bigger one\'s sign.',
steps:['Signs: ' + (a < 0 === b < 0 ? 'same — add the magnitudes' : 'different — subtract the magnitudes') + '.',
'Answer: ' + ans + '']};
}});
/* easy: subtract integers */
T.push({id:'sub', name:'Subtract integers', level:'easy', gen:function(rng){
var a = nz(rng, -9, 9), b = nz(rng, -9, 9);
var ans = a - b;
var rw = b < 0 ? sgn(a) + ' + ' + (-b) : null;
return {q:'Compute: ' + mhtml(sgn(a) + ' − ' + sgn(b)), a:String(ans), av:ans, alt:[],
data:{kind:'sub', a:a, b:b},
hint:'Rewrite as adding the opposite: a − b = a + (−b).',
steps:[b < 0 ? 'Minus a negative: ' + mhtml(sgn(a) + ' − ' + sgn(b) + ' = ' + sgn(a) + ' + ' + (-b)) : 'Add the opposite: ' + mhtml(sgn(a) + ' + (' + (-b) + ')'),
'Answer: ' + ans + '']};
}});
/* easy: absolute value */
T.push({id:'abs', name:'Absolute value', level:'easy', gen:function(rng){
var x = nz(rng, -12, 12);
var ans = Math.abs(x);
return {q:'Compute: ' + mhtml('|' + x + '|'), a:String(ans), av:ans, alt:[],
data:{kind:'abs', x:x},
hint:'Absolute value is distance from zero — never negative.',
steps:['|' + x + '| is the distance from ' + x + ' to 0.',
'Answer: ' + ans + '']};
}});
/* medium: multiply integers */
T.push({id:'mul', name:'Multiply integers', level:'medium', gen:function(rng){
var a = nz(rng, -9, 9), b = nz(rng, -9, 9);
var ans = a * b;
return {q:'Compute: ' + mhtml(sgn(a) + ' × ' + sgn(b)), a:String(ans), av:ans, alt:[],
data:{kind:'mul', a:a, b:b},
hint:'Same signs → positive. Different signs → negative.',
steps:['Signs: ' + ((a < 0) === (b < 0) ? 'same → positive' : 'different → negative') + '.',
'Magnitudes: ' + Math.abs(a) + ' × ' + Math.abs(b) + ' = ' + Math.abs(ans) + '.',
'Answer: ' + ans + '']};
}});
/* medium: divide integers (exact) */
T.push({id:'div', name:'Divide integers', level:'medium', gen:function(rng){
var b = nz(rng, -9, 9);
if (b === 1 || b === -1) b = pick(rng, [2, -2, 3, -3]);
var q = nz(rng, -9, 9), a = b * q;
return {q:'Compute: ' + mhtml(sgn(a) + ' ÷ ' + sgn(b)), a:String(q), av:q, alt:[],
data:{kind:'div', a:a, b:b},
hint:'Same signs → positive. Different signs → negative. Then divide the magnitudes.',
steps:['Signs: ' + ((a < 0) === (b < 0) ? 'same → positive' : 'different → negative') + '.',
'Magnitudes: ' + Math.abs(a) + ' ÷ ' + Math.abs(b) + ' = ' + Math.abs(q) + '.',
'Answer: ' + q + '']};
}});
/* hard: order of operations with negatives */
var PATS = [
function(rng){ var a = nz(rng,-6,6), b = nz(rng,-6,6), c = nz(rng,-6,6);
return {e: a + '+' + b + '*' + c, d:'Multiply ' + sgn(b) + ' × ' + sgn(c) + ' first.'}; },
function(rng){ var a = nz(rng,-6,6), b = nz(rng,-6,6), c = nz(rng,-6,6);
return {e: '(' + a + '+' + b + ')*' + c, d:'Parentheses first: ' + sgn(a) + ' + ' + sgn(b) + '.'}; },
function(rng){ var a = nz(rng,-6,6), b = nz(rng,-6,6), c = nz(rng,-6,6), d = nz(rng,-6,6);
return {e: a + '-' + b + '*' + c + '+' + d, d:'Multiply before adding or subtracting.'}; },
function(rng){ var a = ri(rng, 2, 5);
return {e: '-(' + a + '^2)', d:'The exponent applies to ' + a + ' only, then the minus.'}; },
function(rng){ var a = ri(rng, 2, 5);
return {e: '(-' + a + ')^2', d:'Parentheses hand the exponent the whole (−' + a + ').'}; }
];
T.push({id:'ooo-neg', name:'Order of operations with negatives', level:'hard', gen:function(rng){
var p = pick(rng, PATS)(rng);
var ans = evalExpr(p.e);
var disp = p.e.replace(/\*/g, '×').replace(/\^/g, '^').replace(/-/g, '−');
return {q:'Evaluate: ' + mhtml(disp), a:String(ans), av:ans, alt:[],
data:{kind:'expr', expr:p.e},
hint:p.d,
steps:['Follow PEMDAS — exponents and ×/÷ before +/−.',
'Answer: ' + ans + '']};
}});
function checkAnswer(user, canonical, av){
var s = String(user).trim().replace(/,/g, '');
if (!/^-?(\d+(\.\d+)?|\.\d+)$/.test(s)) return false;
if (av === null || av === undefined) return false;
return Math.abs(parseFloat(s) - av) < 1e-9;
}
return {templates:T, checkAnswer:checkAnswer, evalExpr:evalExpr};
})();
// __QCORE_END__
// __GAME_BEGIN__
var GAME_API = (function(){
'use strict';
var DURATION = 90;
function create(){
return {score:0, streak:0, best:0, lives:3, answered:0, correct:0, over:false, reason:''};
}
function onCorrect(s){
s.answered++; s.correct++; s.streak++;
if (s.streak > s.best) s.best = s.streak;
s.score += 10 + 5 * Math.min(s.streak - 1, 8);
return s;
}
function onWrong(s){
s.answered++; s.streak = 0; s.lives--;
if (s.lives <= 0){ s.over = true; s.reason = 'lives'; }
return s;
}
function onTimeout(s){ s.over = true; s.reason = 'time'; return s; }
function accuracy(s){ return s.answered === 0 ? 0 : Math.round(100 * s.correct / s.answered); }
return {create:create, onCorrect:onCorrect, onWrong:onWrong, onTimeout:onTimeout,
accuracy:accuracy, DURATION:DURATION};
})();
// __GAME_END__
/* Shared arcade engine (Wave 2C). Requires QGEN and GAME_API globals. */ (function(){ 'use strict'; var S = null, cur = null, curT = null, lastIdx = -1, timerId = null, timeLeft = 0; function $(id){ return document.getElementById(id); } function esc(s){ return String(s).replace(/\x26/g,'&').replace(//g,'>'); } function showScreen(id){ document.querySelectorAll('.screen').forEach(function(el){ el.classList.remove('show'); }); $(id).classList.add('show'); } function hearts(n){ var out = ''; for (var i = 0; i < 3; i++){ out += ''; } return out; } function hud(){ $('hScore').textContent = S.score; $('hStreak').textContent = S.streak; $('hLives').innerHTML = hearts(S.lives); $('hTime').textContent = timeLeft; var tb = $('hTimeBox'); if (timeLeft <= 15) tb.classList.add('timer-low'); else tb.classList.remove('timer-low'); var pct = Math.max(0, Math.min(100, 100 * timeLeft / GAME_API.DURATION)); $('timeFill').style.width = pct + '%'; } function newQ(){ var Ts = QGEN.templates, i = 0, guard = 0; if (Ts.length > 1){ i = Math.floor(Math.random() * Ts.length); for (guard = 0; guard < 11; guard++){ if (i !== lastIdx) break; i = Math.floor(Math.random() * Ts.length); } lastIdx = i; } curT = Ts[i]; cur = curT.gen(Math.random); $('qType').textContent = curT.name; $('qPrompt').innerHTML = cur.q; $('gAnswer').value = ''; $('gAnswer').disabled = false; hideFb(); $('gAnswer').focus(); } function showFb(ok, title, why){ var f = $('gFb'); f.className = 'feedback show ' + (ok ? 'correct' : 'wrong'); f.innerHTML = '
'; } function hideFb(){ var f = $('gFb'); f.className = 'feedback'; f.innerHTML = ''; } function submit(){ if (!S || S.over) return; var user = $('gAnswer').value; if (!user.trim()) return; var good = false; try { good = QGEN.checkAnswer(user, cur.a, cur.av); } catch(e){ good = false; } if (good){ GAME_API.onCorrect(S); showFb(true, 'Correct! +' + (10 + 5 * Math.min(S.streak - 1, 8)) + ' pts', 'Answer: ' + esc(cur.a) + ''); } else { GAME_API.onWrong(S); showFb(false, 'Missed — the answer was ' + esc(cur.a), cur.hint); } hud(); if (S.over){ endGame(); return; } $('gAnswer').disabled = true; setTimeout(function(){ if (!S.over) newQ(); }, good ? 650 : 1400); } function tick(){ timeLeft--; hud(); if (timeLeft <= 0){ GAME_API.onTimeout(S); endGame(); } } function start(){ S = GAME_API.create(); timeLeft = GAME_API.DURATION; showScreen('scrPlay'); hud(); newQ(); if (timerId) clearInterval(timerId); timerId = setInterval(tick, 1000); } function endGame(){ if (timerId){ clearInterval(timerId); timerId = null; } showScreen('scrOver'); var reason = S.reason === 'time' ? 'Time ran out.' : 'Out of lives.'; $('overReason').textContent = reason + ' Scores are session-only and reset when you start again.'; $('stScore').textContent = S.score; $('stCorrect').textContent = S.correct + '/' + S.answered; $('stStreak').textContent = S.best; $('stAcc').textContent = GAME_API.accuracy(S) + '%'; } document.addEventListener('DOMContentLoaded', function(){ $('gStart').addEventListener('click', start); $('gAgain').addEventListener('click', start); $('gSubmit').addEventListener('click', submit); $('gAnswer').addEventListener('keydown', function(e){ if (e.key === 'Enter') submit(); }); }); })();