/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Tile = Container.expand(function (value, gridX, gridY) { var self = Container.call(this); self.value = value; self.gridX = gridX; self.gridY = gridY; self.hasMerged = false; // Get the appropriate asset based on value var assetName = 'tile' + value; var tileGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); // Add text for the number self.numberText = new Text2(value.toString(), { size: value >= 1000 ? 36 : value >= 100 ? 42 : 48, fill: value <= 4 ? "#776e65" : "#f9f6f2" }); self.numberText.anchor.set(0.5, 0.5); self.addChild(self.numberText); self.updatePosition = function () { var targetX = gridStartX + self.gridX * (tileSize + tileGap); var targetY = gridStartY + self.gridY * (tileSize + tileGap); tween(self, { x: targetX, y: targetY }, { duration: 150, easing: tween.easeOut }); }; self.setValue = function (newValue) { self.value = newValue; // Remove old text and create new one with correct size self.removeChild(self.numberText); self.numberText = new Text2(newValue.toString(), { size: newValue >= 1000 ? 36 : newValue >= 100 ? 42 : 48, fill: newValue <= 4 ? "#776e65" : "#f9f6f2" }); self.numberText.anchor.set(0.5, 0.5); self.addChild(self.numberText); // Update graphics self.removeChild(tileGraphics); assetName = 'tile' + newValue; tileGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.addChildAt(tileGraphics, 0); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xfaf8ef }); /**** * Game Code ****/ // Game constants var gridSize = 4; var tileSize = 120; var tileGap = 10; var gridStartX = (2048 - (gridSize * tileSize + (gridSize - 1) * tileGap)) / 2; var gridStartY = (2732 - (gridSize * tileSize + (gridSize - 1) * tileGap)) / 2; // Game state var grid = []; var tiles = []; var isMoving = false; var gameWon = false; var gameOver = false; // Initialize grid for (var i = 0; i < gridSize; i++) { grid[i] = []; for (var j = 0; j < gridSize; j++) { grid[i][j] = null; } } // Create grid background var gridContainer = game.addChild(new Container()); for (var i = 0; i < gridSize; i++) { for (var j = 0; j < gridSize; j++) { var cell = gridContainer.attachAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: gridStartX + j * (tileSize + tileGap), y: gridStartY + i * (tileSize + tileGap) }); } } // Score display var scoreTxt = new Text2('0', { size: 80, fill: 0x776E65 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 100; // Add initial tiles function addRandomTile() { var emptyCells = []; for (var i = 0; i < gridSize; i++) { for (var j = 0; j < gridSize; j++) { if (grid[i][j] === null) { emptyCells.push({ x: j, y: i }); } } } if (emptyCells.length > 0) { var randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)]; var value = Math.random() < 0.9 ? 2 : 4; var tile = new Tile(value, randomCell.x, randomCell.y); tile.x = gridStartX + randomCell.x * (tileSize + tileGap); tile.y = gridStartY + randomCell.y * (tileSize + tileGap); grid[randomCell.y][randomCell.x] = tile; tiles.push(tile); game.addChild(tile); // Scale animation for new tile tile.scaleX = 0.1; tile.scaleY = 0.1; tween(tile, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.bounceOut }); } } function checkGameWon() { for (var i = 0; i < tiles.length; i++) { if (tiles[i].value >= 2048 && !gameWon) { gameWon = true; LK.showYouWin(); return true; } } return false; } function checkGameOver() { // Check if grid is full var fullGrid = true; for (var i = 0; i < gridSize; i++) { for (var j = 0; j < gridSize; j++) { if (grid[i][j] === null) { fullGrid = false; break; } } if (!fullGrid) break; } if (!fullGrid) return false; // Check for possible moves for (var i = 0; i < gridSize; i++) { for (var j = 0; j < gridSize; j++) { var currentValue = grid[i][j].value; // Check right if (j < gridSize - 1 && grid[i][j + 1].value === currentValue) { return false; } // Check down if (i < gridSize - 1 && grid[i + 1][j].value === currentValue) { return false; } } } gameOver = true; LK.showGameOver(); return true; } function moveTiles(direction) { if (isMoving || gameOver || gameWon) return false; var moved = false; var score = 0; // Reset merge flags for (var i = 0; i < tiles.length; i++) { tiles[i].hasMerged = false; } // Create new grid state var newGrid = []; for (var i = 0; i < gridSize; i++) { newGrid[i] = []; for (var j = 0; j < gridSize; j++) { newGrid[i][j] = null; } } if (direction === 'left') { for (var i = 0; i < gridSize; i++) { var row = []; // Collect non-null tiles for (var j = 0; j < gridSize; j++) { if (grid[i][j] !== null) { row.push(grid[i][j]); } } // Merge tiles for (var k = 0; k < row.length - 1; k++) { if (row[k].value === row[k + 1].value && !row[k].hasMerged && !row[k + 1].hasMerged) { row[k].setValue(row[k].value * 2); row[k].hasMerged = true; score += row[k].value; // Remove merged tile game.removeChild(row[k + 1]); tiles.splice(tiles.indexOf(row[k + 1]), 1); row.splice(k + 1, 1); LK.getSound('merge').play(); } } // Place tiles in new positions for (var k = 0; k < row.length; k++) { if (row[k].gridX !== k || row[k].gridY !== i) { moved = true; } row[k].gridX = k; row[k].gridY = i; newGrid[i][k] = row[k]; row[k].updatePosition(); } } } else if (direction === 'right') { for (var i = 0; i < gridSize; i++) { var row = []; // Collect non-null tiles for (var j = gridSize - 1; j >= 0; j--) { if (grid[i][j] !== null) { row.push(grid[i][j]); } } // Merge tiles for (var k = 0; k < row.length - 1; k++) { if (row[k].value === row[k + 1].value && !row[k].hasMerged && !row[k + 1].hasMerged) { row[k].setValue(row[k].value * 2); row[k].hasMerged = true; score += row[k].value; // Remove merged tile game.removeChild(row[k + 1]); tiles.splice(tiles.indexOf(row[k + 1]), 1); row.splice(k + 1, 1); LK.getSound('merge').play(); } } // Place tiles in new positions for (var k = 0; k < row.length; k++) { var newX = gridSize - 1 - k; if (row[k].gridX !== newX || row[k].gridY !== i) { moved = true; } row[k].gridX = newX; row[k].gridY = i; newGrid[i][newX] = row[k]; row[k].updatePosition(); } } } else if (direction === 'up') { for (var j = 0; j < gridSize; j++) { var column = []; // Collect non-null tiles for (var i = 0; i < gridSize; i++) { if (grid[i][j] !== null) { column.push(grid[i][j]); } } // Merge tiles for (var k = 0; k < column.length - 1; k++) { if (column[k].value === column[k + 1].value && !column[k].hasMerged && !column[k + 1].hasMerged) { column[k].setValue(column[k].value * 2); column[k].hasMerged = true; score += column[k].value; // Remove merged tile game.removeChild(column[k + 1]); tiles.splice(tiles.indexOf(column[k + 1]), 1); column.splice(k + 1, 1); LK.getSound('merge').play(); } } // Place tiles in new positions for (var k = 0; k < column.length; k++) { if (column[k].gridX !== j || column[k].gridY !== k) { moved = true; } column[k].gridX = j; column[k].gridY = k; newGrid[k][j] = column[k]; column[k].updatePosition(); } } } else if (direction === 'down') { for (var j = 0; j < gridSize; j++) { var column = []; // Collect non-null tiles for (var i = gridSize - 1; i >= 0; i--) { if (grid[i][j] !== null) { column.push(grid[i][j]); } } // Merge tiles for (var k = 0; k < column.length - 1; k++) { if (column[k].value === column[k + 1].value && !column[k].hasMerged && !column[k + 1].hasMerged) { column[k].setValue(column[k].value * 2); column[k].hasMerged = true; score += column[k].value; // Remove merged tile game.removeChild(column[k + 1]); tiles.splice(tiles.indexOf(column[k + 1]), 1); column.splice(k + 1, 1); LK.getSound('merge').play(); } } // Place tiles in new positions for (var k = 0; k < column.length; k++) { var newY = gridSize - 1 - k; if (column[k].gridX !== j || column[k].gridY !== newY) { moved = true; } column[k].gridX = j; column[k].gridY = newY; newGrid[newY][j] = column[k]; column[k].updatePosition(); } } } grid = newGrid; if (moved) { LK.setScore(LK.getScore() + score); scoreTxt.setText(LK.getScore()); LK.getSound('move').play(); isMoving = true; LK.setTimeout(function () { addRandomTile(); isMoving = false; if (!checkGameWon()) { checkGameOver(); } }, 200); } return moved; } // Touch controls var startX = 0; var startY = 0; var isDragging = false; game.down = function (x, y, obj) { startX = x; startY = y; isDragging = true; }; game.up = function (x, y, obj) { if (!isDragging) return; var deltaX = x - startX; var deltaY = y - startY; var minSwipeDistance = 50; if (Math.abs(deltaX) > minSwipeDistance || Math.abs(deltaY) > minSwipeDistance) { if (Math.abs(deltaX) > Math.abs(deltaY)) { // Horizontal swipe if (deltaX > 0) { moveTiles('right'); } else { moveTiles('left'); } } else { // Vertical swipe if (deltaY > 0) { moveTiles('down'); } else { moveTiles('up'); } } } isDragging = false; }; // Add initial tiles addRandomTile(); addRandomTile(); game.update = function () { scoreTxt.setText(LK.getScore()); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Tile = Container.expand(function (value, gridX, gridY) {
var self = Container.call(this);
self.value = value;
self.gridX = gridX;
self.gridY = gridY;
self.hasMerged = false;
// Get the appropriate asset based on value
var assetName = 'tile' + value;
var tileGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Add text for the number
self.numberText = new Text2(value.toString(), {
size: value >= 1000 ? 36 : value >= 100 ? 42 : 48,
fill: value <= 4 ? "#776e65" : "#f9f6f2"
});
self.numberText.anchor.set(0.5, 0.5);
self.addChild(self.numberText);
self.updatePosition = function () {
var targetX = gridStartX + self.gridX * (tileSize + tileGap);
var targetY = gridStartY + self.gridY * (tileSize + tileGap);
tween(self, {
x: targetX,
y: targetY
}, {
duration: 150,
easing: tween.easeOut
});
};
self.setValue = function (newValue) {
self.value = newValue;
// Remove old text and create new one with correct size
self.removeChild(self.numberText);
self.numberText = new Text2(newValue.toString(), {
size: newValue >= 1000 ? 36 : newValue >= 100 ? 42 : 48,
fill: newValue <= 4 ? "#776e65" : "#f9f6f2"
});
self.numberText.anchor.set(0.5, 0.5);
self.addChild(self.numberText);
// Update graphics
self.removeChild(tileGraphics);
assetName = 'tile' + newValue;
tileGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.addChildAt(tileGraphics, 0);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xfaf8ef
});
/****
* Game Code
****/
// Game constants
var gridSize = 4;
var tileSize = 120;
var tileGap = 10;
var gridStartX = (2048 - (gridSize * tileSize + (gridSize - 1) * tileGap)) / 2;
var gridStartY = (2732 - (gridSize * tileSize + (gridSize - 1) * tileGap)) / 2;
// Game state
var grid = [];
var tiles = [];
var isMoving = false;
var gameWon = false;
var gameOver = false;
// Initialize grid
for (var i = 0; i < gridSize; i++) {
grid[i] = [];
for (var j = 0; j < gridSize; j++) {
grid[i][j] = null;
}
}
// Create grid background
var gridContainer = game.addChild(new Container());
for (var i = 0; i < gridSize; i++) {
for (var j = 0; j < gridSize; j++) {
var cell = gridContainer.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: gridStartX + j * (tileSize + tileGap),
y: gridStartY + i * (tileSize + tileGap)
});
}
}
// Score display
var scoreTxt = new Text2('0', {
size: 80,
fill: 0x776E65
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 100;
// Add initial tiles
function addRandomTile() {
var emptyCells = [];
for (var i = 0; i < gridSize; i++) {
for (var j = 0; j < gridSize; j++) {
if (grid[i][j] === null) {
emptyCells.push({
x: j,
y: i
});
}
}
}
if (emptyCells.length > 0) {
var randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
var value = Math.random() < 0.9 ? 2 : 4;
var tile = new Tile(value, randomCell.x, randomCell.y);
tile.x = gridStartX + randomCell.x * (tileSize + tileGap);
tile.y = gridStartY + randomCell.y * (tileSize + tileGap);
grid[randomCell.y][randomCell.x] = tile;
tiles.push(tile);
game.addChild(tile);
// Scale animation for new tile
tile.scaleX = 0.1;
tile.scaleY = 0.1;
tween(tile, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.bounceOut
});
}
}
function checkGameWon() {
for (var i = 0; i < tiles.length; i++) {
if (tiles[i].value >= 2048 && !gameWon) {
gameWon = true;
LK.showYouWin();
return true;
}
}
return false;
}
function checkGameOver() {
// Check if grid is full
var fullGrid = true;
for (var i = 0; i < gridSize; i++) {
for (var j = 0; j < gridSize; j++) {
if (grid[i][j] === null) {
fullGrid = false;
break;
}
}
if (!fullGrid) break;
}
if (!fullGrid) return false;
// Check for possible moves
for (var i = 0; i < gridSize; i++) {
for (var j = 0; j < gridSize; j++) {
var currentValue = grid[i][j].value;
// Check right
if (j < gridSize - 1 && grid[i][j + 1].value === currentValue) {
return false;
}
// Check down
if (i < gridSize - 1 && grid[i + 1][j].value === currentValue) {
return false;
}
}
}
gameOver = true;
LK.showGameOver();
return true;
}
function moveTiles(direction) {
if (isMoving || gameOver || gameWon) return false;
var moved = false;
var score = 0;
// Reset merge flags
for (var i = 0; i < tiles.length; i++) {
tiles[i].hasMerged = false;
}
// Create new grid state
var newGrid = [];
for (var i = 0; i < gridSize; i++) {
newGrid[i] = [];
for (var j = 0; j < gridSize; j++) {
newGrid[i][j] = null;
}
}
if (direction === 'left') {
for (var i = 0; i < gridSize; i++) {
var row = [];
// Collect non-null tiles
for (var j = 0; j < gridSize; j++) {
if (grid[i][j] !== null) {
row.push(grid[i][j]);
}
}
// Merge tiles
for (var k = 0; k < row.length - 1; k++) {
if (row[k].value === row[k + 1].value && !row[k].hasMerged && !row[k + 1].hasMerged) {
row[k].setValue(row[k].value * 2);
row[k].hasMerged = true;
score += row[k].value;
// Remove merged tile
game.removeChild(row[k + 1]);
tiles.splice(tiles.indexOf(row[k + 1]), 1);
row.splice(k + 1, 1);
LK.getSound('merge').play();
}
}
// Place tiles in new positions
for (var k = 0; k < row.length; k++) {
if (row[k].gridX !== k || row[k].gridY !== i) {
moved = true;
}
row[k].gridX = k;
row[k].gridY = i;
newGrid[i][k] = row[k];
row[k].updatePosition();
}
}
} else if (direction === 'right') {
for (var i = 0; i < gridSize; i++) {
var row = [];
// Collect non-null tiles
for (var j = gridSize - 1; j >= 0; j--) {
if (grid[i][j] !== null) {
row.push(grid[i][j]);
}
}
// Merge tiles
for (var k = 0; k < row.length - 1; k++) {
if (row[k].value === row[k + 1].value && !row[k].hasMerged && !row[k + 1].hasMerged) {
row[k].setValue(row[k].value * 2);
row[k].hasMerged = true;
score += row[k].value;
// Remove merged tile
game.removeChild(row[k + 1]);
tiles.splice(tiles.indexOf(row[k + 1]), 1);
row.splice(k + 1, 1);
LK.getSound('merge').play();
}
}
// Place tiles in new positions
for (var k = 0; k < row.length; k++) {
var newX = gridSize - 1 - k;
if (row[k].gridX !== newX || row[k].gridY !== i) {
moved = true;
}
row[k].gridX = newX;
row[k].gridY = i;
newGrid[i][newX] = row[k];
row[k].updatePosition();
}
}
} else if (direction === 'up') {
for (var j = 0; j < gridSize; j++) {
var column = [];
// Collect non-null tiles
for (var i = 0; i < gridSize; i++) {
if (grid[i][j] !== null) {
column.push(grid[i][j]);
}
}
// Merge tiles
for (var k = 0; k < column.length - 1; k++) {
if (column[k].value === column[k + 1].value && !column[k].hasMerged && !column[k + 1].hasMerged) {
column[k].setValue(column[k].value * 2);
column[k].hasMerged = true;
score += column[k].value;
// Remove merged tile
game.removeChild(column[k + 1]);
tiles.splice(tiles.indexOf(column[k + 1]), 1);
column.splice(k + 1, 1);
LK.getSound('merge').play();
}
}
// Place tiles in new positions
for (var k = 0; k < column.length; k++) {
if (column[k].gridX !== j || column[k].gridY !== k) {
moved = true;
}
column[k].gridX = j;
column[k].gridY = k;
newGrid[k][j] = column[k];
column[k].updatePosition();
}
}
} else if (direction === 'down') {
for (var j = 0; j < gridSize; j++) {
var column = [];
// Collect non-null tiles
for (var i = gridSize - 1; i >= 0; i--) {
if (grid[i][j] !== null) {
column.push(grid[i][j]);
}
}
// Merge tiles
for (var k = 0; k < column.length - 1; k++) {
if (column[k].value === column[k + 1].value && !column[k].hasMerged && !column[k + 1].hasMerged) {
column[k].setValue(column[k].value * 2);
column[k].hasMerged = true;
score += column[k].value;
// Remove merged tile
game.removeChild(column[k + 1]);
tiles.splice(tiles.indexOf(column[k + 1]), 1);
column.splice(k + 1, 1);
LK.getSound('merge').play();
}
}
// Place tiles in new positions
for (var k = 0; k < column.length; k++) {
var newY = gridSize - 1 - k;
if (column[k].gridX !== j || column[k].gridY !== newY) {
moved = true;
}
column[k].gridX = j;
column[k].gridY = newY;
newGrid[newY][j] = column[k];
column[k].updatePosition();
}
}
}
grid = newGrid;
if (moved) {
LK.setScore(LK.getScore() + score);
scoreTxt.setText(LK.getScore());
LK.getSound('move').play();
isMoving = true;
LK.setTimeout(function () {
addRandomTile();
isMoving = false;
if (!checkGameWon()) {
checkGameOver();
}
}, 200);
}
return moved;
}
// Touch controls
var startX = 0;
var startY = 0;
var isDragging = false;
game.down = function (x, y, obj) {
startX = x;
startY = y;
isDragging = true;
};
game.up = function (x, y, obj) {
if (!isDragging) return;
var deltaX = x - startX;
var deltaY = y - startY;
var minSwipeDistance = 50;
if (Math.abs(deltaX) > minSwipeDistance || Math.abs(deltaY) > minSwipeDistance) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
// Horizontal swipe
if (deltaX > 0) {
moveTiles('right');
} else {
moveTiles('left');
}
} else {
// Vertical swipe
if (deltaY > 0) {
moveTiles('down');
} else {
moveTiles('up');
}
}
}
isDragging = false;
};
// Add initial tiles
addRandomTile();
addRandomTile();
game.update = function () {
scoreTxt.setText(LK.getScore());
};