/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.bounce = function () {
tween(self, {
y: self.y - 50
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
y: self.y + 50
}, {
duration: 300,
easing: tween.easeIn
});
}
});
};
return self;
});
var Cup = Container.expand(function () {
var self = Container.call(this);
var cupGraphics = self.attachAsset('cup', {
anchorX: 0.5,
anchorY: 1.0
});
self.originalY = 0;
self.isLifted = false;
self.hasBall = false;
self.lift = function () {
if (!self.isLifted) {
self.isLifted = true;
tween(self, {
y: self.originalY - 100
}, {
duration: 500,
easing: tween.easeOut
});
}
};
self.lower = function () {
if (self.isLifted) {
self.isLifted = false;
tween(self, {
y: self.originalY
}, {
duration: 500,
easing: tween.easeOut
});
}
};
self.down = function (x, y, obj) {
if (gameState === 'guessing') {
makeGuess(self);
}
};
return self;
});
var GameButton = Container.expand(function (text, color) {
var self = Container.call(this);
var buttonBg = self.attachAsset(color === 0x4CAF50 ? 'startButton' : 'playAgainButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 62,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.setText = function (newText) {
buttonText.setText(newText);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2E8B57
});
/****
* Game Code
****/
var gameState = 'setup'; // setup, shuffling, guessing, revealing, gameOver
var cups = [];
var ball;
var ballUnderCup = -1;
var startButton;
var playAgainButton;
var scoreTxt;
var levelTxt;
var messageText;
var shuffleSequence = [];
var shuffleIndex = 0;
var shuffleTimer = 0;
var currentLevel = 1;
var baseShuffleDuration = 400;
// Initialize UI
scoreTxt = new Text2('Score: 0', {
size: 78,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
levelTxt = new Text2('Level: 1', {
size: 78,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.y = 104;
LK.gui.top.addChild(levelTxt);
messageText = new Text2('Watch the ball!', {
size: 65,
fill: 0xFFFFFF
});
messageText.anchor.set(0.5, 0.5);
messageText.x = 1024;
messageText.y = 400;
game.addChild(messageText);
// Create cups
for (var i = 0; i < 3; i++) {
var cup = new Cup();
cup.x = 370 + i * 454;
cup.y = 1800;
cup.originalY = cup.y;
cups.push(cup);
game.addChild(cup);
}
// Create ball
ball = new Ball();
ball.x = 1024;
ball.y = 1000;
game.addChild(ball);
// Create start button
startButton = new GameButton('Start Shuffle', 0x4CAF50);
startButton.x = 1024;
startButton.y = 2200;
game.addChild(startButton);
// Create play again button (initially hidden)
playAgainButton = new GameButton('Play Again', 0x2196F3);
playAgainButton.x = 1024;
playAgainButton.y = 2200;
playAgainButton.visible = false;
game.addChild(playAgainButton);
startButton.down = function (x, y, obj) {
if (gameState === 'setup') {
startShufflePhase();
}
};
playAgainButton.down = function (x, y, obj) {
if (gameState === 'gameOver') {
resetGame();
}
};
function updateScore() {
scoreTxt.setText('Score: ' + LK.getScore());
}
function updateLevel() {
levelTxt.setText('Level: ' + currentLevel);
}
function getCurrentShuffleDuration() {
// Reduce duration by 10% per level, minimum 150ms
var durationReduction = (currentLevel - 1) * 0.1;
var adjustedDuration = baseShuffleDuration * (1 - durationReduction);
return Math.max(150, adjustedDuration);
}
function startShufflePhase() {
gameState = 'shuffling';
startButton.visible = false;
// Hide ball under random cup
ballUnderCup = Math.floor(Math.random() * 3);
// Animate ball to cup position
tween(ball, {
x: cups[ballUnderCup].x,
y: cups[ballUnderCup].y - 50
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
ball.visible = false;
// Generate shuffle sequence
generateShuffleSequence();
messageText.setText('Follow the cups!');
LK.getSound('shuffle').play();
// Start shuffling after a brief pause
LK.setTimeout(function () {
shuffleIndex = 0;
shuffleTimer = 0;
}, 500);
}
});
}
function generateShuffleSequence() {
shuffleSequence = [];
// Create 8-12 swaps
var numSwaps = 8 + Math.floor(Math.random() * 5);
for (var i = 0; i < numSwaps; i++) {
var cup1 = Math.floor(Math.random() * 3);
var cup2 = Math.floor(Math.random() * 3);
// Make sure we don't swap a cup with itself
while (cup2 === cup1) {
cup2 = Math.floor(Math.random() * 3);
}
shuffleSequence.push({
cup1: cup1,
cup2: cup2
});
}
}
function performShuffle(cup1Index, cup2Index) {
var cup1 = cups[cup1Index];
var cup2 = cups[cup2Index];
var tempX = cup1.x;
var shuffleDuration = getCurrentShuffleDuration();
// Animate cup swap
tween(cup1, {
x: cup2.x
}, {
duration: shuffleDuration,
easing: tween.easeInOut
});
tween(cup2, {
x: tempX
}, {
duration: shuffleDuration,
easing: tween.easeInOut
});
// Update ball position tracking
if (ballUnderCup === cup1Index) {
ballUnderCup = cup2Index;
} else if (ballUnderCup === cup2Index) {
ballUnderCup = cup1Index;
}
// Swap cups in array
cups[cup1Index] = cup2;
cups[cup2Index] = cup1;
}
function endShuffle() {
gameState = 'guessing';
messageText.setText('Tap a cup to guess!');
}
function makeGuess(selectedCup) {
gameState = 'revealing';
// Find which cup was selected
var selectedIndex = cups.indexOf(selectedCup);
// Lift all cups
for (var i = 0; i < cups.length; i++) {
cups[i].lift();
}
// Show ball under correct cup
ball.visible = true;
ball.x = cups[ballUnderCup].x;
ball.y = cups[ballUnderCup].y - 50;
LK.getSound('reveal').play();
// Check if guess was correct
LK.setTimeout(function () {
if (selectedIndex === ballUnderCup) {
// Correct guess
LK.setScore(LK.getScore() + 1);
updateScore();
currentLevel++;
updateLevel();
if (currentLevel > 20) {
messageText.setText('You Win! Level 20 Complete!');
LK.getSound('success').play();
LK.effects.flashScreen(0x00FF00, 1000);
// Show you win after delay
LK.setTimeout(function () {
LK.showYouWin();
}, 2000);
return;
} else {
messageText.setText('Correct! Level ' + currentLevel + '!');
ball.bounce();
LK.getSound('success').play();
LK.effects.flashScreen(0x00FF00, 500);
// Automatically start next level after delay
LK.setTimeout(function () {
resetForNextLevel();
}, 2000);
}
} else {
// Wrong guess - reset level to 1 and score to 0
currentLevel = 1;
LK.setScore(0);
updateLevel();
updateScore();
messageText.setText('Wrong! Back to Level 1!');
LK.getSound('fail').play();
LK.effects.flashScreen(0xFF0000, 500);
// Show play again button for wrong guess
LK.setTimeout(function () {
gameState = 'gameOver';
playAgainButton.visible = true;
}, 2000);
}
}, 1000);
}
function resetForNextLevel() {
gameState = 'setup';
messageText.setText('Watch the ball!');
// Lower all cups
for (var i = 0; i < cups.length; i++) {
cups[i].lower();
}
// Reset ball
ball.visible = true;
ball.x = 1024;
ball.y = 1000;
// Reset shuffle data
shuffleSequence = [];
shuffleIndex = 0;
shuffleTimer = 0;
ballUnderCup = -1;
// Auto start next level after brief delay
LK.setTimeout(function () {
startShufflePhase();
}, 1000);
}
function resetGame() {
gameState = 'setup';
// Reset UI
playAgainButton.visible = false;
startButton.visible = true;
messageText.setText('Watch the ball!');
// Reset level
currentLevel = 1;
updateLevel();
// Lower all cups
for (var i = 0; i < cups.length; i++) {
cups[i].lower();
}
// Reset ball
ball.visible = true;
ball.x = 1024;
ball.y = 1000;
// Reset shuffle data
shuffleSequence = [];
shuffleIndex = 0;
shuffleTimer = 0;
ballUnderCup = -1;
}
game.update = function () {
if (gameState === 'shuffling' && shuffleSequence.length > 0) {
shuffleTimer++;
// Perform shuffles with timing
if (shuffleTimer % 40 === 0 && shuffleIndex < shuffleSequence.length) {
var swap = shuffleSequence[shuffleIndex];
performShuffle(swap.cup1, swap.cup2);
shuffleIndex++;
}
// End shuffling when all swaps are done
if (shuffleIndex >= shuffleSequence.length && shuffleTimer > shuffleSequence.length * 40 + 30) {
endShuffle();
}
}
};
// Initialize score display
updateScore();
// Initialize level display
updateLevel(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.bounce = function () {
tween(self, {
y: self.y - 50
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
y: self.y + 50
}, {
duration: 300,
easing: tween.easeIn
});
}
});
};
return self;
});
var Cup = Container.expand(function () {
var self = Container.call(this);
var cupGraphics = self.attachAsset('cup', {
anchorX: 0.5,
anchorY: 1.0
});
self.originalY = 0;
self.isLifted = false;
self.hasBall = false;
self.lift = function () {
if (!self.isLifted) {
self.isLifted = true;
tween(self, {
y: self.originalY - 100
}, {
duration: 500,
easing: tween.easeOut
});
}
};
self.lower = function () {
if (self.isLifted) {
self.isLifted = false;
tween(self, {
y: self.originalY
}, {
duration: 500,
easing: tween.easeOut
});
}
};
self.down = function (x, y, obj) {
if (gameState === 'guessing') {
makeGuess(self);
}
};
return self;
});
var GameButton = Container.expand(function (text, color) {
var self = Container.call(this);
var buttonBg = self.attachAsset(color === 0x4CAF50 ? 'startButton' : 'playAgainButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 62,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.setText = function (newText) {
buttonText.setText(newText);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2E8B57
});
/****
* Game Code
****/
var gameState = 'setup'; // setup, shuffling, guessing, revealing, gameOver
var cups = [];
var ball;
var ballUnderCup = -1;
var startButton;
var playAgainButton;
var scoreTxt;
var levelTxt;
var messageText;
var shuffleSequence = [];
var shuffleIndex = 0;
var shuffleTimer = 0;
var currentLevel = 1;
var baseShuffleDuration = 400;
// Initialize UI
scoreTxt = new Text2('Score: 0', {
size: 78,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
levelTxt = new Text2('Level: 1', {
size: 78,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.y = 104;
LK.gui.top.addChild(levelTxt);
messageText = new Text2('Watch the ball!', {
size: 65,
fill: 0xFFFFFF
});
messageText.anchor.set(0.5, 0.5);
messageText.x = 1024;
messageText.y = 400;
game.addChild(messageText);
// Create cups
for (var i = 0; i < 3; i++) {
var cup = new Cup();
cup.x = 370 + i * 454;
cup.y = 1800;
cup.originalY = cup.y;
cups.push(cup);
game.addChild(cup);
}
// Create ball
ball = new Ball();
ball.x = 1024;
ball.y = 1000;
game.addChild(ball);
// Create start button
startButton = new GameButton('Start Shuffle', 0x4CAF50);
startButton.x = 1024;
startButton.y = 2200;
game.addChild(startButton);
// Create play again button (initially hidden)
playAgainButton = new GameButton('Play Again', 0x2196F3);
playAgainButton.x = 1024;
playAgainButton.y = 2200;
playAgainButton.visible = false;
game.addChild(playAgainButton);
startButton.down = function (x, y, obj) {
if (gameState === 'setup') {
startShufflePhase();
}
};
playAgainButton.down = function (x, y, obj) {
if (gameState === 'gameOver') {
resetGame();
}
};
function updateScore() {
scoreTxt.setText('Score: ' + LK.getScore());
}
function updateLevel() {
levelTxt.setText('Level: ' + currentLevel);
}
function getCurrentShuffleDuration() {
// Reduce duration by 10% per level, minimum 150ms
var durationReduction = (currentLevel - 1) * 0.1;
var adjustedDuration = baseShuffleDuration * (1 - durationReduction);
return Math.max(150, adjustedDuration);
}
function startShufflePhase() {
gameState = 'shuffling';
startButton.visible = false;
// Hide ball under random cup
ballUnderCup = Math.floor(Math.random() * 3);
// Animate ball to cup position
tween(ball, {
x: cups[ballUnderCup].x,
y: cups[ballUnderCup].y - 50
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
ball.visible = false;
// Generate shuffle sequence
generateShuffleSequence();
messageText.setText('Follow the cups!');
LK.getSound('shuffle').play();
// Start shuffling after a brief pause
LK.setTimeout(function () {
shuffleIndex = 0;
shuffleTimer = 0;
}, 500);
}
});
}
function generateShuffleSequence() {
shuffleSequence = [];
// Create 8-12 swaps
var numSwaps = 8 + Math.floor(Math.random() * 5);
for (var i = 0; i < numSwaps; i++) {
var cup1 = Math.floor(Math.random() * 3);
var cup2 = Math.floor(Math.random() * 3);
// Make sure we don't swap a cup with itself
while (cup2 === cup1) {
cup2 = Math.floor(Math.random() * 3);
}
shuffleSequence.push({
cup1: cup1,
cup2: cup2
});
}
}
function performShuffle(cup1Index, cup2Index) {
var cup1 = cups[cup1Index];
var cup2 = cups[cup2Index];
var tempX = cup1.x;
var shuffleDuration = getCurrentShuffleDuration();
// Animate cup swap
tween(cup1, {
x: cup2.x
}, {
duration: shuffleDuration,
easing: tween.easeInOut
});
tween(cup2, {
x: tempX
}, {
duration: shuffleDuration,
easing: tween.easeInOut
});
// Update ball position tracking
if (ballUnderCup === cup1Index) {
ballUnderCup = cup2Index;
} else if (ballUnderCup === cup2Index) {
ballUnderCup = cup1Index;
}
// Swap cups in array
cups[cup1Index] = cup2;
cups[cup2Index] = cup1;
}
function endShuffle() {
gameState = 'guessing';
messageText.setText('Tap a cup to guess!');
}
function makeGuess(selectedCup) {
gameState = 'revealing';
// Find which cup was selected
var selectedIndex = cups.indexOf(selectedCup);
// Lift all cups
for (var i = 0; i < cups.length; i++) {
cups[i].lift();
}
// Show ball under correct cup
ball.visible = true;
ball.x = cups[ballUnderCup].x;
ball.y = cups[ballUnderCup].y - 50;
LK.getSound('reveal').play();
// Check if guess was correct
LK.setTimeout(function () {
if (selectedIndex === ballUnderCup) {
// Correct guess
LK.setScore(LK.getScore() + 1);
updateScore();
currentLevel++;
updateLevel();
if (currentLevel > 20) {
messageText.setText('You Win! Level 20 Complete!');
LK.getSound('success').play();
LK.effects.flashScreen(0x00FF00, 1000);
// Show you win after delay
LK.setTimeout(function () {
LK.showYouWin();
}, 2000);
return;
} else {
messageText.setText('Correct! Level ' + currentLevel + '!');
ball.bounce();
LK.getSound('success').play();
LK.effects.flashScreen(0x00FF00, 500);
// Automatically start next level after delay
LK.setTimeout(function () {
resetForNextLevel();
}, 2000);
}
} else {
// Wrong guess - reset level to 1 and score to 0
currentLevel = 1;
LK.setScore(0);
updateLevel();
updateScore();
messageText.setText('Wrong! Back to Level 1!');
LK.getSound('fail').play();
LK.effects.flashScreen(0xFF0000, 500);
// Show play again button for wrong guess
LK.setTimeout(function () {
gameState = 'gameOver';
playAgainButton.visible = true;
}, 2000);
}
}, 1000);
}
function resetForNextLevel() {
gameState = 'setup';
messageText.setText('Watch the ball!');
// Lower all cups
for (var i = 0; i < cups.length; i++) {
cups[i].lower();
}
// Reset ball
ball.visible = true;
ball.x = 1024;
ball.y = 1000;
// Reset shuffle data
shuffleSequence = [];
shuffleIndex = 0;
shuffleTimer = 0;
ballUnderCup = -1;
// Auto start next level after brief delay
LK.setTimeout(function () {
startShufflePhase();
}, 1000);
}
function resetGame() {
gameState = 'setup';
// Reset UI
playAgainButton.visible = false;
startButton.visible = true;
messageText.setText('Watch the ball!');
// Reset level
currentLevel = 1;
updateLevel();
// Lower all cups
for (var i = 0; i < cups.length; i++) {
cups[i].lower();
}
// Reset ball
ball.visible = true;
ball.x = 1024;
ball.y = 1000;
// Reset shuffle data
shuffleSequence = [];
shuffleIndex = 0;
shuffleTimer = 0;
ballUnderCup = -1;
}
game.update = function () {
if (gameState === 'shuffling' && shuffleSequence.length > 0) {
shuffleTimer++;
// Perform shuffles with timing
if (shuffleTimer % 40 === 0 && shuffleIndex < shuffleSequence.length) {
var swap = shuffleSequence[shuffleIndex];
performShuffle(swap.cup1, swap.cup2);
shuffleIndex++;
}
// End shuffling when all swaps are done
if (shuffleIndex >= shuffleSequence.length && shuffleTimer > shuffleSequence.length * 40 + 30) {
endShuffle();
}
}
};
// Initialize score display
updateScore();
// Initialize level display
updateLevel();