User prompt
make background smooth midnight blue and make tiles 6x6 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fontSize')' in or related to this line: 'tileText.style.fontSize = newValue >= 128 ? 40 : newValue >= 32 ? 50 : 60;' Line Number: 364
User prompt
make it all black and make tiles color as variants from green to blue ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught TypeError: callback is not a function' in or related to this line: 'callback(false);' Line Number: 301
User prompt
Please fix the bug: 'Uncaught TypeError: callback is not a function' in or related to this line: 'callback(false);' Line Number: 301
User prompt
Please fix the bug: 'self.getCellX is not a function' in or related to this line: 'cell.x = self.getCellX(j);' Line Number: 55
Code edit (1 edits merged)
Please save this source code
User prompt
2048 Merge: Smooth Edition
Initial prompt
make me a simple smooth animated and have poping effects 2028 game
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var GameControlButtons = Container.expand(function (gameGrid, onRestart, onStop) {
var self = Container.call(this);
self.gameGrid = gameGrid;
self.onRestart = onRestart;
self.onStop = onStop;
self.isPaused = false;
// Create restart button
var restartShadow = self.attachAsset('buttonShadow', {
anchorX: 0.5,
anchorY: 0.5
});
restartShadow.x = -60 + 3;
restartShadow.y = 3;
var restartButton = self.attachAsset('restartButton', {
anchorX: 0.5,
anchorY: 0.5
});
restartButton.x = -60;
restartButton.y = 0;
var restartText = new Text2('↻', {
size: 50,
fill: 0xf0f0f0
});
restartText.anchor.set(0.5, 0.5);
restartText.x = -60;
self.addChild(restartText);
// Create stop/play button
var stopShadow = self.attachAsset('buttonShadow', {
anchorX: 0.5,
anchorY: 0.5
});
stopShadow.x = 60 + 3;
stopShadow.y = 3;
var stopButton = self.attachAsset('stopButton', {
anchorX: 0.5,
anchorY: 0.5
});
stopButton.x = 60;
stopButton.y = 0;
self.stopText = new Text2('⏸', {
size: 50,
fill: 0xf0f0f0
});
self.stopText.anchor.set(0.5, 0.5);
self.stopText.x = 60;
self.addChild(self.stopText);
// Restart button functionality
restartButton.down = function (x, y, obj) {
// Button press animation
tween(restartButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(restartButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Clear all game state immediately
LK.setScore(0);
gameWon = false;
gameOver = false;
if (self.onRestart) {
self.onRestart();
}
};
// Stop/Play button functionality
stopButton.down = function (x, y, obj) {
// Button press animation
tween(stopButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(stopButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOut
});
}
});
self.isPaused = !self.isPaused;
self.stopText.setText(self.isPaused ? '▶' : '⏸');
if (self.onStop) {
self.onStop(self.isPaused);
}
};
return self;
});
var GameGrid = Container.expand(function () {
var self = Container.call(this);
self.gridSize = 6;
self.cellSize = 120;
self.cellGap = 10;
self.grid = [];
self.tiles = [];
self.isAnimating = false;
// Initialize grid array
for (var i = 0; i < self.gridSize; i++) {
self.grid[i] = [];
for (var j = 0; j < self.gridSize; j++) {
self.grid[i][j] = null;
}
}
// Create visual grid background with 3D shadow
var gridShadow = self.attachAsset('gridShadow', {
anchorX: 0.5,
anchorY: 0.5
});
gridShadow.x = 5;
gridShadow.y = 5;
var gridBg = self.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5
});
self.getCellX = function (col) {
return (col - 2.5) * (self.cellSize + self.cellGap);
};
self.getCellY = function (row) {
return (row - 2.5) * (self.cellSize + self.cellGap);
};
// Create grid cells with 3D shadow
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
var cellShadow = LK.getAsset('cellShadow', {
anchorX: 0.5,
anchorY: 0.5
});
cellShadow.x = self.getCellX(j) + 3;
cellShadow.y = self.getCellY(i) + 3;
self.addChild(cellShadow);
var cell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
cell.x = self.getCellX(j);
cell.y = self.getCellY(i);
self.addChild(cell);
}
}
self.addTile = function (value, row, col) {
var tile = new Tile(value);
tile.gridX = col;
tile.gridY = row;
tile.x = self.getCellX(col);
tile.y = self.getCellY(row);
self.grid[row][col] = tile;
self.tiles.push(tile);
self.addChild(tile);
// Spawn animation with 3D effect
tile.scaleX = 0;
tile.scaleY = 0;
tile.rotation = 0.5;
tile.alpha = 0.8;
tween(tile, {
scaleX: 1,
scaleY: 1,
rotation: 0,
alpha: 1
}, {
duration: 300,
easing: tween.elasticOut
});
return tile;
};
self.removeTile = function (tile) {
var row = tile.gridY;
var col = tile.gridX;
self.grid[row][col] = null;
var index = self.tiles.indexOf(tile);
if (index > -1) {
self.tiles.splice(index, 1);
}
self.removeChild(tile);
};
self.getEmptyCells = function () {
var emptyCells = [];
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
if (self.grid[i][j] === null) {
emptyCells.push({
row: i,
col: j
});
}
}
}
return emptyCells;
};
self.spawnRandomTile = function () {
var emptyCells = self.getEmptyCells();
if (emptyCells.length === 0) return false;
var randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
var value = Math.random() < 0.9 ? 2 : 4;
self.addTile(value, randomCell.row, randomCell.col);
return true;
};
self.canMove = function () {
// Check for empty cells
if (self.getEmptyCells().length > 0) return true;
// Check for possible merges
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
var current = self.grid[i][j];
if (current) {
// Check right
if (j < self.gridSize - 1 && self.grid[i][j + 1] && self.grid[i][j + 1].value === current.value) {
return true;
}
// Check down
if (i < self.gridSize - 1 && self.grid[i + 1][j] && self.grid[i + 1][j].value === current.value) {
return true;
}
}
}
}
return false;
};
self.move = function (direction, callback) {
if (self.isAnimating) return false;
var moved = false;
var merges = [];
self.isAnimating = true;
// Reset merge flags
for (var i = 0; i < self.tiles.length; i++) {
self.tiles[i].justMerged = false;
}
if (direction === 'left') {
for (var row = 0; row < self.gridSize; row++) {
var line = [];
for (var col = 0; col < self.gridSize; col++) {
if (self.grid[row][col]) {
line.push(self.grid[row][col]);
self.grid[row][col] = null;
}
}
var newLine = self.mergeLine(line);
for (var i = 0; i < newLine.length; i++) {
if (newLine[i]) {
var tile = newLine[i];
var oldCol = tile.gridX;
var newCol = i;
if (oldCol !== newCol || tile.justMerged) {
moved = true;
}
tile.gridX = newCol;
tile.gridY = row;
self.grid[row][newCol] = tile;
if (tile.justMerged) {
merges.push(tile);
}
}
}
}
} else if (direction === 'right') {
for (var row = 0; row < self.gridSize; row++) {
var line = [];
for (var col = self.gridSize - 1; col >= 0; col--) {
if (self.grid[row][col]) {
line.push(self.grid[row][col]);
self.grid[row][col] = null;
}
}
var newLine = self.mergeLine(line);
for (var i = 0; i < newLine.length; i++) {
if (newLine[i]) {
var tile = newLine[i];
var oldCol = tile.gridX;
var newCol = self.gridSize - 1 - i;
if (oldCol !== newCol || tile.justMerged) {
moved = true;
}
tile.gridX = newCol;
tile.gridY = row;
self.grid[row][newCol] = tile;
if (tile.justMerged) {
merges.push(tile);
}
}
}
}
} else if (direction === 'up') {
for (var col = 0; col < self.gridSize; col++) {
var line = [];
for (var row = 0; row < self.gridSize; row++) {
if (self.grid[row][col]) {
line.push(self.grid[row][col]);
self.grid[row][col] = null;
}
}
var newLine = self.mergeLine(line);
for (var i = 0; i < newLine.length; i++) {
if (newLine[i]) {
var tile = newLine[i];
var oldRow = tile.gridY;
var newRow = i;
if (oldRow !== newRow || tile.justMerged) {
moved = true;
}
tile.gridX = col;
tile.gridY = newRow;
self.grid[newRow][col] = tile;
if (tile.justMerged) {
merges.push(tile);
}
}
}
}
} else if (direction === 'down') {
for (var col = 0; col < self.gridSize; col++) {
var line = [];
for (var row = self.gridSize - 1; row >= 0; row--) {
if (self.grid[row][col]) {
line.push(self.grid[row][col]);
self.grid[row][col] = null;
}
}
var newLine = self.mergeLine(line);
for (var i = 0; i < newLine.length; i++) {
if (newLine[i]) {
var tile = newLine[i];
var oldRow = tile.gridY;
var newRow = self.gridSize - 1 - i;
if (oldRow !== newRow || tile.justMerged) {
moved = true;
}
tile.gridX = col;
tile.gridY = newRow;
self.grid[newRow][col] = tile;
if (tile.justMerged) {
merges.push(tile);
}
}
}
}
}
if (moved) {
// Animate tiles to new positions
var animatingTiles = 0;
for (var i = 0; i < self.tiles.length; i++) {
var tile = self.tiles[i];
var targetX = self.getCellX(tile.gridX);
var targetY = self.getCellY(tile.gridY);
if (tile.x !== targetX || tile.y !== targetY) {
animatingTiles++;
tile.animateMoveTo(targetX, targetY, function () {
animatingTiles--;
if (animatingTiles === 0) {
// All animations complete
for (var j = 0; j < merges.length; j++) {
merges[j].animateMerge();
LK.getSound('merge').play();
}
self.isAnimating = false;
if (callback && typeof callback === 'function') callback(true);
}
});
}
}
if (animatingTiles === 0) {
self.isAnimating = false;
if (callback && typeof callback === 'function') callback(true);
}
if (merges.length === 0) {
LK.getSound('slide').play();
}
} else {
self.isAnimating = false;
if (callback && typeof callback === 'function') callback(false);
}
return moved;
};
self.mergeLine = function (line) {
var result = [];
var i = 0;
while (i < line.length) {
if (i < line.length - 1 && line[i].value === line[i + 1].value) {
// Merge tiles
var newValue = line[i].value * 2;
line[i].setValue(newValue);
line[i].justMerged = true;
LK.setScore(LK.getScore() + newValue);
// Remove the second tile
self.removeTile(line[i + 1]);
result.push(line[i]);
i += 2;
} else {
result.push(line[i]);
i++;
}
}
return result;
};
self.updateGridSize = function (newSize, callback) {
self.isAnimating = true;
// Always clear all tiles when updating grid size
var tileData = [];
// Clear existing tiles and grid immediately
for (var i = 0; i < self.tiles.length; i++) {
self.tiles[i].destroy();
}
self.tiles = [];
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
self.grid[i][j] = null;
}
}
// Clear all visual elements
self.removeChildren();
// Rebuild immediately with new size
self.rebuildGrid(newSize, tileData, callback);
};
self.rebuildGrid = function (newSize, tileData, callback) {
// Clear everything
self.removeChildren();
self.tiles = [];
self.gridSize = newSize;
// Update cell positioning
var totalSize = self.gridSize * self.cellSize + (self.gridSize - 1) * self.cellGap;
var halfSize = (self.gridSize - 1) / 2;
self.getCellX = function (col) {
return (col - halfSize) * (self.cellSize + self.cellGap);
};
self.getCellY = function (row) {
return (row - halfSize) * (self.cellSize + self.cellGap);
};
// Recreate grid array
self.grid = [];
for (var i = 0; i < self.gridSize; i++) {
self.grid[i] = [];
for (var j = 0; j < self.gridSize; j++) {
self.grid[i][j] = null;
}
}
// Create new visual grid with updated size
var gridShadow = self.attachAsset('gridShadow', {
anchorX: 0.5,
anchorY: 0.5
});
gridShadow.x = 5;
gridShadow.y = 5;
gridShadow.width = totalSize + 40;
gridShadow.height = totalSize + 40;
gridShadow.alpha = 0;
var gridBg = self.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5
});
gridBg.width = totalSize + 40;
gridBg.height = totalSize + 40;
gridBg.alpha = 0;
// Create new grid cells
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
var cellShadow = LK.getAsset('cellShadow', {
anchorX: 0.5,
anchorY: 0.5
});
cellShadow.x = self.getCellX(j) + 3;
cellShadow.y = self.getCellY(i) + 3;
cellShadow.alpha = 0;
self.addChild(cellShadow);
var cell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
cell.x = self.getCellX(j);
cell.y = self.getCellY(i);
cell.alpha = 0;
self.addChild(cell);
}
}
// Restore tiles that fit in new grid
for (var i = 0; i < tileData.length; i++) {
var data = tileData[i];
self.addTile(data.value, data.gridY, data.gridX);
var tile = self.grid[data.gridY][data.gridX];
tile.alpha = 0;
}
// Animate in new grid
var fadeInCount = self.children.length;
for (var i = 0; i < self.children.length; i++) {
var child = self.children[i];
tween(child, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
fadeInCount--;
if (fadeInCount === 0) {
self.isAnimating = false;
if (callback) callback();
}
}
});
}
};
return self;
});
var GridSizeControl = Container.expand(function (gameGrid, onSizeChange) {
var self = Container.call(this);
self.currentSize = 6;
self.gameGrid = gameGrid;
self.onSizeChange = onSizeChange;
self.isAnimating = false;
self.isDragging = false;
self.bubbles = [];
self.lastGridSize = self.currentSize;
// Create control bar background with shadow (now elliptical for smooth corners)
var barShadow = self.attachAsset('controlBarShadow', {
anchorX: 0.5,
anchorY: 0.5
});
barShadow.x = 3;
barShadow.y = 3;
var controlBar = self.attachAsset('controlBar', {
anchorX: 0.5,
anchorY: 0.5
});
// Create grid size markers
self.markers = [];
var markerSpacing = 200;
var startX = -600;
for (var i = 4; i <= 10; i++) {
var marker = new Container();
marker.size = i;
var markerText = new Text2(i + 'x' + i, {
size: 28,
fill: 0xa8a8c0
});
markerText.anchor.set(0.5, 0.5);
marker.addChild(markerText);
marker.x = startX + (i - 4) * markerSpacing;
marker.y = -15;
self.markers.push(marker);
self.addChild(marker);
}
// Create draggable ball
var ballShadow = self.attachAsset('sliderBallShadow', {
anchorX: 0.5,
anchorY: 0.5
});
ballShadow.x = 2;
ballShadow.y = 2;
var ball = self.attachAsset('sliderBall', {
anchorX: 0.5,
anchorY: 0.5
});
self.ball = ball;
self.ballShadow = ballShadow;
// Position ball at current grid size
self.ball.x = startX + (self.currentSize - 4) * markerSpacing;
self.ballShadow.x = self.ball.x + 2;
self.getGridSizeFromPosition = function (x) {
var relativeX = x - startX;
var gridIndex = Math.round(relativeX / markerSpacing);
return Math.max(4, Math.min(10, gridIndex + 4));
};
self.getPositionFromGridSize = function (size) {
return startX + (size - 4) * markerSpacing;
};
self.createBubble = function (x, y) {
var bubble = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5
});
bubble.x = x + (Math.random() - 0.5) * 30;
bubble.y = y;
bubble.alpha = 0.8;
bubble.scaleX = 0.5;
bubble.scaleY = 0.5;
self.addChild(bubble);
self.bubbles.push(bubble);
// Animate bubble up and fade out
tween(bubble, {
y: y - 100,
alpha: 0,
scaleX: 1.2,
scaleY: 1.2,
rotation: Math.random() * Math.PI
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.removeChild(bubble);
var index = self.bubbles.indexOf(bubble);
if (index > -1) {
self.bubbles.splice(index, 1);
}
}
});
};
self.ball.down = function (x, y, obj) {
if (self.isAnimating) return;
self.isDragging = true;
self.dragOffset = x - self.ball.x;
};
self.ball.move = function (x, y, obj) {
if (!self.isDragging) return;
var newX = x - self.dragOffset;
var minX = startX;
var maxX = startX + 6 * markerSpacing;
newX = Math.max(minX, Math.min(maxX, newX));
self.ball.x = newX;
self.ballShadow.x = newX + 2;
var newGridSize = self.getGridSizeFromPosition(newX);
if (newGridSize !== self.lastGridSize) {
// Create bubbles when reaching a new grid size
for (var i = 0; i < 3; i++) {
LK.setTimeout(function () {
self.createBubble(self.ball.x, self.ball.y - 30);
}, i * 100);
}
// Immediately trigger grid change during drag
self.changeGridSize(newGridSize);
self.lastGridSize = newGridSize;
}
};
self.ball.up = function (x, y, obj) {
if (!self.isDragging) return;
self.isDragging = false;
var finalGridSize = self.getGridSizeFromPosition(self.ball.x);
var targetX = self.getPositionFromGridSize(finalGridSize);
// Smooth snap to position
tween(self.ball, {
x: targetX
}, {
duration: 200,
easing: tween.easeOut
});
tween(self.ballShadow, {
x: targetX + 2
}, {
duration: 200,
easing: tween.easeOut
});
};
self.changeGridSize = function (newSize) {
if (newSize === self.currentSize) return;
self.currentSize = newSize;
// Call the callback to update game grid and reset
if (self.onSizeChange) {
self.onSizeChange(newSize, function () {
// Animation complete
});
}
};
return self;
});
var Tile = Container.expand(function (value) {
var self = Container.call(this);
self.value = value;
self.gridX = 0;
self.gridY = 0;
self.isMoving = false;
self.justMerged = false;
function getTileAssetId(value) {
return 'tile' + value;
}
function getTileColor(value) {
return 0xa8a8c0;
}
function getTileShadowId(value) {
return 'tileShadow' + value;
}
var tileShadow = self.attachAsset(getTileShadowId(value), {
anchorX: 0.5,
anchorY: 0.5
});
tileShadow.x = 3;
tileShadow.y = 3;
var tileGraphics = self.attachAsset(getTileAssetId(value), {
anchorX: 0.5,
anchorY: 0.5
});
var tileText = new Text2(value.toString(), {
size: value >= 128 ? 40 : value >= 32 ? 50 : 60,
fill: getTileColor(value)
});
tileText.anchor.set(0.5, 0.5);
self.addChild(tileText);
self.setValue = function (newValue) {
self.value = newValue;
self.removeChild(tileShadow);
self.removeChild(tileGraphics);
tileShadow = self.attachAsset(getTileShadowId(newValue), {
anchorX: 0.5,
anchorY: 0.5
});
tileShadow.x = 3;
tileShadow.y = 3;
self.addChildAt(tileShadow, 0);
tileGraphics = self.attachAsset(getTileAssetId(newValue), {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(tileGraphics);
// Remove old text and create new one with updated properties
self.removeChild(tileText);
tileText = new Text2(newValue.toString(), {
size: newValue >= 128 ? 40 : newValue >= 32 ? 50 : 60,
fill: getTileColor(newValue)
});
tileText.anchor.set(0.5, 0.5);
self.addChild(tileText);
// Pop animation for new value with 3D effect
self.scaleX = 1.2;
self.scaleY = 1.2;
self.y -= 5;
tween(self, {
scaleX: 1,
scaleY: 1,
y: self.y + 5
}, {
duration: 200,
easing: tween.easeOut
});
};
self.animateMoveTo = function (x, y, callback) {
self.isMoving = true;
// Add slight 3D rotation during movement
tween(self, {
x: x,
y: y,
rotation: 0.1
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Return to normal rotation
tween(self, {
rotation: 0
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isMoving = false;
if (callback && typeof callback === 'function') callback();
}
});
}
});
};
self.animateMerge = function () {
// 3D pop effect with warm glow
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
rotation: 0.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
rotation: 0
}, {
duration: 200,
easing: tween.elasticOut
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xc0d0e0
});
/****
* Game Code
****/
var gameGrid;
var scoreTxt;
var bestTxt;
var gameWon = false;
var gameOver = false;
var touchStartX = 0;
var touchStartY = 0;
var minSwipeDistance = 30;
// Initialize score display
scoreTxt = new Text2('0', {
size: 60,
fill: 0xa8a8c0
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 100;
// Create and position game grid
gameGrid = new GameGrid();
gameGrid.x = 2048 / 2;
gameGrid.y = 2732 / 2 - 100;
game.addChild(gameGrid);
// Create grid size control
var gridSizeControl = new GridSizeControl(gameGrid, function (newSize, callback) {
// Reset game state when changing grid size
LK.setScore(0);
scoreTxt.setText('Score: 0');
gameWon = false;
gameOver = false;
gameGrid.updateGridSize(newSize, function () {
// Spawn initial tiles for new grid
gameGrid.spawnRandomTile();
gameGrid.spawnRandomTile();
callback();
});
});
gridSizeControl.x = 2048 / 2;
gridSizeControl.y = 2732 - 150;
game.addChild(gridSizeControl);
// Add game control buttons
var gameControlButtons = new GameControlButtons(gameGrid, function () {
// Restart game - force complete reset
LK.setScore(0);
scoreTxt.setText('Score: 0');
gameWon = false;
gameOver = false;
// Force grid rebuild with current size
gameGrid.updateGridSize(gameGrid.gridSize, function () {
gameGrid.spawnRandomTile();
gameGrid.spawnRandomTile();
});
}, function (isPaused) {
// Stop/Resume game
gameGrid.isAnimating = isPaused;
});
gameControlButtons.x = gameGrid.x + 400;
gameControlButtons.y = gameGrid.y - 500;
game.addChild(gameControlButtons);
// Spawn initial tiles
gameGrid.spawnRandomTile();
gameGrid.spawnRandomTile();
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
// Touch/swipe handling
game.down = function (x, y, obj) {
if (gameOver || gameWon) return;
touchStartX = x;
touchStartY = y;
};
game.up = function (x, y, obj) {
if (gameOver || gameWon || gameGrid.isAnimating) return;
var deltaX = x - touchStartX;
var deltaY = y - touchStartY;
var absDeltaX = Math.abs(deltaX);
var absDeltaY = Math.abs(deltaY);
if (Math.max(absDeltaX, absDeltaY) < minSwipeDistance) return;
var direction;
if (absDeltaX > absDeltaY) {
direction = deltaX > 0 ? 'right' : 'left';
} else {
direction = deltaY > 0 ? 'down' : 'up';
}
gameGrid.move(direction, function (moved) {
if (moved) {
scoreTxt.setText('Score: ' + LK.getScore());
// Check for 2048 tile (win condition)
if (!gameWon) {
for (var i = 0; i < gameGrid.tiles.length; i++) {
if (gameGrid.tiles[i].value === 2048) {
gameWon = true;
LK.setTimeout(function () {
LK.showYouWin();
}, 500);
return;
}
}
}
// Spawn new tile
if (!gameGrid.spawnRandomTile()) {
// Grid is full, check if game over
if (!gameGrid.canMove()) {
gameOver = true;
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
}
}
}
});
};
game.update = function () {
// Game loop updates handled by other systems
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var GameControlButtons = Container.expand(function (gameGrid, onRestart, onStop) {
var self = Container.call(this);
self.gameGrid = gameGrid;
self.onRestart = onRestart;
self.onStop = onStop;
self.isPaused = false;
// Create restart button
var restartShadow = self.attachAsset('buttonShadow', {
anchorX: 0.5,
anchorY: 0.5
});
restartShadow.x = -60 + 3;
restartShadow.y = 3;
var restartButton = self.attachAsset('restartButton', {
anchorX: 0.5,
anchorY: 0.5
});
restartButton.x = -60;
restartButton.y = 0;
var restartText = new Text2('↻', {
size: 50,
fill: 0xf0f0f0
});
restartText.anchor.set(0.5, 0.5);
restartText.x = -60;
self.addChild(restartText);
// Create stop/play button
var stopShadow = self.attachAsset('buttonShadow', {
anchorX: 0.5,
anchorY: 0.5
});
stopShadow.x = 60 + 3;
stopShadow.y = 3;
var stopButton = self.attachAsset('stopButton', {
anchorX: 0.5,
anchorY: 0.5
});
stopButton.x = 60;
stopButton.y = 0;
self.stopText = new Text2('⏸', {
size: 50,
fill: 0xf0f0f0
});
self.stopText.anchor.set(0.5, 0.5);
self.stopText.x = 60;
self.addChild(self.stopText);
// Restart button functionality
restartButton.down = function (x, y, obj) {
// Button press animation
tween(restartButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(restartButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Clear all game state immediately
LK.setScore(0);
gameWon = false;
gameOver = false;
if (self.onRestart) {
self.onRestart();
}
};
// Stop/Play button functionality
stopButton.down = function (x, y, obj) {
// Button press animation
tween(stopButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(stopButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOut
});
}
});
self.isPaused = !self.isPaused;
self.stopText.setText(self.isPaused ? '▶' : '⏸');
if (self.onStop) {
self.onStop(self.isPaused);
}
};
return self;
});
var GameGrid = Container.expand(function () {
var self = Container.call(this);
self.gridSize = 6;
self.cellSize = 120;
self.cellGap = 10;
self.grid = [];
self.tiles = [];
self.isAnimating = false;
// Initialize grid array
for (var i = 0; i < self.gridSize; i++) {
self.grid[i] = [];
for (var j = 0; j < self.gridSize; j++) {
self.grid[i][j] = null;
}
}
// Create visual grid background with 3D shadow
var gridShadow = self.attachAsset('gridShadow', {
anchorX: 0.5,
anchorY: 0.5
});
gridShadow.x = 5;
gridShadow.y = 5;
var gridBg = self.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5
});
self.getCellX = function (col) {
return (col - 2.5) * (self.cellSize + self.cellGap);
};
self.getCellY = function (row) {
return (row - 2.5) * (self.cellSize + self.cellGap);
};
// Create grid cells with 3D shadow
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
var cellShadow = LK.getAsset('cellShadow', {
anchorX: 0.5,
anchorY: 0.5
});
cellShadow.x = self.getCellX(j) + 3;
cellShadow.y = self.getCellY(i) + 3;
self.addChild(cellShadow);
var cell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
cell.x = self.getCellX(j);
cell.y = self.getCellY(i);
self.addChild(cell);
}
}
self.addTile = function (value, row, col) {
var tile = new Tile(value);
tile.gridX = col;
tile.gridY = row;
tile.x = self.getCellX(col);
tile.y = self.getCellY(row);
self.grid[row][col] = tile;
self.tiles.push(tile);
self.addChild(tile);
// Spawn animation with 3D effect
tile.scaleX = 0;
tile.scaleY = 0;
tile.rotation = 0.5;
tile.alpha = 0.8;
tween(tile, {
scaleX: 1,
scaleY: 1,
rotation: 0,
alpha: 1
}, {
duration: 300,
easing: tween.elasticOut
});
return tile;
};
self.removeTile = function (tile) {
var row = tile.gridY;
var col = tile.gridX;
self.grid[row][col] = null;
var index = self.tiles.indexOf(tile);
if (index > -1) {
self.tiles.splice(index, 1);
}
self.removeChild(tile);
};
self.getEmptyCells = function () {
var emptyCells = [];
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
if (self.grid[i][j] === null) {
emptyCells.push({
row: i,
col: j
});
}
}
}
return emptyCells;
};
self.spawnRandomTile = function () {
var emptyCells = self.getEmptyCells();
if (emptyCells.length === 0) return false;
var randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
var value = Math.random() < 0.9 ? 2 : 4;
self.addTile(value, randomCell.row, randomCell.col);
return true;
};
self.canMove = function () {
// Check for empty cells
if (self.getEmptyCells().length > 0) return true;
// Check for possible merges
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
var current = self.grid[i][j];
if (current) {
// Check right
if (j < self.gridSize - 1 && self.grid[i][j + 1] && self.grid[i][j + 1].value === current.value) {
return true;
}
// Check down
if (i < self.gridSize - 1 && self.grid[i + 1][j] && self.grid[i + 1][j].value === current.value) {
return true;
}
}
}
}
return false;
};
self.move = function (direction, callback) {
if (self.isAnimating) return false;
var moved = false;
var merges = [];
self.isAnimating = true;
// Reset merge flags
for (var i = 0; i < self.tiles.length; i++) {
self.tiles[i].justMerged = false;
}
if (direction === 'left') {
for (var row = 0; row < self.gridSize; row++) {
var line = [];
for (var col = 0; col < self.gridSize; col++) {
if (self.grid[row][col]) {
line.push(self.grid[row][col]);
self.grid[row][col] = null;
}
}
var newLine = self.mergeLine(line);
for (var i = 0; i < newLine.length; i++) {
if (newLine[i]) {
var tile = newLine[i];
var oldCol = tile.gridX;
var newCol = i;
if (oldCol !== newCol || tile.justMerged) {
moved = true;
}
tile.gridX = newCol;
tile.gridY = row;
self.grid[row][newCol] = tile;
if (tile.justMerged) {
merges.push(tile);
}
}
}
}
} else if (direction === 'right') {
for (var row = 0; row < self.gridSize; row++) {
var line = [];
for (var col = self.gridSize - 1; col >= 0; col--) {
if (self.grid[row][col]) {
line.push(self.grid[row][col]);
self.grid[row][col] = null;
}
}
var newLine = self.mergeLine(line);
for (var i = 0; i < newLine.length; i++) {
if (newLine[i]) {
var tile = newLine[i];
var oldCol = tile.gridX;
var newCol = self.gridSize - 1 - i;
if (oldCol !== newCol || tile.justMerged) {
moved = true;
}
tile.gridX = newCol;
tile.gridY = row;
self.grid[row][newCol] = tile;
if (tile.justMerged) {
merges.push(tile);
}
}
}
}
} else if (direction === 'up') {
for (var col = 0; col < self.gridSize; col++) {
var line = [];
for (var row = 0; row < self.gridSize; row++) {
if (self.grid[row][col]) {
line.push(self.grid[row][col]);
self.grid[row][col] = null;
}
}
var newLine = self.mergeLine(line);
for (var i = 0; i < newLine.length; i++) {
if (newLine[i]) {
var tile = newLine[i];
var oldRow = tile.gridY;
var newRow = i;
if (oldRow !== newRow || tile.justMerged) {
moved = true;
}
tile.gridX = col;
tile.gridY = newRow;
self.grid[newRow][col] = tile;
if (tile.justMerged) {
merges.push(tile);
}
}
}
}
} else if (direction === 'down') {
for (var col = 0; col < self.gridSize; col++) {
var line = [];
for (var row = self.gridSize - 1; row >= 0; row--) {
if (self.grid[row][col]) {
line.push(self.grid[row][col]);
self.grid[row][col] = null;
}
}
var newLine = self.mergeLine(line);
for (var i = 0; i < newLine.length; i++) {
if (newLine[i]) {
var tile = newLine[i];
var oldRow = tile.gridY;
var newRow = self.gridSize - 1 - i;
if (oldRow !== newRow || tile.justMerged) {
moved = true;
}
tile.gridX = col;
tile.gridY = newRow;
self.grid[newRow][col] = tile;
if (tile.justMerged) {
merges.push(tile);
}
}
}
}
}
if (moved) {
// Animate tiles to new positions
var animatingTiles = 0;
for (var i = 0; i < self.tiles.length; i++) {
var tile = self.tiles[i];
var targetX = self.getCellX(tile.gridX);
var targetY = self.getCellY(tile.gridY);
if (tile.x !== targetX || tile.y !== targetY) {
animatingTiles++;
tile.animateMoveTo(targetX, targetY, function () {
animatingTiles--;
if (animatingTiles === 0) {
// All animations complete
for (var j = 0; j < merges.length; j++) {
merges[j].animateMerge();
LK.getSound('merge').play();
}
self.isAnimating = false;
if (callback && typeof callback === 'function') callback(true);
}
});
}
}
if (animatingTiles === 0) {
self.isAnimating = false;
if (callback && typeof callback === 'function') callback(true);
}
if (merges.length === 0) {
LK.getSound('slide').play();
}
} else {
self.isAnimating = false;
if (callback && typeof callback === 'function') callback(false);
}
return moved;
};
self.mergeLine = function (line) {
var result = [];
var i = 0;
while (i < line.length) {
if (i < line.length - 1 && line[i].value === line[i + 1].value) {
// Merge tiles
var newValue = line[i].value * 2;
line[i].setValue(newValue);
line[i].justMerged = true;
LK.setScore(LK.getScore() + newValue);
// Remove the second tile
self.removeTile(line[i + 1]);
result.push(line[i]);
i += 2;
} else {
result.push(line[i]);
i++;
}
}
return result;
};
self.updateGridSize = function (newSize, callback) {
self.isAnimating = true;
// Always clear all tiles when updating grid size
var tileData = [];
// Clear existing tiles and grid immediately
for (var i = 0; i < self.tiles.length; i++) {
self.tiles[i].destroy();
}
self.tiles = [];
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
self.grid[i][j] = null;
}
}
// Clear all visual elements
self.removeChildren();
// Rebuild immediately with new size
self.rebuildGrid(newSize, tileData, callback);
};
self.rebuildGrid = function (newSize, tileData, callback) {
// Clear everything
self.removeChildren();
self.tiles = [];
self.gridSize = newSize;
// Update cell positioning
var totalSize = self.gridSize * self.cellSize + (self.gridSize - 1) * self.cellGap;
var halfSize = (self.gridSize - 1) / 2;
self.getCellX = function (col) {
return (col - halfSize) * (self.cellSize + self.cellGap);
};
self.getCellY = function (row) {
return (row - halfSize) * (self.cellSize + self.cellGap);
};
// Recreate grid array
self.grid = [];
for (var i = 0; i < self.gridSize; i++) {
self.grid[i] = [];
for (var j = 0; j < self.gridSize; j++) {
self.grid[i][j] = null;
}
}
// Create new visual grid with updated size
var gridShadow = self.attachAsset('gridShadow', {
anchorX: 0.5,
anchorY: 0.5
});
gridShadow.x = 5;
gridShadow.y = 5;
gridShadow.width = totalSize + 40;
gridShadow.height = totalSize + 40;
gridShadow.alpha = 0;
var gridBg = self.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5
});
gridBg.width = totalSize + 40;
gridBg.height = totalSize + 40;
gridBg.alpha = 0;
// Create new grid cells
for (var i = 0; i < self.gridSize; i++) {
for (var j = 0; j < self.gridSize; j++) {
var cellShadow = LK.getAsset('cellShadow', {
anchorX: 0.5,
anchorY: 0.5
});
cellShadow.x = self.getCellX(j) + 3;
cellShadow.y = self.getCellY(i) + 3;
cellShadow.alpha = 0;
self.addChild(cellShadow);
var cell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
cell.x = self.getCellX(j);
cell.y = self.getCellY(i);
cell.alpha = 0;
self.addChild(cell);
}
}
// Restore tiles that fit in new grid
for (var i = 0; i < tileData.length; i++) {
var data = tileData[i];
self.addTile(data.value, data.gridY, data.gridX);
var tile = self.grid[data.gridY][data.gridX];
tile.alpha = 0;
}
// Animate in new grid
var fadeInCount = self.children.length;
for (var i = 0; i < self.children.length; i++) {
var child = self.children[i];
tween(child, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
fadeInCount--;
if (fadeInCount === 0) {
self.isAnimating = false;
if (callback) callback();
}
}
});
}
};
return self;
});
var GridSizeControl = Container.expand(function (gameGrid, onSizeChange) {
var self = Container.call(this);
self.currentSize = 6;
self.gameGrid = gameGrid;
self.onSizeChange = onSizeChange;
self.isAnimating = false;
self.isDragging = false;
self.bubbles = [];
self.lastGridSize = self.currentSize;
// Create control bar background with shadow (now elliptical for smooth corners)
var barShadow = self.attachAsset('controlBarShadow', {
anchorX: 0.5,
anchorY: 0.5
});
barShadow.x = 3;
barShadow.y = 3;
var controlBar = self.attachAsset('controlBar', {
anchorX: 0.5,
anchorY: 0.5
});
// Create grid size markers
self.markers = [];
var markerSpacing = 200;
var startX = -600;
for (var i = 4; i <= 10; i++) {
var marker = new Container();
marker.size = i;
var markerText = new Text2(i + 'x' + i, {
size: 28,
fill: 0xa8a8c0
});
markerText.anchor.set(0.5, 0.5);
marker.addChild(markerText);
marker.x = startX + (i - 4) * markerSpacing;
marker.y = -15;
self.markers.push(marker);
self.addChild(marker);
}
// Create draggable ball
var ballShadow = self.attachAsset('sliderBallShadow', {
anchorX: 0.5,
anchorY: 0.5
});
ballShadow.x = 2;
ballShadow.y = 2;
var ball = self.attachAsset('sliderBall', {
anchorX: 0.5,
anchorY: 0.5
});
self.ball = ball;
self.ballShadow = ballShadow;
// Position ball at current grid size
self.ball.x = startX + (self.currentSize - 4) * markerSpacing;
self.ballShadow.x = self.ball.x + 2;
self.getGridSizeFromPosition = function (x) {
var relativeX = x - startX;
var gridIndex = Math.round(relativeX / markerSpacing);
return Math.max(4, Math.min(10, gridIndex + 4));
};
self.getPositionFromGridSize = function (size) {
return startX + (size - 4) * markerSpacing;
};
self.createBubble = function (x, y) {
var bubble = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5
});
bubble.x = x + (Math.random() - 0.5) * 30;
bubble.y = y;
bubble.alpha = 0.8;
bubble.scaleX = 0.5;
bubble.scaleY = 0.5;
self.addChild(bubble);
self.bubbles.push(bubble);
// Animate bubble up and fade out
tween(bubble, {
y: y - 100,
alpha: 0,
scaleX: 1.2,
scaleY: 1.2,
rotation: Math.random() * Math.PI
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.removeChild(bubble);
var index = self.bubbles.indexOf(bubble);
if (index > -1) {
self.bubbles.splice(index, 1);
}
}
});
};
self.ball.down = function (x, y, obj) {
if (self.isAnimating) return;
self.isDragging = true;
self.dragOffset = x - self.ball.x;
};
self.ball.move = function (x, y, obj) {
if (!self.isDragging) return;
var newX = x - self.dragOffset;
var minX = startX;
var maxX = startX + 6 * markerSpacing;
newX = Math.max(minX, Math.min(maxX, newX));
self.ball.x = newX;
self.ballShadow.x = newX + 2;
var newGridSize = self.getGridSizeFromPosition(newX);
if (newGridSize !== self.lastGridSize) {
// Create bubbles when reaching a new grid size
for (var i = 0; i < 3; i++) {
LK.setTimeout(function () {
self.createBubble(self.ball.x, self.ball.y - 30);
}, i * 100);
}
// Immediately trigger grid change during drag
self.changeGridSize(newGridSize);
self.lastGridSize = newGridSize;
}
};
self.ball.up = function (x, y, obj) {
if (!self.isDragging) return;
self.isDragging = false;
var finalGridSize = self.getGridSizeFromPosition(self.ball.x);
var targetX = self.getPositionFromGridSize(finalGridSize);
// Smooth snap to position
tween(self.ball, {
x: targetX
}, {
duration: 200,
easing: tween.easeOut
});
tween(self.ballShadow, {
x: targetX + 2
}, {
duration: 200,
easing: tween.easeOut
});
};
self.changeGridSize = function (newSize) {
if (newSize === self.currentSize) return;
self.currentSize = newSize;
// Call the callback to update game grid and reset
if (self.onSizeChange) {
self.onSizeChange(newSize, function () {
// Animation complete
});
}
};
return self;
});
var Tile = Container.expand(function (value) {
var self = Container.call(this);
self.value = value;
self.gridX = 0;
self.gridY = 0;
self.isMoving = false;
self.justMerged = false;
function getTileAssetId(value) {
return 'tile' + value;
}
function getTileColor(value) {
return 0xa8a8c0;
}
function getTileShadowId(value) {
return 'tileShadow' + value;
}
var tileShadow = self.attachAsset(getTileShadowId(value), {
anchorX: 0.5,
anchorY: 0.5
});
tileShadow.x = 3;
tileShadow.y = 3;
var tileGraphics = self.attachAsset(getTileAssetId(value), {
anchorX: 0.5,
anchorY: 0.5
});
var tileText = new Text2(value.toString(), {
size: value >= 128 ? 40 : value >= 32 ? 50 : 60,
fill: getTileColor(value)
});
tileText.anchor.set(0.5, 0.5);
self.addChild(tileText);
self.setValue = function (newValue) {
self.value = newValue;
self.removeChild(tileShadow);
self.removeChild(tileGraphics);
tileShadow = self.attachAsset(getTileShadowId(newValue), {
anchorX: 0.5,
anchorY: 0.5
});
tileShadow.x = 3;
tileShadow.y = 3;
self.addChildAt(tileShadow, 0);
tileGraphics = self.attachAsset(getTileAssetId(newValue), {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(tileGraphics);
// Remove old text and create new one with updated properties
self.removeChild(tileText);
tileText = new Text2(newValue.toString(), {
size: newValue >= 128 ? 40 : newValue >= 32 ? 50 : 60,
fill: getTileColor(newValue)
});
tileText.anchor.set(0.5, 0.5);
self.addChild(tileText);
// Pop animation for new value with 3D effect
self.scaleX = 1.2;
self.scaleY = 1.2;
self.y -= 5;
tween(self, {
scaleX: 1,
scaleY: 1,
y: self.y + 5
}, {
duration: 200,
easing: tween.easeOut
});
};
self.animateMoveTo = function (x, y, callback) {
self.isMoving = true;
// Add slight 3D rotation during movement
tween(self, {
x: x,
y: y,
rotation: 0.1
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Return to normal rotation
tween(self, {
rotation: 0
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isMoving = false;
if (callback && typeof callback === 'function') callback();
}
});
}
});
};
self.animateMerge = function () {
// 3D pop effect with warm glow
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
rotation: 0.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
rotation: 0
}, {
duration: 200,
easing: tween.elasticOut
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xc0d0e0
});
/****
* Game Code
****/
var gameGrid;
var scoreTxt;
var bestTxt;
var gameWon = false;
var gameOver = false;
var touchStartX = 0;
var touchStartY = 0;
var minSwipeDistance = 30;
// Initialize score display
scoreTxt = new Text2('0', {
size: 60,
fill: 0xa8a8c0
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 100;
// Create and position game grid
gameGrid = new GameGrid();
gameGrid.x = 2048 / 2;
gameGrid.y = 2732 / 2 - 100;
game.addChild(gameGrid);
// Create grid size control
var gridSizeControl = new GridSizeControl(gameGrid, function (newSize, callback) {
// Reset game state when changing grid size
LK.setScore(0);
scoreTxt.setText('Score: 0');
gameWon = false;
gameOver = false;
gameGrid.updateGridSize(newSize, function () {
// Spawn initial tiles for new grid
gameGrid.spawnRandomTile();
gameGrid.spawnRandomTile();
callback();
});
});
gridSizeControl.x = 2048 / 2;
gridSizeControl.y = 2732 - 150;
game.addChild(gridSizeControl);
// Add game control buttons
var gameControlButtons = new GameControlButtons(gameGrid, function () {
// Restart game - force complete reset
LK.setScore(0);
scoreTxt.setText('Score: 0');
gameWon = false;
gameOver = false;
// Force grid rebuild with current size
gameGrid.updateGridSize(gameGrid.gridSize, function () {
gameGrid.spawnRandomTile();
gameGrid.spawnRandomTile();
});
}, function (isPaused) {
// Stop/Resume game
gameGrid.isAnimating = isPaused;
});
gameControlButtons.x = gameGrid.x + 400;
gameControlButtons.y = gameGrid.y - 500;
game.addChild(gameControlButtons);
// Spawn initial tiles
gameGrid.spawnRandomTile();
gameGrid.spawnRandomTile();
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
// Touch/swipe handling
game.down = function (x, y, obj) {
if (gameOver || gameWon) return;
touchStartX = x;
touchStartY = y;
};
game.up = function (x, y, obj) {
if (gameOver || gameWon || gameGrid.isAnimating) return;
var deltaX = x - touchStartX;
var deltaY = y - touchStartY;
var absDeltaX = Math.abs(deltaX);
var absDeltaY = Math.abs(deltaY);
if (Math.max(absDeltaX, absDeltaY) < minSwipeDistance) return;
var direction;
if (absDeltaX > absDeltaY) {
direction = deltaX > 0 ? 'right' : 'left';
} else {
direction = deltaY > 0 ? 'down' : 'up';
}
gameGrid.move(direction, function (moved) {
if (moved) {
scoreTxt.setText('Score: ' + LK.getScore());
// Check for 2048 tile (win condition)
if (!gameWon) {
for (var i = 0; i < gameGrid.tiles.length; i++) {
if (gameGrid.tiles[i].value === 2048) {
gameWon = true;
LK.setTimeout(function () {
LK.showYouWin();
}, 500);
return;
}
}
}
// Spawn new tile
if (!gameGrid.spawnRandomTile()) {
// Grid is full, check if game over
if (!gameGrid.canMove()) {
gameOver = true;
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
}
}
}
});
};
game.update = function () {
// Game loop updates handled by other systems
};