/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
	highScore: 0,
	leaderboard: []
});
/**** 
* Classes
****/ 
var MenuButton = Container.expand(function (text, width, height) {
	var self = Container.call(this);
	// Create background
	var background = self.attachAsset('tile', {
		width: width || 300,
		height: height || 100,
		tint: 0x8f7a66,
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Add text
	var buttonText = new Text2(text, {
		size: 70,
		fill: 0xFFFFFF
	});
	buttonText.anchor.set(0.5, 0.5);
	self.addChild(buttonText);
	// Make interactive
	self.interactive = true;
	return self;
});
// -------------- Particle -----------------
var Particle = Container.expand(function (color) {
	var self = Container.call(this);
	var size = Math.random() * 35 + 20; // Make particles bigger
	// Create particle
	var particle = self.attachAsset('tile', {
		width: size,
		height: size,
		tint: color || 0xEDC22E,
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Random velocity
	self.vx = (Math.random() - 0.5) * 10;
	self.vy = (Math.random() - 0.5) * 10;
	// Update particle movement
	self.update = function () {
		self.x += self.vx;
		self.y += self.vy;
		self.alpha -= 0.02;
		// Remove when faded out
		if (self.alpha <= 0 && self.parent) {
			self.parent.removeChild(self);
		}
	};
	return self;
});
// -------------- Popup -----------------
var Popup = Container.expand(function (title, content) {
	var self = Container.call(this);
	// Ensure the whole popup container itself is centered
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	// Background overlay
	var overlay = self.attachAsset('tile', {
		width: 2048,
		height: 2732,
		tint: 0x000000,
		anchorX: 0.5,
		anchorY: 0.5
	});
	overlay.alpha = 0.7;
	// Popup panel
	var popup = self.attachAsset('tile', {
		width: 1600,
		height: 1600,
		tint: 0xFAF8EF,
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Title
	var titleText = new Text2(title, {
		size: 80,
		fill: 0x776E65
	});
	titleText.anchor.set(0.5, 0);
	titleText.y = -700;
	popup.addChild(titleText);
	// Content
	var contentText = new Text2(content, {
		size: 50,
		fill: 0x776E65
	});
	contentText.anchor.set(0.5, 0.5);
	popup.addChild(contentText);
	// Close button
	var closeButton = new MenuButton("Close", 200, 80);
	closeButton.y = 700;
	closeButton.down = function () {
		LK.getSound('click').play();
		if (self.parent) {
			self.parent.removeChild(self);
		}
	};
	popup.addChild(closeButton);
	return self;
});
// -------------- Tile -----------------
var Tile = Container.expand(function (value) {
	var self = Container.call(this);
	self.value = value || 0;
	// Background tile – uses global CELL_SIZE
	var background = self.attachAsset('tile', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: CELL_SIZE,
		height: CELL_SIZE
	});
	// Text
	self.valueText = new Text2(self.value > 0 ? self.value.toString() : '', {
		size: 60,
		fill: 0x776E65
	});
	self.valueText.anchor.set(0.5, 0.5);
	self.addChild(self.valueText);
	// Update appearance helper
	self.updateAppearance = function () {
		var colors = {
			0: 0xCDC1B4,
			2: 0xEEE4DA,
			4: 0xEDE0C8,
			8: 0xF2B179,
			16: 0xF59563,
			32: 0xF67C5F,
			64: 0xF65E3B,
			128: 0xEDCF72,
			256: 0xEDCC61,
			512: 0xEDC850,
			1024: 0xEDC53F,
			2048: 0xEDC22E
		};
		background.tint = colors[self.value] || 0xCDC1B4;
		self.valueText.setText(self.value > 0 ? self.value.toString() : '');
		var fontSize = 60;
		if (self.value >= 1000) {
			fontSize = 40;
		} else if (self.value >= 100) {
			fontSize = 50;
		}
		if (self.valueText && self.valueText.style) {
			self.valueText.style.size = fontSize;
			self.valueText.style.fill = self.value <= 4 ? "#776E65" : "#FFFFFF";
		}
	};
	self.setValue = function (newValue) {
		self.value = newValue;
		self.updateAppearance();
	};
	self.updateAppearance();
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x333333
});
/**** 
* Game Code
****/ 
// Game constants
var GRID_SIZE = 4;
var CELL_SIZE = 250; //  ←  Bigger tiles
var CELL_SPACING = 15;
var GRID_PADDING = 20;
var START_SCORE = 1000000;
var INITIAL_DEDUCT_RATE = 100;
var MAX_DEDUCT_RATE = 5000;
var DEDUCT_INCREASE_TIME = 10000;
// Game state variables
var grid = [];
var tiles = [];
var score = START_SCORE;
var gameActive = false;
var lastUpdateTime = 0;
var currentDeductRate = INITIAL_DEDUCT_RATE;
var gameWon = false;
var movesInProgress = 0;
// UI elements
var boardBackground;
var scoreText;
var timerText;
var instructionsText;
var restartButton; // NEW
// ------------------------------------------------------------
//  Board / UI setup
// ------------------------------------------------------------
function initializeBoard() {
	var boardWidth = GRID_SIZE * CELL_SIZE + (GRID_SIZE + 1) * CELL_SPACING + 2 * GRID_PADDING;
	var boardHeight = boardWidth;
	boardBackground = LK.getAsset('tile', {
		width: boardWidth,
		height: boardHeight,
		anchorX: 0.5,
		anchorY: 0.5,
		tint: 0xBBADA0
	});
	boardBackground.x = 2048 / 2;
	boardBackground.y = 2732 / 2;
	game.addChild(boardBackground);
	// Empty cells
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			var cellBg = LK.getAsset('tile', {
				width: CELL_SIZE,
				height: CELL_SIZE,
				anchorX: 0.5,
				anchorY: 0.5,
				tint: 0xCDC1B4
			});
			cellBg.x = getPositionX(j);
			cellBg.y = getPositionY(i);
			boardBackground.addChild(cellBg);
		}
	}
}
function initializeUI() {
	// Score
	scoreText = new Text2("Score: " + score, {
		size: 50,
		fill: 0x776E65
	});
	scoreText.anchor.set(0.5, 0);
	// Timer (deduction rate)
	timerText = new Text2("", {
		size: 40,
		fill: 0x776E65
	});
	timerText.anchor.set(0.5, 0);
	timerText.y = 70;
	// Instructions footer
	instructionsText = new Text2("Glaud warns: Hurry, time is running out.\nSwipe to move the tiles.\nCombine the same numbers to reach 2048!", {
		size: 40,
		fill: 0x776E65
	});
	instructionsText.anchor.set(0.5, 1);
	// Menu title
	var menuTitle = new Text2("2048", {
		size: 120,
		fill: 0x776E65
	});
	menuTitle.anchor.set(0.5, 0);
	menuTitle.x = 2048 / 2;
	menuTitle.y = 400;
	game.addChild(menuTitle);
	// Menu buttons
	var startButton = new MenuButton("Start Game", 900, 150);
	startButton.x = 2048 / 2;
	startButton.y = 2732 / 2 - 120;
	game.addChild(startButton);
	var leaderboardButton = new MenuButton("Leaderboard", 900, 150);
	leaderboardButton.x = 2048 / 2;
	leaderboardButton.y = 2732 / 2 + 60;
	game.addChild(leaderboardButton);
	var instructionsButton = new MenuButton("Instructions", 900, 150);
	instructionsButton.x = 2048 / 2;
	instructionsButton.y = 2732 / 2 + 240;
	game.addChild(instructionsButton);
	// ---------------- Restart button (hidden until game starts) ----------------
	restartButton = new MenuButton("Restart", 400, 150);
	restartButton.visible = false;
	restartButton.down = function () {
		LK.getSound('click').play();
		resetGame();
	};
	LK.gui.top.addChild(restartButton);
	// Leaderboard container (hidden initially)
	var leaderboardContainer = new Container();
	leaderboardContainer.y = 220;
	leaderboardContainer.visible = false;
	LK.gui.top.addChild(leaderboardContainer);
	// Event handlers
	startButton.down = function () {
		LK.getSound('click').play();
		// Hide menu elements
		menuTitle.visible = false;
		startButton.visible = false;
		leaderboardButton.visible = false;
		instructionsButton.visible = false;
		resetGame();
	};
	leaderboardButton.down = function () {
		LK.getSound('click').play();
		showLeaderboard();
	};
	instructionsButton.down = function () {
		LK.getSound('click').play();
		showInstructions();
	};
	// Add persistent UI to GUI layers
	LK.gui.top.addChild(scoreText);
	LK.gui.top.addChild(timerText);
	LK.gui.bottom.addChild(instructionsText);
	updateUIPositions();
}
function updateUIPositions() {
	scoreText.y = 20;
	timerText.y = 80;
	instructionsText.y = -20;
	if (boardBackground && restartButton) {
		// Place restart button just above the board
		restartButton.x = 0;
		restartButton.y = 400;
		// restartButton.x = boardBackground.x;
		// restartButton.y = boardBackground.y - boardBackground.height / 2 - 60;
	}
}
// ------------------------------------------------------------
//  Board effects
// ------------------------------------------------------------
function shakeBoard(intensity) {
	if (!boardBackground) {
		return;
	}
	// Save original position
	var originalX = boardBackground.x;
	var originalY = boardBackground.y;
	// Cancel any ongoing shake animations
	tween.stop(boardBackground, {
		x: true,
		y: true
	});
	// Shake in random direction
	tween(boardBackground, {
		x: originalX + (Math.random() - 0.5) * intensity,
		y: originalY + (Math.random() - 0.5) * intensity
	}, {
		duration: 50,
		onFinish: function onFinish() {
			// Shake again in different direction
			tween(boardBackground, {
				x: originalX + (Math.random() - 0.5) * intensity,
				y: originalY + (Math.random() - 0.5) * intensity
			}, {
				duration: 50,
				onFinish: function onFinish() {
					// Return to original position
					tween(boardBackground, {
						x: originalX,
						y: originalY
					}, {
						duration: 50
					});
				}
			});
		}
	});
}
// Create particles at tile position
function createParticles(x, y, value) {
	// Get color based on tile value
	var colors = {
		2: 0xEEE4DA,
		4: 0xEDE0C8,
		8: 0xF2B179,
		16: 0xF59563,
		32: 0xF67C5F,
		64: 0xF65E3B,
		128: 0xEDCF72,
		256: 0xEDCC61,
		512: 0xEDC850,
		1024: 0xEDC53F,
		2048: 0xEDC22E
	};
	var color = colors[value] || 0xEDC22E;
	// Create multiple particles
	for (var i = 0; i < 12; i++) {
		var particle = new Particle(color);
		particle.x = x;
		particle.y = y;
		boardBackground.addChild(particle);
	}
}
//  Helper positions (use CELL_SIZE)
// ------------------------------------------------------------
function getPositionX(col) {
	return -((GRID_SIZE * CELL_SIZE + (GRID_SIZE + 1) * CELL_SPACING) / 2) + CELL_SPACING + col * (CELL_SIZE + CELL_SPACING) + CELL_SIZE / 2;
}
function getPositionY(row) {
	return -((GRID_SIZE * CELL_SIZE + (GRID_SIZE + 1) * CELL_SPACING) / 2) + CELL_SPACING + row * (CELL_SIZE + CELL_SPACING) + CELL_SIZE / 2;
}
// Initialize the game grid
function initializeGrid() {
	grid = [];
	tiles = [];
	// Create empty grid
	for (var i = 0; i < GRID_SIZE; i++) {
		grid[i] = [];
		tiles[i] = [];
		for (var j = 0; j < GRID_SIZE; j++) {
			grid[i][j] = 0;
			tiles[i][j] = null;
		}
	}
	// Add initial tiles
	addRandomTile();
	addRandomTile();
}
// Add a random tile (2 or 4) to an empty cell
function addRandomTile() {
	var emptyCells = [];
	// Find all empty cells
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			if (grid[i][j] === 0) {
				emptyCells.push({
					row: i,
					col: j
				});
			}
		}
	}
	// If there are no empty cells, return
	if (emptyCells.length === 0) {
		return;
	}
	// Choose a random empty cell
	var randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
	// Create a tile with value 2 (90% chance) or 4 (10% chance)
	var value = Math.random() < 0.9 ? 2 : 4;
	grid[randomCell.row][randomCell.col] = value;
	// Create and add the tile object
	var tile = new Tile(value);
	tile.x = getPositionX(randomCell.col);
	tile.y = getPositionY(randomCell.row);
	tile.scale.x = 0;
	tile.scale.y = 0;
	boardBackground.addChild(tile);
	tiles[randomCell.row][randomCell.col] = tile;
	LK.getSound('spawn').play();
	// Animate the tile appearing with a bounce effect
	tween(tile.scale, {
		x: 1.2,
		y: 1.2
	}, {
		duration: 150,
		easing: tween.easeOutQuad,
		onFinish: function onFinish() {
			tween(tile.scale, {
				x: 1,
				y: 1
			}, {
				duration: 100,
				easing: tween.easeInQuad
			});
		}
	});
}
// Move tiles in a specific direction
function moveTiles(direction) {
	if (!gameActive || movesInProgress > 0) {
		return;
	}
	var hasMoved = false;
	var rowStart, rowEnd, rowStep;
	var colStart, colEnd, colStep;
	var tileAdded = false; // Track if a tile has been added in this move
	// Set up iteration direction based on swipe direction
	if (direction === 'up') {
		rowStart = 1;
		rowEnd = GRID_SIZE;
		rowStep = 1;
		colStart = 0;
		colEnd = GRID_SIZE;
		colStep = 1;
	} else if (direction === 'down') {
		rowStart = GRID_SIZE - 2;
		rowEnd = -1;
		rowStep = -1;
		colStart = 0;
		colEnd = GRID_SIZE;
		colStep = 1;
	} else if (direction === 'left') {
		rowStart = 0;
		rowEnd = GRID_SIZE;
		rowStep = 1;
		colStart = 1;
		colEnd = GRID_SIZE;
		colStep = 1;
	} else if (direction === 'right') {
		rowStart = 0;
		rowEnd = GRID_SIZE;
		rowStep = 1;
		colStart = GRID_SIZE - 2;
		colEnd = -1;
		colStep = -1;
	}
	// Create a temporary grid to track merged tiles
	var mergedGrid = [];
	for (var i = 0; i < GRID_SIZE; i++) {
		mergedGrid[i] = [];
		for (var j = 0; j < GRID_SIZE; j++) {
			mergedGrid[i][j] = false;
		}
	}
	// Perform the move
	for (var i = rowStart; i !== rowEnd; i += rowStep) {
		for (var j = colStart; j !== colEnd; j += colStep) {
			if (grid[i][j] !== 0) {
				var result = moveTile(i, j, direction, mergedGrid);
				if (result.moved) {
					hasMoved = true;
				}
			}
		}
	}
	// If no tiles moved, don't add a new random tile
	if (!hasMoved) {
		return;
	}
	// Play move sound
	LK.getSound('move').play();
	// shakeBoard(30);
	// Add a new random tile after the animation completes
	LK.setTimeout(function () {
		if (gameActive) {
			// Only add one tile per move
			if (!tileAdded) {
				addRandomTile();
				tileAdded = true;
			}
			// Check for game over
			if (!canMove()) {
				// Only trigger game over if we haven't won already
				if (!gameWon) {
					gameActive = false;
					LK.getSound('gameover').play();
					// Update high score if needed
					if (score > storage.highScore) {
						storage.highScore = score;
					}
					// Add score to leaderboard
					if (score > 0) {
						// Ensure leaderboard exists
						if (!Array.isArray(storage.leaderboard)) {
							storage.leaderboard = [];
						}
						// Add score to leaderboard
						storage.leaderboard.push(score);
						// Sort leaderboard (highest scores first)
						storage.leaderboard.sort(function (a, b) {
							return b - a;
						});
						// Keep only top 5 scores
						if (storage.leaderboard.length > 5) {
							storage.leaderboard = storage.leaderboard.slice(0, 5);
						}
						// Update the leaderboard display
						updateLeaderboard();
					}
					LK.setTimeout(function () {
						LK.showGameOver();
					}, 1000);
				}
			}
		}
	}, 250);
}
// Move a single tile in the specified direction
function moveTile(row, col, direction, mergedGrid) {
	var targetRow = row;
	var targetCol = col;
	var moved = false;
	/* ------------------------------------------------------------------
		1.  IDENTICAL directional search loops (no change)
	------------------------------------------------------------------ */ 
	if (direction === 'up') {
		while (targetRow > 0 && (grid[targetRow - 1][targetCol] === 0 || grid[targetRow - 1][targetCol] === grid[row][col] && !mergedGrid[targetRow - 1][targetCol])) {
			if (grid[targetRow - 1][targetCol] === 0) {
				targetRow--;
			} else {
				targetRow--;
				mergedGrid[targetRow][targetCol] = true;
				break;
			}
		}
	} else if (direction === 'down') {
		while (targetRow < GRID_SIZE - 1 && (grid[targetRow + 1][targetCol] === 0 || grid[targetRow + 1][targetCol] === grid[row][col] && !mergedGrid[targetRow + 1][targetCol])) {
			if (grid[targetRow + 1][targetCol] === 0) {
				targetRow++;
			} else {
				targetRow++;
				mergedGrid[targetRow][targetCol] = true;
				break;
			}
		}
	} else if (direction === 'left') {
		while (targetCol > 0 && (grid[targetRow][targetCol - 1] === 0 || grid[targetRow][targetCol - 1] === grid[row][col] && !mergedGrid[targetRow][targetCol - 1])) {
			if (grid[targetRow][targetCol - 1] === 0) {
				targetCol--;
			} else {
				targetCol--;
				mergedGrid[targetRow][targetCol] = true;
				break;
			}
		}
	} else if (direction === 'right') {
		while (targetCol < GRID_SIZE - 1 && (grid[targetRow][targetCol + 1] === 0 || grid[targetRow][targetCol + 1] === grid[row][col] && !mergedGrid[targetRow][targetCol + 1])) {
			if (grid[targetRow][targetCol + 1] === 0) {
				targetCol++;
			} else {
				targetCol++;
				mergedGrid[targetRow][targetCol] = true;
				break;
			}
		}
	}
	/* ------------------------------------------------------------------
		2.  Same move bookkeeping, but save the sprite we may consume
	------------------------------------------------------------------ */ 
	if (targetRow !== row || targetCol !== col) {
		moved = true;
		movesInProgress++;
		var movingTile = tiles[row][col]; // sprite that moves
		var targetTile = tiles[targetRow][targetCol]; // <<< keep a reference
		var targetValue = grid[targetRow][targetCol];
		var newValue = targetValue === 0 ? grid[row][col] : grid[row][col] * 2;
		// update model
		grid[targetRow][targetCol] = newValue;
		grid[row][col] = 0;
		// update sprite matrix
		tiles[targetRow][targetCol] = movingTile;
		tiles[row][col] = null;
		/* ------------------------------------------------------------------
			3.  Animate – and if we merged, discard the absorbed sprite
		------------------------------------------------------------------ */ 
		tween(movingTile, {
			x: getPositionX(targetCol),
			y: getPositionY(targetRow)
		}, {
			duration: 150,
			easing: tween.easeOutQuad,
			onFinish: function onFinish() {
				if (targetValue !== 0) {
					// we DID merge
					if (targetTile && targetTile.parent) {
						// <<< remove duplicate
						targetTile.parent.removeChild(targetTile);
					}
					LK.getSound('merge').play();
					movingTile.setValue(newValue);
					movingTile.updateAppearance();
					// Create particles at the merge position
					createParticles(movingTile.x, movingTile.y, newValue);
					// Enhanced "pop" animation
					tween(movingTile.scale, {
						x: 1.3,
						y: 1.3
					}, {
						duration: 120,
						easing: tween.elasticOut,
						onFinish: function onFinish() {
							return tween(movingTile.scale, {
								x: 1,
								y: 1
							}, {
								duration: 150,
								easing: tween.easeOutQuad
							});
						}
					});
					// win check
					if (newValue === 2048 && !gameWon) {
						gameWon = true;
						LK.getSound('victory').play();
						if (score > storage.highScore) {
							storage.highScore = score;
						}
						// Add score to leaderboard when winning
						if (score > 0) {
							// Ensure leaderboard exists
							if (!Array.isArray(storage.leaderboard)) {
								storage.leaderboard = [];
							}
							// Add score to leaderboard
							storage.leaderboard.push(score);
							// Sort leaderboard (highest scores first)
							storage.leaderboard.sort(function (a, b) {
								return b - a;
							});
							// Keep only top 5 scores
							if (storage.leaderboard.length > 5) {
								storage.leaderboard = storage.leaderboard.slice(0, 5);
							}
							// Update the leaderboard display
							updateLeaderboard();
						}
						LK.setTimeout(function () {
							return LK.showYouWin();
						}, 1000);
					}
				}
				movesInProgress--;
			}
		});
	}
	return {
		moved: moved
	};
}
// Check if any moves are possible
function canMove() {
	// Check for empty cells
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			if (grid[i][j] === 0) {
				return true;
			}
		}
	}
	// Check for possible merges
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			var currentValue = grid[i][j];
			// Check adjacent cells for the same value
			if (i < GRID_SIZE - 1 && grid[i + 1][j] === currentValue) {
				return true;
			}
			if (j < GRID_SIZE - 1 && grid[i][j + 1] === currentValue) {
				return true;
			}
		}
	}
	// No moves possible
	return false;
}
// Reset game (for the reset button)
function resetGame() {
	// Stop current game if active
	gameActive = false;
	// Clear the game board
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			if (tiles[i] && tiles[i][j] && tiles[i][j].parent) {
				tiles[i][j].parent.removeChild(tiles[i][j]);
			}
		}
	}
	// Start a new game
	startGame();
}
// Show leaderboard popup
function showLeaderboard() {
	// Ensure leaderboard is an array
	if (!Array.isArray(storage.leaderboard)) {
		storage.leaderboard = [];
	}
	// Prepare leaderboard content
	var content = "";
	if (storage.leaderboard.length === 0) {
		content = "No scores yet. Play the game to set records!";
	} else {
		// Sort leaderboard (highest scores first)
		storage.leaderboard.sort(function (a, b) {
			return b - a;
		});
		// Keep only top 5 scores
		if (storage.leaderboard.length > 5) {
			storage.leaderboard = storage.leaderboard.slice(0, 5);
		}
		// Format leaderboard entries
		for (var i = 0; i < storage.leaderboard.length; i++) {
			content += i + 1 + ". " + Math.floor(storage.leaderboard[i]) + "\n\n";
		}
	}
	// Create and show popup
	var leaderboardPopup = new Popup("Leaderboard", content);
	game.addChild(leaderboardPopup);
}
// Show instructions popup
function showInstructions() {
	var instructions = "How to play 2048:\n\n" + "• Swipe to move all tiles\n\n" + "• When two tiles with the same number touch, they merge into one\n\n" + "• Create a tile with the number 2048 to win\n\n" + "• Score points decrease over time, so play quickly!\n\n" + "• Game ends when no more moves are possible\n\n\n\n                             Make queriell - Edit By Glaud";
	var instructionsPopup = new Popup("Instructions", instructions);
	game.addChild(instructionsPopup);
}
// Add Glaud text in the upper right corner
var glaudText = new Text2("Glaud", {
	size: 40,
	fill: 0xFFA500 // Orange color
});
glaudText.anchor.set(1, 0); // Anchor to top right
LK.gui.topRight.addChild(glaudText);
// Update leaderboard display
function updateLeaderboard() {
	// Get leaderboard container
	var leaderboardContainer = LK.gui.top.children.find(function (child) {
		return child instanceof Container && child.y === 220;
	});
	if (!leaderboardContainer) {
		return;
	}
	// Clear existing entries
	while (leaderboardContainer.children.length > 0) {
		leaderboardContainer.removeChild(leaderboardContainer.children[0]);
	}
	// Ensure leaderboard is an array
	if (!Array.isArray(storage.leaderboard)) {
		storage.leaderboard = [];
	}
	// Add current high score if not in leaderboard
	var highScoreInLeaderboard = false;
	for (var i = 0; i < storage.leaderboard.length; i++) {
		if (storage.leaderboard[i] === storage.highScore) {
			highScoreInLeaderboard = true;
			break;
		}
	}
	if (!highScoreInLeaderboard && storage.highScore > 0) {
		storage.leaderboard.push(storage.highScore);
	}
	// Sort leaderboard (highest scores first)
	storage.leaderboard.sort(function (a, b) {
		return b - a;
	});
	// Keep only top 5 scores
	if (storage.leaderboard.length > 5) {
		storage.leaderboard = storage.leaderboard.slice(0, 5);
	}
	// Add leaderboard entries
	for (var i = 0; i < storage.leaderboard.length; i++) {
		var entryText = new Text2(i + 1 + ". " + Math.floor(storage.leaderboard[i]), {
			size: 40,
			fill: 0x776E65
		});
		entryText.anchor.set(0.5, 0);
		entryText.y = i * 50;
		leaderboardContainer.addChild(entryText);
	}
}
// Start a new game
function startGame() {
	// Clear any existing tiles
	// Make sure tiles array is properly initialized first
	if (!tiles || tiles.length === 0) {
		tiles = [];
		for (var i = 0; i < GRID_SIZE; i++) {
			tiles[i] = [];
		}
	}
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			if (tiles[i] && tiles[i][j] && tiles[i][j].parent) {
				tiles[i][j].parent.removeChild(tiles[i][j]);
			}
		}
	}
	// Reset game state
	score = START_SCORE;
	currentDeductRate = INITIAL_DEDUCT_RATE;
	gameActive = true;
	gameWon = false;
	lastUpdateTime = Date.now();
	movesInProgress = 0;
	// Show game elements
	boardBackground.visible = true;
	scoreText.visible = true;
	timerText.visible = true;
	instructionsText.visible = true;
	restartButton.visible = true;
	// Initialize the grid and UI
	initializeGrid();
	// Update score display
	updateScore();
	// Update leaderboard display
	updateLeaderboard();
	// Start playing background music
	LK.playMusic('bgMusic');
}
// Update the score display
function updateScore() {
	scoreText.setText("Score: " + Math.floor(score));
	timerText.setText("Deduction: " + currentDeductRate + " points/sec");
}
// Touch/swipe handling variables
var touchStartX = 0;
var touchStartY = 0;
var touchEndX = 0;
var touchEndY = 0;
var minSwipeDistance = 50; // Minimum distance for a valid swipe
// Game event handlers
game.down = function (x, y, obj) {
	touchStartX = x;
	touchStartY = y;
};
game.up = function (x, y, obj) {
	touchEndX = x;
	touchEndY = y;
	// Calculate swipe distance and direction
	var dx = touchEndX - touchStartX;
	var dy = touchEndY - touchStartY;
	// Only process swipe if game is active
	if (gameActive && (Math.abs(dx) > minSwipeDistance || Math.abs(dy) > minSwipeDistance)) {
		// Determine swipe direction
		if (Math.abs(dx) > Math.abs(dy)) {
			// Horizontal swipe
			if (dx > 0) {
				moveTiles('right');
			} else {
				moveTiles('left');
			}
		} else {
			// Vertical swipe
			if (dy > 0) {
				moveTiles('down');
			} else {
				moveTiles('up');
			}
		}
	}
};
// Game update loop
game.update = function () {
	if (gameActive) {
		var currentTime = Date.now();
		var deltaTime = (currentTime - lastUpdateTime) / 1000; // Convert to seconds
		lastUpdateTime = currentTime;
		// Set deduction rate based on current score
		if (score > 100000) {
			currentDeductRate = 2500;
		} else if (score > 10000) {
			currentDeductRate = 250;
		} else if (score > 1000) {
			currentDeductRate = 25;
		} else if (score > 0) {
			currentDeductRate = 1;
		} else {
			currentDeductRate = 0;
		}
		// Deduct points based on time passed and current rate
		score -= currentDeductRate * deltaTime;
		// Ensure score doesn't go below zero
		if (score < 0) {
			score = 0;
			gameActive = false;
			// Update high score if needed
			if (score > storage.highScore) {
				storage.highScore = score;
			}
			LK.getSound('gameover').play();
			LK.setTimeout(function () {
				LK.showGameOver();
			}, 1000);
		}
		// Update score display
		updateScore();
	}
};
// Initialize the game
initializeBoard();
initializeUI();
// Start game timer
var startTime = Date.now();
lastUpdateTime = startTime;
// Initial leaderboard setup
updateLeaderboard();
// Set game inactive until Start is pressed
gameActive = false;
// Hide board and game elements initially
boardBackground.visible = false;
scoreText.visible = false;
timerText.visible = false;
instructionsText.visible = false;
// Play background music
LK.playMusic('bgMusic', {
	fade: {
		start: 0,
		end: 0.4,
		duration: 1000
	}
}); /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
	highScore: 0,
	leaderboard: []
});
/**** 
* Classes
****/ 
var MenuButton = Container.expand(function (text, width, height) {
	var self = Container.call(this);
	// Create background
	var background = self.attachAsset('tile', {
		width: width || 300,
		height: height || 100,
		tint: 0x8f7a66,
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Add text
	var buttonText = new Text2(text, {
		size: 70,
		fill: 0xFFFFFF
	});
	buttonText.anchor.set(0.5, 0.5);
	self.addChild(buttonText);
	// Make interactive
	self.interactive = true;
	return self;
});
// -------------- Particle -----------------
var Particle = Container.expand(function (color) {
	var self = Container.call(this);
	var size = Math.random() * 35 + 20; // Make particles bigger
	// Create particle
	var particle = self.attachAsset('tile', {
		width: size,
		height: size,
		tint: color || 0xEDC22E,
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Random velocity
	self.vx = (Math.random() - 0.5) * 10;
	self.vy = (Math.random() - 0.5) * 10;
	// Update particle movement
	self.update = function () {
		self.x += self.vx;
		self.y += self.vy;
		self.alpha -= 0.02;
		// Remove when faded out
		if (self.alpha <= 0 && self.parent) {
			self.parent.removeChild(self);
		}
	};
	return self;
});
// -------------- Popup -----------------
var Popup = Container.expand(function (title, content) {
	var self = Container.call(this);
	// Ensure the whole popup container itself is centered
	self.x = 2048 / 2;
	self.y = 2732 / 2;
	// Background overlay
	var overlay = self.attachAsset('tile', {
		width: 2048,
		height: 2732,
		tint: 0x000000,
		anchorX: 0.5,
		anchorY: 0.5
	});
	overlay.alpha = 0.7;
	// Popup panel
	var popup = self.attachAsset('tile', {
		width: 1600,
		height: 1600,
		tint: 0xFAF8EF,
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Title
	var titleText = new Text2(title, {
		size: 80,
		fill: 0x776E65
	});
	titleText.anchor.set(0.5, 0);
	titleText.y = -700;
	popup.addChild(titleText);
	// Content
	var contentText = new Text2(content, {
		size: 50,
		fill: 0x776E65
	});
	contentText.anchor.set(0.5, 0.5);
	popup.addChild(contentText);
	// Close button
	var closeButton = new MenuButton("Close", 200, 80);
	closeButton.y = 700;
	closeButton.down = function () {
		LK.getSound('click').play();
		if (self.parent) {
			self.parent.removeChild(self);
		}
	};
	popup.addChild(closeButton);
	return self;
});
// -------------- Tile -----------------
var Tile = Container.expand(function (value) {
	var self = Container.call(this);
	self.value = value || 0;
	// Background tile – uses global CELL_SIZE
	var background = self.attachAsset('tile', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: CELL_SIZE,
		height: CELL_SIZE
	});
	// Text
	self.valueText = new Text2(self.value > 0 ? self.value.toString() : '', {
		size: 60,
		fill: 0x776E65
	});
	self.valueText.anchor.set(0.5, 0.5);
	self.addChild(self.valueText);
	// Update appearance helper
	self.updateAppearance = function () {
		var colors = {
			0: 0xCDC1B4,
			2: 0xEEE4DA,
			4: 0xEDE0C8,
			8: 0xF2B179,
			16: 0xF59563,
			32: 0xF67C5F,
			64: 0xF65E3B,
			128: 0xEDCF72,
			256: 0xEDCC61,
			512: 0xEDC850,
			1024: 0xEDC53F,
			2048: 0xEDC22E
		};
		background.tint = colors[self.value] || 0xCDC1B4;
		self.valueText.setText(self.value > 0 ? self.value.toString() : '');
		var fontSize = 60;
		if (self.value >= 1000) {
			fontSize = 40;
		} else if (self.value >= 100) {
			fontSize = 50;
		}
		if (self.valueText && self.valueText.style) {
			self.valueText.style.size = fontSize;
			self.valueText.style.fill = self.value <= 4 ? "#776E65" : "#FFFFFF";
		}
	};
	self.setValue = function (newValue) {
		self.value = newValue;
		self.updateAppearance();
	};
	self.updateAppearance();
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x333333
});
/**** 
* Game Code
****/ 
// Game constants
var GRID_SIZE = 4;
var CELL_SIZE = 250; //  ←  Bigger tiles
var CELL_SPACING = 15;
var GRID_PADDING = 20;
var START_SCORE = 1000000;
var INITIAL_DEDUCT_RATE = 100;
var MAX_DEDUCT_RATE = 5000;
var DEDUCT_INCREASE_TIME = 10000;
// Game state variables
var grid = [];
var tiles = [];
var score = START_SCORE;
var gameActive = false;
var lastUpdateTime = 0;
var currentDeductRate = INITIAL_DEDUCT_RATE;
var gameWon = false;
var movesInProgress = 0;
// UI elements
var boardBackground;
var scoreText;
var timerText;
var instructionsText;
var restartButton; // NEW
// ------------------------------------------------------------
//  Board / UI setup
// ------------------------------------------------------------
function initializeBoard() {
	var boardWidth = GRID_SIZE * CELL_SIZE + (GRID_SIZE + 1) * CELL_SPACING + 2 * GRID_PADDING;
	var boardHeight = boardWidth;
	boardBackground = LK.getAsset('tile', {
		width: boardWidth,
		height: boardHeight,
		anchorX: 0.5,
		anchorY: 0.5,
		tint: 0xBBADA0
	});
	boardBackground.x = 2048 / 2;
	boardBackground.y = 2732 / 2;
	game.addChild(boardBackground);
	// Empty cells
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			var cellBg = LK.getAsset('tile', {
				width: CELL_SIZE,
				height: CELL_SIZE,
				anchorX: 0.5,
				anchorY: 0.5,
				tint: 0xCDC1B4
			});
			cellBg.x = getPositionX(j);
			cellBg.y = getPositionY(i);
			boardBackground.addChild(cellBg);
		}
	}
}
function initializeUI() {
	// Score
	scoreText = new Text2("Score: " + score, {
		size: 50,
		fill: 0x776E65
	});
	scoreText.anchor.set(0.5, 0);
	// Timer (deduction rate)
	timerText = new Text2("", {
		size: 40,
		fill: 0x776E65
	});
	timerText.anchor.set(0.5, 0);
	timerText.y = 70;
	// Instructions footer
	instructionsText = new Text2("Glaud warns: Hurry, time is running out.\nSwipe to move the tiles.\nCombine the same numbers to reach 2048!", {
		size: 40,
		fill: 0x776E65
	});
	instructionsText.anchor.set(0.5, 1);
	// Menu title
	var menuTitle = new Text2("2048", {
		size: 120,
		fill: 0x776E65
	});
	menuTitle.anchor.set(0.5, 0);
	menuTitle.x = 2048 / 2;
	menuTitle.y = 400;
	game.addChild(menuTitle);
	// Menu buttons
	var startButton = new MenuButton("Start Game", 900, 150);
	startButton.x = 2048 / 2;
	startButton.y = 2732 / 2 - 120;
	game.addChild(startButton);
	var leaderboardButton = new MenuButton("Leaderboard", 900, 150);
	leaderboardButton.x = 2048 / 2;
	leaderboardButton.y = 2732 / 2 + 60;
	game.addChild(leaderboardButton);
	var instructionsButton = new MenuButton("Instructions", 900, 150);
	instructionsButton.x = 2048 / 2;
	instructionsButton.y = 2732 / 2 + 240;
	game.addChild(instructionsButton);
	// ---------------- Restart button (hidden until game starts) ----------------
	restartButton = new MenuButton("Restart", 400, 150);
	restartButton.visible = false;
	restartButton.down = function () {
		LK.getSound('click').play();
		resetGame();
	};
	LK.gui.top.addChild(restartButton);
	// Leaderboard container (hidden initially)
	var leaderboardContainer = new Container();
	leaderboardContainer.y = 220;
	leaderboardContainer.visible = false;
	LK.gui.top.addChild(leaderboardContainer);
	// Event handlers
	startButton.down = function () {
		LK.getSound('click').play();
		// Hide menu elements
		menuTitle.visible = false;
		startButton.visible = false;
		leaderboardButton.visible = false;
		instructionsButton.visible = false;
		resetGame();
	};
	leaderboardButton.down = function () {
		LK.getSound('click').play();
		showLeaderboard();
	};
	instructionsButton.down = function () {
		LK.getSound('click').play();
		showInstructions();
	};
	// Add persistent UI to GUI layers
	LK.gui.top.addChild(scoreText);
	LK.gui.top.addChild(timerText);
	LK.gui.bottom.addChild(instructionsText);
	updateUIPositions();
}
function updateUIPositions() {
	scoreText.y = 20;
	timerText.y = 80;
	instructionsText.y = -20;
	if (boardBackground && restartButton) {
		// Place restart button just above the board
		restartButton.x = 0;
		restartButton.y = 400;
		// restartButton.x = boardBackground.x;
		// restartButton.y = boardBackground.y - boardBackground.height / 2 - 60;
	}
}
// ------------------------------------------------------------
//  Board effects
// ------------------------------------------------------------
function shakeBoard(intensity) {
	if (!boardBackground) {
		return;
	}
	// Save original position
	var originalX = boardBackground.x;
	var originalY = boardBackground.y;
	// Cancel any ongoing shake animations
	tween.stop(boardBackground, {
		x: true,
		y: true
	});
	// Shake in random direction
	tween(boardBackground, {
		x: originalX + (Math.random() - 0.5) * intensity,
		y: originalY + (Math.random() - 0.5) * intensity
	}, {
		duration: 50,
		onFinish: function onFinish() {
			// Shake again in different direction
			tween(boardBackground, {
				x: originalX + (Math.random() - 0.5) * intensity,
				y: originalY + (Math.random() - 0.5) * intensity
			}, {
				duration: 50,
				onFinish: function onFinish() {
					// Return to original position
					tween(boardBackground, {
						x: originalX,
						y: originalY
					}, {
						duration: 50
					});
				}
			});
		}
	});
}
// Create particles at tile position
function createParticles(x, y, value) {
	// Get color based on tile value
	var colors = {
		2: 0xEEE4DA,
		4: 0xEDE0C8,
		8: 0xF2B179,
		16: 0xF59563,
		32: 0xF67C5F,
		64: 0xF65E3B,
		128: 0xEDCF72,
		256: 0xEDCC61,
		512: 0xEDC850,
		1024: 0xEDC53F,
		2048: 0xEDC22E
	};
	var color = colors[value] || 0xEDC22E;
	// Create multiple particles
	for (var i = 0; i < 12; i++) {
		var particle = new Particle(color);
		particle.x = x;
		particle.y = y;
		boardBackground.addChild(particle);
	}
}
//  Helper positions (use CELL_SIZE)
// ------------------------------------------------------------
function getPositionX(col) {
	return -((GRID_SIZE * CELL_SIZE + (GRID_SIZE + 1) * CELL_SPACING) / 2) + CELL_SPACING + col * (CELL_SIZE + CELL_SPACING) + CELL_SIZE / 2;
}
function getPositionY(row) {
	return -((GRID_SIZE * CELL_SIZE + (GRID_SIZE + 1) * CELL_SPACING) / 2) + CELL_SPACING + row * (CELL_SIZE + CELL_SPACING) + CELL_SIZE / 2;
}
// Initialize the game grid
function initializeGrid() {
	grid = [];
	tiles = [];
	// Create empty grid
	for (var i = 0; i < GRID_SIZE; i++) {
		grid[i] = [];
		tiles[i] = [];
		for (var j = 0; j < GRID_SIZE; j++) {
			grid[i][j] = 0;
			tiles[i][j] = null;
		}
	}
	// Add initial tiles
	addRandomTile();
	addRandomTile();
}
// Add a random tile (2 or 4) to an empty cell
function addRandomTile() {
	var emptyCells = [];
	// Find all empty cells
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			if (grid[i][j] === 0) {
				emptyCells.push({
					row: i,
					col: j
				});
			}
		}
	}
	// If there are no empty cells, return
	if (emptyCells.length === 0) {
		return;
	}
	// Choose a random empty cell
	var randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
	// Create a tile with value 2 (90% chance) or 4 (10% chance)
	var value = Math.random() < 0.9 ? 2 : 4;
	grid[randomCell.row][randomCell.col] = value;
	// Create and add the tile object
	var tile = new Tile(value);
	tile.x = getPositionX(randomCell.col);
	tile.y = getPositionY(randomCell.row);
	tile.scale.x = 0;
	tile.scale.y = 0;
	boardBackground.addChild(tile);
	tiles[randomCell.row][randomCell.col] = tile;
	LK.getSound('spawn').play();
	// Animate the tile appearing with a bounce effect
	tween(tile.scale, {
		x: 1.2,
		y: 1.2
	}, {
		duration: 150,
		easing: tween.easeOutQuad,
		onFinish: function onFinish() {
			tween(tile.scale, {
				x: 1,
				y: 1
			}, {
				duration: 100,
				easing: tween.easeInQuad
			});
		}
	});
}
// Move tiles in a specific direction
function moveTiles(direction) {
	if (!gameActive || movesInProgress > 0) {
		return;
	}
	var hasMoved = false;
	var rowStart, rowEnd, rowStep;
	var colStart, colEnd, colStep;
	var tileAdded = false; // Track if a tile has been added in this move
	// Set up iteration direction based on swipe direction
	if (direction === 'up') {
		rowStart = 1;
		rowEnd = GRID_SIZE;
		rowStep = 1;
		colStart = 0;
		colEnd = GRID_SIZE;
		colStep = 1;
	} else if (direction === 'down') {
		rowStart = GRID_SIZE - 2;
		rowEnd = -1;
		rowStep = -1;
		colStart = 0;
		colEnd = GRID_SIZE;
		colStep = 1;
	} else if (direction === 'left') {
		rowStart = 0;
		rowEnd = GRID_SIZE;
		rowStep = 1;
		colStart = 1;
		colEnd = GRID_SIZE;
		colStep = 1;
	} else if (direction === 'right') {
		rowStart = 0;
		rowEnd = GRID_SIZE;
		rowStep = 1;
		colStart = GRID_SIZE - 2;
		colEnd = -1;
		colStep = -1;
	}
	// Create a temporary grid to track merged tiles
	var mergedGrid = [];
	for (var i = 0; i < GRID_SIZE; i++) {
		mergedGrid[i] = [];
		for (var j = 0; j < GRID_SIZE; j++) {
			mergedGrid[i][j] = false;
		}
	}
	// Perform the move
	for (var i = rowStart; i !== rowEnd; i += rowStep) {
		for (var j = colStart; j !== colEnd; j += colStep) {
			if (grid[i][j] !== 0) {
				var result = moveTile(i, j, direction, mergedGrid);
				if (result.moved) {
					hasMoved = true;
				}
			}
		}
	}
	// If no tiles moved, don't add a new random tile
	if (!hasMoved) {
		return;
	}
	// Play move sound
	LK.getSound('move').play();
	// shakeBoard(30);
	// Add a new random tile after the animation completes
	LK.setTimeout(function () {
		if (gameActive) {
			// Only add one tile per move
			if (!tileAdded) {
				addRandomTile();
				tileAdded = true;
			}
			// Check for game over
			if (!canMove()) {
				// Only trigger game over if we haven't won already
				if (!gameWon) {
					gameActive = false;
					LK.getSound('gameover').play();
					// Update high score if needed
					if (score > storage.highScore) {
						storage.highScore = score;
					}
					// Add score to leaderboard
					if (score > 0) {
						// Ensure leaderboard exists
						if (!Array.isArray(storage.leaderboard)) {
							storage.leaderboard = [];
						}
						// Add score to leaderboard
						storage.leaderboard.push(score);
						// Sort leaderboard (highest scores first)
						storage.leaderboard.sort(function (a, b) {
							return b - a;
						});
						// Keep only top 5 scores
						if (storage.leaderboard.length > 5) {
							storage.leaderboard = storage.leaderboard.slice(0, 5);
						}
						// Update the leaderboard display
						updateLeaderboard();
					}
					LK.setTimeout(function () {
						LK.showGameOver();
					}, 1000);
				}
			}
		}
	}, 250);
}
// Move a single tile in the specified direction
function moveTile(row, col, direction, mergedGrid) {
	var targetRow = row;
	var targetCol = col;
	var moved = false;
	/* ------------------------------------------------------------------
		1.  IDENTICAL directional search loops (no change)
	------------------------------------------------------------------ */ 
	if (direction === 'up') {
		while (targetRow > 0 && (grid[targetRow - 1][targetCol] === 0 || grid[targetRow - 1][targetCol] === grid[row][col] && !mergedGrid[targetRow - 1][targetCol])) {
			if (grid[targetRow - 1][targetCol] === 0) {
				targetRow--;
			} else {
				targetRow--;
				mergedGrid[targetRow][targetCol] = true;
				break;
			}
		}
	} else if (direction === 'down') {
		while (targetRow < GRID_SIZE - 1 && (grid[targetRow + 1][targetCol] === 0 || grid[targetRow + 1][targetCol] === grid[row][col] && !mergedGrid[targetRow + 1][targetCol])) {
			if (grid[targetRow + 1][targetCol] === 0) {
				targetRow++;
			} else {
				targetRow++;
				mergedGrid[targetRow][targetCol] = true;
				break;
			}
		}
	} else if (direction === 'left') {
		while (targetCol > 0 && (grid[targetRow][targetCol - 1] === 0 || grid[targetRow][targetCol - 1] === grid[row][col] && !mergedGrid[targetRow][targetCol - 1])) {
			if (grid[targetRow][targetCol - 1] === 0) {
				targetCol--;
			} else {
				targetCol--;
				mergedGrid[targetRow][targetCol] = true;
				break;
			}
		}
	} else if (direction === 'right') {
		while (targetCol < GRID_SIZE - 1 && (grid[targetRow][targetCol + 1] === 0 || grid[targetRow][targetCol + 1] === grid[row][col] && !mergedGrid[targetRow][targetCol + 1])) {
			if (grid[targetRow][targetCol + 1] === 0) {
				targetCol++;
			} else {
				targetCol++;
				mergedGrid[targetRow][targetCol] = true;
				break;
			}
		}
	}
	/* ------------------------------------------------------------------
		2.  Same move bookkeeping, but save the sprite we may consume
	------------------------------------------------------------------ */ 
	if (targetRow !== row || targetCol !== col) {
		moved = true;
		movesInProgress++;
		var movingTile = tiles[row][col]; // sprite that moves
		var targetTile = tiles[targetRow][targetCol]; // <<< keep a reference
		var targetValue = grid[targetRow][targetCol];
		var newValue = targetValue === 0 ? grid[row][col] : grid[row][col] * 2;
		// update model
		grid[targetRow][targetCol] = newValue;
		grid[row][col] = 0;
		// update sprite matrix
		tiles[targetRow][targetCol] = movingTile;
		tiles[row][col] = null;
		/* ------------------------------------------------------------------
			3.  Animate – and if we merged, discard the absorbed sprite
		------------------------------------------------------------------ */ 
		tween(movingTile, {
			x: getPositionX(targetCol),
			y: getPositionY(targetRow)
		}, {
			duration: 150,
			easing: tween.easeOutQuad,
			onFinish: function onFinish() {
				if (targetValue !== 0) {
					// we DID merge
					if (targetTile && targetTile.parent) {
						// <<< remove duplicate
						targetTile.parent.removeChild(targetTile);
					}
					LK.getSound('merge').play();
					movingTile.setValue(newValue);
					movingTile.updateAppearance();
					// Create particles at the merge position
					createParticles(movingTile.x, movingTile.y, newValue);
					// Enhanced "pop" animation
					tween(movingTile.scale, {
						x: 1.3,
						y: 1.3
					}, {
						duration: 120,
						easing: tween.elasticOut,
						onFinish: function onFinish() {
							return tween(movingTile.scale, {
								x: 1,
								y: 1
							}, {
								duration: 150,
								easing: tween.easeOutQuad
							});
						}
					});
					// win check
					if (newValue === 2048 && !gameWon) {
						gameWon = true;
						LK.getSound('victory').play();
						if (score > storage.highScore) {
							storage.highScore = score;
						}
						// Add score to leaderboard when winning
						if (score > 0) {
							// Ensure leaderboard exists
							if (!Array.isArray(storage.leaderboard)) {
								storage.leaderboard = [];
							}
							// Add score to leaderboard
							storage.leaderboard.push(score);
							// Sort leaderboard (highest scores first)
							storage.leaderboard.sort(function (a, b) {
								return b - a;
							});
							// Keep only top 5 scores
							if (storage.leaderboard.length > 5) {
								storage.leaderboard = storage.leaderboard.slice(0, 5);
							}
							// Update the leaderboard display
							updateLeaderboard();
						}
						LK.setTimeout(function () {
							return LK.showYouWin();
						}, 1000);
					}
				}
				movesInProgress--;
			}
		});
	}
	return {
		moved: moved
	};
}
// Check if any moves are possible
function canMove() {
	// Check for empty cells
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			if (grid[i][j] === 0) {
				return true;
			}
		}
	}
	// Check for possible merges
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			var currentValue = grid[i][j];
			// Check adjacent cells for the same value
			if (i < GRID_SIZE - 1 && grid[i + 1][j] === currentValue) {
				return true;
			}
			if (j < GRID_SIZE - 1 && grid[i][j + 1] === currentValue) {
				return true;
			}
		}
	}
	// No moves possible
	return false;
}
// Reset game (for the reset button)
function resetGame() {
	// Stop current game if active
	gameActive = false;
	// Clear the game board
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			if (tiles[i] && tiles[i][j] && tiles[i][j].parent) {
				tiles[i][j].parent.removeChild(tiles[i][j]);
			}
		}
	}
	// Start a new game
	startGame();
}
// Show leaderboard popup
function showLeaderboard() {
	// Ensure leaderboard is an array
	if (!Array.isArray(storage.leaderboard)) {
		storage.leaderboard = [];
	}
	// Prepare leaderboard content
	var content = "";
	if (storage.leaderboard.length === 0) {
		content = "No scores yet. Play the game to set records!";
	} else {
		// Sort leaderboard (highest scores first)
		storage.leaderboard.sort(function (a, b) {
			return b - a;
		});
		// Keep only top 5 scores
		if (storage.leaderboard.length > 5) {
			storage.leaderboard = storage.leaderboard.slice(0, 5);
		}
		// Format leaderboard entries
		for (var i = 0; i < storage.leaderboard.length; i++) {
			content += i + 1 + ". " + Math.floor(storage.leaderboard[i]) + "\n\n";
		}
	}
	// Create and show popup
	var leaderboardPopup = new Popup("Leaderboard", content);
	game.addChild(leaderboardPopup);
}
// Show instructions popup
function showInstructions() {
	var instructions = "How to play 2048:\n\n" + "• Swipe to move all tiles\n\n" + "• When two tiles with the same number touch, they merge into one\n\n" + "• Create a tile with the number 2048 to win\n\n" + "• Score points decrease over time, so play quickly!\n\n" + "• Game ends when no more moves are possible\n\n\n\n                             Make queriell - Edit By Glaud";
	var instructionsPopup = new Popup("Instructions", instructions);
	game.addChild(instructionsPopup);
}
// Add Glaud text in the upper right corner
var glaudText = new Text2("Glaud", {
	size: 40,
	fill: 0xFFA500 // Orange color
});
glaudText.anchor.set(1, 0); // Anchor to top right
LK.gui.topRight.addChild(glaudText);
// Update leaderboard display
function updateLeaderboard() {
	// Get leaderboard container
	var leaderboardContainer = LK.gui.top.children.find(function (child) {
		return child instanceof Container && child.y === 220;
	});
	if (!leaderboardContainer) {
		return;
	}
	// Clear existing entries
	while (leaderboardContainer.children.length > 0) {
		leaderboardContainer.removeChild(leaderboardContainer.children[0]);
	}
	// Ensure leaderboard is an array
	if (!Array.isArray(storage.leaderboard)) {
		storage.leaderboard = [];
	}
	// Add current high score if not in leaderboard
	var highScoreInLeaderboard = false;
	for (var i = 0; i < storage.leaderboard.length; i++) {
		if (storage.leaderboard[i] === storage.highScore) {
			highScoreInLeaderboard = true;
			break;
		}
	}
	if (!highScoreInLeaderboard && storage.highScore > 0) {
		storage.leaderboard.push(storage.highScore);
	}
	// Sort leaderboard (highest scores first)
	storage.leaderboard.sort(function (a, b) {
		return b - a;
	});
	// Keep only top 5 scores
	if (storage.leaderboard.length > 5) {
		storage.leaderboard = storage.leaderboard.slice(0, 5);
	}
	// Add leaderboard entries
	for (var i = 0; i < storage.leaderboard.length; i++) {
		var entryText = new Text2(i + 1 + ". " + Math.floor(storage.leaderboard[i]), {
			size: 40,
			fill: 0x776E65
		});
		entryText.anchor.set(0.5, 0);
		entryText.y = i * 50;
		leaderboardContainer.addChild(entryText);
	}
}
// Start a new game
function startGame() {
	// Clear any existing tiles
	// Make sure tiles array is properly initialized first
	if (!tiles || tiles.length === 0) {
		tiles = [];
		for (var i = 0; i < GRID_SIZE; i++) {
			tiles[i] = [];
		}
	}
	for (var i = 0; i < GRID_SIZE; i++) {
		for (var j = 0; j < GRID_SIZE; j++) {
			if (tiles[i] && tiles[i][j] && tiles[i][j].parent) {
				tiles[i][j].parent.removeChild(tiles[i][j]);
			}
		}
	}
	// Reset game state
	score = START_SCORE;
	currentDeductRate = INITIAL_DEDUCT_RATE;
	gameActive = true;
	gameWon = false;
	lastUpdateTime = Date.now();
	movesInProgress = 0;
	// Show game elements
	boardBackground.visible = true;
	scoreText.visible = true;
	timerText.visible = true;
	instructionsText.visible = true;
	restartButton.visible = true;
	// Initialize the grid and UI
	initializeGrid();
	// Update score display
	updateScore();
	// Update leaderboard display
	updateLeaderboard();
	// Start playing background music
	LK.playMusic('bgMusic');
}
// Update the score display
function updateScore() {
	scoreText.setText("Score: " + Math.floor(score));
	timerText.setText("Deduction: " + currentDeductRate + " points/sec");
}
// Touch/swipe handling variables
var touchStartX = 0;
var touchStartY = 0;
var touchEndX = 0;
var touchEndY = 0;
var minSwipeDistance = 50; // Minimum distance for a valid swipe
// Game event handlers
game.down = function (x, y, obj) {
	touchStartX = x;
	touchStartY = y;
};
game.up = function (x, y, obj) {
	touchEndX = x;
	touchEndY = y;
	// Calculate swipe distance and direction
	var dx = touchEndX - touchStartX;
	var dy = touchEndY - touchStartY;
	// Only process swipe if game is active
	if (gameActive && (Math.abs(dx) > minSwipeDistance || Math.abs(dy) > minSwipeDistance)) {
		// Determine swipe direction
		if (Math.abs(dx) > Math.abs(dy)) {
			// Horizontal swipe
			if (dx > 0) {
				moveTiles('right');
			} else {
				moveTiles('left');
			}
		} else {
			// Vertical swipe
			if (dy > 0) {
				moveTiles('down');
			} else {
				moveTiles('up');
			}
		}
	}
};
// Game update loop
game.update = function () {
	if (gameActive) {
		var currentTime = Date.now();
		var deltaTime = (currentTime - lastUpdateTime) / 1000; // Convert to seconds
		lastUpdateTime = currentTime;
		// Set deduction rate based on current score
		if (score > 100000) {
			currentDeductRate = 2500;
		} else if (score > 10000) {
			currentDeductRate = 250;
		} else if (score > 1000) {
			currentDeductRate = 25;
		} else if (score > 0) {
			currentDeductRate = 1;
		} else {
			currentDeductRate = 0;
		}
		// Deduct points based on time passed and current rate
		score -= currentDeductRate * deltaTime;
		// Ensure score doesn't go below zero
		if (score < 0) {
			score = 0;
			gameActive = false;
			// Update high score if needed
			if (score > storage.highScore) {
				storage.highScore = score;
			}
			LK.getSound('gameover').play();
			LK.setTimeout(function () {
				LK.showGameOver();
			}, 1000);
		}
		// Update score display
		updateScore();
	}
};
// Initialize the game
initializeBoard();
initializeUI();
// Start game timer
var startTime = Date.now();
lastUpdateTime = startTime;
// Initial leaderboard setup
updateLeaderboard();
// Set game inactive until Start is pressed
gameActive = false;
// Hide board and game elements initially
boardBackground.visible = false;
scoreText.visible = false;
timerText.visible = false;
instructionsText.visible = false;
// Play background music
LK.playMusic('bgMusic', {
	fade: {
		start: 0,
		end: 0.4,
		duration: 1000
	}
});