/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Tile class: represents a single tile on the board var Tile = Container.expand(function () { var self = Container.call(this); // Properties self.value = 0; // 0 means empty self.row = 0; self.col = 0; self.tileBG = null; self.tileText = null; // Set up tile self.setValue = function (val) { self.value = val; // Remove previous children if any if (self.tileBG) { self.removeChild(self.tileBG); self.tileBG = null; } if (self.tileText) { self.removeChild(self.tileText); self.tileText = null; } // Choose asset based on value var assetId = 'tile'; if (val === 0) { assetId = 'tileBG'; } else if (val === 2) { assetId = 'tile2'; } else if (val === 4) { assetId = 'tile4'; } else if (val === 8) { assetId = 'tile8'; } else if (val === 16) { assetId = 'tile16'; } else if (val === 32) { assetId = 'tile32'; } else if (val === 64) { assetId = 'tile64'; } else if (val === 128) { assetId = 'tile128'; } else if (val === 256) { assetId = 'tile256'; } else if (val === 512) { assetId = 'tile512'; } else if (val === 1024) { assetId = 'tile1024'; } else if (val === 2048) { assetId = 'tile2048'; } self.tileBG = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); if (val > 0) { var textColor = "#776e65"; if (val >= 8) textColor = "#f9f6f2"; self.tileText = new Text2(val + '', { size: 120, fill: textColor }); self.tileText.anchor.set(0.5, 0.5); self.addChild(self.tileText); } }; // Animate pop self.pop = function () { self.scaleX = 0.7; self.scaleY = 0.7; tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.easeOut }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xfaf8ef }); /**** * Game Code ****/ // Tiles: We'll use colored boxes for tiles, and white for empty background. // Board constants var GRID_SIZE = 4; var TILE_SIZE = 400; var TILE_MARGIN = 20; var BOARD_SIZE = GRID_SIZE * TILE_SIZE + (GRID_SIZE + 1) * TILE_MARGIN; var BOARD_X = (2048 - BOARD_SIZE) / 2; var BOARD_Y = (2732 - BOARD_SIZE) / 2 + 100; // +100 to avoid top menu // Board background var boardBG = LK.getAsset('boardBG', { anchorX: 0, anchorY: 0, x: BOARD_X, y: BOARD_Y }); game.addChild(boardBG); // 2D array of tiles var tiles = []; // 2D array of tile values (for logic) var tileVals = []; // For swipe detection var touchStartX = 0, touchStartY = 0, touchEndX = 0, touchEndY = 0, isTouching = false; // Score var score = 0; var bestScore = 0; // Score text var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); scoreTxt.x = 2048 / 2; scoreTxt.y = 20; LK.gui.top.addChild(scoreTxt); // Best score text var bestScoreTxt = new Text2('BEST: 0', { size: 60, fill: 0xFFFFFF }); bestScoreTxt.anchor.set(0.5, 0); bestScoreTxt.x = 2048 / 2; bestScoreTxt.y = 150; LK.gui.top.addChild(bestScoreTxt); // Initialize board function initBoard() { // Remove old tiles if any for (var r = 0; r < GRID_SIZE; r++) { for (var c = 0; c < GRID_SIZE; c++) { if (tiles[r] && tiles[r][c]) { tiles[r][c].destroy(); } } } tiles = []; tileVals = []; for (var r = 0; r < GRID_SIZE; r++) { tiles[r] = []; tileVals[r] = []; for (var c = 0; c < GRID_SIZE; c++) { var tile = new Tile(); tile.row = r; tile.col = c; tile.setValue(0); // Position tile.x = BOARD_X + TILE_MARGIN + c * (TILE_SIZE + TILE_MARGIN) + TILE_SIZE / 2; tile.y = BOARD_Y + TILE_MARGIN + r * (TILE_SIZE + TILE_MARGIN) + TILE_SIZE / 2; tile.scaleX = 1; tile.scaleY = 1; game.addChild(tile); tiles[r][c] = tile; tileVals[r][c] = 0; } } score = 0; updateScore(); addRandomTile(); addRandomTile(); } // Add a random tile (2 or 4) to an empty spot function addRandomTile() { var empties = []; for (var r = 0; r < GRID_SIZE; r++) { for (var c = 0; c < GRID_SIZE; c++) { if (tileVals[r][c] === 0) { empties.push({ r: r, c: c }); } } } if (empties.length === 0) return; var idx = Math.floor(Math.random() * empties.length); var pos = empties[idx]; var val = Math.random() < 0.9 ? 2 : 4; tileVals[pos.r][pos.c] = val; tiles[pos.r][pos.c].setValue(val); tiles[pos.r][pos.c].pop(); } // Update all tile visuals from tileVals function updateTiles() { for (var r = 0; r < GRID_SIZE; r++) { for (var c = 0; c < GRID_SIZE; c++) { tiles[r][c].setValue(tileVals[r][c]); } } } // Update score display function updateScore() { scoreTxt.setText(score + ''); if (score > bestScore) { bestScore = score; } bestScoreTxt.setText('BEST: ' + bestScore); LK.setScore(score); } // Check if any moves are possible function canMove() { for (var r = 0; r < GRID_SIZE; r++) { for (var c = 0; c < GRID_SIZE; c++) { if (tileVals[r][c] === 0) return true; if (c < GRID_SIZE - 1 && tileVals[r][c] === tileVals[r][c + 1]) return true; if (r < GRID_SIZE - 1 && tileVals[r][c] === tileVals[r + 1][c]) return true; } } return false; } // Check if 2048 tile exists function has2048() { for (var r = 0; r < GRID_SIZE; r++) { for (var c = 0; c < GRID_SIZE; c++) { if (tileVals[r][c] === 2048) return true; } } return false; } // Move logic function move(direction) { // direction: 'left', 'right', 'up', 'down' var moved = false; var merged = []; for (var r = 0; r < GRID_SIZE; r++) { merged[r] = []; for (var c = 0; c < GRID_SIZE; c++) { merged[r][c] = false; } } var start, end, step; if (direction === 'left') { for (var r = 0; r < GRID_SIZE; r++) { for (var c = 1; c < GRID_SIZE; c++) { if (tileVals[r][c] === 0) continue; var target = c; while (target > 0 && tileVals[r][target - 1] === 0) { target--; } if (target > 0 && tileVals[r][target - 1] === tileVals[r][c] && !merged[r][target - 1]) { // Merge tileVals[r][target - 1] *= 2; score += tileVals[r][target - 1]; tileVals[r][c] = 0; merged[r][target - 1] = true; moved = true; tiles[r][target - 1].pop(); } else if (target !== c) { // Move tileVals[r][target] = tileVals[r][c]; tileVals[r][c] = 0; moved = true; } } } } else if (direction === 'right') { for (var r = 0; r < GRID_SIZE; r++) { for (var c = GRID_SIZE - 2; c >= 0; c--) { if (tileVals[r][c] === 0) continue; var target = c; while (target < GRID_SIZE - 1 && tileVals[r][target + 1] === 0) { target++; } if (target < GRID_SIZE - 1 && tileVals[r][target + 1] === tileVals[r][c] && !merged[r][target + 1]) { // Merge tileVals[r][target + 1] *= 2; score += tileVals[r][target + 1]; tileVals[r][c] = 0; merged[r][target + 1] = true; moved = true; tiles[r][target + 1].pop(); } else if (target !== c) { // Move tileVals[r][target] = tileVals[r][c]; tileVals[r][c] = 0; moved = true; } } } } else if (direction === 'up') { for (var c = 0; c < GRID_SIZE; c++) { for (var r = 1; r < GRID_SIZE; r++) { if (tileVals[r][c] === 0) continue; var target = r; while (target > 0 && tileVals[target - 1][c] === 0) { target--; } if (target > 0 && tileVals[target - 1][c] === tileVals[r][c] && !merged[target - 1][c]) { // Merge tileVals[target - 1][c] *= 2; score += tileVals[target - 1][c]; tileVals[r][c] = 0; merged[target - 1][c] = true; moved = true; tiles[target - 1][c].pop(); } else if (target !== r) { // Move tileVals[target][c] = tileVals[r][c]; tileVals[r][c] = 0; moved = true; } } } } else if (direction === 'down') { for (var c = 0; c < GRID_SIZE; c++) { for (var r = GRID_SIZE - 2; r >= 0; r--) { if (tileVals[r][c] === 0) continue; var target = r; while (target < GRID_SIZE - 1 && tileVals[target + 1][c] === 0) { target++; } if (target < GRID_SIZE - 1 && tileVals[target + 1][c] === tileVals[r][c] && !merged[target + 1][c]) { // Merge tileVals[target + 1][c] *= 2; score += tileVals[target + 1][c]; tileVals[r][c] = 0; merged[target + 1][c] = true; moved = true; tiles[target + 1][c].pop(); } else if (target !== r) { // Move tileVals[target][c] = tileVals[r][c]; tileVals[r][c] = 0; moved = true; } } } } if (moved) { addRandomTile(); updateTiles(); updateScore(); if (has2048()) { LK.showYouWin(); } else if (!canMove()) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } } // Touch/drag/swipe handling game.down = function (x, y, obj) { // Only start if inside board area if (x >= BOARD_X && x <= BOARD_X + BOARD_SIZE && y >= BOARD_Y && y <= BOARD_Y + BOARD_SIZE) { touchStartX = x; touchStartY = y; isTouching = true; } }; game.move = function (x, y, obj) { if (!isTouching) return; touchEndX = x; touchEndY = y; }; game.up = function (x, y, obj) { if (!isTouching) return; var dx = touchEndX - touchStartX; var dy = touchEndY - touchStartY; var absDx = Math.abs(dx); var absDy = Math.abs(dy); if (absDx > 50 || absDy > 50) { if (absDx > absDy) { if (dx > 0) { move('right'); } else { move('left'); } } else { if (dy > 0) { move('down'); } else { move('up'); } } } isTouching = false; touchStartX = 0; touchStartY = 0; touchEndX = 0; touchEndY = 0; }; // Reset game on game over or win game.on('reset', function () { initBoard(); }); // Initialize initBoard(); // Play background music LK.playMusic('arkaplan');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Tile class: represents a single tile on the board
var Tile = Container.expand(function () {
var self = Container.call(this);
// Properties
self.value = 0; // 0 means empty
self.row = 0;
self.col = 0;
self.tileBG = null;
self.tileText = null;
// Set up tile
self.setValue = function (val) {
self.value = val;
// Remove previous children if any
if (self.tileBG) {
self.removeChild(self.tileBG);
self.tileBG = null;
}
if (self.tileText) {
self.removeChild(self.tileText);
self.tileText = null;
}
// Choose asset based on value
var assetId = 'tile';
if (val === 0) {
assetId = 'tileBG';
} else if (val === 2) {
assetId = 'tile2';
} else if (val === 4) {
assetId = 'tile4';
} else if (val === 8) {
assetId = 'tile8';
} else if (val === 16) {
assetId = 'tile16';
} else if (val === 32) {
assetId = 'tile32';
} else if (val === 64) {
assetId = 'tile64';
} else if (val === 128) {
assetId = 'tile128';
} else if (val === 256) {
assetId = 'tile256';
} else if (val === 512) {
assetId = 'tile512';
} else if (val === 1024) {
assetId = 'tile1024';
} else if (val === 2048) {
assetId = 'tile2048';
}
self.tileBG = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
if (val > 0) {
var textColor = "#776e65";
if (val >= 8) textColor = "#f9f6f2";
self.tileText = new Text2(val + '', {
size: 120,
fill: textColor
});
self.tileText.anchor.set(0.5, 0.5);
self.addChild(self.tileText);
}
};
// Animate pop
self.pop = function () {
self.scaleX = 0.7;
self.scaleY = 0.7;
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeOut
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xfaf8ef
});
/****
* Game Code
****/
// Tiles: We'll use colored boxes for tiles, and white for empty background.
// Board constants
var GRID_SIZE = 4;
var TILE_SIZE = 400;
var TILE_MARGIN = 20;
var BOARD_SIZE = GRID_SIZE * TILE_SIZE + (GRID_SIZE + 1) * TILE_MARGIN;
var BOARD_X = (2048 - BOARD_SIZE) / 2;
var BOARD_Y = (2732 - BOARD_SIZE) / 2 + 100; // +100 to avoid top menu
// Board background
var boardBG = LK.getAsset('boardBG', {
anchorX: 0,
anchorY: 0,
x: BOARD_X,
y: BOARD_Y
});
game.addChild(boardBG);
// 2D array of tiles
var tiles = [];
// 2D array of tile values (for logic)
var tileVals = [];
// For swipe detection
var touchStartX = 0,
touchStartY = 0,
touchEndX = 0,
touchEndY = 0,
isTouching = false;
// Score
var score = 0;
var bestScore = 0;
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.x = 2048 / 2;
scoreTxt.y = 20;
LK.gui.top.addChild(scoreTxt);
// Best score text
var bestScoreTxt = new Text2('BEST: 0', {
size: 60,
fill: 0xFFFFFF
});
bestScoreTxt.anchor.set(0.5, 0);
bestScoreTxt.x = 2048 / 2;
bestScoreTxt.y = 150;
LK.gui.top.addChild(bestScoreTxt);
// Initialize board
function initBoard() {
// Remove old tiles if any
for (var r = 0; r < GRID_SIZE; r++) {
for (var c = 0; c < GRID_SIZE; c++) {
if (tiles[r] && tiles[r][c]) {
tiles[r][c].destroy();
}
}
}
tiles = [];
tileVals = [];
for (var r = 0; r < GRID_SIZE; r++) {
tiles[r] = [];
tileVals[r] = [];
for (var c = 0; c < GRID_SIZE; c++) {
var tile = new Tile();
tile.row = r;
tile.col = c;
tile.setValue(0);
// Position
tile.x = BOARD_X + TILE_MARGIN + c * (TILE_SIZE + TILE_MARGIN) + TILE_SIZE / 2;
tile.y = BOARD_Y + TILE_MARGIN + r * (TILE_SIZE + TILE_MARGIN) + TILE_SIZE / 2;
tile.scaleX = 1;
tile.scaleY = 1;
game.addChild(tile);
tiles[r][c] = tile;
tileVals[r][c] = 0;
}
}
score = 0;
updateScore();
addRandomTile();
addRandomTile();
}
// Add a random tile (2 or 4) to an empty spot
function addRandomTile() {
var empties = [];
for (var r = 0; r < GRID_SIZE; r++) {
for (var c = 0; c < GRID_SIZE; c++) {
if (tileVals[r][c] === 0) {
empties.push({
r: r,
c: c
});
}
}
}
if (empties.length === 0) return;
var idx = Math.floor(Math.random() * empties.length);
var pos = empties[idx];
var val = Math.random() < 0.9 ? 2 : 4;
tileVals[pos.r][pos.c] = val;
tiles[pos.r][pos.c].setValue(val);
tiles[pos.r][pos.c].pop();
}
// Update all tile visuals from tileVals
function updateTiles() {
for (var r = 0; r < GRID_SIZE; r++) {
for (var c = 0; c < GRID_SIZE; c++) {
tiles[r][c].setValue(tileVals[r][c]);
}
}
}
// Update score display
function updateScore() {
scoreTxt.setText(score + '');
if (score > bestScore) {
bestScore = score;
}
bestScoreTxt.setText('BEST: ' + bestScore);
LK.setScore(score);
}
// Check if any moves are possible
function canMove() {
for (var r = 0; r < GRID_SIZE; r++) {
for (var c = 0; c < GRID_SIZE; c++) {
if (tileVals[r][c] === 0) return true;
if (c < GRID_SIZE - 1 && tileVals[r][c] === tileVals[r][c + 1]) return true;
if (r < GRID_SIZE - 1 && tileVals[r][c] === tileVals[r + 1][c]) return true;
}
}
return false;
}
// Check if 2048 tile exists
function has2048() {
for (var r = 0; r < GRID_SIZE; r++) {
for (var c = 0; c < GRID_SIZE; c++) {
if (tileVals[r][c] === 2048) return true;
}
}
return false;
}
// Move logic
function move(direction) {
// direction: 'left', 'right', 'up', 'down'
var moved = false;
var merged = [];
for (var r = 0; r < GRID_SIZE; r++) {
merged[r] = [];
for (var c = 0; c < GRID_SIZE; c++) {
merged[r][c] = false;
}
}
var start, end, step;
if (direction === 'left') {
for (var r = 0; r < GRID_SIZE; r++) {
for (var c = 1; c < GRID_SIZE; c++) {
if (tileVals[r][c] === 0) continue;
var target = c;
while (target > 0 && tileVals[r][target - 1] === 0) {
target--;
}
if (target > 0 && tileVals[r][target - 1] === tileVals[r][c] && !merged[r][target - 1]) {
// Merge
tileVals[r][target - 1] *= 2;
score += tileVals[r][target - 1];
tileVals[r][c] = 0;
merged[r][target - 1] = true;
moved = true;
tiles[r][target - 1].pop();
} else if (target !== c) {
// Move
tileVals[r][target] = tileVals[r][c];
tileVals[r][c] = 0;
moved = true;
}
}
}
} else if (direction === 'right') {
for (var r = 0; r < GRID_SIZE; r++) {
for (var c = GRID_SIZE - 2; c >= 0; c--) {
if (tileVals[r][c] === 0) continue;
var target = c;
while (target < GRID_SIZE - 1 && tileVals[r][target + 1] === 0) {
target++;
}
if (target < GRID_SIZE - 1 && tileVals[r][target + 1] === tileVals[r][c] && !merged[r][target + 1]) {
// Merge
tileVals[r][target + 1] *= 2;
score += tileVals[r][target + 1];
tileVals[r][c] = 0;
merged[r][target + 1] = true;
moved = true;
tiles[r][target + 1].pop();
} else if (target !== c) {
// Move
tileVals[r][target] = tileVals[r][c];
tileVals[r][c] = 0;
moved = true;
}
}
}
} else if (direction === 'up') {
for (var c = 0; c < GRID_SIZE; c++) {
for (var r = 1; r < GRID_SIZE; r++) {
if (tileVals[r][c] === 0) continue;
var target = r;
while (target > 0 && tileVals[target - 1][c] === 0) {
target--;
}
if (target > 0 && tileVals[target - 1][c] === tileVals[r][c] && !merged[target - 1][c]) {
// Merge
tileVals[target - 1][c] *= 2;
score += tileVals[target - 1][c];
tileVals[r][c] = 0;
merged[target - 1][c] = true;
moved = true;
tiles[target - 1][c].pop();
} else if (target !== r) {
// Move
tileVals[target][c] = tileVals[r][c];
tileVals[r][c] = 0;
moved = true;
}
}
}
} else if (direction === 'down') {
for (var c = 0; c < GRID_SIZE; c++) {
for (var r = GRID_SIZE - 2; r >= 0; r--) {
if (tileVals[r][c] === 0) continue;
var target = r;
while (target < GRID_SIZE - 1 && tileVals[target + 1][c] === 0) {
target++;
}
if (target < GRID_SIZE - 1 && tileVals[target + 1][c] === tileVals[r][c] && !merged[target + 1][c]) {
// Merge
tileVals[target + 1][c] *= 2;
score += tileVals[target + 1][c];
tileVals[r][c] = 0;
merged[target + 1][c] = true;
moved = true;
tiles[target + 1][c].pop();
} else if (target !== r) {
// Move
tileVals[target][c] = tileVals[r][c];
tileVals[r][c] = 0;
moved = true;
}
}
}
}
if (moved) {
addRandomTile();
updateTiles();
updateScore();
if (has2048()) {
LK.showYouWin();
} else if (!canMove()) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
}
// Touch/drag/swipe handling
game.down = function (x, y, obj) {
// Only start if inside board area
if (x >= BOARD_X && x <= BOARD_X + BOARD_SIZE && y >= BOARD_Y && y <= BOARD_Y + BOARD_SIZE) {
touchStartX = x;
touchStartY = y;
isTouching = true;
}
};
game.move = function (x, y, obj) {
if (!isTouching) return;
touchEndX = x;
touchEndY = y;
};
game.up = function (x, y, obj) {
if (!isTouching) return;
var dx = touchEndX - touchStartX;
var dy = touchEndY - touchStartY;
var absDx = Math.abs(dx);
var absDy = Math.abs(dy);
if (absDx > 50 || absDy > 50) {
if (absDx > absDy) {
if (dx > 0) {
move('right');
} else {
move('left');
}
} else {
if (dy > 0) {
move('down');
} else {
move('up');
}
}
}
isTouching = false;
touchStartX = 0;
touchStartY = 0;
touchEndX = 0;
touchEndY = 0;
};
// Reset game on game over or win
game.on('reset', function () {
initBoard();
});
// Initialize
initBoard();
// Play background music
LK.playMusic('arkaplan');