/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Card class: represents a single card in the 3 card shuffle game
var Card = Container.expand(function () {
var self = Container.call(this);
// Card states: 'faceup', 'facedown', 'revealed'
self.state = 'facedown';
self.isPrize = false;
self.index = 0; // 0,1,2 for left, center, right
// Card dimensions
var cardWidth = 350;
var cardHeight = 500;
// Face down color
var backColor = 0x444444;
// Face up color
var faceColor = 0xffffff;
// Prize color
var prizeColor = 0xffd700;
// Card back (facedown)
var back = self.attachAsset('cardBack', {
width: cardWidth,
height: cardHeight,
color: backColor,
shape: 'box',
anchorX: 0.5,
anchorY: 1
});
// Card face (faceup)
var face = self.attachAsset('cardFace', {
width: cardWidth,
height: cardHeight,
color: faceColor,
shape: 'box',
anchorX: 0.5,
anchorY: 1
});
face.visible = false;
// Prize indicator (only visible if isPrize and faceup)
var prize = self.attachAsset('prizeCircle', {
width: 120,
height: 120,
color: prizeColor,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -cardHeight / 2
});
prize.visible = false;
// For tap feedback
var highlight = self.attachAsset('highlight', {
width: cardWidth + 20,
height: cardHeight + 20,
color: 0x00ff00,
shape: 'box',
anchorX: 0.5,
anchorY: 1
});
highlight.alpha = 0;
highlight.visible = true;
// Card label (for debugging or future use)
// var label = new Text2('', {size: 60, fill: "#222"});
// label.anchor.set(0.5, 0.5);
// label.x = 0;
// label.y = -cardHeight / 2;
// self.addChild(label);
// Set card state: 'faceup', 'facedown', 'revealed'
self.setState = function (state) {
self.state = state;
if (state === 'faceup') {
face.visible = true;
back.visible = false;
prize.visible = self.isPrize;
} else if (state === 'facedown') {
face.visible = false;
back.visible = true;
prize.visible = false;
} else if (state === 'revealed') {
face.visible = true;
back.visible = false;
prize.visible = self.isPrize;
}
};
// Show highlight (for tap feedback)
self.flashHighlight = function () {
highlight.alpha = 0.5;
tween(highlight, {
alpha: 0
}, {
duration: 400,
easing: tween.linear
});
};
// Card tap event
self.down = function (x, y, obj) {
if (typeof onCardTap === 'function') {
onCardTap(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x003300
});
/****
* Game Code
****/
// Game constants
var CARD_SPACING = 420;
var CARD_YS = [900, 1500, 2100]; // y positions for 3 rows
var cardWidth = 350;
var cardHeight = 500;
var maxRounds = 9; // Only up to 9 cards (3 rows of 3)
// Game state
var cards = [];
var prizeIndex = 0;
var round = 1;
var shuffling = false;
var canPick = false;
var score = 0;
var shuffleCount = 3;
var shuffleSpeed = 500;
var pickTimeout = undefined;
// Helper: get card positions for indices 0..n-1, centered in up to 3 rows
function getCardPos(idx, numCards) {
// Determine row and column for up to 9 cards (3x3)
var row, col, rowCardCount, colInRow;
if (numCards <= 3) {
row = 0;
rowCardCount = numCards;
colInRow = idx;
} else if (numCards <= 6) {
// 4-6 cards: fill first row, then start second row
if (idx < 3) {
row = 0;
rowCardCount = Math.min(3, numCards);
colInRow = idx;
} else {
row = 1;
rowCardCount = numCards - 3;
colInRow = idx - 3;
}
} else {
// 7-9 cards: fill first two rows, then third row
if (idx < 3) {
row = 0;
rowCardCount = 3;
colInRow = idx;
} else if (idx < 6) {
row = 1;
rowCardCount = 3;
colInRow = idx - 3;
} else {
row = 2;
rowCardCount = numCards - 6;
colInRow = idx - 6;
}
}
// Center cards in their row
var totalWidth = (rowCardCount - 1) * CARD_SPACING;
var startX = 2048 / 2 - totalWidth / 2;
return {
x: startX + colInRow * CARD_SPACING,
y: CARD_YS[row]
};
}
// GUI elements
var scoreTxt = new Text2('Score: 0', {
size: 110,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var roundTxt = new Text2('Round 1', {
size: 80,
fill: "#fff"
});
roundTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(roundTxt);
roundTxt.y = 120;
// Message text (centered)
var messageTxt = new Text2('', {
size: 120,
fill: "#fff"
});
messageTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(messageTxt);
messageTxt.visible = false;
// Helper: show message in center for ms milliseconds
function showMessage(msg, ms) {
messageTxt.setText(msg);
messageTxt.visible = true;
if (ms) {
LK.setTimeout(function () {
messageTxt.visible = false;
}, ms);
}
}
// Helper: update score and round display
function updateGUI() {
scoreTxt.setText('Score: ' + score);
roundTxt.setText('Round ' + round);
}
// Helper: reset all cards to facedown
function allCardsFacedown() {
for (var i = 0; i < cards.length; i++) {
cards[i].setState('facedown');
}
}
// Helper: flip all cards faceup (show prize)
function allCardsFaceup() {
for (var i = 0; i < cards.length; i++) {
cards[i].setState('faceup');
}
}
// Helper: reveal all cards (show which had prize)
function revealAllCards() {
for (var i = 0; i < cards.length; i++) {
cards[i].setState('revealed');
}
}
// (Moved and updated getCardPos above to take numCards argument)
// Helper: shuffle array in place
function shuffleArray(arr) {
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;
}
}
// Helper: animate swap between two cards
function animateSwap(cardA, cardB, duration, onFinish) {
var ax = cardA.x,
ay = cardA.y;
var bx = cardB.x,
by = cardB.y;
tween(cardA, {
x: bx,
y: by
}, {
duration: duration,
easing: tween.cubicInOut
});
tween(cardB, {
x: ax,
y: ay
}, {
duration: duration,
easing: tween.cubicInOut,
onFinish: onFinish
});
}
// Helper: perform a shuffle sequence (array of swaps)
function performShuffleSequence(swaps, idx, onFinish) {
if (idx >= swaps.length) {
if (onFinish) onFinish();
return;
}
var pair = swaps[idx];
animateSwap(cards[pair[0]], cards[pair[1]], shuffleSpeed, function () {
// Swap card objects in array
var temp = cards[pair[0]];
cards[pair[0]] = cards[pair[1]];
cards[pair[1]] = temp;
// Update indices
for (var i = 0; i < cards.length; i++) {
cards[i].index = i;
}
performShuffleSequence(swaps, idx + 1, onFinish);
});
}
// Helper: generate a random shuffle sequence (array of [i,j] swaps)
function generateShuffleSequence(count) {
var swaps = [];
var numCards = cards.length;
if (numCards < 2) return swaps;
// Make the prize card get shuffled more than the others
var prizeSwaps = Math.max(2, Math.floor(count * 0.6)); // 60% of swaps involve the prize card
var otherSwaps = count - prizeSwaps;
// First, shuffle the prize card with random others
for (var i = 0; i < prizeSwaps; i++) {
var a = prizeIndex;
var b;
do {
b = Math.floor(Math.random() * numCards);
} while (b === a);
swaps.push([a, b]);
}
// Then, shuffle random pairs (not always involving the prize card)
for (var i = 0; i < otherSwaps; i++) {
var a = Math.floor(Math.random() * numCards);
var b;
do {
b = Math.floor(Math.random() * numCards);
} while (b === a);
swaps.push([a, b]);
}
// Shuffle the swaps array so the prize swaps are not all at the start
shuffleArray(swaps);
return swaps;
}
// Start a new round
function startRound() {
shuffling = false;
canPick = false;
updateGUI();
// Determine number of cards for this round (start at 2, max 9)
var numCards = Math.min(2 + (round - 1), maxRounds);
// Add new card if needed
while (cards.length < numCards) {
var card = new Card();
card.index = cards.length;
card.setState('facedown');
cards.push(card);
game.addChild(card);
}
// Remove extra cards if any (shouldn't happen, but defensive)
while (cards.length > numCards) {
var removed = cards.pop();
removed.destroy();
}
// Place cards in initial positions (using new getCardPos for 3 rows)
for (var i = 0; i < cards.length; i++) {
var pos = getCardPos(i, cards.length);
cards[i].x = pos.x;
cards[i].y = pos.y;
cards[i].index = i;
cards[i].isPrize = false;
cards[i].setState('facedown');
}
// Randomly assign prize
prizeIndex = Math.floor(Math.random() * cards.length);
cards[prizeIndex].isPrize = true;
// Show all cards faceup for 1s
allCardsFaceup();
showMessage('Find the Prize!', 1000);
LK.setTimeout(function () {
// Flip all cards facedown
allCardsFacedown();
LK.setTimeout(function () {
// Start shuffling
shuffleCount = 3 + round; // Increase shuffle count per round
// Make shuffling faster each round: start at 600ms, decrease by 40ms per round, min 180ms
shuffleSpeed = Math.max(180, 600 - (round - 1) * 40);
var swaps = generateShuffleSequence(shuffleCount);
shuffling = true;
performShuffleSequence(swaps, 0, function () {
shuffling = false;
canPick = true;
showMessage('Pick a Card!', 800);
// Set a timer for player to pick a card (e.g. 5 seconds)
if (typeof pickTimeout !== "undefined") {
LK.clearTimeout(pickTimeout);
pickTimeout = undefined;
}
pickTimeout = LK.setTimeout(function () {
if (canPick) {
canPick = false;
revealAllCards();
showMessage('Time\'s up!', 1200);
LK.effects.flashScreen(0xff0000, 600);
LK.setTimeout(function () {
LK.showGameOver();
}, 1300);
}
}, 5000 - Math.min(3000, round * 200)); // Slightly less time as rounds increase
});
}, 500);
}, 1000);
}
// Card tap handler
function onCardTap(card) {
if (!canPick || shuffling) return;
canPick = false;
if (typeof pickTimeout !== "undefined") {
LK.clearTimeout(pickTimeout);
pickTimeout = undefined;
}
card.flashHighlight();
// Reveal all cards
revealAllCards();
if (card.isPrize) {
score += 1;
updateGUI();
showMessage('Correct!', 900);
LK.effects.flashObject(card, 0x00ff00, 600);
LK.setTimeout(function () {
nextRound();
}, 1100);
} else {
showMessage('Wrong!', 1200);
LK.effects.flashObject(card, 0xff0000, 600);
LK.setTimeout(function () {
// Show game over
LK.showGameOver();
}, 1300);
}
}
// Next round or win
function nextRound() {
round += 1;
if (round > maxRounds) {
showMessage('You Win!', 1500);
LK.setTimeout(function () {
LK.showYouWin();
}, 1700);
} else {
startRound();
}
}
// --- Game setup ---
// Prevent cards from being placed in top left 100x100
// (Cards are at y=1800, so this is always satisfied)
// Start first round after short delay
LK.setTimeout(function () {
startRound();
}, 600);
// --- Game update loop (not used for logic, but could be used for future animations) ---
game.update = function () {
// No per-frame logic needed for MVP
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Card class: represents a single card in the 3 card shuffle game
var Card = Container.expand(function () {
var self = Container.call(this);
// Card states: 'faceup', 'facedown', 'revealed'
self.state = 'facedown';
self.isPrize = false;
self.index = 0; // 0,1,2 for left, center, right
// Card dimensions
var cardWidth = 350;
var cardHeight = 500;
// Face down color
var backColor = 0x444444;
// Face up color
var faceColor = 0xffffff;
// Prize color
var prizeColor = 0xffd700;
// Card back (facedown)
var back = self.attachAsset('cardBack', {
width: cardWidth,
height: cardHeight,
color: backColor,
shape: 'box',
anchorX: 0.5,
anchorY: 1
});
// Card face (faceup)
var face = self.attachAsset('cardFace', {
width: cardWidth,
height: cardHeight,
color: faceColor,
shape: 'box',
anchorX: 0.5,
anchorY: 1
});
face.visible = false;
// Prize indicator (only visible if isPrize and faceup)
var prize = self.attachAsset('prizeCircle', {
width: 120,
height: 120,
color: prizeColor,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -cardHeight / 2
});
prize.visible = false;
// For tap feedback
var highlight = self.attachAsset('highlight', {
width: cardWidth + 20,
height: cardHeight + 20,
color: 0x00ff00,
shape: 'box',
anchorX: 0.5,
anchorY: 1
});
highlight.alpha = 0;
highlight.visible = true;
// Card label (for debugging or future use)
// var label = new Text2('', {size: 60, fill: "#222"});
// label.anchor.set(0.5, 0.5);
// label.x = 0;
// label.y = -cardHeight / 2;
// self.addChild(label);
// Set card state: 'faceup', 'facedown', 'revealed'
self.setState = function (state) {
self.state = state;
if (state === 'faceup') {
face.visible = true;
back.visible = false;
prize.visible = self.isPrize;
} else if (state === 'facedown') {
face.visible = false;
back.visible = true;
prize.visible = false;
} else if (state === 'revealed') {
face.visible = true;
back.visible = false;
prize.visible = self.isPrize;
}
};
// Show highlight (for tap feedback)
self.flashHighlight = function () {
highlight.alpha = 0.5;
tween(highlight, {
alpha: 0
}, {
duration: 400,
easing: tween.linear
});
};
// Card tap event
self.down = function (x, y, obj) {
if (typeof onCardTap === 'function') {
onCardTap(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x003300
});
/****
* Game Code
****/
// Game constants
var CARD_SPACING = 420;
var CARD_YS = [900, 1500, 2100]; // y positions for 3 rows
var cardWidth = 350;
var cardHeight = 500;
var maxRounds = 9; // Only up to 9 cards (3 rows of 3)
// Game state
var cards = [];
var prizeIndex = 0;
var round = 1;
var shuffling = false;
var canPick = false;
var score = 0;
var shuffleCount = 3;
var shuffleSpeed = 500;
var pickTimeout = undefined;
// Helper: get card positions for indices 0..n-1, centered in up to 3 rows
function getCardPos(idx, numCards) {
// Determine row and column for up to 9 cards (3x3)
var row, col, rowCardCount, colInRow;
if (numCards <= 3) {
row = 0;
rowCardCount = numCards;
colInRow = idx;
} else if (numCards <= 6) {
// 4-6 cards: fill first row, then start second row
if (idx < 3) {
row = 0;
rowCardCount = Math.min(3, numCards);
colInRow = idx;
} else {
row = 1;
rowCardCount = numCards - 3;
colInRow = idx - 3;
}
} else {
// 7-9 cards: fill first two rows, then third row
if (idx < 3) {
row = 0;
rowCardCount = 3;
colInRow = idx;
} else if (idx < 6) {
row = 1;
rowCardCount = 3;
colInRow = idx - 3;
} else {
row = 2;
rowCardCount = numCards - 6;
colInRow = idx - 6;
}
}
// Center cards in their row
var totalWidth = (rowCardCount - 1) * CARD_SPACING;
var startX = 2048 / 2 - totalWidth / 2;
return {
x: startX + colInRow * CARD_SPACING,
y: CARD_YS[row]
};
}
// GUI elements
var scoreTxt = new Text2('Score: 0', {
size: 110,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var roundTxt = new Text2('Round 1', {
size: 80,
fill: "#fff"
});
roundTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(roundTxt);
roundTxt.y = 120;
// Message text (centered)
var messageTxt = new Text2('', {
size: 120,
fill: "#fff"
});
messageTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(messageTxt);
messageTxt.visible = false;
// Helper: show message in center for ms milliseconds
function showMessage(msg, ms) {
messageTxt.setText(msg);
messageTxt.visible = true;
if (ms) {
LK.setTimeout(function () {
messageTxt.visible = false;
}, ms);
}
}
// Helper: update score and round display
function updateGUI() {
scoreTxt.setText('Score: ' + score);
roundTxt.setText('Round ' + round);
}
// Helper: reset all cards to facedown
function allCardsFacedown() {
for (var i = 0; i < cards.length; i++) {
cards[i].setState('facedown');
}
}
// Helper: flip all cards faceup (show prize)
function allCardsFaceup() {
for (var i = 0; i < cards.length; i++) {
cards[i].setState('faceup');
}
}
// Helper: reveal all cards (show which had prize)
function revealAllCards() {
for (var i = 0; i < cards.length; i++) {
cards[i].setState('revealed');
}
}
// (Moved and updated getCardPos above to take numCards argument)
// Helper: shuffle array in place
function shuffleArray(arr) {
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;
}
}
// Helper: animate swap between two cards
function animateSwap(cardA, cardB, duration, onFinish) {
var ax = cardA.x,
ay = cardA.y;
var bx = cardB.x,
by = cardB.y;
tween(cardA, {
x: bx,
y: by
}, {
duration: duration,
easing: tween.cubicInOut
});
tween(cardB, {
x: ax,
y: ay
}, {
duration: duration,
easing: tween.cubicInOut,
onFinish: onFinish
});
}
// Helper: perform a shuffle sequence (array of swaps)
function performShuffleSequence(swaps, idx, onFinish) {
if (idx >= swaps.length) {
if (onFinish) onFinish();
return;
}
var pair = swaps[idx];
animateSwap(cards[pair[0]], cards[pair[1]], shuffleSpeed, function () {
// Swap card objects in array
var temp = cards[pair[0]];
cards[pair[0]] = cards[pair[1]];
cards[pair[1]] = temp;
// Update indices
for (var i = 0; i < cards.length; i++) {
cards[i].index = i;
}
performShuffleSequence(swaps, idx + 1, onFinish);
});
}
// Helper: generate a random shuffle sequence (array of [i,j] swaps)
function generateShuffleSequence(count) {
var swaps = [];
var numCards = cards.length;
if (numCards < 2) return swaps;
// Make the prize card get shuffled more than the others
var prizeSwaps = Math.max(2, Math.floor(count * 0.6)); // 60% of swaps involve the prize card
var otherSwaps = count - prizeSwaps;
// First, shuffle the prize card with random others
for (var i = 0; i < prizeSwaps; i++) {
var a = prizeIndex;
var b;
do {
b = Math.floor(Math.random() * numCards);
} while (b === a);
swaps.push([a, b]);
}
// Then, shuffle random pairs (not always involving the prize card)
for (var i = 0; i < otherSwaps; i++) {
var a = Math.floor(Math.random() * numCards);
var b;
do {
b = Math.floor(Math.random() * numCards);
} while (b === a);
swaps.push([a, b]);
}
// Shuffle the swaps array so the prize swaps are not all at the start
shuffleArray(swaps);
return swaps;
}
// Start a new round
function startRound() {
shuffling = false;
canPick = false;
updateGUI();
// Determine number of cards for this round (start at 2, max 9)
var numCards = Math.min(2 + (round - 1), maxRounds);
// Add new card if needed
while (cards.length < numCards) {
var card = new Card();
card.index = cards.length;
card.setState('facedown');
cards.push(card);
game.addChild(card);
}
// Remove extra cards if any (shouldn't happen, but defensive)
while (cards.length > numCards) {
var removed = cards.pop();
removed.destroy();
}
// Place cards in initial positions (using new getCardPos for 3 rows)
for (var i = 0; i < cards.length; i++) {
var pos = getCardPos(i, cards.length);
cards[i].x = pos.x;
cards[i].y = pos.y;
cards[i].index = i;
cards[i].isPrize = false;
cards[i].setState('facedown');
}
// Randomly assign prize
prizeIndex = Math.floor(Math.random() * cards.length);
cards[prizeIndex].isPrize = true;
// Show all cards faceup for 1s
allCardsFaceup();
showMessage('Find the Prize!', 1000);
LK.setTimeout(function () {
// Flip all cards facedown
allCardsFacedown();
LK.setTimeout(function () {
// Start shuffling
shuffleCount = 3 + round; // Increase shuffle count per round
// Make shuffling faster each round: start at 600ms, decrease by 40ms per round, min 180ms
shuffleSpeed = Math.max(180, 600 - (round - 1) * 40);
var swaps = generateShuffleSequence(shuffleCount);
shuffling = true;
performShuffleSequence(swaps, 0, function () {
shuffling = false;
canPick = true;
showMessage('Pick a Card!', 800);
// Set a timer for player to pick a card (e.g. 5 seconds)
if (typeof pickTimeout !== "undefined") {
LK.clearTimeout(pickTimeout);
pickTimeout = undefined;
}
pickTimeout = LK.setTimeout(function () {
if (canPick) {
canPick = false;
revealAllCards();
showMessage('Time\'s up!', 1200);
LK.effects.flashScreen(0xff0000, 600);
LK.setTimeout(function () {
LK.showGameOver();
}, 1300);
}
}, 5000 - Math.min(3000, round * 200)); // Slightly less time as rounds increase
});
}, 500);
}, 1000);
}
// Card tap handler
function onCardTap(card) {
if (!canPick || shuffling) return;
canPick = false;
if (typeof pickTimeout !== "undefined") {
LK.clearTimeout(pickTimeout);
pickTimeout = undefined;
}
card.flashHighlight();
// Reveal all cards
revealAllCards();
if (card.isPrize) {
score += 1;
updateGUI();
showMessage('Correct!', 900);
LK.effects.flashObject(card, 0x00ff00, 600);
LK.setTimeout(function () {
nextRound();
}, 1100);
} else {
showMessage('Wrong!', 1200);
LK.effects.flashObject(card, 0xff0000, 600);
LK.setTimeout(function () {
// Show game over
LK.showGameOver();
}, 1300);
}
}
// Next round or win
function nextRound() {
round += 1;
if (round > maxRounds) {
showMessage('You Win!', 1500);
LK.setTimeout(function () {
LK.showYouWin();
}, 1700);
} else {
startRound();
}
}
// --- Game setup ---
// Prevent cards from being placed in top left 100x100
// (Cards are at y=1800, so this is always satisfied)
// Start first round after short delay
LK.setTimeout(function () {
startRound();
}, 600);
// --- Game update loop (not used for logic, but could be used for future animations) ---
game.update = function () {
// No per-frame logic needed for MVP
};