/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Card = Container.expand(function (symbolType) { var self = Container.call(this); self.symbolType = symbolType; self.isFlipped = false; self.isMatched = false; self.canFlip = true; // Card back (always visible when not flipped) var cardBack = LK.getAsset('cardBack', { anchorX: 0.5, anchorY: 0.5 }); self.addChild(cardBack); // Card front (white background) var cardFront = LK.getAsset('cardFront', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); self.addChild(cardFront); // Symbol on the card var symbol = LK.getAsset('symbol' + symbolType, { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); self.addChild(symbol); self.flip = function () { if (!self.canFlip || self.isMatched) return; LK.getSound('cardFlip').play(); self.isFlipped = !self.isFlipped; if (self.isFlipped) { // Show front and symbol tween(cardFront, { alpha: 1 }, { duration: 200 }); tween(symbol, { alpha: 1 }, { duration: 200 }); } else { // Hide front and symbol tween(cardFront, { alpha: 0 }, { duration: 200 }); tween(symbol, { alpha: 0 }, { duration: 200 }); } }; self.setMatched = function () { self.isMatched = true; self.canFlip = false; // Add a subtle scale effect for matched cards tween(self, { scaleX: 1.1, scaleY: 1.1 }, { duration: 300, easing: tween.easeOut }); }; self.down = function (x, y, obj) { if (self.canFlip && !self.isFlipped && !self.isMatched) { handleCardTap(self); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c3e50 }); /**** * Game Code ****/ // Bright Orange // Bright Red // Deep Purple // Deep Purple // Deep Blue // Deep Blue // Deep Green // Deep Green // Deep Yellow // Deep Yellow // Deep Orange // Deep Orange // Deep Red // Deep Red // Light Purple // Light Purple // Light Blue // Light Blue // Light Green // Light Green // Light Yellow // Light Yellow // Light Orange // Light Orange // Light Red // Light Red // Dark Purple // Dark Purple // Dark Blue // Dark Blue // Dark Green // Dark Green // Dark Yellow // Dark Yellow // Dark Orange // Dark Orange // Dark Red // Dark Red // Purple // Purple // Blue // Blue // Green // Green // Yellow // Yellow // Orange // Orange // Red // Red // Game variables var cards = []; var flippedCards = []; var moves = 0; var startTime = Date.now(); var gameCompleted = false; var canFlipCards = true; var currentLevel = storage.currentLevel || 1; var gameStartTime = Date.now(); var LEVEL_TIME_LIMIT = 120; // 2 minutes in seconds var MOVE_LIMIT = 15; var gameOver = false; var showMenu = true; // Grid configuration var GRID_COLS = 4; var GRID_ROWS = 4; var CARD_SIZE = 200; var CARD_SPACING = 20; // UI elements var movesTxt = new Text2('Moves: 0', { size: 60, fill: 0xFFFFFF }); movesTxt.anchor.set(0.5, 0); LK.gui.top.addChild(movesTxt); movesTxt.y = 100; var timerTxt = new Text2('Time: 0:00', { size: 60, fill: 0xFFFFFF }); timerTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timerTxt); timerTxt.y = 180; // Initialize coins from storage var coins = storage.coins || 0; var coinsTxt = new Text2('Coins: ' + coins, { size: 60, fill: 0xFFD700 }); coinsTxt.anchor.set(0.5, 0); LK.gui.top.addChild(coinsTxt); coinsTxt.y = 260; // Initialize saved games from individual storage slots var savedGames = []; for (var i = 0; i < 5; i++) { var slotData = storage['savedGame' + i] || null; savedGames.push(slotData); } var levelTxt = new Text2('Level: ' + currentLevel, { size: 60, fill: 0xFFFFFF }); levelTxt.anchor.set(0.5, 0); LK.gui.top.addChild(levelTxt); levelTxt.y = 340; // Level completion notification text var levelCompleteTxt = new Text2('LEVEL COMPLETE!', { size: 100, fill: 0x00FF00 }); levelCompleteTxt.anchor.set(0.5, 0.5); levelCompleteTxt.x = 2048 / 2; levelCompleteTxt.y = 2732 / 2; levelCompleteTxt.visible = false; game.addChild(levelCompleteTxt); // Power-up UI elements var bombTxt = new Text2('Bomb (10 coins)', { size: 40, fill: 0xFF6B6B }); bombTxt.anchor.set(0, 0); LK.gui.topLeft.addChild(bombTxt); bombTxt.x = 120; bombTxt.y = 100; var lightbulbTxt = new Text2('Hint (15 coins)', { size: 40, fill: 0xFFD700 }); lightbulbTxt.anchor.set(0, 0); LK.gui.topLeft.addChild(lightbulbTxt); lightbulbTxt.x = 120; lightbulbTxt.y = 160; var clockTxt = new Text2('Clock (20 coins)', { size: 40, fill: 0x4ECDC4 }); clockTxt.anchor.set(0, 0); LK.gui.topLeft.addChild(clockTxt); clockTxt.x = 120; clockTxt.y = 220; var timerPowerTxt = new Text2('Timer (5 coins)', { size: 40, fill: 0x96CEB4 }); timerPowerTxt.anchor.set(0, 0); LK.gui.topLeft.addChild(timerPowerTxt); timerPowerTxt.x = 120; timerPowerTxt.y = 280; var skipTxt = new Text2('Skip (100 coins)', { size: 40, fill: 0xFF9FF3 }); skipTxt.anchor.set(0, 0); LK.gui.topLeft.addChild(skipTxt); skipTxt.x = 120; skipTxt.y = 340; // Menu screen elements var menuContainer = new Container(); game.addChild(menuContainer); var titleTxt = new Text2('MEMORY MATCH', { size: 120, fill: 0xFFFFFF }); titleTxt.anchor.set(0.5, 0.5); titleTxt.x = 2048 / 2; titleTxt.y = 600; menuContainer.addChild(titleTxt); var levelInfoTxt = new Text2('Current Level: ' + currentLevel, { size: 80, fill: 0xFFD700 }); levelInfoTxt.anchor.set(0.5, 0.5); levelInfoTxt.x = 2048 / 2; levelInfoTxt.y = 800; menuContainer.addChild(levelInfoTxt); var coinsInfoTxt = new Text2('Coins: ' + coins, { size: 80, fill: 0xFFD700 }); coinsInfoTxt.anchor.set(0.5, 0.5); coinsInfoTxt.x = 2048 / 2; coinsInfoTxt.y = 900; menuContainer.addChild(coinsInfoTxt); var playBtn = new Text2('PLAY', { size: 100, fill: 0x4ECDC4 }); playBtn.anchor.set(0.5, 0.5); playBtn.x = 2048 / 2; playBtn.y = 1200; menuContainer.addChild(playBtn); var instructionsTxt = new Text2('Match pairs of cards to advance levels!\nEarn coins to buy power-ups!', { size: 60, fill: 0xCCCCCC }); instructionsTxt.anchor.set(0.5, 0.5); instructionsTxt.x = 2048 / 2; instructionsTxt.y = 1400; menuContainer.addChild(instructionsTxt); var progressTxt = new Text2('Progress: ' + currentLevel + '/500', { size: 70, fill: 0x96CEB4 }); progressTxt.anchor.set(0.5, 0.5); progressTxt.x = 2048 / 2; progressTxt.y = 1600; menuContainer.addChild(progressTxt); // Save game slots UI var saveGamesTxt = new Text2('SAVED GAMES', { size: 80, fill: 0xFFFFFF }); saveGamesTxt.anchor.set(0.5, 0.5); saveGamesTxt.x = 2048 / 2; saveGamesTxt.y = 1800; menuContainer.addChild(saveGamesTxt); // Create 5 save game slot buttons var saveSlots = []; for (var i = 0; i < 5; i++) { var slotData = savedGames[i]; var slotText = 'Slot ' + (i + 1); if (slotData) { slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)'; } else { slotText += ' - Empty'; } var slotBtn = new Text2(slotText, { size: 50, fill: slotData ? 0x4ECDC4 : 0x666666 }); slotBtn.anchor.set(0.5, 0.5); slotBtn.x = 2048 / 2; slotBtn.y = 1900 + i * 80; slotBtn.slotIndex = i; menuContainer.addChild(slotBtn); saveSlots.push(slotBtn); } // Create card types array based on level (4 tiles for first level + 6 tiles per additional level) var totalTiles; if (currentLevel === 1) { totalTiles = 4; // First level has exactly 4 tiles } else { totalTiles = 4 + (currentLevel - 1) * 6; // 4 base tiles + 6 per level } var pairCount = totalTiles / 2; var cardTypes = []; for (var i = 1; i <= pairCount; i++) { var symbolIndex = (i - 1) % 50 + 1; // Cycle through available symbols (now 50 total) cardTypes.push(symbolIndex); cardTypes.push(symbolIndex); // Add pair } // Shuffle function function shuffleArray(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } // Initialize cards function initializeCards() { var shuffledTypes = shuffleArray(cardTypes); // Calculate dynamic grid size based on total tiles var totalTiles = cardTypes.length; GRID_COLS = Math.ceil(Math.sqrt(totalTiles)); GRID_ROWS = Math.ceil(totalTiles / GRID_COLS); // Adjust card size based on grid size if (totalTiles > 16) { CARD_SIZE = Math.max(120, 200 - (totalTiles - 16) * 5); CARD_SPACING = Math.max(10, 20 - (totalTiles - 16) * 1); } // Calculate grid positioning var gridWidth = GRID_COLS * CARD_SIZE + (GRID_COLS - 1) * CARD_SPACING; var gridHeight = GRID_ROWS * CARD_SIZE + (GRID_ROWS - 1) * CARD_SPACING; var startX = (2048 - gridWidth) / 2 + CARD_SIZE / 2; var startY = (2732 - gridHeight) / 2 + CARD_SIZE / 2; for (var i = 0; i < totalTiles; i++) { var row = Math.floor(i / GRID_COLS); var col = i % GRID_COLS; var card = new Card(shuffledTypes[i]); card.x = startX + col * (CARD_SIZE + CARD_SPACING); card.y = startY + row * (CARD_SIZE + CARD_SPACING); cards.push(card); game.addChild(card); } } // Handle card tap function handleCardTap(card) { if (!canFlipCards || flippedCards.length >= 2 || gameOver) return; card.flip(); flippedCards.push(card); if (flippedCards.length === 2) { moves++; movesTxt.setText('Moves: ' + moves); // Check if move limit exceeded if (moves >= MOVE_LIMIT) { gameOver = true; // Save current level progress before game over storage.currentLevel = currentLevel; // Auto-save failed attempt autoSaveGame('Failed - Move Limit'); LK.setTimeout(function () { LK.showGameOver(); }, 1000); return; } canFlipCards = false; // Check for match after a short delay LK.setTimeout(function () { checkForMatch(); }, 1000); } } // Check if two flipped cards match function checkForMatch() { var card1 = flippedCards[0]; var card2 = flippedCards[1]; if (card1.symbolType === card2.symbolType) { // Match found LK.getSound('match').play(); card1.setMatched(); card2.setMatched(); // Award 5 coins for matching pair coins += 5; storage.coins = coins; coinsTxt.setText('Coins: ' + coins); // Check if game is complete var matchedCount = 0; for (var i = 0; i < cards.length; i++) { if (cards[i].isMatched) { matchedCount++; } } if (matchedCount === cards.length) { gameCompleted = true; // Save current level completion storage.currentLevel = currentLevel; // Auto-save completed level autoSaveGame('Completed'); // Award bonus coins for completing level coins += 10; storage.coins = coins; coinsTxt.setText('Coins: ' + coins); // Check if we've completed all 500 levels if (currentLevel >= 500) { // Show you win when all levels are completed LK.setTimeout(function () { LK.showYouWin(); }, 1000); } else { // Advance to next level currentLevel++; storage.currentLevel = currentLevel; // Save current level to storage levelTxt.setText('Level: ' + currentLevel); // Update menu progress display levelInfoTxt.setText('Current Level: ' + currentLevel); progressTxt.setText('Progress: ' + currentLevel + '/500'); // Show level completion message LK.effects.flashScreen(0x00FF00, 1000); // Reset for next level LK.setTimeout(function () { resetLevel(); }, 1500); } } } else { // No match LK.getSound('noMatch').play(); card1.flip(); card2.flip(); } flippedCards = []; canFlipCards = true; } // Reset level for progression function resetLevel() { // Show level completion message briefly if advancing if (currentLevel > 1) { levelCompleteTxt.setText('LEVEL ' + (currentLevel - 1) + ' COMPLETE!'); levelCompleteTxt.visible = true; LK.setTimeout(function () { levelCompleteTxt.visible = false; }, 2000); } // Clear existing cards for (var i = 0; i < cards.length; i++) { cards[i].destroy(); } cards = []; flippedCards = []; moves = 0; gameCompleted = false; canFlipCards = true; gameOver = false; gameStartTime = Date.now(); startTime = Date.now(); // Update UI movesTxt.setText('Moves: 0'); timerTxt.setText('Time: 0:00'); // Recreate card types for new level var totalTiles; if (currentLevel === 1) { totalTiles = 4; // First level has exactly 4 tiles } else { totalTiles = 4 + (currentLevel - 1) * 6; // 4 base tiles + 6 per level } var pairCount = totalTiles / 2; cardTypes = []; for (var i = 1; i <= pairCount; i++) { var symbolIndex = (i - 1) % 50 + 1; // Cycle through available symbols (now 50 total) cardTypes.push(symbolIndex); cardTypes.push(symbolIndex); } // Initialize new cards initializeCards(); } // Format time display function formatTime(seconds) { var minutes = Math.floor(seconds / 60); var remainingSeconds = seconds % 60; return minutes + ':' + (remainingSeconds < 10 ? '0' : '') + remainingSeconds; } // Power-up functions function useBomb() { if (coins >= 10) { coins -= 10; storage.coins = coins; coinsTxt.setText('Coins: ' + coins); // Delete 6 random non-matched cards var availableCards = []; for (var i = 0; i < cards.length; i++) { if (!cards[i].isMatched) { availableCards.push(cards[i]); } } var cardsToRemove = Math.min(6, availableCards.length); for (var i = 0; i < cardsToRemove; i++) { var randomIndex = Math.floor(Math.random() * availableCards.length); var cardToRemove = availableCards[randomIndex]; cardToRemove.destroy(); cards.splice(cards.indexOf(cardToRemove), 1); availableCards.splice(randomIndex, 1); } } } function useLightbulb() { if (coins >= 15) { coins -= 15; storage.coins = coins; coinsTxt.setText('Coins: ' + coins); // Find a matching pair that's not matched yet var unmatchedCards = []; for (var i = 0; i < cards.length; i++) { if (!cards[i].isMatched) { unmatchedCards.push(cards[i]); } } // Group by symbol type var symbolGroups = {}; for (var i = 0; i < unmatchedCards.length; i++) { var card = unmatchedCards[i]; if (!symbolGroups[card.symbolType]) { symbolGroups[card.symbolType] = []; } symbolGroups[card.symbolType].push(card); } // Find first pair and show them for (var symbolType in symbolGroups) { if (symbolGroups[symbolType].length >= 2) { var pair = symbolGroups[symbolType].slice(0, 2); for (var i = 0; i < pair.length; i++) { if (!pair[i].isFlipped) { pair[i].flip(); } } break; } } } } function useClock() { if (coins >= 20) { coins -= 20; storage.coins = coins; coinsTxt.setText('Coins: ' + coins); // Add 5 minutes (300 seconds) to game time gameStartTime -= 300000; // Subtract 5 minutes from start time to effectively add time } } function useTimer() { if (coins >= 5) { coins -= 5; storage.coins = coins; coinsTxt.setText('Coins: ' + coins); // Add 1 minute (60 seconds) to game time gameStartTime -= 60000; // Subtract 1 minute from start time to effectively add time } } function useSkip() { if (coins >= 100) { coins -= 100; storage.coins = coins; coinsTxt.setText('Coins: ' + coins); // Save current level before skipping storage.currentLevel = currentLevel; // Check if we've completed all 500 levels if (currentLevel >= 500) { // Show you win when all levels are completed LK.setTimeout(function () { LK.showYouWin(); }, 1000); } else { // Advance to next level currentLevel++; storage.currentLevel = currentLevel; // Save current level to storage levelTxt.setText('Level: ' + currentLevel); // Reset for next level LK.setTimeout(function () { resetLevel(); }, 1000); } } } // Function to save current game state function saveGame(slotIndex) { // Create flattened gameData object with only literals var gameData = { level: currentLevel, coins: coins, timestamp: Date.now() }; // Ensure savedGames is initialized as an array if (!Array.isArray(savedGames)) { savedGames = []; } // Ensure we have enough slots while (savedGames.length <= slotIndex) { savedGames.push(null); } savedGames[slotIndex] = gameData; storage['savedGame' + slotIndex] = gameData; // Update the slot button text only if saveSlots exists and has the slot if (saveSlots && saveSlots[slotIndex]) { var slotBtn = saveSlots[slotIndex]; slotBtn.setText('Slot ' + (slotIndex + 1) + ' - Level ' + gameData.level + ' (' + gameData.coins + ' coins)'); slotBtn.tint = 0x4ECDC4; } } // Function to auto-save to circular buffer of latest 5 games function autoSaveGame(gameResult) { // Create flattened gameData object with only literals var gameData = { level: currentLevel, coins: coins, timestamp: Date.now(), result: gameResult }; // Ensure savedGames is initialized as an array if (!Array.isArray(savedGames)) { savedGames = []; } // Create a new array to avoid reference issues var newSavedGames = []; // Add new game to the beginning newSavedGames.push(gameData); // Add existing games (up to 4 more) for (var i = 0; i < Math.min(4, savedGames.length); i++) { if (savedGames[i]) { newSavedGames.push(savedGames[i]); } } savedGames = newSavedGames; // Keep only the latest 5 games if (savedGames.length > 5) { savedGames = savedGames.slice(0, 5); } // Save each slot individually to storage with only literals for (var i = 0; i < 5; i++) { if (i < savedGames.length && savedGames[i]) { storage['savedGame' + i] = { level: savedGames[i].level, coins: savedGames[i].coins, timestamp: savedGames[i].timestamp, result: savedGames[i].result || '' }; } else { storage['savedGame' + i] = null; } } // Update all slot buttons if they exist if (saveSlots) { for (var i = 0; i < 5; i++) { if (saveSlots[i]) { var slotData = savedGames[i]; var slotText = 'Slot ' + (i + 1); if (slotData) { slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)'; if (slotData.result) { slotText += ' - ' + slotData.result; } } else { slotText += ' - Empty'; } saveSlots[i].setText(slotText); saveSlots[i].tint = slotData ? 0x4ECDC4 : 0x666666; } } } } // Function to load saved game function loadGame(slotIndex) { var gameData = savedGames[slotIndex]; if (gameData) { currentLevel = gameData.level; coins = gameData.coins; storage.currentLevel = currentLevel; storage.coins = coins; // Update UI levelInfoTxt.setText('Current Level: ' + currentLevel); coinsInfoTxt.setText('Coins: ' + coins); progressTxt.setText('Progress: ' + currentLevel + '/500'); } } // Function to start the game function startGame() { showMenu = false; menuContainer.visible = false; // Show game UI elements movesTxt.visible = true; timerTxt.visible = true; coinsTxt.visible = true; levelTxt.visible = true; bombTxt.visible = true; lightbulbTxt.visible = true; clockTxt.visible = true; timerPowerTxt.visible = true; skipTxt.visible = true; // Initialize game initializeCards(); gameStartTime = Date.now(); startTime = Date.now(); } // Add click handlers for power-ups bombTxt.down = function (x, y, obj) { useBomb(); }; lightbulbTxt.down = function (x, y, obj) { useLightbulb(); }; clockTxt.down = function (x, y, obj) { useClock(); }; timerPowerTxt.down = function (x, y, obj) { useTimer(); }; skipTxt.down = function (x, y, obj) { useSkip(); }; // Save game slot click handlers for (var i = 0; i < saveSlots.length; i++) { saveSlots[i].down = function (x, y, obj) { var slotIndex = obj.slotIndex; var gameData = savedGames[slotIndex]; if (gameData) { // Load existing save loadGame(slotIndex); } else { // Save current game to empty slot saveGame(slotIndex); } }; } // Play button click handler playBtn.down = function (x, y, obj) { // Always start from level 1 when play button is pressed currentLevel = 1; storage.currentLevel = currentLevel; // Reset coin count when play button is pressed coins = 0; storage.coins = coins; coinsTxt.setText('Coins: ' + coins); coinsInfoTxt.setText('Coins: ' + coins); levelTxt.setText('Level: ' + currentLevel); levelInfoTxt.setText('Current Level: ' + currentLevel); progressTxt.setText('Progress: ' + currentLevel + '/500'); startGame(); }; // Hide game UI elements initially (show only in game) movesTxt.visible = false; timerTxt.visible = false; coinsTxt.visible = false; levelTxt.visible = false; bombTxt.visible = false; lightbulbTxt.visible = false; clockTxt.visible = false; timerPowerTxt.visible = false; skipTxt.visible = false; // Don't initialize cards immediately - wait for menu // initializeCards(); // This will be called when starting the game // Main game update loop game.update = function () { // Only update game logic if not in menu if (!showMenu) { // Update timer if (!gameCompleted && !gameOver) { var elapsedTime = Math.floor((Date.now() - gameStartTime) / 1000); var remainingTime = Math.max(0, LEVEL_TIME_LIMIT - elapsedTime); timerTxt.setText('Time: ' + formatTime(remainingTime)); // Check if time is up if (remainingTime <= 0) { gameOver = true; // Save current level progress before game over storage.currentLevel = currentLevel; // Auto-save failed attempt autoSaveGame('Failed - Time Limit'); LK.showGameOver(); } } } }; // Start background music LK.playMusic('bgmusic');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Card = Container.expand(function (symbolType) {
var self = Container.call(this);
self.symbolType = symbolType;
self.isFlipped = false;
self.isMatched = false;
self.canFlip = true;
// Card back (always visible when not flipped)
var cardBack = LK.getAsset('cardBack', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(cardBack);
// Card front (white background)
var cardFront = LK.getAsset('cardFront', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.addChild(cardFront);
// Symbol on the card
var symbol = LK.getAsset('symbol' + symbolType, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.addChild(symbol);
self.flip = function () {
if (!self.canFlip || self.isMatched) return;
LK.getSound('cardFlip').play();
self.isFlipped = !self.isFlipped;
if (self.isFlipped) {
// Show front and symbol
tween(cardFront, {
alpha: 1
}, {
duration: 200
});
tween(symbol, {
alpha: 1
}, {
duration: 200
});
} else {
// Hide front and symbol
tween(cardFront, {
alpha: 0
}, {
duration: 200
});
tween(symbol, {
alpha: 0
}, {
duration: 200
});
}
};
self.setMatched = function () {
self.isMatched = true;
self.canFlip = false;
// Add a subtle scale effect for matched cards
tween(self, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeOut
});
};
self.down = function (x, y, obj) {
if (self.canFlip && !self.isFlipped && !self.isMatched) {
handleCardTap(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Bright Orange
// Bright Red
// Deep Purple
// Deep Purple
// Deep Blue
// Deep Blue
// Deep Green
// Deep Green
// Deep Yellow
// Deep Yellow
// Deep Orange
// Deep Orange
// Deep Red
// Deep Red
// Light Purple
// Light Purple
// Light Blue
// Light Blue
// Light Green
// Light Green
// Light Yellow
// Light Yellow
// Light Orange
// Light Orange
// Light Red
// Light Red
// Dark Purple
// Dark Purple
// Dark Blue
// Dark Blue
// Dark Green
// Dark Green
// Dark Yellow
// Dark Yellow
// Dark Orange
// Dark Orange
// Dark Red
// Dark Red
// Purple
// Purple
// Blue
// Blue
// Green
// Green
// Yellow
// Yellow
// Orange
// Orange
// Red
// Red
// Game variables
var cards = [];
var flippedCards = [];
var moves = 0;
var startTime = Date.now();
var gameCompleted = false;
var canFlipCards = true;
var currentLevel = storage.currentLevel || 1;
var gameStartTime = Date.now();
var LEVEL_TIME_LIMIT = 120; // 2 minutes in seconds
var MOVE_LIMIT = 15;
var gameOver = false;
var showMenu = true;
// Grid configuration
var GRID_COLS = 4;
var GRID_ROWS = 4;
var CARD_SIZE = 200;
var CARD_SPACING = 20;
// UI elements
var movesTxt = new Text2('Moves: 0', {
size: 60,
fill: 0xFFFFFF
});
movesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(movesTxt);
movesTxt.y = 100;
var timerTxt = new Text2('Time: 0:00', {
size: 60,
fill: 0xFFFFFF
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
timerTxt.y = 180;
// Initialize coins from storage
var coins = storage.coins || 0;
var coinsTxt = new Text2('Coins: ' + coins, {
size: 60,
fill: 0xFFD700
});
coinsTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(coinsTxt);
coinsTxt.y = 260;
// Initialize saved games from individual storage slots
var savedGames = [];
for (var i = 0; i < 5; i++) {
var slotData = storage['savedGame' + i] || null;
savedGames.push(slotData);
}
var levelTxt = new Text2('Level: ' + currentLevel, {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 340;
// Level completion notification text
var levelCompleteTxt = new Text2('LEVEL COMPLETE!', {
size: 100,
fill: 0x00FF00
});
levelCompleteTxt.anchor.set(0.5, 0.5);
levelCompleteTxt.x = 2048 / 2;
levelCompleteTxt.y = 2732 / 2;
levelCompleteTxt.visible = false;
game.addChild(levelCompleteTxt);
// Power-up UI elements
var bombTxt = new Text2('Bomb (10 coins)', {
size: 40,
fill: 0xFF6B6B
});
bombTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(bombTxt);
bombTxt.x = 120;
bombTxt.y = 100;
var lightbulbTxt = new Text2('Hint (15 coins)', {
size: 40,
fill: 0xFFD700
});
lightbulbTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(lightbulbTxt);
lightbulbTxt.x = 120;
lightbulbTxt.y = 160;
var clockTxt = new Text2('Clock (20 coins)', {
size: 40,
fill: 0x4ECDC4
});
clockTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(clockTxt);
clockTxt.x = 120;
clockTxt.y = 220;
var timerPowerTxt = new Text2('Timer (5 coins)', {
size: 40,
fill: 0x96CEB4
});
timerPowerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(timerPowerTxt);
timerPowerTxt.x = 120;
timerPowerTxt.y = 280;
var skipTxt = new Text2('Skip (100 coins)', {
size: 40,
fill: 0xFF9FF3
});
skipTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(skipTxt);
skipTxt.x = 120;
skipTxt.y = 340;
// Menu screen elements
var menuContainer = new Container();
game.addChild(menuContainer);
var titleTxt = new Text2('MEMORY MATCH', {
size: 120,
fill: 0xFFFFFF
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 2048 / 2;
titleTxt.y = 600;
menuContainer.addChild(titleTxt);
var levelInfoTxt = new Text2('Current Level: ' + currentLevel, {
size: 80,
fill: 0xFFD700
});
levelInfoTxt.anchor.set(0.5, 0.5);
levelInfoTxt.x = 2048 / 2;
levelInfoTxt.y = 800;
menuContainer.addChild(levelInfoTxt);
var coinsInfoTxt = new Text2('Coins: ' + coins, {
size: 80,
fill: 0xFFD700
});
coinsInfoTxt.anchor.set(0.5, 0.5);
coinsInfoTxt.x = 2048 / 2;
coinsInfoTxt.y = 900;
menuContainer.addChild(coinsInfoTxt);
var playBtn = new Text2('PLAY', {
size: 100,
fill: 0x4ECDC4
});
playBtn.anchor.set(0.5, 0.5);
playBtn.x = 2048 / 2;
playBtn.y = 1200;
menuContainer.addChild(playBtn);
var instructionsTxt = new Text2('Match pairs of cards to advance levels!\nEarn coins to buy power-ups!', {
size: 60,
fill: 0xCCCCCC
});
instructionsTxt.anchor.set(0.5, 0.5);
instructionsTxt.x = 2048 / 2;
instructionsTxt.y = 1400;
menuContainer.addChild(instructionsTxt);
var progressTxt = new Text2('Progress: ' + currentLevel + '/500', {
size: 70,
fill: 0x96CEB4
});
progressTxt.anchor.set(0.5, 0.5);
progressTxt.x = 2048 / 2;
progressTxt.y = 1600;
menuContainer.addChild(progressTxt);
// Save game slots UI
var saveGamesTxt = new Text2('SAVED GAMES', {
size: 80,
fill: 0xFFFFFF
});
saveGamesTxt.anchor.set(0.5, 0.5);
saveGamesTxt.x = 2048 / 2;
saveGamesTxt.y = 1800;
menuContainer.addChild(saveGamesTxt);
// Create 5 save game slot buttons
var saveSlots = [];
for (var i = 0; i < 5; i++) {
var slotData = savedGames[i];
var slotText = 'Slot ' + (i + 1);
if (slotData) {
slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)';
} else {
slotText += ' - Empty';
}
var slotBtn = new Text2(slotText, {
size: 50,
fill: slotData ? 0x4ECDC4 : 0x666666
});
slotBtn.anchor.set(0.5, 0.5);
slotBtn.x = 2048 / 2;
slotBtn.y = 1900 + i * 80;
slotBtn.slotIndex = i;
menuContainer.addChild(slotBtn);
saveSlots.push(slotBtn);
}
// Create card types array based on level (4 tiles for first level + 6 tiles per additional level)
var totalTiles;
if (currentLevel === 1) {
totalTiles = 4; // First level has exactly 4 tiles
} else {
totalTiles = 4 + (currentLevel - 1) * 6; // 4 base tiles + 6 per level
}
var pairCount = totalTiles / 2;
var cardTypes = [];
for (var i = 1; i <= pairCount; i++) {
var symbolIndex = (i - 1) % 50 + 1; // Cycle through available symbols (now 50 total)
cardTypes.push(symbolIndex);
cardTypes.push(symbolIndex); // Add pair
}
// Shuffle function
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
// Initialize cards
function initializeCards() {
var shuffledTypes = shuffleArray(cardTypes);
// Calculate dynamic grid size based on total tiles
var totalTiles = cardTypes.length;
GRID_COLS = Math.ceil(Math.sqrt(totalTiles));
GRID_ROWS = Math.ceil(totalTiles / GRID_COLS);
// Adjust card size based on grid size
if (totalTiles > 16) {
CARD_SIZE = Math.max(120, 200 - (totalTiles - 16) * 5);
CARD_SPACING = Math.max(10, 20 - (totalTiles - 16) * 1);
}
// Calculate grid positioning
var gridWidth = GRID_COLS * CARD_SIZE + (GRID_COLS - 1) * CARD_SPACING;
var gridHeight = GRID_ROWS * CARD_SIZE + (GRID_ROWS - 1) * CARD_SPACING;
var startX = (2048 - gridWidth) / 2 + CARD_SIZE / 2;
var startY = (2732 - gridHeight) / 2 + CARD_SIZE / 2;
for (var i = 0; i < totalTiles; i++) {
var row = Math.floor(i / GRID_COLS);
var col = i % GRID_COLS;
var card = new Card(shuffledTypes[i]);
card.x = startX + col * (CARD_SIZE + CARD_SPACING);
card.y = startY + row * (CARD_SIZE + CARD_SPACING);
cards.push(card);
game.addChild(card);
}
}
// Handle card tap
function handleCardTap(card) {
if (!canFlipCards || flippedCards.length >= 2 || gameOver) return;
card.flip();
flippedCards.push(card);
if (flippedCards.length === 2) {
moves++;
movesTxt.setText('Moves: ' + moves);
// Check if move limit exceeded
if (moves >= MOVE_LIMIT) {
gameOver = true;
// Save current level progress before game over
storage.currentLevel = currentLevel;
// Auto-save failed attempt
autoSaveGame('Failed - Move Limit');
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
return;
}
canFlipCards = false;
// Check for match after a short delay
LK.setTimeout(function () {
checkForMatch();
}, 1000);
}
}
// Check if two flipped cards match
function checkForMatch() {
var card1 = flippedCards[0];
var card2 = flippedCards[1];
if (card1.symbolType === card2.symbolType) {
// Match found
LK.getSound('match').play();
card1.setMatched();
card2.setMatched();
// Award 5 coins for matching pair
coins += 5;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Check if game is complete
var matchedCount = 0;
for (var i = 0; i < cards.length; i++) {
if (cards[i].isMatched) {
matchedCount++;
}
}
if (matchedCount === cards.length) {
gameCompleted = true;
// Save current level completion
storage.currentLevel = currentLevel;
// Auto-save completed level
autoSaveGame('Completed');
// Award bonus coins for completing level
coins += 10;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Check if we've completed all 500 levels
if (currentLevel >= 500) {
// Show you win when all levels are completed
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
} else {
// Advance to next level
currentLevel++;
storage.currentLevel = currentLevel; // Save current level to storage
levelTxt.setText('Level: ' + currentLevel);
// Update menu progress display
levelInfoTxt.setText('Current Level: ' + currentLevel);
progressTxt.setText('Progress: ' + currentLevel + '/500');
// Show level completion message
LK.effects.flashScreen(0x00FF00, 1000);
// Reset for next level
LK.setTimeout(function () {
resetLevel();
}, 1500);
}
}
} else {
// No match
LK.getSound('noMatch').play();
card1.flip();
card2.flip();
}
flippedCards = [];
canFlipCards = true;
}
// Reset level for progression
function resetLevel() {
// Show level completion message briefly if advancing
if (currentLevel > 1) {
levelCompleteTxt.setText('LEVEL ' + (currentLevel - 1) + ' COMPLETE!');
levelCompleteTxt.visible = true;
LK.setTimeout(function () {
levelCompleteTxt.visible = false;
}, 2000);
}
// Clear existing cards
for (var i = 0; i < cards.length; i++) {
cards[i].destroy();
}
cards = [];
flippedCards = [];
moves = 0;
gameCompleted = false;
canFlipCards = true;
gameOver = false;
gameStartTime = Date.now();
startTime = Date.now();
// Update UI
movesTxt.setText('Moves: 0');
timerTxt.setText('Time: 0:00');
// Recreate card types for new level
var totalTiles;
if (currentLevel === 1) {
totalTiles = 4; // First level has exactly 4 tiles
} else {
totalTiles = 4 + (currentLevel - 1) * 6; // 4 base tiles + 6 per level
}
var pairCount = totalTiles / 2;
cardTypes = [];
for (var i = 1; i <= pairCount; i++) {
var symbolIndex = (i - 1) % 50 + 1; // Cycle through available symbols (now 50 total)
cardTypes.push(symbolIndex);
cardTypes.push(symbolIndex);
}
// Initialize new cards
initializeCards();
}
// Format time display
function formatTime(seconds) {
var minutes = Math.floor(seconds / 60);
var remainingSeconds = seconds % 60;
return minutes + ':' + (remainingSeconds < 10 ? '0' : '') + remainingSeconds;
}
// Power-up functions
function useBomb() {
if (coins >= 10) {
coins -= 10;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Delete 6 random non-matched cards
var availableCards = [];
for (var i = 0; i < cards.length; i++) {
if (!cards[i].isMatched) {
availableCards.push(cards[i]);
}
}
var cardsToRemove = Math.min(6, availableCards.length);
for (var i = 0; i < cardsToRemove; i++) {
var randomIndex = Math.floor(Math.random() * availableCards.length);
var cardToRemove = availableCards[randomIndex];
cardToRemove.destroy();
cards.splice(cards.indexOf(cardToRemove), 1);
availableCards.splice(randomIndex, 1);
}
}
}
function useLightbulb() {
if (coins >= 15) {
coins -= 15;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Find a matching pair that's not matched yet
var unmatchedCards = [];
for (var i = 0; i < cards.length; i++) {
if (!cards[i].isMatched) {
unmatchedCards.push(cards[i]);
}
}
// Group by symbol type
var symbolGroups = {};
for (var i = 0; i < unmatchedCards.length; i++) {
var card = unmatchedCards[i];
if (!symbolGroups[card.symbolType]) {
symbolGroups[card.symbolType] = [];
}
symbolGroups[card.symbolType].push(card);
}
// Find first pair and show them
for (var symbolType in symbolGroups) {
if (symbolGroups[symbolType].length >= 2) {
var pair = symbolGroups[symbolType].slice(0, 2);
for (var i = 0; i < pair.length; i++) {
if (!pair[i].isFlipped) {
pair[i].flip();
}
}
break;
}
}
}
}
function useClock() {
if (coins >= 20) {
coins -= 20;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Add 5 minutes (300 seconds) to game time
gameStartTime -= 300000; // Subtract 5 minutes from start time to effectively add time
}
}
function useTimer() {
if (coins >= 5) {
coins -= 5;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Add 1 minute (60 seconds) to game time
gameStartTime -= 60000; // Subtract 1 minute from start time to effectively add time
}
}
function useSkip() {
if (coins >= 100) {
coins -= 100;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
// Save current level before skipping
storage.currentLevel = currentLevel;
// Check if we've completed all 500 levels
if (currentLevel >= 500) {
// Show you win when all levels are completed
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
} else {
// Advance to next level
currentLevel++;
storage.currentLevel = currentLevel; // Save current level to storage
levelTxt.setText('Level: ' + currentLevel);
// Reset for next level
LK.setTimeout(function () {
resetLevel();
}, 1000);
}
}
}
// Function to save current game state
function saveGame(slotIndex) {
// Create flattened gameData object with only literals
var gameData = {
level: currentLevel,
coins: coins,
timestamp: Date.now()
};
// Ensure savedGames is initialized as an array
if (!Array.isArray(savedGames)) {
savedGames = [];
}
// Ensure we have enough slots
while (savedGames.length <= slotIndex) {
savedGames.push(null);
}
savedGames[slotIndex] = gameData;
storage['savedGame' + slotIndex] = gameData;
// Update the slot button text only if saveSlots exists and has the slot
if (saveSlots && saveSlots[slotIndex]) {
var slotBtn = saveSlots[slotIndex];
slotBtn.setText('Slot ' + (slotIndex + 1) + ' - Level ' + gameData.level + ' (' + gameData.coins + ' coins)');
slotBtn.tint = 0x4ECDC4;
}
}
// Function to auto-save to circular buffer of latest 5 games
function autoSaveGame(gameResult) {
// Create flattened gameData object with only literals
var gameData = {
level: currentLevel,
coins: coins,
timestamp: Date.now(),
result: gameResult
};
// Ensure savedGames is initialized as an array
if (!Array.isArray(savedGames)) {
savedGames = [];
}
// Create a new array to avoid reference issues
var newSavedGames = [];
// Add new game to the beginning
newSavedGames.push(gameData);
// Add existing games (up to 4 more)
for (var i = 0; i < Math.min(4, savedGames.length); i++) {
if (savedGames[i]) {
newSavedGames.push(savedGames[i]);
}
}
savedGames = newSavedGames;
// Keep only the latest 5 games
if (savedGames.length > 5) {
savedGames = savedGames.slice(0, 5);
}
// Save each slot individually to storage with only literals
for (var i = 0; i < 5; i++) {
if (i < savedGames.length && savedGames[i]) {
storage['savedGame' + i] = {
level: savedGames[i].level,
coins: savedGames[i].coins,
timestamp: savedGames[i].timestamp,
result: savedGames[i].result || ''
};
} else {
storage['savedGame' + i] = null;
}
}
// Update all slot buttons if they exist
if (saveSlots) {
for (var i = 0; i < 5; i++) {
if (saveSlots[i]) {
var slotData = savedGames[i];
var slotText = 'Slot ' + (i + 1);
if (slotData) {
slotText += ' - Level ' + slotData.level + ' (' + slotData.coins + ' coins)';
if (slotData.result) {
slotText += ' - ' + slotData.result;
}
} else {
slotText += ' - Empty';
}
saveSlots[i].setText(slotText);
saveSlots[i].tint = slotData ? 0x4ECDC4 : 0x666666;
}
}
}
}
// Function to load saved game
function loadGame(slotIndex) {
var gameData = savedGames[slotIndex];
if (gameData) {
currentLevel = gameData.level;
coins = gameData.coins;
storage.currentLevel = currentLevel;
storage.coins = coins;
// Update UI
levelInfoTxt.setText('Current Level: ' + currentLevel);
coinsInfoTxt.setText('Coins: ' + coins);
progressTxt.setText('Progress: ' + currentLevel + '/500');
}
}
// Function to start the game
function startGame() {
showMenu = false;
menuContainer.visible = false;
// Show game UI elements
movesTxt.visible = true;
timerTxt.visible = true;
coinsTxt.visible = true;
levelTxt.visible = true;
bombTxt.visible = true;
lightbulbTxt.visible = true;
clockTxt.visible = true;
timerPowerTxt.visible = true;
skipTxt.visible = true;
// Initialize game
initializeCards();
gameStartTime = Date.now();
startTime = Date.now();
}
// Add click handlers for power-ups
bombTxt.down = function (x, y, obj) {
useBomb();
};
lightbulbTxt.down = function (x, y, obj) {
useLightbulb();
};
clockTxt.down = function (x, y, obj) {
useClock();
};
timerPowerTxt.down = function (x, y, obj) {
useTimer();
};
skipTxt.down = function (x, y, obj) {
useSkip();
};
// Save game slot click handlers
for (var i = 0; i < saveSlots.length; i++) {
saveSlots[i].down = function (x, y, obj) {
var slotIndex = obj.slotIndex;
var gameData = savedGames[slotIndex];
if (gameData) {
// Load existing save
loadGame(slotIndex);
} else {
// Save current game to empty slot
saveGame(slotIndex);
}
};
}
// Play button click handler
playBtn.down = function (x, y, obj) {
// Always start from level 1 when play button is pressed
currentLevel = 1;
storage.currentLevel = currentLevel;
// Reset coin count when play button is pressed
coins = 0;
storage.coins = coins;
coinsTxt.setText('Coins: ' + coins);
coinsInfoTxt.setText('Coins: ' + coins);
levelTxt.setText('Level: ' + currentLevel);
levelInfoTxt.setText('Current Level: ' + currentLevel);
progressTxt.setText('Progress: ' + currentLevel + '/500');
startGame();
};
// Hide game UI elements initially (show only in game)
movesTxt.visible = false;
timerTxt.visible = false;
coinsTxt.visible = false;
levelTxt.visible = false;
bombTxt.visible = false;
lightbulbTxt.visible = false;
clockTxt.visible = false;
timerPowerTxt.visible = false;
skipTxt.visible = false;
// Don't initialize cards immediately - wait for menu
// initializeCards(); // This will be called when starting the game
// Main game update loop
game.update = function () {
// Only update game logic if not in menu
if (!showMenu) {
// Update timer
if (!gameCompleted && !gameOver) {
var elapsedTime = Math.floor((Date.now() - gameStartTime) / 1000);
var remainingTime = Math.max(0, LEVEL_TIME_LIMIT - elapsedTime);
timerTxt.setText('Time: ' + formatTime(remainingTime));
// Check if time is up
if (remainingTime <= 0) {
gameOver = true;
// Save current level progress before game over
storage.currentLevel = currentLevel;
// Auto-save failed attempt
autoSaveGame('Failed - Time Limit');
LK.showGameOver();
}
}
}
};
// Start background music
LK.playMusic('bgmusic');