/****
* 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());
};