/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Grid = Container.expand(function () { var self = Container.call(this); self.size = 4; self.cellSize = 180; self.gap = 15; self.tiles = []; // Initialize 2D array for (var i = 0; i < self.size; i++) { self.tiles[i] = []; for (var j = 0; j < self.size; j++) { self.tiles[i][j] = null; } } var gridBackground = self.attachAsset('grid', { anchorX: 0.5, anchorY: 0.5 }); self.getCellPosition = function (x, y) { var startX = -(self.size - 1) * (self.cellSize + self.gap) / 2; var startY = -(self.size - 1) * (self.cellSize + self.gap) / 2; return { x: startX + x * (self.cellSize + self.gap), y: startY + y * (self.cellSize + self.gap) }; }; self.addTile = function (x, y, value) { if (self.tiles[y][x] !== null) return null; var tile = new Tile(value); tile.gridX = x; tile.gridY = y; var pos = self.getCellPosition(x, y); tile.x = pos.x; tile.y = pos.y; self.tiles[y][x] = tile; self.addChild(tile); return tile; }; self.getEmptyCells = function () { var emptyCells = []; for (var y = 0; y < self.size; y++) { for (var x = 0; x < self.size; x++) { if (self.tiles[y][x] === null) { emptyCells.push({ x: x, y: y }); } } } return emptyCells; }; self.addRandomTile = function () { var emptyCells = self.getEmptyCells(); if (emptyCells.length === 0) return; var randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)]; var value = Math.random() < 0.9 ? 2 : 4; self.addTile(randomCell.x, randomCell.y, value); }; self.canMove = function () { // Check for empty cells if (self.getEmptyCells().length > 0) return true; // Check for possible merges for (var y = 0; y < self.size; y++) { for (var x = 0; x < self.size; x++) { var tile = self.tiles[y][x]; if (tile !== null) { // Check right if (x < self.size - 1 && self.tiles[y][x + 1] !== null && self.tiles[y][x + 1].value === tile.value) { return true; } // Check down if (y < self.size - 1 && self.tiles[y + 1][x] !== null && self.tiles[y + 1][x].value === tile.value) { return true; } } } } return false; }; self.moveTiles = function (direction) { var moved = false; var merged = []; // Reset merged flags for (var y = 0; y < self.size; y++) { for (var x = 0; x < self.size; x++) { if (self.tiles[y][x] !== null) { self.tiles[y][x].merged = false; } } } if (direction === 'left') { for (var y = 0; y < self.size; y++) { for (var x = 1; x < self.size; x++) { if (self.tiles[y][x] !== null) { var currentTile = self.tiles[y][x]; var newX = x; // Move as far left as possible while (newX > 0 && self.tiles[y][newX - 1] === null) { newX--; } // Check for merge if (newX > 0 && self.tiles[y][newX - 1] !== null && self.tiles[y][newX - 1].value === currentTile.value && !self.tiles[y][newX - 1].merged) { // Merge self.tiles[y][newX - 1].value *= 2; self.tiles[y][newX - 1].merged = true; self.tiles[y][newX - 1].updateAppearance(); LK.setScore(LK.getScore() + self.tiles[y][newX - 1].value); currentTile.destroy(); self.tiles[y][x] = null; moved = true; if (self.tiles[y][newX - 1].value === 2048) { hasWon = true; } } else if (newX !== x) { // Just move self.tiles[y][newX] = currentTile; self.tiles[y][x] = null; currentTile.gridX = newX; var pos = self.getCellPosition(newX, y); tween(currentTile, { x: pos.x }, { duration: 150 }); moved = true; } } } } } else if (direction === 'right') { for (var y = 0; y < self.size; y++) { for (var x = self.size - 2; x >= 0; x--) { if (self.tiles[y][x] !== null) { var currentTile = self.tiles[y][x]; var newX = x; while (newX < self.size - 1 && self.tiles[y][newX + 1] === null) { newX++; } if (newX < self.size - 1 && self.tiles[y][newX + 1] !== null && self.tiles[y][newX + 1].value === currentTile.value && !self.tiles[y][newX + 1].merged) { self.tiles[y][newX + 1].value *= 2; self.tiles[y][newX + 1].merged = true; self.tiles[y][newX + 1].updateAppearance(); LK.setScore(LK.getScore() + self.tiles[y][newX + 1].value); currentTile.destroy(); self.tiles[y][x] = null; moved = true; if (self.tiles[y][newX + 1].value === 2048) { hasWon = true; } } else if (newX !== x) { self.tiles[y][newX] = currentTile; self.tiles[y][x] = null; currentTile.gridX = newX; var pos = self.getCellPosition(newX, y); tween(currentTile, { x: pos.x }, { duration: 150 }); moved = true; } } } } } else if (direction === 'up') { for (var x = 0; x < self.size; x++) { for (var y = 1; y < self.size; y++) { if (self.tiles[y][x] !== null) { var currentTile = self.tiles[y][x]; var newY = y; while (newY > 0 && self.tiles[newY - 1][x] === null) { newY--; } if (newY > 0 && self.tiles[newY - 1][x] !== null && self.tiles[newY - 1][x].value === currentTile.value && !self.tiles[newY - 1][x].merged) { self.tiles[newY - 1][x].value *= 2; self.tiles[newY - 1][x].merged = true; self.tiles[newY - 1][x].updateAppearance(); LK.setScore(LK.getScore() + self.tiles[newY - 1][x].value); currentTile.destroy(); self.tiles[y][x] = null; moved = true; if (self.tiles[newY - 1][x].value === 2048) { hasWon = true; } } else if (newY !== y) { self.tiles[newY][x] = currentTile; self.tiles[y][x] = null; currentTile.gridY = newY; var pos = self.getCellPosition(x, newY); tween(currentTile, { y: pos.y }, { duration: 150 }); moved = true; } } } } } else if (direction === 'down') { for (var x = 0; x < self.size; x++) { for (var y = self.size - 2; y >= 0; y--) { if (self.tiles[y][x] !== null) { var currentTile = self.tiles[y][x]; var newY = y; while (newY < self.size - 1 && self.tiles[newY + 1][x] === null) { newY++; } if (newY < self.size - 1 && self.tiles[newY + 1][x] !== null && self.tiles[newY + 1][x].value === currentTile.value && !self.tiles[newY + 1][x].merged) { self.tiles[newY + 1][x].value *= 2; self.tiles[newY + 1][x].merged = true; self.tiles[newY + 1][x].updateAppearance(); LK.setScore(LK.getScore() + self.tiles[newY + 1][x].value); currentTile.destroy(); self.tiles[y][x] = null; moved = true; if (self.tiles[newY + 1][x].value === 2048) { hasWon = true; } } else if (newY !== y) { self.tiles[newY][x] = currentTile; self.tiles[y][x] = null; currentTile.gridY = newY; var pos = self.getCellPosition(x, newY); tween(currentTile, { y: pos.y }, { duration: 150 }); moved = true; } } } } } return moved; }; return self; }); var Tile = Container.expand(function (value) { var self = Container.call(this); self.value = value || 2; self.gridX = 0; self.gridY = 0; self.merged = false; var tileBackground = self.attachAsset('tile', { anchorX: 0.5, anchorY: 0.5 }); var valueText = new Text2(self.value.toString(), { size: 90, fill: 0x000000 }); valueText.anchor.set(0.5, 0.5); // Add text outline for better readability var outlineText = new Text2(self.value.toString(), { size: 90, fill: 0x000000, stroke: 0xFFFFFF, strokeThickness: 4 }); outlineText.anchor.set(0.5, 0.5); outlineText.alpha = 0.7; self.addChild(outlineText); self.addChild(valueText); // Store reference to outline text for updates self.outlineText = outlineText; self.updateAppearance = function () { valueText.setText(self.value.toString()); if (self.outlineText) { self.outlineText.setText(self.value.toString()); } // Color based on 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[self.value] || 0x3C3A32; tileBackground.tint = color; if (self.value <= 4) { valueText.fill = "#000000"; // Black for light tiles } else if (self.value >= 8 && self.value < 128) { valueText.fill = "#FFFFFF"; // White for medium tiles } else { valueText.fill = "#FFFFFF"; // White for dark tiles } }; self.updateAppearance(); return self; }); var WoodenFloor = Container.expand(function () { var self = Container.call(this); var plankHeight = 120; var totalPlanks = Math.ceil(2732 / plankHeight) + 1; for (var i = 0; i < totalPlanks; i++) { // Create main plank var plank = self.attachAsset('woodPlank', { anchorX: 0, anchorY: 0 }); plank.x = 0; plank.y = i * plankHeight; // Alternate plank colors for variation var plankColors = [0x8B4513, 0x964B00, 0xA0522D, 0x8B4513]; plank.tint = plankColors[i % plankColors.length]; // Add grain lines on each plank for (var j = 0; j < 3; j++) { var grain = self.attachAsset('woodGrain', { anchorX: 0, anchorY: 0 }); grain.x = 0; grain.y = i * plankHeight + 20 + j * 30; grain.alpha = 0.3; } // Add wood knots randomly if (Math.random() < 0.4) { var knot = self.attachAsset('woodKnot', { anchorX: 0.5, anchorY: 0.5 }); knot.x = Math.random() * 1800 + 124; knot.y = i * plankHeight + plankHeight / 2; knot.alpha = 0.6; } // Add plank separation line if (i > 0) { var separation = self.attachAsset('woodGrain', { anchorX: 0, anchorY: 0 }); separation.x = 0; separation.y = i * plankHeight - 4; separation.tint = 0x3E2723; separation.alpha = 0.8; } } return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xFAF8EF }); /**** * Game Code ****/ var grid; var scoreTxt; var hasWon = false; var gameOver = false; var startX = 0; var startY = 0; var isDragging = false; var minSwipeDistance = 50; var woodenFloor; // Create wooden floor background woodenFloor = game.addChild(new WoodenFloor()); woodenFloor.x = 0; woodenFloor.y = 0; // Create grid grid = game.addChild(new Grid()); grid.x = 2048 / 2; grid.y = 2732 / 2 - 200; // Create score display scoreTxt = new Text2('Score: 0', { size: 80, fill: 0x776E65 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 150; // Add initial tiles grid.addRandomTile(); grid.addRandomTile(); function updateScore() { scoreTxt.setText('Score: ' + LK.getScore()); } function handleSwipe(direction) { if (gameOver) return; var moved = grid.moveTiles(direction); if (moved) { updateScore(); LK.setTimeout(function () { grid.addRandomTile(); if (hasWon) { LK.showYouWin(); return; } if (!grid.canMove()) { gameOver = true; LK.showGameOver(); } }, 200); } } game.down = function (x, y, obj) { startX = x; startY = y; isDragging = true; }; game.up = function (x, y, obj) { if (!isDragging) return; isDragging = false; var deltaX = x - startX; var deltaY = y - startY; var absX = Math.abs(deltaX); var absY = Math.abs(deltaY); if (Math.max(absX, absY) < minSwipeDistance) return; if (absX > absY) { // Horizontal swipe if (deltaX > 0) { handleSwipe('right'); } else { handleSwipe('left'); } } else { // Vertical swipe if (deltaY > 0) { handleSwipe('down'); } else { handleSwipe('up'); } } }; game.update = function () { // Game loop runs here };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Grid = Container.expand(function () {
var self = Container.call(this);
self.size = 4;
self.cellSize = 180;
self.gap = 15;
self.tiles = [];
// Initialize 2D array
for (var i = 0; i < self.size; i++) {
self.tiles[i] = [];
for (var j = 0; j < self.size; j++) {
self.tiles[i][j] = null;
}
}
var gridBackground = self.attachAsset('grid', {
anchorX: 0.5,
anchorY: 0.5
});
self.getCellPosition = function (x, y) {
var startX = -(self.size - 1) * (self.cellSize + self.gap) / 2;
var startY = -(self.size - 1) * (self.cellSize + self.gap) / 2;
return {
x: startX + x * (self.cellSize + self.gap),
y: startY + y * (self.cellSize + self.gap)
};
};
self.addTile = function (x, y, value) {
if (self.tiles[y][x] !== null) return null;
var tile = new Tile(value);
tile.gridX = x;
tile.gridY = y;
var pos = self.getCellPosition(x, y);
tile.x = pos.x;
tile.y = pos.y;
self.tiles[y][x] = tile;
self.addChild(tile);
return tile;
};
self.getEmptyCells = function () {
var emptyCells = [];
for (var y = 0; y < self.size; y++) {
for (var x = 0; x < self.size; x++) {
if (self.tiles[y][x] === null) {
emptyCells.push({
x: x,
y: y
});
}
}
}
return emptyCells;
};
self.addRandomTile = function () {
var emptyCells = self.getEmptyCells();
if (emptyCells.length === 0) return;
var randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
var value = Math.random() < 0.9 ? 2 : 4;
self.addTile(randomCell.x, randomCell.y, value);
};
self.canMove = function () {
// Check for empty cells
if (self.getEmptyCells().length > 0) return true;
// Check for possible merges
for (var y = 0; y < self.size; y++) {
for (var x = 0; x < self.size; x++) {
var tile = self.tiles[y][x];
if (tile !== null) {
// Check right
if (x < self.size - 1 && self.tiles[y][x + 1] !== null && self.tiles[y][x + 1].value === tile.value) {
return true;
}
// Check down
if (y < self.size - 1 && self.tiles[y + 1][x] !== null && self.tiles[y + 1][x].value === tile.value) {
return true;
}
}
}
}
return false;
};
self.moveTiles = function (direction) {
var moved = false;
var merged = [];
// Reset merged flags
for (var y = 0; y < self.size; y++) {
for (var x = 0; x < self.size; x++) {
if (self.tiles[y][x] !== null) {
self.tiles[y][x].merged = false;
}
}
}
if (direction === 'left') {
for (var y = 0; y < self.size; y++) {
for (var x = 1; x < self.size; x++) {
if (self.tiles[y][x] !== null) {
var currentTile = self.tiles[y][x];
var newX = x;
// Move as far left as possible
while (newX > 0 && self.tiles[y][newX - 1] === null) {
newX--;
}
// Check for merge
if (newX > 0 && self.tiles[y][newX - 1] !== null && self.tiles[y][newX - 1].value === currentTile.value && !self.tiles[y][newX - 1].merged) {
// Merge
self.tiles[y][newX - 1].value *= 2;
self.tiles[y][newX - 1].merged = true;
self.tiles[y][newX - 1].updateAppearance();
LK.setScore(LK.getScore() + self.tiles[y][newX - 1].value);
currentTile.destroy();
self.tiles[y][x] = null;
moved = true;
if (self.tiles[y][newX - 1].value === 2048) {
hasWon = true;
}
} else if (newX !== x) {
// Just move
self.tiles[y][newX] = currentTile;
self.tiles[y][x] = null;
currentTile.gridX = newX;
var pos = self.getCellPosition(newX, y);
tween(currentTile, {
x: pos.x
}, {
duration: 150
});
moved = true;
}
}
}
}
} else if (direction === 'right') {
for (var y = 0; y < self.size; y++) {
for (var x = self.size - 2; x >= 0; x--) {
if (self.tiles[y][x] !== null) {
var currentTile = self.tiles[y][x];
var newX = x;
while (newX < self.size - 1 && self.tiles[y][newX + 1] === null) {
newX++;
}
if (newX < self.size - 1 && self.tiles[y][newX + 1] !== null && self.tiles[y][newX + 1].value === currentTile.value && !self.tiles[y][newX + 1].merged) {
self.tiles[y][newX + 1].value *= 2;
self.tiles[y][newX + 1].merged = true;
self.tiles[y][newX + 1].updateAppearance();
LK.setScore(LK.getScore() + self.tiles[y][newX + 1].value);
currentTile.destroy();
self.tiles[y][x] = null;
moved = true;
if (self.tiles[y][newX + 1].value === 2048) {
hasWon = true;
}
} else if (newX !== x) {
self.tiles[y][newX] = currentTile;
self.tiles[y][x] = null;
currentTile.gridX = newX;
var pos = self.getCellPosition(newX, y);
tween(currentTile, {
x: pos.x
}, {
duration: 150
});
moved = true;
}
}
}
}
} else if (direction === 'up') {
for (var x = 0; x < self.size; x++) {
for (var y = 1; y < self.size; y++) {
if (self.tiles[y][x] !== null) {
var currentTile = self.tiles[y][x];
var newY = y;
while (newY > 0 && self.tiles[newY - 1][x] === null) {
newY--;
}
if (newY > 0 && self.tiles[newY - 1][x] !== null && self.tiles[newY - 1][x].value === currentTile.value && !self.tiles[newY - 1][x].merged) {
self.tiles[newY - 1][x].value *= 2;
self.tiles[newY - 1][x].merged = true;
self.tiles[newY - 1][x].updateAppearance();
LK.setScore(LK.getScore() + self.tiles[newY - 1][x].value);
currentTile.destroy();
self.tiles[y][x] = null;
moved = true;
if (self.tiles[newY - 1][x].value === 2048) {
hasWon = true;
}
} else if (newY !== y) {
self.tiles[newY][x] = currentTile;
self.tiles[y][x] = null;
currentTile.gridY = newY;
var pos = self.getCellPosition(x, newY);
tween(currentTile, {
y: pos.y
}, {
duration: 150
});
moved = true;
}
}
}
}
} else if (direction === 'down') {
for (var x = 0; x < self.size; x++) {
for (var y = self.size - 2; y >= 0; y--) {
if (self.tiles[y][x] !== null) {
var currentTile = self.tiles[y][x];
var newY = y;
while (newY < self.size - 1 && self.tiles[newY + 1][x] === null) {
newY++;
}
if (newY < self.size - 1 && self.tiles[newY + 1][x] !== null && self.tiles[newY + 1][x].value === currentTile.value && !self.tiles[newY + 1][x].merged) {
self.tiles[newY + 1][x].value *= 2;
self.tiles[newY + 1][x].merged = true;
self.tiles[newY + 1][x].updateAppearance();
LK.setScore(LK.getScore() + self.tiles[newY + 1][x].value);
currentTile.destroy();
self.tiles[y][x] = null;
moved = true;
if (self.tiles[newY + 1][x].value === 2048) {
hasWon = true;
}
} else if (newY !== y) {
self.tiles[newY][x] = currentTile;
self.tiles[y][x] = null;
currentTile.gridY = newY;
var pos = self.getCellPosition(x, newY);
tween(currentTile, {
y: pos.y
}, {
duration: 150
});
moved = true;
}
}
}
}
}
return moved;
};
return self;
});
var Tile = Container.expand(function (value) {
var self = Container.call(this);
self.value = value || 2;
self.gridX = 0;
self.gridY = 0;
self.merged = false;
var tileBackground = self.attachAsset('tile', {
anchorX: 0.5,
anchorY: 0.5
});
var valueText = new Text2(self.value.toString(), {
size: 90,
fill: 0x000000
});
valueText.anchor.set(0.5, 0.5);
// Add text outline for better readability
var outlineText = new Text2(self.value.toString(), {
size: 90,
fill: 0x000000,
stroke: 0xFFFFFF,
strokeThickness: 4
});
outlineText.anchor.set(0.5, 0.5);
outlineText.alpha = 0.7;
self.addChild(outlineText);
self.addChild(valueText);
// Store reference to outline text for updates
self.outlineText = outlineText;
self.updateAppearance = function () {
valueText.setText(self.value.toString());
if (self.outlineText) {
self.outlineText.setText(self.value.toString());
}
// Color based on 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[self.value] || 0x3C3A32;
tileBackground.tint = color;
if (self.value <= 4) {
valueText.fill = "#000000"; // Black for light tiles
} else if (self.value >= 8 && self.value < 128) {
valueText.fill = "#FFFFFF"; // White for medium tiles
} else {
valueText.fill = "#FFFFFF"; // White for dark tiles
}
};
self.updateAppearance();
return self;
});
var WoodenFloor = Container.expand(function () {
var self = Container.call(this);
var plankHeight = 120;
var totalPlanks = Math.ceil(2732 / plankHeight) + 1;
for (var i = 0; i < totalPlanks; i++) {
// Create main plank
var plank = self.attachAsset('woodPlank', {
anchorX: 0,
anchorY: 0
});
plank.x = 0;
plank.y = i * plankHeight;
// Alternate plank colors for variation
var plankColors = [0x8B4513, 0x964B00, 0xA0522D, 0x8B4513];
plank.tint = plankColors[i % plankColors.length];
// Add grain lines on each plank
for (var j = 0; j < 3; j++) {
var grain = self.attachAsset('woodGrain', {
anchorX: 0,
anchorY: 0
});
grain.x = 0;
grain.y = i * plankHeight + 20 + j * 30;
grain.alpha = 0.3;
}
// Add wood knots randomly
if (Math.random() < 0.4) {
var knot = self.attachAsset('woodKnot', {
anchorX: 0.5,
anchorY: 0.5
});
knot.x = Math.random() * 1800 + 124;
knot.y = i * plankHeight + plankHeight / 2;
knot.alpha = 0.6;
}
// Add plank separation line
if (i > 0) {
var separation = self.attachAsset('woodGrain', {
anchorX: 0,
anchorY: 0
});
separation.x = 0;
separation.y = i * plankHeight - 4;
separation.tint = 0x3E2723;
separation.alpha = 0.8;
}
}
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFAF8EF
});
/****
* Game Code
****/
var grid;
var scoreTxt;
var hasWon = false;
var gameOver = false;
var startX = 0;
var startY = 0;
var isDragging = false;
var minSwipeDistance = 50;
var woodenFloor;
// Create wooden floor background
woodenFloor = game.addChild(new WoodenFloor());
woodenFloor.x = 0;
woodenFloor.y = 0;
// Create grid
grid = game.addChild(new Grid());
grid.x = 2048 / 2;
grid.y = 2732 / 2 - 200;
// Create score display
scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0x776E65
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 150;
// Add initial tiles
grid.addRandomTile();
grid.addRandomTile();
function updateScore() {
scoreTxt.setText('Score: ' + LK.getScore());
}
function handleSwipe(direction) {
if (gameOver) return;
var moved = grid.moveTiles(direction);
if (moved) {
updateScore();
LK.setTimeout(function () {
grid.addRandomTile();
if (hasWon) {
LK.showYouWin();
return;
}
if (!grid.canMove()) {
gameOver = true;
LK.showGameOver();
}
}, 200);
}
}
game.down = function (x, y, obj) {
startX = x;
startY = y;
isDragging = true;
};
game.up = function (x, y, obj) {
if (!isDragging) return;
isDragging = false;
var deltaX = x - startX;
var deltaY = y - startY;
var absX = Math.abs(deltaX);
var absY = Math.abs(deltaY);
if (Math.max(absX, absY) < minSwipeDistance) return;
if (absX > absY) {
// Horizontal swipe
if (deltaX > 0) {
handleSwipe('right');
} else {
handleSwipe('left');
}
} else {
// Vertical swipe
if (deltaY > 0) {
handleSwipe('down');
} else {
handleSwipe('up');
}
}
};
game.update = function () {
// Game loop runs here
};