/**** * 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 } });
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,985 @@
-/****
+/****
+* 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: 0x000000
+ 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
+ }
});
\ No newline at end of file