/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Answer Button Class
var AnswerButton = Container.expand(function () {
var self = Container.call(this);
// Attach default button asset
var btn = self.attachAsset('answerBtn', {
anchorX: 0.5,
anchorY: 0.5
});
// Answer text
self.txt = self.addChild(new Text2('', {
size: 120,
fill: 0xFFFFFF
}));
self.txt.anchor.set(0.5, 0.5);
// Value this button represents
self.value = null;
// State: 'default', 'correct', 'wrong'
self.state = 'default';
// Set answer value and text
self.setValue = function (val) {
self.value = val;
self.txt.setText(val + '');
};
// Set state for visual feedback
self.setState = function (state) {
self.state = state;
if (state === 'default') {
btn.assetId = 'answerBtn';
btn.setAsset('answerBtn');
} else if (state === 'correct') {
btn.assetId = 'answerBtnCorrect';
btn.setAsset('answerBtnCorrect');
} else if (state === 'wrong') {
btn.assetId = 'answerBtnWrong';
btn.setAsset('answerBtnWrong');
}
};
// Reset to default state
self.reset = function () {
self.setState('default');
};
// Touch event handler
self.down = function (x, y, obj) {
if (typeof self.onSelect === 'function') {
self.onSelect(self.value, self);
}
};
return self;
});
// Question Box Class
var QuestionBox = Container.expand(function () {
var self = Container.call(this);
var box = self.attachAsset('questionBox', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7 // slightly translucent to show the colorful background
});
self.txt = self.addChild(new Text2('', {
size: 150,
fill: "#fff",
font: "Comic Sans MS, 'Chalkboard', 'GillSans-Bold', Impact, 'Arial Black', Tahoma" // Chalk-like font
}));
self.txt.anchor.set(0.5, 0.5);
self.setQuestion = function (q) {
self.txt.setText(q);
};
return self;
});
// Timer Bar Class
var TimerBar = Container.expand(function () {
var self = Container.call(this);
// The bar background
var bar = self.attachAsset('timerBar', {
anchorX: 0.5,
anchorY: 0.5
});
// The fill bar (will be scaled)
self.fill = self.attachAsset('timerBar', {
anchorX: 0.5,
anchorY: 0.5
});
self.fill.tint = 0x50E3C2;
self.fill.y = 0;
self.fill.x = 0;
// Set progress (0 to 1)
self.setProgress = function (p) {
if (p < 0) p = 0;
if (p > 1) p = 1;
self.fill.scaleX = p;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Always backgroundColor is black
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game variables
// Math Dash: Quick Solve uses simple shapes for answer buttons and question area.
// Colors are chosen for visual clarity and engagement.
var score = 0;
var question = null;
var correctAnswer = null;
var answerChoices = [];
var answerButtons = [];
var questionBox = null;
var timerBar = null;
var timer = null;
var timePerQuestion = 3500; // ms, will decrease as difficulty increases
var timeLeft = 0;
var questionStartTime = 0;
var scoreTxt = null;
var gameActive = true;
var difficultyLevel = 1; // Increases as player scores
// Layout positions
var centerX = 2048 / 2;
var questionY = 500;
var answersStartY = 1100;
var answerSpacing = 300;
// Add a lively image background (full screen, behind everything)
var livelyBg = LK.getAsset('livelyBgImage', {
anchorX: 0.5,
anchorY: 0,
width: 2048,
height: 2732
});
livelyBg.x = centerX;
livelyBg.y = 0;
game.addChild(livelyBg);
// Create and add question box (visually on the board)
questionBox = new QuestionBox();
questionBox.x = centerX;
questionBox.y = questionY + 40;
game.addChild(questionBox);
// Create and add timer bar (below the board)
timerBar = new TimerBar();
timerBar.x = centerX;
timerBar.y = questionY + 180 + 40;
game.addChild(timerBar);
// Add a numeric countdown timer in the top-right corner (not in top left 100x100)
var timerTxt = new Text2('', {
size: 90,
fill: "#fff"
});
timerTxt.anchor.set(1, 0); // right-top
// Place at top-right, leaving a margin from the edge and out of the top-left 100x100 area
timerTxt.x = 2048 - 60;
timerTxt.y = 40;
LK.gui.top.addChild(timerTxt);
// Create answer buttons (4 choices)
for (var i = 0; i < 4; i++) {
var btn = new AnswerButton();
btn.x = centerX;
btn.y = answersStartY + i * answerSpacing;
btn.idx = i;
btn.onSelect = handleAnswer;
answerButtons.push(btn);
game.addChild(btn);
}
// Score text (top center, not in top left 100x100)
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Generate a new math question
function generateQuestion() {
// Increase difficulty every 10 questions
difficultyLevel = 1 + Math.floor(score / 10);
// Make number ranges and distractors harder as difficulty increases
var ops, op, min, max;
if (score >= 10) {
// After 10 questions, make numbers even larger and allow more complex distractors
ops = ['+', '-', '×', '÷'];
op = ops[randInt(0, ops.length - 1)];
min = 10 + difficultyLevel * 2;
max = 40 + difficultyLevel * 12;
} else {
// Stage 1: simpler questions, only + and -, smaller numbers
ops = ['+', '-'];
op = ops[randInt(0, ops.length - 1)];
min = 1;
max = 10 + difficultyLevel * 4; // smaller range for easier questions
}
var a, b, q, ans;
if (op === '+') {
a = randInt(min, max);
b = randInt(min, max);
ans = a + b;
q = a + " + " + b + " = ?";
} else if (op === '-') {
a = randInt(min, max);
b = randInt(min, a); // ensure non-negative
ans = a - b;
q = a + " - " + b + " = ?";
} else if (op === '×') {
a = randInt(min, max - 5);
b = randInt(2, 20 + Math.floor(difficultyLevel)); // allow larger multipliers after 10 questions
ans = a * b;
q = a + " × " + b + " = ?";
} else if (op === '÷') {
b = randInt(2, 20 + Math.floor(difficultyLevel));
ans = randInt(min, max - 5);
a = ans * b;
q = a + " ÷ " + b + " = ?";
}
correctAnswer = ans;
question = q;
// Generate 3 wrong answers, some close, some far, some with swapped digits for more challenge
var wrongs = [];
while (wrongs.length < 3) {
var delta;
var wrong;
if (score >= 10) {
// After 10 questions, make distractors more challenging
if (wrongs.length === 0) {
// close distractor, but allow negative and larger deltas
delta = randInt(2, 7 + Math.floor(difficultyLevel));
wrong = ans + (Math.random() > 0.5 ? delta : -delta);
} else if (wrongs.length === 1) {
// far distractor, even further
delta = randInt(10 + difficultyLevel, 30 + difficultyLevel * 2);
wrong = ans + (Math.random() > 0.5 ? delta : -delta);
} else {
// digit swap, off-by-two, or reversed number
wrong = ans;
if (ans > 9 && Math.random() > 0.33) {
var s = ans.toString().split('');
if (Math.random() > 0.5 && s.length > 2) {
// reverse digits
s = s.reverse();
wrong = parseInt(s.join(''));
} else {
// swap two random digits
var i = randInt(0, s.length - 2);
var tmp = s[i];
s[i] = s[i + 1];
s[i + 1] = tmp;
wrong = parseInt(s.join(''));
}
} else {
wrong = ans + (Math.random() > 0.5 ? 2 : -2);
}
}
} else {
if (wrongs.length === 0) {
// close distractor
delta = randInt(1, 3 + Math.floor(difficultyLevel / 2));
wrong = ans + (Math.random() > 0.5 ? delta : -delta);
} else if (wrongs.length === 1) {
// far distractor
delta = randInt(5 + difficultyLevel, 15 + difficultyLevel * 2);
wrong = ans + (Math.random() > 0.5 ? delta : -delta);
} else {
// digit swap or off-by-one error
wrong = ans;
if (ans > 9 && Math.random() > 0.5) {
var s = ans.toString().split('');
var i = randInt(0, s.length - 2);
var tmp = s[i];
s[i] = s[i + 1];
s[i + 1] = tmp;
wrong = parseInt(s.join(''));
} else {
wrong = ans + (Math.random() > 0.5 ? 1 : -1);
}
}
}
if (wrong !== ans && wrong >= 0 && wrongs.indexOf(wrong) === -1) {
wrongs.push(wrong);
}
}
// Shuffle answers
answerChoices = wrongs.concat([ans]);
shuffleArray(answerChoices);
// Set question and answers
questionBox.setQuestion(question);
for (var i = 0; i < 4; i++) {
answerButtons[i].setValue(answerChoices[i]);
answerButtons[i].reset();
}
}
// Handle answer selection
function handleAnswer(val, btn) {
if (!gameActive) return;
if (val === correctAnswer) {
btn.setState('correct');
LK.getSound('correct').play();
score += 1;
LK.setScore(score);
scoreTxt.setText(score + '');
// Animate button
tween(btn, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
// Check win condition: 10 correct answers
if (score >= 10) {
gameActive = false;
LK.effects.flashScreen(0x7ed321, 800);
LK.setTimeout(function () {
LK.showYouWin();
}, 600);
} else {
// Next question after short delay
LK.setTimeout(nextQuestion, 350);
}
} else {
btn.setState('wrong');
LK.getSound('wrong').play();
// Decrease score by 1, but not below zero
score = Math.max(0, score - 1);
LK.setScore(score);
scoreTxt.setText(score + '');
// Flash wrong, then game over
tween(btn, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 120,
onFinish: function onFinish() {
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
endGame();
}
}
// Start next question
function nextQuestion() {
if (!gameActive) return;
// Increase speed as score increases
if (score >= 10) {
// After 10 questions, shorten by 1 second (1000ms)
timePerQuestion = Math.max(1200, 2500 - (score - 10) * 80);
} else {
timePerQuestion = Math.max(1200, 3500 - score * 80);
}
timeLeft = timePerQuestion;
questionStartTime = Date.now();
generateQuestion();
}
// End game
function endGame() {
if (!gameActive) return;
gameActive = false;
// Flash screen red
LK.effects.flashScreen(0xD0021B, 800);
// Show game over (handled by LK)
LK.setTimeout(function () {
LK.showGameOver();
}, 600);
}
// Utility: random integer [min, max]
function randInt(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
// Utility: shuffle array in-place
function shuffleArray(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = randInt(0, i);
var t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
// Game update loop
game.update = function () {
if (!gameActive) return;
// Update timer
var now = Date.now();
timeLeft = timePerQuestion - (now - questionStartTime);
var progress = timeLeft / timePerQuestion;
timerBar.setProgress(progress);
// Update numeric timer to show time left (countdown) in seconds, min 0
if (typeof timerTxt !== 'undefined') {
var left = Math.max(0, timeLeft);
var secs = Math.round(left / 100) / 10;
timerTxt.setText(secs.toFixed(1) + 's');
}
// If time runs out
if (timeLeft <= 0) {
// Flash all buttons red
for (var i = 0; i < 4; i++) {
answerButtons[i].setState('wrong');
}
endGame();
}
};
// Start first question
function startGame() {
score = 0;
LK.setScore(0);
scoreTxt.setText('0');
gameActive = true;
difficultyLevel = 1;
nextQuestion();
}
// On game reset, start again
startGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Answer Button Class
var AnswerButton = Container.expand(function () {
var self = Container.call(this);
// Attach default button asset
var btn = self.attachAsset('answerBtn', {
anchorX: 0.5,
anchorY: 0.5
});
// Answer text
self.txt = self.addChild(new Text2('', {
size: 120,
fill: 0xFFFFFF
}));
self.txt.anchor.set(0.5, 0.5);
// Value this button represents
self.value = null;
// State: 'default', 'correct', 'wrong'
self.state = 'default';
// Set answer value and text
self.setValue = function (val) {
self.value = val;
self.txt.setText(val + '');
};
// Set state for visual feedback
self.setState = function (state) {
self.state = state;
if (state === 'default') {
btn.assetId = 'answerBtn';
btn.setAsset('answerBtn');
} else if (state === 'correct') {
btn.assetId = 'answerBtnCorrect';
btn.setAsset('answerBtnCorrect');
} else if (state === 'wrong') {
btn.assetId = 'answerBtnWrong';
btn.setAsset('answerBtnWrong');
}
};
// Reset to default state
self.reset = function () {
self.setState('default');
};
// Touch event handler
self.down = function (x, y, obj) {
if (typeof self.onSelect === 'function') {
self.onSelect(self.value, self);
}
};
return self;
});
// Question Box Class
var QuestionBox = Container.expand(function () {
var self = Container.call(this);
var box = self.attachAsset('questionBox', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7 // slightly translucent to show the colorful background
});
self.txt = self.addChild(new Text2('', {
size: 150,
fill: "#fff",
font: "Comic Sans MS, 'Chalkboard', 'GillSans-Bold', Impact, 'Arial Black', Tahoma" // Chalk-like font
}));
self.txt.anchor.set(0.5, 0.5);
self.setQuestion = function (q) {
self.txt.setText(q);
};
return self;
});
// Timer Bar Class
var TimerBar = Container.expand(function () {
var self = Container.call(this);
// The bar background
var bar = self.attachAsset('timerBar', {
anchorX: 0.5,
anchorY: 0.5
});
// The fill bar (will be scaled)
self.fill = self.attachAsset('timerBar', {
anchorX: 0.5,
anchorY: 0.5
});
self.fill.tint = 0x50E3C2;
self.fill.y = 0;
self.fill.x = 0;
// Set progress (0 to 1)
self.setProgress = function (p) {
if (p < 0) p = 0;
if (p > 1) p = 1;
self.fill.scaleX = p;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Always backgroundColor is black
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game variables
// Math Dash: Quick Solve uses simple shapes for answer buttons and question area.
// Colors are chosen for visual clarity and engagement.
var score = 0;
var question = null;
var correctAnswer = null;
var answerChoices = [];
var answerButtons = [];
var questionBox = null;
var timerBar = null;
var timer = null;
var timePerQuestion = 3500; // ms, will decrease as difficulty increases
var timeLeft = 0;
var questionStartTime = 0;
var scoreTxt = null;
var gameActive = true;
var difficultyLevel = 1; // Increases as player scores
// Layout positions
var centerX = 2048 / 2;
var questionY = 500;
var answersStartY = 1100;
var answerSpacing = 300;
// Add a lively image background (full screen, behind everything)
var livelyBg = LK.getAsset('livelyBgImage', {
anchorX: 0.5,
anchorY: 0,
width: 2048,
height: 2732
});
livelyBg.x = centerX;
livelyBg.y = 0;
game.addChild(livelyBg);
// Create and add question box (visually on the board)
questionBox = new QuestionBox();
questionBox.x = centerX;
questionBox.y = questionY + 40;
game.addChild(questionBox);
// Create and add timer bar (below the board)
timerBar = new TimerBar();
timerBar.x = centerX;
timerBar.y = questionY + 180 + 40;
game.addChild(timerBar);
// Add a numeric countdown timer in the top-right corner (not in top left 100x100)
var timerTxt = new Text2('', {
size: 90,
fill: "#fff"
});
timerTxt.anchor.set(1, 0); // right-top
// Place at top-right, leaving a margin from the edge and out of the top-left 100x100 area
timerTxt.x = 2048 - 60;
timerTxt.y = 40;
LK.gui.top.addChild(timerTxt);
// Create answer buttons (4 choices)
for (var i = 0; i < 4; i++) {
var btn = new AnswerButton();
btn.x = centerX;
btn.y = answersStartY + i * answerSpacing;
btn.idx = i;
btn.onSelect = handleAnswer;
answerButtons.push(btn);
game.addChild(btn);
}
// Score text (top center, not in top left 100x100)
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Generate a new math question
function generateQuestion() {
// Increase difficulty every 10 questions
difficultyLevel = 1 + Math.floor(score / 10);
// Make number ranges and distractors harder as difficulty increases
var ops, op, min, max;
if (score >= 10) {
// After 10 questions, make numbers even larger and allow more complex distractors
ops = ['+', '-', '×', '÷'];
op = ops[randInt(0, ops.length - 1)];
min = 10 + difficultyLevel * 2;
max = 40 + difficultyLevel * 12;
} else {
// Stage 1: simpler questions, only + and -, smaller numbers
ops = ['+', '-'];
op = ops[randInt(0, ops.length - 1)];
min = 1;
max = 10 + difficultyLevel * 4; // smaller range for easier questions
}
var a, b, q, ans;
if (op === '+') {
a = randInt(min, max);
b = randInt(min, max);
ans = a + b;
q = a + " + " + b + " = ?";
} else if (op === '-') {
a = randInt(min, max);
b = randInt(min, a); // ensure non-negative
ans = a - b;
q = a + " - " + b + " = ?";
} else if (op === '×') {
a = randInt(min, max - 5);
b = randInt(2, 20 + Math.floor(difficultyLevel)); // allow larger multipliers after 10 questions
ans = a * b;
q = a + " × " + b + " = ?";
} else if (op === '÷') {
b = randInt(2, 20 + Math.floor(difficultyLevel));
ans = randInt(min, max - 5);
a = ans * b;
q = a + " ÷ " + b + " = ?";
}
correctAnswer = ans;
question = q;
// Generate 3 wrong answers, some close, some far, some with swapped digits for more challenge
var wrongs = [];
while (wrongs.length < 3) {
var delta;
var wrong;
if (score >= 10) {
// After 10 questions, make distractors more challenging
if (wrongs.length === 0) {
// close distractor, but allow negative and larger deltas
delta = randInt(2, 7 + Math.floor(difficultyLevel));
wrong = ans + (Math.random() > 0.5 ? delta : -delta);
} else if (wrongs.length === 1) {
// far distractor, even further
delta = randInt(10 + difficultyLevel, 30 + difficultyLevel * 2);
wrong = ans + (Math.random() > 0.5 ? delta : -delta);
} else {
// digit swap, off-by-two, or reversed number
wrong = ans;
if (ans > 9 && Math.random() > 0.33) {
var s = ans.toString().split('');
if (Math.random() > 0.5 && s.length > 2) {
// reverse digits
s = s.reverse();
wrong = parseInt(s.join(''));
} else {
// swap two random digits
var i = randInt(0, s.length - 2);
var tmp = s[i];
s[i] = s[i + 1];
s[i + 1] = tmp;
wrong = parseInt(s.join(''));
}
} else {
wrong = ans + (Math.random() > 0.5 ? 2 : -2);
}
}
} else {
if (wrongs.length === 0) {
// close distractor
delta = randInt(1, 3 + Math.floor(difficultyLevel / 2));
wrong = ans + (Math.random() > 0.5 ? delta : -delta);
} else if (wrongs.length === 1) {
// far distractor
delta = randInt(5 + difficultyLevel, 15 + difficultyLevel * 2);
wrong = ans + (Math.random() > 0.5 ? delta : -delta);
} else {
// digit swap or off-by-one error
wrong = ans;
if (ans > 9 && Math.random() > 0.5) {
var s = ans.toString().split('');
var i = randInt(0, s.length - 2);
var tmp = s[i];
s[i] = s[i + 1];
s[i + 1] = tmp;
wrong = parseInt(s.join(''));
} else {
wrong = ans + (Math.random() > 0.5 ? 1 : -1);
}
}
}
if (wrong !== ans && wrong >= 0 && wrongs.indexOf(wrong) === -1) {
wrongs.push(wrong);
}
}
// Shuffle answers
answerChoices = wrongs.concat([ans]);
shuffleArray(answerChoices);
// Set question and answers
questionBox.setQuestion(question);
for (var i = 0; i < 4; i++) {
answerButtons[i].setValue(answerChoices[i]);
answerButtons[i].reset();
}
}
// Handle answer selection
function handleAnswer(val, btn) {
if (!gameActive) return;
if (val === correctAnswer) {
btn.setState('correct');
LK.getSound('correct').play();
score += 1;
LK.setScore(score);
scoreTxt.setText(score + '');
// Animate button
tween(btn, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
// Check win condition: 10 correct answers
if (score >= 10) {
gameActive = false;
LK.effects.flashScreen(0x7ed321, 800);
LK.setTimeout(function () {
LK.showYouWin();
}, 600);
} else {
// Next question after short delay
LK.setTimeout(nextQuestion, 350);
}
} else {
btn.setState('wrong');
LK.getSound('wrong').play();
// Decrease score by 1, but not below zero
score = Math.max(0, score - 1);
LK.setScore(score);
scoreTxt.setText(score + '');
// Flash wrong, then game over
tween(btn, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 120,
onFinish: function onFinish() {
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
endGame();
}
}
// Start next question
function nextQuestion() {
if (!gameActive) return;
// Increase speed as score increases
if (score >= 10) {
// After 10 questions, shorten by 1 second (1000ms)
timePerQuestion = Math.max(1200, 2500 - (score - 10) * 80);
} else {
timePerQuestion = Math.max(1200, 3500 - score * 80);
}
timeLeft = timePerQuestion;
questionStartTime = Date.now();
generateQuestion();
}
// End game
function endGame() {
if (!gameActive) return;
gameActive = false;
// Flash screen red
LK.effects.flashScreen(0xD0021B, 800);
// Show game over (handled by LK)
LK.setTimeout(function () {
LK.showGameOver();
}, 600);
}
// Utility: random integer [min, max]
function randInt(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
// Utility: shuffle array in-place
function shuffleArray(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = randInt(0, i);
var t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
// Game update loop
game.update = function () {
if (!gameActive) return;
// Update timer
var now = Date.now();
timeLeft = timePerQuestion - (now - questionStartTime);
var progress = timeLeft / timePerQuestion;
timerBar.setProgress(progress);
// Update numeric timer to show time left (countdown) in seconds, min 0
if (typeof timerTxt !== 'undefined') {
var left = Math.max(0, timeLeft);
var secs = Math.round(left / 100) / 10;
timerTxt.setText(secs.toFixed(1) + 's');
}
// If time runs out
if (timeLeft <= 0) {
// Flash all buttons red
for (var i = 0; i < 4; i++) {
answerButtons[i].setState('wrong');
}
endGame();
}
};
// Start first question
function startGame() {
score = 0;
LK.setScore(0);
scoreTxt.setText('0');
gameActive = true;
difficultyLevel = 1;
nextQuestion();
}
// On game reset, start again
startGame();