/**** * 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
};