/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { coins: 0 }); /**** * Classes ****/ // Bowl class var Bowl = Container.expand(function () { var self = Container.call(this); // Attach bowl asset var bowlAsset = self.attachAsset('bowl', { anchorX: 0.5, anchorY: 0.5 }); self.bowlAsset = bowlAsset; self.isHinted = false; self.isRevealed = false; self.index = 0; // Which bowl in the array self.hasCoin = false; // For animating up/down self.lift = function (onFinish) { // Animate bowl up to reveal coin tween(self, { y: self.y - 120 }, { duration: 300, easing: tween.cubicOut, onFinish: onFinish }); }; self.lower = function (onFinish) { // Animate bowl down to cover coin tween(self, { y: self.y + 120 }, { duration: 300, easing: tween.cubicIn, onFinish: onFinish }); }; self.setHint = function (hinted) { if (hinted && !self.isHinted) { // Swap to hint asset self.removeChild(self.bowlAsset); self.bowlAsset = self.attachAsset('bowlHint', { anchorX: 0.5, anchorY: 0.5 }); self.isHinted = true; } else if (!hinted && self.isHinted) { self.removeChild(self.bowlAsset); self.bowlAsset = self.attachAsset('bowl', { anchorX: 0.5, anchorY: 0.5 }); self.isHinted = false; } }; // Touch event self.down = function (x, y, obj) { if (game.state === 'pick' && !self.isRevealed) { game.pickBowl(self.index); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2e294e }); /**** * Game Code ****/ // Bowls: Use ellipse shapes for bowls, different color for coin bowl highlight/hint // --- Game State Variables --- var bowls = []; var coinIndex = 0; var numBowls = 3; var round = 1; var hearts = 3; var coins = storage.coins || 0; var state = 'idle'; // 'idle', 'shuffling', 'pick', 'reveal', 'gameover' var shuffleCount = 0; var shufflePlan = []; var shuffleSpeed = 600; var coinSprite = null; var heartSprites = []; var hintBtn = null; var hintActive = false; var hintCost = 10; var scoreTxt = null; var roundTxt = null; var coinsTxt = null; var shuffleTimer = null; // --- GUI Elements --- scoreTxt = new Text2('Hearts: 3', { size: 80, fill: 0xFF4444 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); coinsTxt = new Text2('Coins: 0', { size: 80, fill: 0xFFE066 }); coinsTxt.anchor.set(0.5, 0); // Place coins text at top left, but offset to avoid menu (x=120) LK.gui.top.addChild(coinsTxt); coinsTxt.x = -2048 / 2 + 480; // Move even further right (480px from left edge) coinsTxt.y = 180; // Move even further down (180px from top) roundTxt = new Text2('Round 1', { size: 60, fill: 0xFFFFFF }); roundTxt.anchor.set(0.5, 0); LK.gui.top.addChild(roundTxt); roundTxt.y = 90; hintBtn = new Text2('Hint (10)', { size: 80, fill: 0x00E6E6 }); hintBtn.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(hintBtn); hintBtn.y = -100; hintBtn.interactive = true; hintBtn.buttonMode = true; hintBtn.down = function (x, y, obj) { if (game.state === 'pick' && !hintActive && coins >= hintCost) { coins -= hintCost; storage.coins = coins; updateCoins(); activateHint(); } }; // Add Reveal (50) button var revealBtn = new Text2('Reveal (50)', { size: 80, fill: 0xFF66CC }); revealBtn.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(revealBtn); revealBtn.y = 0; // Place below the hint button revealBtn.interactive = true; revealBtn.buttonMode = true; revealBtn.down = function (x, y, obj) { if (game.state === 'pick' && coins >= 50) { coins -= 50; storage.coins = coins; updateCoins(); // Reveal the correct bowl and show the coin for (var i = 0; i < bowls.length; i++) { bowls[i].isRevealed = true; if (bowls[i].hasCoin) { bowls[i].lift(); showCoinUnderBowl(i); LK.effects.flashObject(bowls[i], 0x00ff00, 600); } } game.state = 'reveal'; LK.getSound('reveal').play(); LK.getSound('win').play(); // Next round after short delay LK.setTimeout(function () { round += 1; setupRound(); }, 1200); } }; // --- Bottom Panel for Bowl Number Buttons --- var bowlButtons = []; function createBowlButtons(n) { // Remove old buttons for (var i = 0; i < bowlButtons.length; i++) { if (bowlButtons[i].parent) bowlButtons[i].parent.removeChild(bowlButtons[i]); } bowlButtons = []; // Spread buttons evenly at the bottom var spacing = 320; var totalWidth = (n - 1) * spacing; var startX = 0 - totalWidth / 2; for (var i = 0; i < n; i++) { var btn = new Text2('Bowl ' + (i + 1), { size: 90, fill: 0xffffff }); btn.anchor.set(0.5, 0.5); btn.x = startX + i * spacing; btn.y = -220; // Move panel above the hint text (which is at y = -100) btn.interactive = true; btn.buttonMode = true; (function (idx) { btn.down = function (x, y, obj) { if (game.state === 'pick') { game.pickBowl(idx); } }; })(i); LK.gui.bottom.addChild(btn); bowlButtons.push(btn); } } function removeBowlButtons() { for (var i = 0; i < bowlButtons.length; i++) { if (bowlButtons[i].parent) bowlButtons[i].parent.removeChild(bowlButtons[i]); } bowlButtons = []; } // --- Helper Functions --- function updateHearts() { scoreTxt.setText('Hearts: ' + hearts); } function updateCoins() { coinsTxt.setText('Coins: ' + coins); } function updateRound() { roundTxt.setText('Round ' + round); } function resetHint() { hintActive = false; for (var i = 0; i < bowls.length; i++) { bowls[i].setHint(false); } } function activateHint() { hintActive = true; // Find which half the coin is in, and hide the other half var n = bowls.length; var half = Math.floor(n / 2); // Always keep at least 2 bowls (if n==2, keep both) if (n <= 2) { bowls[coinIndex].setHint(true); LK.setTimeout(function () { resetHint(); }, 1200); return; } // Decide which half to keep // Sort bowls by x position (left to right) var sorted = []; for (var i = 0; i < bowls.length; i++) sorted.push(bowls[i]); sorted.sort(function (a, b) { return a.x - b.x; }); var coinPos = -1; for (var i = 0; i < sorted.length; i++) { if (sorted[i].index === coinIndex) { coinPos = i; break; } } var keepStart, keepEnd; if (coinPos < half) { // Keep left half keepStart = 0; keepEnd = half; } else { // Keep right half keepStart = n - half; keepEnd = n; } for (var i = 0; i < sorted.length; i++) { if (i >= keepStart && i < keepEnd) { sorted[i].visible = true; if (sorted[i].index === coinIndex) { sorted[i].setHint(true); } else { sorted[i].setHint(false); } } else { sorted[i].visible = false; } } // Remove hint and restore all bowls after 1.2s LK.setTimeout(function () { for (var i = 0; i < bowls.length; i++) { bowls[i].visible = true; } resetHint(); }, 1200); } function showCoinUnderBowl(idx) { // Place coin under bowl at idx var bowl = bowls[idx]; if (!coinSprite) { coinSprite = LK.getAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(coinSprite); } coinSprite.x = bowl.x; coinSprite.y = bowl.y + 100; coinSprite.visible = true; } function hideCoin() { if (coinSprite) coinSprite.visible = false; } function showAllBowls() { for (var i = 0; i < bowls.length; i++) { bowls[i].visible = true; } } function hideAllBowls() { for (var i = 0; i < bowls.length; i++) { bowls[i].visible = false; } } function showHearts() { // Remove old for (var i = 0; i < heartSprites.length; i++) { if (heartSprites[i].parent) heartSprites[i].parent.removeChild(heartSprites[i]); } heartSprites = []; // Show hearts at top left, but not in 0,0 (menu) for (var i = 0; i < hearts; i++) { var h = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5 }); h.x = 120 + i * 90; h.y = 120; game.addChild(h); heartSprites.push(h); } } function removeHearts() { for (var i = 0; i < heartSprites.length; i++) { if (heartSprites[i].parent) heartSprites[i].parent.removeChild(heartSprites[i]); } heartSprites = []; } function shuffleArray(arr) { // Fisher-Yates shuffle for (var i = arr.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } function getBowlPositions(n) { // Centered horizontally, spread evenly var spacing = 400; var totalWidth = (n - 1) * spacing; var startX = 2048 / 2 - totalWidth / 2; var y = 1500; var positions = []; for (var i = 0; i < n; i++) { positions.push({ x: startX + i * spacing, y: y }); } return positions; } // --- Game Logic --- function setupRound() { // Remove old bowls for (var i = 0; i < bowls.length; i++) { if (bowls[i].parent) bowls[i].parent.removeChild(bowls[i]); } bowls = []; resetHint(); hideCoin(); removeHearts(); showHearts(); updateHearts(); updateCoins(); updateRound(); // Make shuffle a little more faster each round, but never below 200ms shuffleSpeed = Math.max(600 - (round - 1) * 18, 200); // Add more bowls every 10 rounds, up to 7 numBowls = 3 + Math.floor((round - 1) / 10); if (numBowls > 7) numBowls = 7; // Place bowls var positions = getBowlPositions(numBowls); for (var i = 0; i < numBowls; i++) { var b = new Bowl(); b.x = positions[i].x; b.y = positions[i].y; b.index = i; b.hasCoin = false; bowls.push(b); game.addChild(b); } // Place coin under random bowl coinIndex = Math.floor(Math.random() * numBowls); bowls[coinIndex].hasCoin = true; showCoinUnderBowl(coinIndex); // Show all bowls showAllBowls(); // Show bowl number buttons createBowlButtons(numBowls); // Start shuffle after a short delay game.state = 'idle'; LK.setTimeout(function () { startShuffle(); }, 900); } function startShuffle() { // Hide coin hideCoin(); // Shuffle plan: create a list of shuffle actions shuffleCount = 4 + round; // Fewer swaps, easier to track shufflePlan = []; // Define shuffling tactics // tactic 0: simple swap two random bowls // tactic 1: rotate all bowls left // tactic 2: rotate all bowls right // tactic 3: swap outermost bowls // tactic 4: swap center with random for (var i = 0; i < shuffleCount; i++) { var tactic = Math.floor(Math.random() * 5); if (tactic === 0) { // Simple swap var a = Math.floor(Math.random() * numBowls); var b = Math.floor(Math.random() * numBowls); while (b === a) b = Math.floor(Math.random() * numBowls); shufflePlan.push({ type: 'swap', a: a, b: b }); } else if (tactic === 1 && numBowls > 2) { // Rotate left shufflePlan.push({ type: 'rotateLeft' }); } else if (tactic === 2 && numBowls > 2) { // Rotate right shufflePlan.push({ type: 'rotateRight' }); } else if (tactic === 3 && numBowls > 2) { // Swap outermost shufflePlan.push({ type: 'swap', a: 0, b: numBowls - 1 }); } else if (tactic === 4 && numBowls > 2) { // Swap center with random var center = Math.floor(numBowls / 2); var rand = Math.floor(Math.random() * numBowls); while (rand === center) rand = Math.floor(Math.random() * numBowls); shufflePlan.push({ type: 'swap', a: center, b: rand }); } else { // fallback to simple swap var a = Math.floor(Math.random() * numBowls); var b = Math.floor(Math.random() * numBowls); while (b === a) b = Math.floor(Math.random() * numBowls); shufflePlan.push({ type: 'swap', a: a, b: b }); } } game.state = 'shuffling'; shuffleStep(0); } function shuffleStep(idx) { if (idx >= shufflePlan.length) { // Done shuffling game.state = 'pick'; for (var i = 0; i < bowls.length; i++) { bowls[i].isRevealed = false; } return; } var action = shufflePlan[idx]; LK.getSound('shuffle').play(); if (action.type === 'swap') { var a = action.a, b = action.b; var bowlA = bowls[a], bowlB = bowls[b]; var ax = bowlA.x, bx = bowlB.x; var ay = bowlA.y, by = bowlB.y; // Move up a bit, then swap x tween(bowlA, { y: ay - 60 }, { duration: shuffleSpeed / 2, easing: tween.cubicOut, onFinish: function onFinish() { tween(bowlA, { x: bx }, { duration: shuffleSpeed, easing: tween.linear, onFinish: function onFinish() { tween(bowlA, { y: ay }, { duration: shuffleSpeed / 2, easing: tween.cubicIn }); } }); } }); tween(bowlB, { y: by - 60 }, { duration: shuffleSpeed / 2, easing: tween.cubicOut, onFinish: function onFinish() { tween(bowlB, { x: ax }, { duration: shuffleSpeed, easing: tween.linear, onFinish: function onFinish() { tween(bowlB, { y: by }, { duration: shuffleSpeed / 2, easing: tween.cubicIn, onFinish: function onFinish() { // After both done, swap in array var tmp = bowls[a]; bowls[a] = bowls[b]; bowls[b] = tmp; // If coinIndex was a or b, update if (coinIndex === a) coinIndex = b;else if (coinIndex === b) coinIndex = a; // Add a short pause between swaps for easier tracking LK.setTimeout(function () { shuffleStep(idx + 1); }, 200); } }); } }); } }); } else if (action.type === 'rotateLeft') { // Animate all bowls to the left var positions = []; for (var i = 0; i < bowls.length; i++) { positions.push({ x: bowls[i].x, y: bowls[i].y }); } var doneCount = 0; for (var i = 0; i < bowls.length; i++) { var next = i === 0 ? bowls.length - 1 : i - 1; tween(bowls[i], { x: positions[next].x, y: positions[next].y - 40 }, { duration: shuffleSpeed, easing: tween.cubicInOut, onFinish: function (bowl, origY) { return function () { tween(bowl, { y: origY }, { duration: shuffleSpeed / 2, easing: tween.cubicInOut, onFinish: function onFinish() { doneCount++; if (doneCount === bowls.length) { // Rotate array var first = bowls.shift(); bowls.push(first); // Update coinIndex coinIndex = coinIndex === 0 ? bowls.length - 1 : coinIndex - 1; LK.setTimeout(function () { shuffleStep(idx + 1); }, 200); } } }); }; }(bowls[i], positions[next].y) }); } } else if (action.type === 'rotateRight') { // Animate all bowls to the right var positions = []; for (var i = 0; i < bowls.length; i++) { positions.push({ x: bowls[i].x, y: bowls[i].y }); } var doneCount = 0; for (var i = 0; i < bowls.length; i++) { var next = i === bowls.length - 1 ? 0 : i + 1; tween(bowls[i], { x: positions[next].x, y: positions[next].y - 40 }, { duration: shuffleSpeed, easing: tween.cubicInOut, onFinish: function (bowl, origY) { return function () { tween(bowl, { y: origY }, { duration: shuffleSpeed / 2, easing: tween.cubicInOut, onFinish: function onFinish() { doneCount++; if (doneCount === bowls.length) { // Rotate array var last = bowls.pop(); bowls.unshift(last); // Update coinIndex coinIndex = coinIndex === bowls.length - 1 ? 0 : coinIndex + 1; LK.setTimeout(function () { shuffleStep(idx + 1); }, 200); } } }); }; }(bowls[i], positions[next].y) }); } } } game.pickBowl = function (idx) { if (game.state !== 'pick') return; game.state = 'reveal'; for (var i = 0; i < bowls.length; i++) { bowls[i].isRevealed = true; } // Reveal selected bowl var bowl = bowls[idx]; bowl.lift(function () { LK.getSound('reveal').play(); // Show coin if correct if (bowl.hasCoin) { showCoinUnderBowl(idx); LK.getSound('win').play(); coins += 1; storage.coins = coins; updateCoins(); // Next round after short delay LK.effects.flashObject(bowl, 0x00ff00, 600); LK.setTimeout(function () { round += 1; setupRound(); }, 1200); } else { // Reveal correct bowl too for (var i = 0; i < bowls.length; i++) { if (bowls[i].hasCoin && i !== idx) { bowls[i].lift(); showCoinUnderBowl(i); } } LK.getSound('lose').play(); hearts -= 1; updateHearts(); LK.effects.flashObject(bowl, 0xff0000, 600); if (hearts <= 0) { // Game over removeBowlButtons(); LK.setTimeout(function () { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); }, 1200); } else { LK.setTimeout(function () { round += 1; setupRound(); }, 1200); } } }); }; // --- Game Reset --- game.resetGame = function () { round = 1; hearts = 3; coins = 0; storage.coins = 0; removeBowlButtons(); setupRound(); }; // --- Game State --- game.state = 'idle'; // --- Game Update --- game.update = function () { // No per-frame logic needed }; // --- Start Game --- game.resetGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0
});
/****
* Classes
****/
// Bowl class
var Bowl = Container.expand(function () {
var self = Container.call(this);
// Attach bowl asset
var bowlAsset = self.attachAsset('bowl', {
anchorX: 0.5,
anchorY: 0.5
});
self.bowlAsset = bowlAsset;
self.isHinted = false;
self.isRevealed = false;
self.index = 0; // Which bowl in the array
self.hasCoin = false;
// For animating up/down
self.lift = function (onFinish) {
// Animate bowl up to reveal coin
tween(self, {
y: self.y - 120
}, {
duration: 300,
easing: tween.cubicOut,
onFinish: onFinish
});
};
self.lower = function (onFinish) {
// Animate bowl down to cover coin
tween(self, {
y: self.y + 120
}, {
duration: 300,
easing: tween.cubicIn,
onFinish: onFinish
});
};
self.setHint = function (hinted) {
if (hinted && !self.isHinted) {
// Swap to hint asset
self.removeChild(self.bowlAsset);
self.bowlAsset = self.attachAsset('bowlHint', {
anchorX: 0.5,
anchorY: 0.5
});
self.isHinted = true;
} else if (!hinted && self.isHinted) {
self.removeChild(self.bowlAsset);
self.bowlAsset = self.attachAsset('bowl', {
anchorX: 0.5,
anchorY: 0.5
});
self.isHinted = false;
}
};
// Touch event
self.down = function (x, y, obj) {
if (game.state === 'pick' && !self.isRevealed) {
game.pickBowl(self.index);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2e294e
});
/****
* Game Code
****/
// Bowls: Use ellipse shapes for bowls, different color for coin bowl highlight/hint
// --- Game State Variables ---
var bowls = [];
var coinIndex = 0;
var numBowls = 3;
var round = 1;
var hearts = 3;
var coins = storage.coins || 0;
var state = 'idle'; // 'idle', 'shuffling', 'pick', 'reveal', 'gameover'
var shuffleCount = 0;
var shufflePlan = [];
var shuffleSpeed = 600;
var coinSprite = null;
var heartSprites = [];
var hintBtn = null;
var hintActive = false;
var hintCost = 10;
var scoreTxt = null;
var roundTxt = null;
var coinsTxt = null;
var shuffleTimer = null;
// --- GUI Elements ---
scoreTxt = new Text2('Hearts: 3', {
size: 80,
fill: 0xFF4444
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
coinsTxt = new Text2('Coins: 0', {
size: 80,
fill: 0xFFE066
});
coinsTxt.anchor.set(0.5, 0);
// Place coins text at top left, but offset to avoid menu (x=120)
LK.gui.top.addChild(coinsTxt);
coinsTxt.x = -2048 / 2 + 480; // Move even further right (480px from left edge)
coinsTxt.y = 180; // Move even further down (180px from top)
roundTxt = new Text2('Round 1', {
size: 60,
fill: 0xFFFFFF
});
roundTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(roundTxt);
roundTxt.y = 90;
hintBtn = new Text2('Hint (10)', {
size: 80,
fill: 0x00E6E6
});
hintBtn.anchor.set(0.5, 0.5);
LK.gui.bottom.addChild(hintBtn);
hintBtn.y = -100;
hintBtn.interactive = true;
hintBtn.buttonMode = true;
hintBtn.down = function (x, y, obj) {
if (game.state === 'pick' && !hintActive && coins >= hintCost) {
coins -= hintCost;
storage.coins = coins;
updateCoins();
activateHint();
}
};
// Add Reveal (50) button
var revealBtn = new Text2('Reveal (50)', {
size: 80,
fill: 0xFF66CC
});
revealBtn.anchor.set(0.5, 0.5);
LK.gui.bottom.addChild(revealBtn);
revealBtn.y = 0; // Place below the hint button
revealBtn.interactive = true;
revealBtn.buttonMode = true;
revealBtn.down = function (x, y, obj) {
if (game.state === 'pick' && coins >= 50) {
coins -= 50;
storage.coins = coins;
updateCoins();
// Reveal the correct bowl and show the coin
for (var i = 0; i < bowls.length; i++) {
bowls[i].isRevealed = true;
if (bowls[i].hasCoin) {
bowls[i].lift();
showCoinUnderBowl(i);
LK.effects.flashObject(bowls[i], 0x00ff00, 600);
}
}
game.state = 'reveal';
LK.getSound('reveal').play();
LK.getSound('win').play();
// Next round after short delay
LK.setTimeout(function () {
round += 1;
setupRound();
}, 1200);
}
};
// --- Bottom Panel for Bowl Number Buttons ---
var bowlButtons = [];
function createBowlButtons(n) {
// Remove old buttons
for (var i = 0; i < bowlButtons.length; i++) {
if (bowlButtons[i].parent) bowlButtons[i].parent.removeChild(bowlButtons[i]);
}
bowlButtons = [];
// Spread buttons evenly at the bottom
var spacing = 320;
var totalWidth = (n - 1) * spacing;
var startX = 0 - totalWidth / 2;
for (var i = 0; i < n; i++) {
var btn = new Text2('Bowl ' + (i + 1), {
size: 90,
fill: 0xffffff
});
btn.anchor.set(0.5, 0.5);
btn.x = startX + i * spacing;
btn.y = -220; // Move panel above the hint text (which is at y = -100)
btn.interactive = true;
btn.buttonMode = true;
(function (idx) {
btn.down = function (x, y, obj) {
if (game.state === 'pick') {
game.pickBowl(idx);
}
};
})(i);
LK.gui.bottom.addChild(btn);
bowlButtons.push(btn);
}
}
function removeBowlButtons() {
for (var i = 0; i < bowlButtons.length; i++) {
if (bowlButtons[i].parent) bowlButtons[i].parent.removeChild(bowlButtons[i]);
}
bowlButtons = [];
}
// --- Helper Functions ---
function updateHearts() {
scoreTxt.setText('Hearts: ' + hearts);
}
function updateCoins() {
coinsTxt.setText('Coins: ' + coins);
}
function updateRound() {
roundTxt.setText('Round ' + round);
}
function resetHint() {
hintActive = false;
for (var i = 0; i < bowls.length; i++) {
bowls[i].setHint(false);
}
}
function activateHint() {
hintActive = true;
// Find which half the coin is in, and hide the other half
var n = bowls.length;
var half = Math.floor(n / 2);
// Always keep at least 2 bowls (if n==2, keep both)
if (n <= 2) {
bowls[coinIndex].setHint(true);
LK.setTimeout(function () {
resetHint();
}, 1200);
return;
}
// Decide which half to keep
// Sort bowls by x position (left to right)
var sorted = [];
for (var i = 0; i < bowls.length; i++) sorted.push(bowls[i]);
sorted.sort(function (a, b) {
return a.x - b.x;
});
var coinPos = -1;
for (var i = 0; i < sorted.length; i++) {
if (sorted[i].index === coinIndex) {
coinPos = i;
break;
}
}
var keepStart, keepEnd;
if (coinPos < half) {
// Keep left half
keepStart = 0;
keepEnd = half;
} else {
// Keep right half
keepStart = n - half;
keepEnd = n;
}
for (var i = 0; i < sorted.length; i++) {
if (i >= keepStart && i < keepEnd) {
sorted[i].visible = true;
if (sorted[i].index === coinIndex) {
sorted[i].setHint(true);
} else {
sorted[i].setHint(false);
}
} else {
sorted[i].visible = false;
}
}
// Remove hint and restore all bowls after 1.2s
LK.setTimeout(function () {
for (var i = 0; i < bowls.length; i++) {
bowls[i].visible = true;
}
resetHint();
}, 1200);
}
function showCoinUnderBowl(idx) {
// Place coin under bowl at idx
var bowl = bowls[idx];
if (!coinSprite) {
coinSprite = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(coinSprite);
}
coinSprite.x = bowl.x;
coinSprite.y = bowl.y + 100;
coinSprite.visible = true;
}
function hideCoin() {
if (coinSprite) coinSprite.visible = false;
}
function showAllBowls() {
for (var i = 0; i < bowls.length; i++) {
bowls[i].visible = true;
}
}
function hideAllBowls() {
for (var i = 0; i < bowls.length; i++) {
bowls[i].visible = false;
}
}
function showHearts() {
// Remove old
for (var i = 0; i < heartSprites.length; i++) {
if (heartSprites[i].parent) heartSprites[i].parent.removeChild(heartSprites[i]);
}
heartSprites = [];
// Show hearts at top left, but not in 0,0 (menu)
for (var i = 0; i < hearts; i++) {
var h = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
h.x = 120 + i * 90;
h.y = 120;
game.addChild(h);
heartSprites.push(h);
}
}
function removeHearts() {
for (var i = 0; i < heartSprites.length; i++) {
if (heartSprites[i].parent) heartSprites[i].parent.removeChild(heartSprites[i]);
}
heartSprites = [];
}
function shuffleArray(arr) {
// Fisher-Yates shuffle
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
function getBowlPositions(n) {
// Centered horizontally, spread evenly
var spacing = 400;
var totalWidth = (n - 1) * spacing;
var startX = 2048 / 2 - totalWidth / 2;
var y = 1500;
var positions = [];
for (var i = 0; i < n; i++) {
positions.push({
x: startX + i * spacing,
y: y
});
}
return positions;
}
// --- Game Logic ---
function setupRound() {
// Remove old bowls
for (var i = 0; i < bowls.length; i++) {
if (bowls[i].parent) bowls[i].parent.removeChild(bowls[i]);
}
bowls = [];
resetHint();
hideCoin();
removeHearts();
showHearts();
updateHearts();
updateCoins();
updateRound();
// Make shuffle a little more faster each round, but never below 200ms
shuffleSpeed = Math.max(600 - (round - 1) * 18, 200);
// Add more bowls every 10 rounds, up to 7
numBowls = 3 + Math.floor((round - 1) / 10);
if (numBowls > 7) numBowls = 7;
// Place bowls
var positions = getBowlPositions(numBowls);
for (var i = 0; i < numBowls; i++) {
var b = new Bowl();
b.x = positions[i].x;
b.y = positions[i].y;
b.index = i;
b.hasCoin = false;
bowls.push(b);
game.addChild(b);
}
// Place coin under random bowl
coinIndex = Math.floor(Math.random() * numBowls);
bowls[coinIndex].hasCoin = true;
showCoinUnderBowl(coinIndex);
// Show all bowls
showAllBowls();
// Show bowl number buttons
createBowlButtons(numBowls);
// Start shuffle after a short delay
game.state = 'idle';
LK.setTimeout(function () {
startShuffle();
}, 900);
}
function startShuffle() {
// Hide coin
hideCoin();
// Shuffle plan: create a list of shuffle actions
shuffleCount = 4 + round; // Fewer swaps, easier to track
shufflePlan = [];
// Define shuffling tactics
// tactic 0: simple swap two random bowls
// tactic 1: rotate all bowls left
// tactic 2: rotate all bowls right
// tactic 3: swap outermost bowls
// tactic 4: swap center with random
for (var i = 0; i < shuffleCount; i++) {
var tactic = Math.floor(Math.random() * 5);
if (tactic === 0) {
// Simple swap
var a = Math.floor(Math.random() * numBowls);
var b = Math.floor(Math.random() * numBowls);
while (b === a) b = Math.floor(Math.random() * numBowls);
shufflePlan.push({
type: 'swap',
a: a,
b: b
});
} else if (tactic === 1 && numBowls > 2) {
// Rotate left
shufflePlan.push({
type: 'rotateLeft'
});
} else if (tactic === 2 && numBowls > 2) {
// Rotate right
shufflePlan.push({
type: 'rotateRight'
});
} else if (tactic === 3 && numBowls > 2) {
// Swap outermost
shufflePlan.push({
type: 'swap',
a: 0,
b: numBowls - 1
});
} else if (tactic === 4 && numBowls > 2) {
// Swap center with random
var center = Math.floor(numBowls / 2);
var rand = Math.floor(Math.random() * numBowls);
while (rand === center) rand = Math.floor(Math.random() * numBowls);
shufflePlan.push({
type: 'swap',
a: center,
b: rand
});
} else {
// fallback to simple swap
var a = Math.floor(Math.random() * numBowls);
var b = Math.floor(Math.random() * numBowls);
while (b === a) b = Math.floor(Math.random() * numBowls);
shufflePlan.push({
type: 'swap',
a: a,
b: b
});
}
}
game.state = 'shuffling';
shuffleStep(0);
}
function shuffleStep(idx) {
if (idx >= shufflePlan.length) {
// Done shuffling
game.state = 'pick';
for (var i = 0; i < bowls.length; i++) {
bowls[i].isRevealed = false;
}
return;
}
var action = shufflePlan[idx];
LK.getSound('shuffle').play();
if (action.type === 'swap') {
var a = action.a,
b = action.b;
var bowlA = bowls[a],
bowlB = bowls[b];
var ax = bowlA.x,
bx = bowlB.x;
var ay = bowlA.y,
by = bowlB.y;
// Move up a bit, then swap x
tween(bowlA, {
y: ay - 60
}, {
duration: shuffleSpeed / 2,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(bowlA, {
x: bx
}, {
duration: shuffleSpeed,
easing: tween.linear,
onFinish: function onFinish() {
tween(bowlA, {
y: ay
}, {
duration: shuffleSpeed / 2,
easing: tween.cubicIn
});
}
});
}
});
tween(bowlB, {
y: by - 60
}, {
duration: shuffleSpeed / 2,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(bowlB, {
x: ax
}, {
duration: shuffleSpeed,
easing: tween.linear,
onFinish: function onFinish() {
tween(bowlB, {
y: by
}, {
duration: shuffleSpeed / 2,
easing: tween.cubicIn,
onFinish: function onFinish() {
// After both done, swap in array
var tmp = bowls[a];
bowls[a] = bowls[b];
bowls[b] = tmp;
// If coinIndex was a or b, update
if (coinIndex === a) coinIndex = b;else if (coinIndex === b) coinIndex = a;
// Add a short pause between swaps for easier tracking
LK.setTimeout(function () {
shuffleStep(idx + 1);
}, 200);
}
});
}
});
}
});
} else if (action.type === 'rotateLeft') {
// Animate all bowls to the left
var positions = [];
for (var i = 0; i < bowls.length; i++) {
positions.push({
x: bowls[i].x,
y: bowls[i].y
});
}
var doneCount = 0;
for (var i = 0; i < bowls.length; i++) {
var next = i === 0 ? bowls.length - 1 : i - 1;
tween(bowls[i], {
x: positions[next].x,
y: positions[next].y - 40
}, {
duration: shuffleSpeed,
easing: tween.cubicInOut,
onFinish: function (bowl, origY) {
return function () {
tween(bowl, {
y: origY
}, {
duration: shuffleSpeed / 2,
easing: tween.cubicInOut,
onFinish: function onFinish() {
doneCount++;
if (doneCount === bowls.length) {
// Rotate array
var first = bowls.shift();
bowls.push(first);
// Update coinIndex
coinIndex = coinIndex === 0 ? bowls.length - 1 : coinIndex - 1;
LK.setTimeout(function () {
shuffleStep(idx + 1);
}, 200);
}
}
});
};
}(bowls[i], positions[next].y)
});
}
} else if (action.type === 'rotateRight') {
// Animate all bowls to the right
var positions = [];
for (var i = 0; i < bowls.length; i++) {
positions.push({
x: bowls[i].x,
y: bowls[i].y
});
}
var doneCount = 0;
for (var i = 0; i < bowls.length; i++) {
var next = i === bowls.length - 1 ? 0 : i + 1;
tween(bowls[i], {
x: positions[next].x,
y: positions[next].y - 40
}, {
duration: shuffleSpeed,
easing: tween.cubicInOut,
onFinish: function (bowl, origY) {
return function () {
tween(bowl, {
y: origY
}, {
duration: shuffleSpeed / 2,
easing: tween.cubicInOut,
onFinish: function onFinish() {
doneCount++;
if (doneCount === bowls.length) {
// Rotate array
var last = bowls.pop();
bowls.unshift(last);
// Update coinIndex
coinIndex = coinIndex === bowls.length - 1 ? 0 : coinIndex + 1;
LK.setTimeout(function () {
shuffleStep(idx + 1);
}, 200);
}
}
});
};
}(bowls[i], positions[next].y)
});
}
}
}
game.pickBowl = function (idx) {
if (game.state !== 'pick') return;
game.state = 'reveal';
for (var i = 0; i < bowls.length; i++) {
bowls[i].isRevealed = true;
}
// Reveal selected bowl
var bowl = bowls[idx];
bowl.lift(function () {
LK.getSound('reveal').play();
// Show coin if correct
if (bowl.hasCoin) {
showCoinUnderBowl(idx);
LK.getSound('win').play();
coins += 1;
storage.coins = coins;
updateCoins();
// Next round after short delay
LK.effects.flashObject(bowl, 0x00ff00, 600);
LK.setTimeout(function () {
round += 1;
setupRound();
}, 1200);
} else {
// Reveal correct bowl too
for (var i = 0; i < bowls.length; i++) {
if (bowls[i].hasCoin && i !== idx) {
bowls[i].lift();
showCoinUnderBowl(i);
}
}
LK.getSound('lose').play();
hearts -= 1;
updateHearts();
LK.effects.flashObject(bowl, 0xff0000, 600);
if (hearts <= 0) {
// Game over
removeBowlButtons();
LK.setTimeout(function () {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}, 1200);
} else {
LK.setTimeout(function () {
round += 1;
setupRound();
}, 1200);
}
}
});
};
// --- Game Reset ---
game.resetGame = function () {
round = 1;
hearts = 3;
coins = 0;
storage.coins = 0;
removeBowlButtons();
setupRound();
};
// --- Game State ---
game.state = 'idle';
// --- Game Update ---
game.update = function () {
// No per-frame logic needed
};
// --- Start Game ---
game.resetGame();