Code edit (1 edits merged)
Please save this source code
User prompt
Add DEV button to the game. This button must be at center bottom and always visible. This button take you to next level immediately
User prompt
Next button always appear at bottom
User prompt
Add next button to the game. This button take you to next level
User prompt
After matching cards, matching cards must disappear
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'scaleX' in null' in or related to this line: 'tween(firstCard, {' Line Number: 356
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'scaleX' in null' in or related to this line: 'tween(firstCard, {' Line Number: 355
User prompt
Align level indicator to top middle
User prompt
There must be level system for the game. If you match all cards you will go next level. Maximum level number must be 25.
Code edit (1 edits merged)
Please save this source code
User prompt
Card Match Mania
Initial prompt
I want card match game
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Card class: represents a single card in the memory game
var Card = Container.expand(function () {
	var self = Container.call(this);
	// Card properties
	self.symbol = null; // Symbol or id for matching
	self.isFaceUp = false;
	self.isMatched = false;
	self.index = -1; // Position in the grid
	// Card dimensions (will be set in game code)
	self.cardWidth = 0;
	self.cardHeight = 0;
	// Face-down asset (back of card)
	var backAssetId;
	var frontAssetId = 'cardFront';
	if (typeof currentLevel !== "undefined" && currentLevel > 45) {
		backAssetId = 'CardBack8';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 35) {
		backAssetId = 'Cardback7';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 25) {
		backAssetId = 'CardBack6';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 20) {
		backAssetId = 'CardBack5';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 15) {
		backAssetId = 'CardBack4';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 10) {
		backAssetId = 'CardBack3';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 5) {
		backAssetId = 'CardBack2';
	} else {
		backAssetId = 'cardBack';
	}
	var back = self.attachAsset(backAssetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Face-up asset (front of card, shows symbol)
	var front = self.attachAsset(frontAssetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Symbol text (shows symbol when face up)
	var symbolText = new Text2('', {
		size: 90,
		fill: 0x222222
	});
	symbolText.anchor.set(0.5, 0.5);
	front.addChild(symbolText);
	// Set card size
	self.setSize = function (w, h) {
		self.cardWidth = w;
		self.cardHeight = h;
		back.width = w;
		back.height = h;
		front.width = w;
		front.height = h;
	};
	// Set the symbol for this card
	self.setSymbol = function (symbol) {
		self.symbol = symbol;
		symbolText.setText(symbol);
	};
	// Flip the card to face up (with animation)
	self.flipUp = function (_onFinish) {
		if (self.isFaceUp || self.isMatched) {
			return;
		}
		self.isFaceUp = true;
		// Animate flip: scaleX 1 -> 0, swap, 0 -> 1
		tween(self, {
			scaleX: 0
		}, {
			duration: 120,
			easing: tween.cubicIn,
			onFinish: function onFinish() {
				back.visible = false;
				front.visible = true;
				tween(self, {
					scaleX: 1
				}, {
					duration: 120,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						if (_onFinish) {
							_onFinish();
						}
					}
				});
			}
		});
	};
	// Flip the card to face down (with animation)
	self.flipDown = function (_onFinish2) {
		if (!self.isFaceUp || self.isMatched) {
			return;
		}
		self.isFaceUp = false;
		// Animate flip: scaleX 1 -> 0, swap, 0 -> 1
		tween(self, {
			scaleX: 0
		}, {
			duration: 120,
			easing: tween.cubicIn,
			onFinish: function onFinish() {
				front.visible = false;
				back.visible = true;
				tween(self, {
					scaleX: 1
				}, {
					duration: 120,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						if (_onFinish2) {
							_onFinish2();
						}
					}
				});
			}
		});
	};
	// Instantly show face up (no animation)
	self.showFaceUp = function () {
		self.isFaceUp = true;
		self.scaleX = 1;
		back.visible = false;
		front.visible = true;
	};
	// Instantly show face down (no animation)
	self.showFaceDown = function () {
		self.isFaceUp = false;
		self.scaleX = 1;
		front.visible = false;
		back.visible = true;
	};
	// Mark as matched (disable interaction, highlight)
	self.setMatched = function () {
		self.isMatched = true;
		// Subtle highlight
		tween(self, {
			tint: 0xA0FFA0
		}, {
			duration: 300,
			easing: tween.linear
		});
	};
	// Handle tap/click
	self.down = function (x, y, obj) {
		if (self.isFaceUp || self.isMatched || game.lockInput) {
			return;
		}
		game.onCardTapped(self);
	};
	// Initialize: show face down
	self.showFaceDown();
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x2e3a4f // Deep blue background
});
/**** 
* Game Code
****/ 
// --- Card symbols (use simple emojis for MVP) ---
// Tween plugin for card flip and reveal animations
var cardSymbols = ["๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐ฅ", "๐ฅ", "๐", "๐", "๐", "๐", "๐ฅฅ", "๐
", "๐ฅญ", "๐โ๐ฉ"];
var cardSymbols2 = ["๐ถ", "๐ฑ", "๐ญ", "๐ฐ", "๐ฆ", "๐ป", "๐ผ", "๐จ", "๐ฏ", "๐ฆ", "๐ฎ", "๐ท", "๐ธ", "๐ต", "๐", "๐ง", "๐ฆ", "๐ฆโโฌ", "๐ฅ", "๐ฆ", "๐ฆ
", "๐ฆ", "๐ฆ", "๐บ", "๐ฆ", "๐", "๐ท", "๐ฆ"];
var cardSymbols3 = ["๐", "๐", "๐", "๐", "๐", "๐๏ธ", "๐", "๐", "๐", "๐", "๐ป", "๐", "๐", "๐", "๐ต", "๐๏ธ", "๐ด", "๐ฒ", "๐บ", "๐ค", "โต๏ธ", "๐ถ", "๐ฉ๏ธ", "โ๏ธ", "๐ฐ๏ธ", "๐", "๐", "๐ธ"];
var cardSymbols4 = ["๐งฐ", "๐ช", "๐ง", "๐จ", "โ๏ธ", "๐ ๏ธ", "โ๏ธ", "๐ช", "๐ช", "๐ฉ", "โ๏ธ", "๐ชค", "๐งฑ", "๐งฒ", "๐ซ", "๐ฃ", "๐งจ", "๐ช", "๐ช", "๐ก๏ธ", "โ๏ธ", "๐ก๏ธ", "๐ชฆ", "๐", "๐ช", "๐", "๐", "๐", "๐ฌ", "โ๏ธ", "๐ช", "๐น", "๐ฅ", "๐ฅ", "๐ท", "๐บ", "๐ธ", "๐น", "๐ฅ", "๐ฅ"];
// --- Game settings ---
var maxLevel = 50;
var currentLevel = 1;
// Level configuration: grid size per level (increase difficulty)
function getLevelConfig(level) {
	// Level 1-5: 4x4, 6-10: 5x4, 11-15: 6x5, 16-20: 6x6, 21-25: 7x6
	if (level <= 5) {
		return {
			cols: 4,
			rows: 4
		};
	}
	if (level <= 10) {
		return {
			cols: 5,
			rows: 4
		};
	}
	if (level <= 15) {
		return {
			cols: 6,
			rows: 5
		};
	}
	if (level <= 20) {
		return {
			cols: 6,
			rows: 6
		};
	}
	if (level <= 35) {
		return {
			cols: 8,
			rows: 6
		};
	}
	if (level <= 45) {
		return {
			cols: 8,
			rows: 7
		};
	}
	if (level < 50) {
		return {
			cols: 8,
			rows: 7
		};
	}
	if (level >= 50) {
		return {
			cols: 10,
			rows: 8
		};
	}
	return {
		cols: 9,
		rows: 7
	};
}
var gridCols = getLevelConfig(currentLevel).cols;
var gridRows = getLevelConfig(currentLevel).rows;
var totalPairs = gridCols * gridRows / 2;
var cardSpacing = 36; // px between cards
// --- Card assets (simple colored rectangles) ---
// --- Game state ---
var cards = []; // All card objects
var firstCard = null;
var secondCard = null;
var lockInput = false; // Prevent input during animations
var moves = 0;
var matchesFound = 0;
var timer = null;
var elapsedTime = 0; // in seconds
var timerText = null;
var movesText = null;
// --- GUI: Moves, Timer, Level ---
var levelText = new Text2('Level: 1', {
	size: 80,
	fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
timerText = new Text2('Time: 0s', {
	size: 80,
	fill: 0xFFFFFF
});
timerText.anchor.set(0.5, 0);
LK.gui.top.addChild(timerText);
// Position GUI elements (timer right, avoid top-left 100x100)
// Align level indicator to top middle using LK.gui.top
levelText.x = -10;
levelText.y = 20;
timerText.x = 500;
// --- Timer for random swap after level 35 ---
var randomSwapTimer = null;
var randomSwapTimer10s = null;
var randomSwapTimer5s = null;
var randomSwapTimer2s = null;
// --- Swap queue for level 50+ to prevent overlapping swaps ---
var swapQueue = [];
var swapInProgress = false;
// Helper: Queue a swap (for level 50+)
function queueCardSwap(cardA, cardB) {
	swapQueue.push({
		cardA: cardA,
		cardB: cardB
	});
	processSwapQueue();
}
// Helper: Process swap queue (for level 50+)
function processSwapQueue() {
	if (swapInProgress || swapQueue.length === 0) {
		return;
	}
	swapInProgress = true;
	var swap = swapQueue.shift();
	var cardA = swap.cardA;
	var cardB = swap.cardB;
	var tempSymbol = cardA.symbol;
	var tempIndex = cardA.index;
	cardA.setSymbol(cardB.symbol);
	cardB.setSymbol(tempSymbol);
	// Swap index property as well
	cardA.index = cardB.index;
	cardB.index = tempIndex;
	var tempX = cardA.x;
	var tempY = cardA.y;
	var swapsDone = 0;
	function onSwapDone() {
		swapsDone++;
		if (swapsDone === 2) {
			swapInProgress = false;
			// Snap to grid to avoid drift
			cardA.x = Math.round(cardA.x);
			cardA.y = Math.round(cardA.y);
			cardB.x = Math.round(cardB.x);
			cardB.y = Math.round(cardB.y);
			processSwapQueue();
		}
	}
	tween(cardA, {
		x: cardB.x,
		y: cardB.y
	}, {
		duration: 1200,
		easing: tween.cubicInOut,
		onFinish: onSwapDone
	});
	tween(cardB, {
		x: tempX,
		y: tempY
	}, {
		duration: 1200,
		easing: tween.cubicInOut,
		onFinish: onSwapDone
	});
}
// --- Helper: Shuffle array (Fisher-Yates) ---
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;
	}
}
// --- Layout cards ---
function layoutCards() {
	// Remove old cards if any
	for (var i = 0; i < cards.length; i++) {
		cards[i].destroy();
	}
	cards = [];
	// Prepare symbols (duplicate and shuffle)
	var symbols = [];
	var useSymbols;
	if (currentLevel === 9 || currentLevel === 41) {
		useSymbols = cardSymbols3;
	} else {
		useSymbols = currentLevel >= 50 ? cardSymbols4 : currentLevel > 45 ? cardSymbols3 : currentLevel > 20 ? cardSymbols2 : cardSymbols;
	}
	for (var i = 0; i < totalPairs; i++) {
		symbols.push(useSymbols[i]);
		symbols.push(useSymbols[i]);
	}
	shuffleArray(symbols);
	// Card size: fit grid to 2048x2732 with spacing
	var availableWidth = 2048 - cardSpacing * (gridCols + 1);
	var availableHeight = 1800 - cardSpacing * (gridRows + 1); // leave space for GUI
	var cardWidth = Math.floor(availableWidth / gridCols);
	var cardHeight = Math.floor(availableHeight / gridRows);
	// Center grid
	var gridPixelWidth = cardWidth * gridCols + cardSpacing * (gridCols - 1);
	var gridPixelHeight = cardHeight * gridRows + cardSpacing * (gridRows - 1);
	var startX = Math.floor((2048 - gridPixelWidth) / 2) + cardWidth / 2;
	var startY = 350 + cardHeight / 2; // leave space for GUI
	// Create cards
	for (var row = 0; row < gridRows; row++) {
		for (var col = 0; col < gridCols; col++) {
			var idx = row * gridCols + col;
			var card = new Card();
			card.setSize(cardWidth, cardHeight);
			card.setSymbol(symbols[idx]);
			card.index = idx;
			card.x = startX + col * (cardWidth + cardSpacing);
			card.y = startY + row * (cardHeight + cardSpacing);
			card.showFaceDown();
			game.addChild(card);
			cards.push(card);
		}
	}
}
// --- Reset game state ---
function resetGame() {
	firstCard = null;
	secondCard = null;
	lockInput = false;
	moves = 0;
	matchesFound = 0;
	if (currentLevel === 16) {
		elapsedTime = 150;
	} else if (currentLevel === 21) {
		elapsedTime = 150;
	} else if (currentLevel === 26) {
		elapsedTime = 200;
	} else if (currentLevel === 31) {
		elapsedTime = 200;
	} else if (currentLevel === 36) {
		elapsedTime = 250;
	} else if (currentLevel === 41) {
		elapsedTime = 250;
	} else if (currentLevel === 46) {
		elapsedTime = 300;
	} else if (currentLevel === 50) {
		elapsedTime = 500;
	} else if (currentLevel === 1) {
		elapsedTime = 100;
	} else if (currentLevel === 6 || currentLevel === 11) {
		elapsedTime = 120;
	} else {
		elapsedTime = 120;
	}
	if (typeof levelText !== "undefined") {
		// At level 6, display Level: 2 instead of Level: 6
		if (currentLevel === 6) {
			levelText.setText('Level: 2');
		} else if (currentLevel === 11) {
			levelText.setText('Level: 3');
		} else if (currentLevel === 16) {
			levelText.setText('Level: 4');
		} else if (currentLevel === 21) {
			levelText.setText('Level: 5');
		} else if (currentLevel === 26) {
			levelText.setText('Level: 6');
		} else if (currentLevel === 31) {
			levelText.setText('Level: 7');
		} else if (currentLevel === 36) {
			levelText.setText('Level: 8');
		} else if (currentLevel === 41) {
			levelText.setText('Level: 9');
		} else if (currentLevel === 46) {
			levelText.setText('Level: 10');
		} else if (currentLevel === 50) {
			levelText.setText('Final Level');
		} else if (currentLevel === 26) {
			levelText.setText('Level: 6');
		} else if (currentLevel === 21) {
			levelText.setText('Level: 5');
		} else if (currentLevel === 16) {
			levelText.setText('Level: 4');
		} else {
			levelText.setText('Level: ' + currentLevel);
		}
	}
	// Update grid size for this level
	var config = getLevelConfig(currentLevel);
	gridCols = config.cols;
	gridRows = config.rows;
	totalPairs = gridCols * gridRows / 2;
	timerText.setText('Time: ' + elapsedTime + 's');
	layoutCards();
	// Play lastlevelmusic at level 50
	if (currentLevel === 50) {
		LK.playMusic('lastlevelmusic');
	}
	if (timer) {
		LK.clearInterval(timer);
	}
	timer = LK.setInterval(function () {
		elapsedTime--;
		if (elapsedTime <= 0) {
			elapsedTime = 0;
			timerText.setText('Time: 0s');
			LK.clearInterval(timer);
			LK.showGameOver(); // You lose if timer gets to 0
			return;
		} else {
			timerText.setText('Time: ' + elapsedTime + 's');
		}
	}, 1000);
	// --- Timer-based random swap after level 35 ---
	if (typeof randomSwapTimer !== "undefined" && randomSwapTimer) {
		LK.clearInterval(randomSwapTimer);
		randomSwapTimer = null;
	}
	if (currentLevel >= 50) {
		// At level 50, swap every 2s, 5s, 10s, and 20s using the swap queue
		swapQueue = [];
		swapInProgress = false;
		randomSwapTimer = LK.setInterval(function () {
			// 20s swap
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				queueCardSwap(cardA, cardB);
			}
		}, 20000); // 20 seconds
		// Add a second timer for 10s swap
		if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
			LK.clearInterval(randomSwapTimer10s);
			randomSwapTimer10s = null;
		}
		randomSwapTimer10s = LK.setInterval(function () {
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				queueCardSwap(cardA, cardB);
			}
		}, 10000); // 10 seconds
		// Add a third timer for 5s swap
		if (typeof randomSwapTimer5s !== "undefined" && randomSwapTimer5s) {
			LK.clearInterval(randomSwapTimer5s);
			randomSwapTimer5s = null;
		}
		randomSwapTimer5s = LK.setInterval(function () {
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				queueCardSwap(cardA, cardB);
			}
		}, 5000); // 5 seconds
		// Add a fourth timer for 2s swap
		if (typeof randomSwapTimer2s !== "undefined" && randomSwapTimer2s) {
			LK.clearInterval(randomSwapTimer2s);
			randomSwapTimer2s = null;
		}
		randomSwapTimer2s = LK.setInterval(function () {
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				queueCardSwap(cardA, cardB);
			}
		}, 2000); // 2 seconds
	} else if (currentLevel > 40) {
		// Only 20s swap
		randomSwapTimer = LK.setInterval(function () {
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				var tempSymbol = cardA.symbol;
				var tempIndex = cardA.index;
				cardA.setSymbol(cardB.symbol);
				cardB.setSymbol(tempSymbol);
				// Swap index property as well
				cardA.index = cardB.index;
				cardB.index = tempIndex;
				var tempX = cardA.x;
				var tempY = cardA.y;
				tween(cardA, {
					x: cardB.x,
					y: cardB.y
				}, {
					duration: 1200,
					easing: tween.cubicInOut
				});
				tween(cardB, {
					x: tempX,
					y: tempY
				}, {
					duration: 1200,
					easing: tween.cubicInOut
				});
			}
		}, 20000); // 20 seconds
		// Clear 10s timer if it exists
		if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
			LK.clearInterval(randomSwapTimer10s);
			randomSwapTimer10s = null;
		}
	} else {
		randomSwapTimer = null;
		if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
			LK.clearInterval(randomSwapTimer10s);
			randomSwapTimer10s = null;
		}
	}
}
// --- Card tap handler ---
game.onCardTapped = function (card) {
	if (lockInput || card.isFaceUp || card.isMatched) {
		return;
	}
	if (!firstCard) {
		firstCard = card;
		LK.getSound('cardchose').play();
		card.flipUp();
	} else if (!secondCard && card !== firstCard) {
		secondCard = card;
		lockInput = true;
		LK.getSound('cardchose').play();
		card.flipUp(function () {
			// Check for match after both are face up
			LK.setTimeout(function () {
				checkMatch();
			}, 150);
		});
	}
};
// --- Check for match ---
function checkMatch() {
	moves++;
	// After level 40, swap 2 random cards every 2 moves; after level 30, every 4 moves; after level 25, every 5 moves; after level 20, every 10 moves
	var shouldSwap = false;
	if (currentLevel >= 50 && moves % 1 === 0 && cards.length > 1) {
		shouldSwap = true;
	} else if (currentLevel > 40 && moves % 2 === 0 && cards.length > 1) {
		shouldSwap = true;
	} else if (currentLevel > 30 && moves % 4 === 0 && cards.length > 1) {
		shouldSwap = true;
	} else if (currentLevel > 25 && moves % 5 === 0 && cards.length > 1) {
		shouldSwap = true;
	} else if (currentLevel > 20 && moves % 10 === 0 && cards.length > 1) {
		shouldSwap = true;
	}
	if (shouldSwap) {
		// Find all cards that are not matched and not currently face up
		var swappable = [];
		for (var i = 0; i < cards.length; i++) {
			if (!cards[i].isMatched && !cards[i].isFaceUp) {
				swappable.push(cards[i]);
			}
		}
		if (swappable.length >= 2) {
			var idx1 = Math.floor(Math.random() * swappable.length);
			var idx2 = idx1;
			while (idx2 === idx1) {
				idx2 = Math.floor(Math.random() * swappable.length);
			}
			var cardA = swappable[idx1];
			var cardB = swappable[idx2];
			if (currentLevel >= 50) {
				queueCardSwap(cardA, cardB);
			} else {
				// Old behavior for lower levels
				var tempSymbol = cardA.symbol;
				var tempIndex = cardA.index;
				cardA.setSymbol(cardB.symbol);
				cardB.setSymbol(tempSymbol);
				// Swap index property as well
				cardA.index = cardB.index;
				cardB.index = tempIndex;
				var tempX = cardA.x;
				var tempY = cardA.y;
				tween(cardA, {
					x: cardB.x,
					y: cardB.y
				}, {
					duration: 1200,
					easing: tween.cubicInOut
				});
				tween(cardB, {
					x: tempX,
					y: tempY
				}, {
					duration: 1200,
					easing: tween.cubicInOut
				});
			}
		}
	}
	if (firstCard && secondCard && firstCard.symbol === secondCard.symbol) {
		// Match!
		LK.getSound('cardmatch').play();
		firstCard.setMatched();
		secondCard.setMatched();
		matchesFound++;
		// Subtle scale animation for matched cards, then disappear
		if (firstCard && secondCard) {
			var matched1 = firstCard;
			var matched2 = secondCard;
			if (matched1) {
				tween(matched1, {
					scaleX: 1.15,
					scaleY: 1.15
				}, {
					duration: 80,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						if (matched1) {
							tween(matched1, {
								scaleX: 0,
								scaleY: 0,
								alpha: 0
							}, {
								duration: 120,
								onFinish: function onFinish() {
									if (matched1) {
										matched1.destroy();
									}
								}
							});
						}
					}
				});
			}
			if (matched2) {
				tween(matched2, {
					scaleX: 1.15,
					scaleY: 1.15
				}, {
					duration: 80,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						if (matched2) {
							tween(matched2, {
								scaleX: 0,
								scaleY: 0,
								alpha: 0
							}, {
								duration: 120,
								onFinish: function onFinish() {
									if (matched2) {
										matched2.destroy();
									}
								}
							});
						}
					}
				});
			}
		}
		// Check for win
		if (matchesFound === totalPairs) {
			LK.clearInterval(timer);
			LK.setScore(moves); // Use moves as score (lower is better)
			if (currentLevel < maxLevel) {
				// Play level completed sound
				LK.getSound('levelcompleted').play();
				// Show "Level Completed" text in the center of the screen
				var levelCompletedText = new Text2('Level Completed!', {
					size: 180,
					fill: 0xFFD700,
					font: "Impact"
				});
				levelCompletedText.anchor.set(0.5, 0.5);
				levelCompletedText.x = 2048 / 2;
				levelCompletedText.y = 2732 / 2;
				game.addChild(levelCompletedText);
				if (currentLevel === 1) {
					currentLevel = 6;
				} else if (currentLevel === 6) {
					currentLevel = 11;
				} else if (currentLevel === 11) {
					currentLevel = 16;
				} else if (currentLevel === 16) {
					currentLevel = 21;
				} else if (currentLevel === 21) {
					currentLevel = 26;
				} else if (currentLevel === 26) {
					currentLevel = 31;
				} else if (currentLevel === 31) {
					currentLevel = 36;
				} else if (currentLevel === 36) {
					currentLevel = 41;
				} else if (currentLevel === 41) {
					currentLevel = 46;
				} else if (currentLevel === 46) {
					currentLevel = 50;
				} else {
					currentLevel++;
				}
				// Short delay before next level, remove text before reset
				LK.setTimeout(function () {
					if (levelCompletedText && typeof levelCompletedText.destroy === "function") {
						levelCompletedText.destroy();
					}
					resetGame();
				}, 1200);
			} else {
				LK.showYouWin();
			}
		}
		// Reset selection
		firstCard = null;
		secondCard = null;
		lockInput = false;
	} else {
		// Not a match: flip both back after short delay
		LK.setTimeout(function () {
			if (firstCard && typeof firstCard.flipDown === "function") {
				firstCard.flipDown();
			}
			if (secondCard && typeof secondCard.flipDown === "function") {
				secondCard.flipDown(function () {
					firstCard = null;
					secondCard = null;
					lockInput = false;
				});
			} else {
				// If secondCard is missing, still reset state after delay
				firstCard = null;
				secondCard = null;
				lockInput = false;
			}
		}, 250);
	}
}
// --- Game update (not used for logic, but required) ---
game.update = function () {
	// Movement-based card swap: if two cards are dragged/moved to each other's position, swap their symbol and index
	// (Assuming you have a drag/move system, e.g. for future features or mobile drag-to-swap)
	// This is a defensive implementation: checks for cards that overlap and are not already matched or face up
	for (var i = 0; i < cards.length; i++) {
		var cardA = cards[i];
		if (cardA.isMatched || cardA.isFaceUp) continue;
		for (var j = i + 1; j < cards.length; j++) {
			var cardB = cards[j];
			if (cardB.isMatched || cardB.isFaceUp) continue;
			// If cardA and cardB overlap (bounding box collision)
			if (Math.abs(cardA.x - cardB.x) < (cardA.cardWidth + cardB.cardWidth) / 2 && Math.abs(cardA.y - cardB.y) < (cardA.cardHeight + cardB.cardHeight) / 2) {
				// Only swap if not already swapped this frame
				if (!cardA._swappedThisFrame && !cardB._swappedThisFrame) {
					// Swap symbol and index
					var tempSymbol = cardA.symbol;
					var tempIndex = cardA.index;
					cardA.setSymbol(cardB.symbol);
					cardB.setSymbol(tempSymbol);
					cardA.index = cardB.index;
					cardB.index = tempIndex;
					cardA._swappedThisFrame = true;
					cardB._swappedThisFrame = true;
				}
			}
		}
	}
	// Reset swap flags for next frame
	for (var i = 0; i < cards.length; i++) {
		cards[i]._swappedThisFrame = false;
	}
};
// --- DEV Button: Center bottom, always visible, go to next level ---
// (Hidden for production, but function remains for later update test)
var devBtn = new Text2('DEV', {
	size: 90,
	fill: 0xFF00FF,
	font: "Impact"
});
devBtn.anchor.set(0.5, 1);
// Place DEV button slightly left of center
devBtn.x = -120;
devBtn.y = -50;
// LK.gui.bottom.addChild(devBtn); // HIDDEN: do not add to GUI, but keep logic for later test
devBtn.visible = false; // Hide from view
// DEV button handler: go to next level immediately (max 25)
devBtn.down = function (x, y, obj) {
	if (matchesFound === totalPairs) {
		// Already won, do nothing
		return;
	}
	// Replicate the level pass logic from checkMatch()
	if (currentLevel < maxLevel) {
		// Play level completed sound
		LK.getSound('levelcompleted').play();
		// Show "Level Completed" text in the center of the screen
		var levelCompletedText = new Text2('Level Completed!', {
			size: 180,
			fill: 0xFFD700,
			font: "Impact"
		});
		levelCompletedText.anchor.set(0.5, 0.5);
		levelCompletedText.x = 2048 / 2;
		levelCompletedText.y = 2732 / 2;
		game.addChild(levelCompletedText);
		if (currentLevel === 1) {
			currentLevel = 6;
		} else if (currentLevel === 6) {
			currentLevel = 11;
		} else if (currentLevel === 11) {
			currentLevel = 16;
		} else if (currentLevel === 16) {
			currentLevel = 21;
		} else if (currentLevel === 21) {
			currentLevel = 26;
		} else if (currentLevel === 26) {
			levelText.setText('Level: 6');
			currentLevel = 31;
		} else if (currentLevel === 31) {
			levelText.setText('Level: 7');
			currentLevel = 36;
		} else if (currentLevel === 36) {
			levelText.setText('Level: 8');
			levelText.setText('Level: 8');
			currentLevel = 41;
		} else if (currentLevel === 41) {
			levelText.setText('Level: 9');
			currentLevel = 46;
		} else if (currentLevel === 46) {
			levelText.setText('Level: 10');
			currentLevel = 50;
		} else if (currentLevel === 50) {
			levelText.setText('Final Level');
			currentLevel = 50;
		} else {
			currentLevel++;
		}
		LK.setTimeout(function () {
			if (levelCompletedText && typeof levelCompletedText.destroy === "function") {
				levelCompletedText.destroy();
			}
			resetGame();
		}, 1200);
	} else {
		LK.showYouWin();
	}
};
// --- Reveal Button: Center bottom, always visible, reveals all cards ---
// (Hidden for production, but function remains for later update test)
var revealBtn = new Text2('Reveal', {
	size: 90,
	fill: 0x00CCFF,
	font: "Impact"
});
revealBtn.anchor.set(0.5, 1);
// Place Reveal button slightly right of center
revealBtn.x = 120;
revealBtn.y = -50;
// LK.gui.bottom.addChild(revealBtn); // HIDDEN: do not add to GUI, but keep logic for later test
revealBtn.visible = false; // Hide from view
// Reveal button handler: toggle all cards between revealed and hidden
var revealState = false; // false = hidden, true = revealed
revealBtn.down = function (x, y, obj) {
	revealState = !revealState;
	if (revealState) {
		// Reveal all cards (that are not matched)
		for (var i = 0; i < cards.length; i++) {
			var card = cards[i];
			if (!card.isFaceUp && !card.isMatched) {
				card.flipUp();
			}
		}
		revealBtn.setText('Hide');
	} else {
		// Hide all cards (that are not matched)
		for (var i = 0; i < cards.length; i++) {
			var card = cards[i];
			if (card.isFaceUp && !card.isMatched) {
				card.flipDown();
			}
		}
		revealBtn.setText('Reveal');
	}
};
// --- Start game ---
LK.playMusic('gamemusic');
resetGame();
// --- Game over handler (reset on game over) ---
game.onGameOver = function () {
	if (timer) {
		LK.clearInterval(timer);
	}
	if (typeof randomSwapTimer !== "undefined" && randomSwapTimer) {
		LK.clearInterval(randomSwapTimer);
		randomSwapTimer = null;
	}
	if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
		LK.clearInterval(randomSwapTimer10s);
		randomSwapTimer10s = null;
	}
	if (typeof randomSwapTimer5s !== "undefined" && randomSwapTimer5s) {
		LK.clearInterval(randomSwapTimer5s);
		randomSwapTimer5s = null;
	}
	if (typeof randomSwapTimer2s !== "undefined" && randomSwapTimer2s) {
		LK.clearInterval(randomSwapTimer2s);
		randomSwapTimer2s = null;
	}
	currentLevel = 1;
	LK.stopMusic();
	resetGame();
};
// --- Win handler (reset on win) ---
game.onYouWin = function () {
	if (timer) {
		LK.clearInterval(timer);
	}
	if (typeof randomSwapTimer !== "undefined" && randomSwapTimer) {
		LK.clearInterval(randomSwapTimer);
		randomSwapTimer = null;
	}
	if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
		LK.clearInterval(randomSwapTimer10s);
		randomSwapTimer10s = null;
	}
	if (typeof randomSwapTimer5s !== "undefined" && randomSwapTimer5s) {
		LK.clearInterval(randomSwapTimer5s);
		randomSwapTimer5s = null;
	}
	if (typeof randomSwapTimer2s !== "undefined" && randomSwapTimer2s) {
		LK.clearInterval(randomSwapTimer2s);
		randomSwapTimer2s = null;
	}
	currentLevel = 1;
	LK.stopMusic();
	// Game will reset automatically by LK
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Card class: represents a single card in the memory game
var Card = Container.expand(function () {
	var self = Container.call(this);
	// Card properties
	self.symbol = null; // Symbol or id for matching
	self.isFaceUp = false;
	self.isMatched = false;
	self.index = -1; // Position in the grid
	// Card dimensions (will be set in game code)
	self.cardWidth = 0;
	self.cardHeight = 0;
	// Face-down asset (back of card)
	var backAssetId;
	var frontAssetId = 'cardFront';
	if (typeof currentLevel !== "undefined" && currentLevel > 45) {
		backAssetId = 'CardBack8';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 35) {
		backAssetId = 'Cardback7';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 25) {
		backAssetId = 'CardBack6';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 20) {
		backAssetId = 'CardBack5';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 15) {
		backAssetId = 'CardBack4';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 10) {
		backAssetId = 'CardBack3';
	} else if (typeof currentLevel !== "undefined" && currentLevel > 5) {
		backAssetId = 'CardBack2';
	} else {
		backAssetId = 'cardBack';
	}
	var back = self.attachAsset(backAssetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Face-up asset (front of card, shows symbol)
	var front = self.attachAsset(frontAssetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Symbol text (shows symbol when face up)
	var symbolText = new Text2('', {
		size: 90,
		fill: 0x222222
	});
	symbolText.anchor.set(0.5, 0.5);
	front.addChild(symbolText);
	// Set card size
	self.setSize = function (w, h) {
		self.cardWidth = w;
		self.cardHeight = h;
		back.width = w;
		back.height = h;
		front.width = w;
		front.height = h;
	};
	// Set the symbol for this card
	self.setSymbol = function (symbol) {
		self.symbol = symbol;
		symbolText.setText(symbol);
	};
	// Flip the card to face up (with animation)
	self.flipUp = function (_onFinish) {
		if (self.isFaceUp || self.isMatched) {
			return;
		}
		self.isFaceUp = true;
		// Animate flip: scaleX 1 -> 0, swap, 0 -> 1
		tween(self, {
			scaleX: 0
		}, {
			duration: 120,
			easing: tween.cubicIn,
			onFinish: function onFinish() {
				back.visible = false;
				front.visible = true;
				tween(self, {
					scaleX: 1
				}, {
					duration: 120,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						if (_onFinish) {
							_onFinish();
						}
					}
				});
			}
		});
	};
	// Flip the card to face down (with animation)
	self.flipDown = function (_onFinish2) {
		if (!self.isFaceUp || self.isMatched) {
			return;
		}
		self.isFaceUp = false;
		// Animate flip: scaleX 1 -> 0, swap, 0 -> 1
		tween(self, {
			scaleX: 0
		}, {
			duration: 120,
			easing: tween.cubicIn,
			onFinish: function onFinish() {
				front.visible = false;
				back.visible = true;
				tween(self, {
					scaleX: 1
				}, {
					duration: 120,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						if (_onFinish2) {
							_onFinish2();
						}
					}
				});
			}
		});
	};
	// Instantly show face up (no animation)
	self.showFaceUp = function () {
		self.isFaceUp = true;
		self.scaleX = 1;
		back.visible = false;
		front.visible = true;
	};
	// Instantly show face down (no animation)
	self.showFaceDown = function () {
		self.isFaceUp = false;
		self.scaleX = 1;
		front.visible = false;
		back.visible = true;
	};
	// Mark as matched (disable interaction, highlight)
	self.setMatched = function () {
		self.isMatched = true;
		// Subtle highlight
		tween(self, {
			tint: 0xA0FFA0
		}, {
			duration: 300,
			easing: tween.linear
		});
	};
	// Handle tap/click
	self.down = function (x, y, obj) {
		if (self.isFaceUp || self.isMatched || game.lockInput) {
			return;
		}
		game.onCardTapped(self);
	};
	// Initialize: show face down
	self.showFaceDown();
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x2e3a4f // Deep blue background
});
/**** 
* Game Code
****/ 
// --- Card symbols (use simple emojis for MVP) ---
// Tween plugin for card flip and reveal animations
var cardSymbols = ["๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐ฅ", "๐ฅ", "๐", "๐", "๐", "๐", "๐ฅฅ", "๐
", "๐ฅญ", "๐โ๐ฉ"];
var cardSymbols2 = ["๐ถ", "๐ฑ", "๐ญ", "๐ฐ", "๐ฆ", "๐ป", "๐ผ", "๐จ", "๐ฏ", "๐ฆ", "๐ฎ", "๐ท", "๐ธ", "๐ต", "๐", "๐ง", "๐ฆ", "๐ฆโโฌ", "๐ฅ", "๐ฆ", "๐ฆ
", "๐ฆ", "๐ฆ", "๐บ", "๐ฆ", "๐", "๐ท", "๐ฆ"];
var cardSymbols3 = ["๐", "๐", "๐", "๐", "๐", "๐๏ธ", "๐", "๐", "๐", "๐", "๐ป", "๐", "๐", "๐", "๐ต", "๐๏ธ", "๐ด", "๐ฒ", "๐บ", "๐ค", "โต๏ธ", "๐ถ", "๐ฉ๏ธ", "โ๏ธ", "๐ฐ๏ธ", "๐", "๐", "๐ธ"];
var cardSymbols4 = ["๐งฐ", "๐ช", "๐ง", "๐จ", "โ๏ธ", "๐ ๏ธ", "โ๏ธ", "๐ช", "๐ช", "๐ฉ", "โ๏ธ", "๐ชค", "๐งฑ", "๐งฒ", "๐ซ", "๐ฃ", "๐งจ", "๐ช", "๐ช", "๐ก๏ธ", "โ๏ธ", "๐ก๏ธ", "๐ชฆ", "๐", "๐ช", "๐", "๐", "๐", "๐ฌ", "โ๏ธ", "๐ช", "๐น", "๐ฅ", "๐ฅ", "๐ท", "๐บ", "๐ธ", "๐น", "๐ฅ", "๐ฅ"];
// --- Game settings ---
var maxLevel = 50;
var currentLevel = 1;
// Level configuration: grid size per level (increase difficulty)
function getLevelConfig(level) {
	// Level 1-5: 4x4, 6-10: 5x4, 11-15: 6x5, 16-20: 6x6, 21-25: 7x6
	if (level <= 5) {
		return {
			cols: 4,
			rows: 4
		};
	}
	if (level <= 10) {
		return {
			cols: 5,
			rows: 4
		};
	}
	if (level <= 15) {
		return {
			cols: 6,
			rows: 5
		};
	}
	if (level <= 20) {
		return {
			cols: 6,
			rows: 6
		};
	}
	if (level <= 35) {
		return {
			cols: 8,
			rows: 6
		};
	}
	if (level <= 45) {
		return {
			cols: 8,
			rows: 7
		};
	}
	if (level < 50) {
		return {
			cols: 8,
			rows: 7
		};
	}
	if (level >= 50) {
		return {
			cols: 10,
			rows: 8
		};
	}
	return {
		cols: 9,
		rows: 7
	};
}
var gridCols = getLevelConfig(currentLevel).cols;
var gridRows = getLevelConfig(currentLevel).rows;
var totalPairs = gridCols * gridRows / 2;
var cardSpacing = 36; // px between cards
// --- Card assets (simple colored rectangles) ---
// --- Game state ---
var cards = []; // All card objects
var firstCard = null;
var secondCard = null;
var lockInput = false; // Prevent input during animations
var moves = 0;
var matchesFound = 0;
var timer = null;
var elapsedTime = 0; // in seconds
var timerText = null;
var movesText = null;
// --- GUI: Moves, Timer, Level ---
var levelText = new Text2('Level: 1', {
	size: 80,
	fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
timerText = new Text2('Time: 0s', {
	size: 80,
	fill: 0xFFFFFF
});
timerText.anchor.set(0.5, 0);
LK.gui.top.addChild(timerText);
// Position GUI elements (timer right, avoid top-left 100x100)
// Align level indicator to top middle using LK.gui.top
levelText.x = -10;
levelText.y = 20;
timerText.x = 500;
// --- Timer for random swap after level 35 ---
var randomSwapTimer = null;
var randomSwapTimer10s = null;
var randomSwapTimer5s = null;
var randomSwapTimer2s = null;
// --- Swap queue for level 50+ to prevent overlapping swaps ---
var swapQueue = [];
var swapInProgress = false;
// Helper: Queue a swap (for level 50+)
function queueCardSwap(cardA, cardB) {
	swapQueue.push({
		cardA: cardA,
		cardB: cardB
	});
	processSwapQueue();
}
// Helper: Process swap queue (for level 50+)
function processSwapQueue() {
	if (swapInProgress || swapQueue.length === 0) {
		return;
	}
	swapInProgress = true;
	var swap = swapQueue.shift();
	var cardA = swap.cardA;
	var cardB = swap.cardB;
	var tempSymbol = cardA.symbol;
	var tempIndex = cardA.index;
	cardA.setSymbol(cardB.symbol);
	cardB.setSymbol(tempSymbol);
	// Swap index property as well
	cardA.index = cardB.index;
	cardB.index = tempIndex;
	var tempX = cardA.x;
	var tempY = cardA.y;
	var swapsDone = 0;
	function onSwapDone() {
		swapsDone++;
		if (swapsDone === 2) {
			swapInProgress = false;
			// Snap to grid to avoid drift
			cardA.x = Math.round(cardA.x);
			cardA.y = Math.round(cardA.y);
			cardB.x = Math.round(cardB.x);
			cardB.y = Math.round(cardB.y);
			processSwapQueue();
		}
	}
	tween(cardA, {
		x: cardB.x,
		y: cardB.y
	}, {
		duration: 1200,
		easing: tween.cubicInOut,
		onFinish: onSwapDone
	});
	tween(cardB, {
		x: tempX,
		y: tempY
	}, {
		duration: 1200,
		easing: tween.cubicInOut,
		onFinish: onSwapDone
	});
}
// --- Helper: Shuffle array (Fisher-Yates) ---
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;
	}
}
// --- Layout cards ---
function layoutCards() {
	// Remove old cards if any
	for (var i = 0; i < cards.length; i++) {
		cards[i].destroy();
	}
	cards = [];
	// Prepare symbols (duplicate and shuffle)
	var symbols = [];
	var useSymbols;
	if (currentLevel === 9 || currentLevel === 41) {
		useSymbols = cardSymbols3;
	} else {
		useSymbols = currentLevel >= 50 ? cardSymbols4 : currentLevel > 45 ? cardSymbols3 : currentLevel > 20 ? cardSymbols2 : cardSymbols;
	}
	for (var i = 0; i < totalPairs; i++) {
		symbols.push(useSymbols[i]);
		symbols.push(useSymbols[i]);
	}
	shuffleArray(symbols);
	// Card size: fit grid to 2048x2732 with spacing
	var availableWidth = 2048 - cardSpacing * (gridCols + 1);
	var availableHeight = 1800 - cardSpacing * (gridRows + 1); // leave space for GUI
	var cardWidth = Math.floor(availableWidth / gridCols);
	var cardHeight = Math.floor(availableHeight / gridRows);
	// Center grid
	var gridPixelWidth = cardWidth * gridCols + cardSpacing * (gridCols - 1);
	var gridPixelHeight = cardHeight * gridRows + cardSpacing * (gridRows - 1);
	var startX = Math.floor((2048 - gridPixelWidth) / 2) + cardWidth / 2;
	var startY = 350 + cardHeight / 2; // leave space for GUI
	// Create cards
	for (var row = 0; row < gridRows; row++) {
		for (var col = 0; col < gridCols; col++) {
			var idx = row * gridCols + col;
			var card = new Card();
			card.setSize(cardWidth, cardHeight);
			card.setSymbol(symbols[idx]);
			card.index = idx;
			card.x = startX + col * (cardWidth + cardSpacing);
			card.y = startY + row * (cardHeight + cardSpacing);
			card.showFaceDown();
			game.addChild(card);
			cards.push(card);
		}
	}
}
// --- Reset game state ---
function resetGame() {
	firstCard = null;
	secondCard = null;
	lockInput = false;
	moves = 0;
	matchesFound = 0;
	if (currentLevel === 16) {
		elapsedTime = 150;
	} else if (currentLevel === 21) {
		elapsedTime = 150;
	} else if (currentLevel === 26) {
		elapsedTime = 200;
	} else if (currentLevel === 31) {
		elapsedTime = 200;
	} else if (currentLevel === 36) {
		elapsedTime = 250;
	} else if (currentLevel === 41) {
		elapsedTime = 250;
	} else if (currentLevel === 46) {
		elapsedTime = 300;
	} else if (currentLevel === 50) {
		elapsedTime = 500;
	} else if (currentLevel === 1) {
		elapsedTime = 100;
	} else if (currentLevel === 6 || currentLevel === 11) {
		elapsedTime = 120;
	} else {
		elapsedTime = 120;
	}
	if (typeof levelText !== "undefined") {
		// At level 6, display Level: 2 instead of Level: 6
		if (currentLevel === 6) {
			levelText.setText('Level: 2');
		} else if (currentLevel === 11) {
			levelText.setText('Level: 3');
		} else if (currentLevel === 16) {
			levelText.setText('Level: 4');
		} else if (currentLevel === 21) {
			levelText.setText('Level: 5');
		} else if (currentLevel === 26) {
			levelText.setText('Level: 6');
		} else if (currentLevel === 31) {
			levelText.setText('Level: 7');
		} else if (currentLevel === 36) {
			levelText.setText('Level: 8');
		} else if (currentLevel === 41) {
			levelText.setText('Level: 9');
		} else if (currentLevel === 46) {
			levelText.setText('Level: 10');
		} else if (currentLevel === 50) {
			levelText.setText('Final Level');
		} else if (currentLevel === 26) {
			levelText.setText('Level: 6');
		} else if (currentLevel === 21) {
			levelText.setText('Level: 5');
		} else if (currentLevel === 16) {
			levelText.setText('Level: 4');
		} else {
			levelText.setText('Level: ' + currentLevel);
		}
	}
	// Update grid size for this level
	var config = getLevelConfig(currentLevel);
	gridCols = config.cols;
	gridRows = config.rows;
	totalPairs = gridCols * gridRows / 2;
	timerText.setText('Time: ' + elapsedTime + 's');
	layoutCards();
	// Play lastlevelmusic at level 50
	if (currentLevel === 50) {
		LK.playMusic('lastlevelmusic');
	}
	if (timer) {
		LK.clearInterval(timer);
	}
	timer = LK.setInterval(function () {
		elapsedTime--;
		if (elapsedTime <= 0) {
			elapsedTime = 0;
			timerText.setText('Time: 0s');
			LK.clearInterval(timer);
			LK.showGameOver(); // You lose if timer gets to 0
			return;
		} else {
			timerText.setText('Time: ' + elapsedTime + 's');
		}
	}, 1000);
	// --- Timer-based random swap after level 35 ---
	if (typeof randomSwapTimer !== "undefined" && randomSwapTimer) {
		LK.clearInterval(randomSwapTimer);
		randomSwapTimer = null;
	}
	if (currentLevel >= 50) {
		// At level 50, swap every 2s, 5s, 10s, and 20s using the swap queue
		swapQueue = [];
		swapInProgress = false;
		randomSwapTimer = LK.setInterval(function () {
			// 20s swap
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				queueCardSwap(cardA, cardB);
			}
		}, 20000); // 20 seconds
		// Add a second timer for 10s swap
		if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
			LK.clearInterval(randomSwapTimer10s);
			randomSwapTimer10s = null;
		}
		randomSwapTimer10s = LK.setInterval(function () {
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				queueCardSwap(cardA, cardB);
			}
		}, 10000); // 10 seconds
		// Add a third timer for 5s swap
		if (typeof randomSwapTimer5s !== "undefined" && randomSwapTimer5s) {
			LK.clearInterval(randomSwapTimer5s);
			randomSwapTimer5s = null;
		}
		randomSwapTimer5s = LK.setInterval(function () {
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				queueCardSwap(cardA, cardB);
			}
		}, 5000); // 5 seconds
		// Add a fourth timer for 2s swap
		if (typeof randomSwapTimer2s !== "undefined" && randomSwapTimer2s) {
			LK.clearInterval(randomSwapTimer2s);
			randomSwapTimer2s = null;
		}
		randomSwapTimer2s = LK.setInterval(function () {
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				queueCardSwap(cardA, cardB);
			}
		}, 2000); // 2 seconds
	} else if (currentLevel > 40) {
		// Only 20s swap
		randomSwapTimer = LK.setInterval(function () {
			var swappable = [];
			for (var i = 0; i < cards.length; i++) {
				if (!cards[i].isMatched && !cards[i].isFaceUp) {
					swappable.push(cards[i]);
				}
			}
			if (swappable.length >= 2) {
				var idx1 = Math.floor(Math.random() * swappable.length);
				var idx2 = idx1;
				while (idx2 === idx1) {
					idx2 = Math.floor(Math.random() * swappable.length);
				}
				var cardA = swappable[idx1];
				var cardB = swappable[idx2];
				var tempSymbol = cardA.symbol;
				var tempIndex = cardA.index;
				cardA.setSymbol(cardB.symbol);
				cardB.setSymbol(tempSymbol);
				// Swap index property as well
				cardA.index = cardB.index;
				cardB.index = tempIndex;
				var tempX = cardA.x;
				var tempY = cardA.y;
				tween(cardA, {
					x: cardB.x,
					y: cardB.y
				}, {
					duration: 1200,
					easing: tween.cubicInOut
				});
				tween(cardB, {
					x: tempX,
					y: tempY
				}, {
					duration: 1200,
					easing: tween.cubicInOut
				});
			}
		}, 20000); // 20 seconds
		// Clear 10s timer if it exists
		if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
			LK.clearInterval(randomSwapTimer10s);
			randomSwapTimer10s = null;
		}
	} else {
		randomSwapTimer = null;
		if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
			LK.clearInterval(randomSwapTimer10s);
			randomSwapTimer10s = null;
		}
	}
}
// --- Card tap handler ---
game.onCardTapped = function (card) {
	if (lockInput || card.isFaceUp || card.isMatched) {
		return;
	}
	if (!firstCard) {
		firstCard = card;
		LK.getSound('cardchose').play();
		card.flipUp();
	} else if (!secondCard && card !== firstCard) {
		secondCard = card;
		lockInput = true;
		LK.getSound('cardchose').play();
		card.flipUp(function () {
			// Check for match after both are face up
			LK.setTimeout(function () {
				checkMatch();
			}, 150);
		});
	}
};
// --- Check for match ---
function checkMatch() {
	moves++;
	// After level 40, swap 2 random cards every 2 moves; after level 30, every 4 moves; after level 25, every 5 moves; after level 20, every 10 moves
	var shouldSwap = false;
	if (currentLevel >= 50 && moves % 1 === 0 && cards.length > 1) {
		shouldSwap = true;
	} else if (currentLevel > 40 && moves % 2 === 0 && cards.length > 1) {
		shouldSwap = true;
	} else if (currentLevel > 30 && moves % 4 === 0 && cards.length > 1) {
		shouldSwap = true;
	} else if (currentLevel > 25 && moves % 5 === 0 && cards.length > 1) {
		shouldSwap = true;
	} else if (currentLevel > 20 && moves % 10 === 0 && cards.length > 1) {
		shouldSwap = true;
	}
	if (shouldSwap) {
		// Find all cards that are not matched and not currently face up
		var swappable = [];
		for (var i = 0; i < cards.length; i++) {
			if (!cards[i].isMatched && !cards[i].isFaceUp) {
				swappable.push(cards[i]);
			}
		}
		if (swappable.length >= 2) {
			var idx1 = Math.floor(Math.random() * swappable.length);
			var idx2 = idx1;
			while (idx2 === idx1) {
				idx2 = Math.floor(Math.random() * swappable.length);
			}
			var cardA = swappable[idx1];
			var cardB = swappable[idx2];
			if (currentLevel >= 50) {
				queueCardSwap(cardA, cardB);
			} else {
				// Old behavior for lower levels
				var tempSymbol = cardA.symbol;
				var tempIndex = cardA.index;
				cardA.setSymbol(cardB.symbol);
				cardB.setSymbol(tempSymbol);
				// Swap index property as well
				cardA.index = cardB.index;
				cardB.index = tempIndex;
				var tempX = cardA.x;
				var tempY = cardA.y;
				tween(cardA, {
					x: cardB.x,
					y: cardB.y
				}, {
					duration: 1200,
					easing: tween.cubicInOut
				});
				tween(cardB, {
					x: tempX,
					y: tempY
				}, {
					duration: 1200,
					easing: tween.cubicInOut
				});
			}
		}
	}
	if (firstCard && secondCard && firstCard.symbol === secondCard.symbol) {
		// Match!
		LK.getSound('cardmatch').play();
		firstCard.setMatched();
		secondCard.setMatched();
		matchesFound++;
		// Subtle scale animation for matched cards, then disappear
		if (firstCard && secondCard) {
			var matched1 = firstCard;
			var matched2 = secondCard;
			if (matched1) {
				tween(matched1, {
					scaleX: 1.15,
					scaleY: 1.15
				}, {
					duration: 80,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						if (matched1) {
							tween(matched1, {
								scaleX: 0,
								scaleY: 0,
								alpha: 0
							}, {
								duration: 120,
								onFinish: function onFinish() {
									if (matched1) {
										matched1.destroy();
									}
								}
							});
						}
					}
				});
			}
			if (matched2) {
				tween(matched2, {
					scaleX: 1.15,
					scaleY: 1.15
				}, {
					duration: 80,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						if (matched2) {
							tween(matched2, {
								scaleX: 0,
								scaleY: 0,
								alpha: 0
							}, {
								duration: 120,
								onFinish: function onFinish() {
									if (matched2) {
										matched2.destroy();
									}
								}
							});
						}
					}
				});
			}
		}
		// Check for win
		if (matchesFound === totalPairs) {
			LK.clearInterval(timer);
			LK.setScore(moves); // Use moves as score (lower is better)
			if (currentLevel < maxLevel) {
				// Play level completed sound
				LK.getSound('levelcompleted').play();
				// Show "Level Completed" text in the center of the screen
				var levelCompletedText = new Text2('Level Completed!', {
					size: 180,
					fill: 0xFFD700,
					font: "Impact"
				});
				levelCompletedText.anchor.set(0.5, 0.5);
				levelCompletedText.x = 2048 / 2;
				levelCompletedText.y = 2732 / 2;
				game.addChild(levelCompletedText);
				if (currentLevel === 1) {
					currentLevel = 6;
				} else if (currentLevel === 6) {
					currentLevel = 11;
				} else if (currentLevel === 11) {
					currentLevel = 16;
				} else if (currentLevel === 16) {
					currentLevel = 21;
				} else if (currentLevel === 21) {
					currentLevel = 26;
				} else if (currentLevel === 26) {
					currentLevel = 31;
				} else if (currentLevel === 31) {
					currentLevel = 36;
				} else if (currentLevel === 36) {
					currentLevel = 41;
				} else if (currentLevel === 41) {
					currentLevel = 46;
				} else if (currentLevel === 46) {
					currentLevel = 50;
				} else {
					currentLevel++;
				}
				// Short delay before next level, remove text before reset
				LK.setTimeout(function () {
					if (levelCompletedText && typeof levelCompletedText.destroy === "function") {
						levelCompletedText.destroy();
					}
					resetGame();
				}, 1200);
			} else {
				LK.showYouWin();
			}
		}
		// Reset selection
		firstCard = null;
		secondCard = null;
		lockInput = false;
	} else {
		// Not a match: flip both back after short delay
		LK.setTimeout(function () {
			if (firstCard && typeof firstCard.flipDown === "function") {
				firstCard.flipDown();
			}
			if (secondCard && typeof secondCard.flipDown === "function") {
				secondCard.flipDown(function () {
					firstCard = null;
					secondCard = null;
					lockInput = false;
				});
			} else {
				// If secondCard is missing, still reset state after delay
				firstCard = null;
				secondCard = null;
				lockInput = false;
			}
		}, 250);
	}
}
// --- Game update (not used for logic, but required) ---
game.update = function () {
	// Movement-based card swap: if two cards are dragged/moved to each other's position, swap their symbol and index
	// (Assuming you have a drag/move system, e.g. for future features or mobile drag-to-swap)
	// This is a defensive implementation: checks for cards that overlap and are not already matched or face up
	for (var i = 0; i < cards.length; i++) {
		var cardA = cards[i];
		if (cardA.isMatched || cardA.isFaceUp) continue;
		for (var j = i + 1; j < cards.length; j++) {
			var cardB = cards[j];
			if (cardB.isMatched || cardB.isFaceUp) continue;
			// If cardA and cardB overlap (bounding box collision)
			if (Math.abs(cardA.x - cardB.x) < (cardA.cardWidth + cardB.cardWidth) / 2 && Math.abs(cardA.y - cardB.y) < (cardA.cardHeight + cardB.cardHeight) / 2) {
				// Only swap if not already swapped this frame
				if (!cardA._swappedThisFrame && !cardB._swappedThisFrame) {
					// Swap symbol and index
					var tempSymbol = cardA.symbol;
					var tempIndex = cardA.index;
					cardA.setSymbol(cardB.symbol);
					cardB.setSymbol(tempSymbol);
					cardA.index = cardB.index;
					cardB.index = tempIndex;
					cardA._swappedThisFrame = true;
					cardB._swappedThisFrame = true;
				}
			}
		}
	}
	// Reset swap flags for next frame
	for (var i = 0; i < cards.length; i++) {
		cards[i]._swappedThisFrame = false;
	}
};
// --- DEV Button: Center bottom, always visible, go to next level ---
// (Hidden for production, but function remains for later update test)
var devBtn = new Text2('DEV', {
	size: 90,
	fill: 0xFF00FF,
	font: "Impact"
});
devBtn.anchor.set(0.5, 1);
// Place DEV button slightly left of center
devBtn.x = -120;
devBtn.y = -50;
// LK.gui.bottom.addChild(devBtn); // HIDDEN: do not add to GUI, but keep logic for later test
devBtn.visible = false; // Hide from view
// DEV button handler: go to next level immediately (max 25)
devBtn.down = function (x, y, obj) {
	if (matchesFound === totalPairs) {
		// Already won, do nothing
		return;
	}
	// Replicate the level pass logic from checkMatch()
	if (currentLevel < maxLevel) {
		// Play level completed sound
		LK.getSound('levelcompleted').play();
		// Show "Level Completed" text in the center of the screen
		var levelCompletedText = new Text2('Level Completed!', {
			size: 180,
			fill: 0xFFD700,
			font: "Impact"
		});
		levelCompletedText.anchor.set(0.5, 0.5);
		levelCompletedText.x = 2048 / 2;
		levelCompletedText.y = 2732 / 2;
		game.addChild(levelCompletedText);
		if (currentLevel === 1) {
			currentLevel = 6;
		} else if (currentLevel === 6) {
			currentLevel = 11;
		} else if (currentLevel === 11) {
			currentLevel = 16;
		} else if (currentLevel === 16) {
			currentLevel = 21;
		} else if (currentLevel === 21) {
			currentLevel = 26;
		} else if (currentLevel === 26) {
			levelText.setText('Level: 6');
			currentLevel = 31;
		} else if (currentLevel === 31) {
			levelText.setText('Level: 7');
			currentLevel = 36;
		} else if (currentLevel === 36) {
			levelText.setText('Level: 8');
			levelText.setText('Level: 8');
			currentLevel = 41;
		} else if (currentLevel === 41) {
			levelText.setText('Level: 9');
			currentLevel = 46;
		} else if (currentLevel === 46) {
			levelText.setText('Level: 10');
			currentLevel = 50;
		} else if (currentLevel === 50) {
			levelText.setText('Final Level');
			currentLevel = 50;
		} else {
			currentLevel++;
		}
		LK.setTimeout(function () {
			if (levelCompletedText && typeof levelCompletedText.destroy === "function") {
				levelCompletedText.destroy();
			}
			resetGame();
		}, 1200);
	} else {
		LK.showYouWin();
	}
};
// --- Reveal Button: Center bottom, always visible, reveals all cards ---
// (Hidden for production, but function remains for later update test)
var revealBtn = new Text2('Reveal', {
	size: 90,
	fill: 0x00CCFF,
	font: "Impact"
});
revealBtn.anchor.set(0.5, 1);
// Place Reveal button slightly right of center
revealBtn.x = 120;
revealBtn.y = -50;
// LK.gui.bottom.addChild(revealBtn); // HIDDEN: do not add to GUI, but keep logic for later test
revealBtn.visible = false; // Hide from view
// Reveal button handler: toggle all cards between revealed and hidden
var revealState = false; // false = hidden, true = revealed
revealBtn.down = function (x, y, obj) {
	revealState = !revealState;
	if (revealState) {
		// Reveal all cards (that are not matched)
		for (var i = 0; i < cards.length; i++) {
			var card = cards[i];
			if (!card.isFaceUp && !card.isMatched) {
				card.flipUp();
			}
		}
		revealBtn.setText('Hide');
	} else {
		// Hide all cards (that are not matched)
		for (var i = 0; i < cards.length; i++) {
			var card = cards[i];
			if (card.isFaceUp && !card.isMatched) {
				card.flipDown();
			}
		}
		revealBtn.setText('Reveal');
	}
};
// --- Start game ---
LK.playMusic('gamemusic');
resetGame();
// --- Game over handler (reset on game over) ---
game.onGameOver = function () {
	if (timer) {
		LK.clearInterval(timer);
	}
	if (typeof randomSwapTimer !== "undefined" && randomSwapTimer) {
		LK.clearInterval(randomSwapTimer);
		randomSwapTimer = null;
	}
	if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
		LK.clearInterval(randomSwapTimer10s);
		randomSwapTimer10s = null;
	}
	if (typeof randomSwapTimer5s !== "undefined" && randomSwapTimer5s) {
		LK.clearInterval(randomSwapTimer5s);
		randomSwapTimer5s = null;
	}
	if (typeof randomSwapTimer2s !== "undefined" && randomSwapTimer2s) {
		LK.clearInterval(randomSwapTimer2s);
		randomSwapTimer2s = null;
	}
	currentLevel = 1;
	LK.stopMusic();
	resetGame();
};
// --- Win handler (reset on win) ---
game.onYouWin = function () {
	if (timer) {
		LK.clearInterval(timer);
	}
	if (typeof randomSwapTimer !== "undefined" && randomSwapTimer) {
		LK.clearInterval(randomSwapTimer);
		randomSwapTimer = null;
	}
	if (typeof randomSwapTimer10s !== "undefined" && randomSwapTimer10s) {
		LK.clearInterval(randomSwapTimer10s);
		randomSwapTimer10s = null;
	}
	if (typeof randomSwapTimer5s !== "undefined" && randomSwapTimer5s) {
		LK.clearInterval(randomSwapTimer5s);
		randomSwapTimer5s = null;
	}
	if (typeof randomSwapTimer2s !== "undefined" && randomSwapTimer2s) {
		LK.clearInterval(randomSwapTimer2s);
		randomSwapTimer2s = null;
	}
	currentLevel = 1;
	LK.stopMusic();
	// Game will reset automatically by LK
};