User prompt
I don't want to drag and drop tiles. I just want to click it to add the bar
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading '0')' in or related to this line: 'if (prevGrid[row][col]) {' Line Number: 326
User prompt
Make it harder every level
User prompt
I want to see my level and my point
User prompt
Make the layer system
User prompt
No every color tile need to 3 or it's multiples
User prompt
I can't clear the layer because tiles not matching at the end. Make tile count 3 or it's multiplication
User prompt
I want a multiple layer design. Every level layers gonna increase
User prompt
I want to select tiles and grab to bottom bar to match same shaped tiles
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'row')' in or related to this line: 'var temp = board[tileA.row][tileA.col];' Line Number: 132
Code edit (1 edits merged)
Please save this source code
User prompt
Tile Match Mania
Initial prompt
Make me a basic tile matching game for mobile devices
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Tile class var Tile = Container.expand(function () { var self = Container.call(this); // Properties self.row = 0; self.col = 0; self.colorId = 0; // 0-5 self.isMoving = false; // Attach asset self.setColor = function (colorId) { self.colorId = colorId; if (self.tileAsset) { self.removeChild(self.tileAsset); } var colorNames = ['red', 'blue', 'green', 'yellow', 'purple', 'orange']; var assetId = 'tile_' + colorNames[colorId]; self.tileAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); }; // Animate to a new position (row, col) self.moveTo = function (row, col, duration, _onFinish) { self.row = row; self.col = col; self.isMoving = true; var targetX = boardOffsetX + col * tileSize + tileSize / 2; var targetY = boardOffsetY + row * tileSize + tileSize / 2; tween(self, { x: targetX, y: targetY }, { duration: duration, easing: tween.cubicOut, onFinish: function onFinish() { self.isMoving = false; if (_onFinish) _onFinish(); } }); }; // Instantly set position self.setPosition = function (row, col) { self.row = row; self.col = col; self.x = boardOffsetX + col * tileSize + tileSize / 2; self.y = boardOffsetY + row * tileSize + tileSize / 2; }; // Flash when matched self.flash = function (_onFinish2) { tween(self.tileAsset, { alpha: 0 }, { duration: 150, easing: tween.linear, onFinish: function onFinish() { self.tileAsset.alpha = 1; if (_onFinish2) _onFinish2(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Simple pop sound for matches // 6 tile colors for variety // --- Board settings --- var boardRows = 8; var boardCols = 8; var tileSize = 180; var boardWidth = boardCols * tileSize; var boardHeight = boardRows * tileSize; var boardOffsetX = Math.floor((2048 - boardWidth) / 2); var boardOffsetY = Math.floor((2732 - boardHeight) / 2); // --- Game state --- var board = []; // 2D array [row][col] of Tile var tiles = []; // Flat array of all Tile objects var selectedTile = null; var swappingTile = null; var isSwapping = false; var isProcessing = false; var score = 0; var timeLeft = 60; // seconds var timerInterval = null; var scoreTxt = null; var timerTxt = null; // --- Utility functions --- function randomColorId() { return Math.floor(Math.random() * 6); } // Check if two tiles are adjacent function areAdjacent(tileA, tileB) { var dr = Math.abs(tileA.row - tileB.row); var dc = Math.abs(tileA.col - tileB.col); return dr + dc === 1; } // Swap two tiles in board and animate function swapTiles(tileA, tileB, animate, onFinish) { // Defensive: Only swap if both tiles are valid if (!tileA || !tileB) { if (onFinish) onFinish(); return; } // Swap in board array var temp = board[tileA.row][tileA.col]; board[tileA.row][tileA.col] = board[tileB.row][tileB.col]; board[tileB.row][tileB.col] = temp; // Swap row/col var tempRow = tileA.row, tempCol = tileA.col; if (animate) { tileA.moveTo(tileB.row, tileB.col, 120); tileB.moveTo(tempRow, tempCol, 120, onFinish); } else { tileA.setPosition(tileB.row, tileB.col); tileB.setPosition(tempRow, tempCol); if (onFinish) onFinish(); } var trow = tileA.row, tcol = tileA.col; tileA.row = tileB.row; tileA.col = tileB.col; tileB.row = tempRow; tileB.col = tempCol; } // Find all matches on the board. Returns array of arrays of matched tiles. function findAllMatches() { var matches = []; // Horizontal for (var r = 0; r < boardRows; r++) { var run = [board[r][0]]; for (var c = 1; c < boardCols; c++) { if (board[r][c].colorId === board[r][c - 1].colorId) { run.push(board[r][c]); } else { if (run.length >= 3) matches.push(run.slice()); run = [board[r][c]]; } } if (run.length >= 3) matches.push(run.slice()); } // Vertical for (var c = 0; c < boardCols; c++) { var run = [board[0][c]]; for (var r = 1; r < boardRows; r++) { if (board[r][c].colorId === board[r - 1][c].colorId) { run.push(board[r][c]); } else { if (run.length >= 3) matches.push(run.slice()); run = [board[r][c]]; } } if (run.length >= 3) matches.push(run.slice()); } return matches; } // Remove matched tiles, return array of removed tiles function removeMatches(matches, onFinish) { var removed = []; var toRemove = {}; for (var i = 0; i < matches.length; i++) { for (var j = 0; j < matches[i].length; j++) { var t = matches[i][j]; var key = t.row + ',' + t.col; if (!toRemove[key]) { toRemove[key] = t; removed.push(t); } } } var count = removed.length; if (count === 0) { if (onFinish) onFinish(); return; } var finished = 0; for (var i = 0; i < removed.length; i++) { (function (tile) { tile.flash(function () { tile.visible = false; finished++; if (finished === count && onFinish) onFinish(); }); })(removed[i]); } LK.getSound('pop').play(); score += removed.length * 10; scoreTxt.setText(score); } // Drop tiles to fill empty spaces, return true if any tile moved function dropTiles(onFinish) { var moved = false; var dropCount = 0; for (var c = 0; c < boardCols; c++) { for (var r = boardRows - 1; r >= 0; r--) { if (!board[r][c].visible) { // Find nearest above visible tile for (var rr = r - 1; rr >= 0; rr--) { if (board[rr][c].visible) { // Move tile down var tile = board[rr][c]; board[r][c] = tile; tile.moveTo(r, c, 120, function () {}); board[rr][c] = new Tile(); board[rr][c].setColor(randomColorId()); board[rr][c].setPosition(rr, c); board[rr][c].visible = false; game.addChild(board[rr][c]); moved = true; dropCount++; break; } } } } } // Animate new tiles for empty spaces at top for (var c = 0; c < boardCols; c++) { for (var r = 0; r < boardRows; r++) { if (!board[r][c].visible) { var tile = board[r][c]; tile.setColor(randomColorId()); tile.visible = true; tile.y = boardOffsetY - tileSize / 2; tile.moveTo(r, c, 180, function () {}); moved = true; dropCount++; } } } // Wait for all drops to finish if (dropCount === 0) { if (onFinish) onFinish(); } else { LK.setTimeout(function () { if (onFinish) onFinish(); }, 200); } } // Check if any tile is moving function anyTileMoving() { for (var i = 0; i < tiles.length; i++) { if (tiles[i].isMoving) return true; } return false; } // Deselect all tiles function deselectTiles() { if (selectedTile) { tween.stop(selectedTile.tileAsset, { scaleX: true, scaleY: true }); selectedTile.tileAsset.scaleX = 1; selectedTile.tileAsset.scaleY = 1; selectedTile = null; } } // --- Board initialization --- function fillBoardNoMatches() { // Fill board with random tiles, but avoid initial matches for (var r = 0; r < boardRows; r++) { board[r] = []; for (var c = 0; c < boardCols; c++) { var colorId; do { colorId = randomColorId(); // Avoid horizontal match if (c >= 2 && board[r][c - 1].colorId === colorId && board[r][c - 2].colorId === colorId) continue; // Avoid vertical match if (r >= 2 && board[r - 1][c].colorId === colorId && board[r - 2][c].colorId === colorId) continue; break; } while (true); var tile = new Tile(); tile.setColor(colorId); tile.setPosition(r, c); board[r][c] = tile; tiles.push(tile); game.addChild(tile); } } } // --- GUI --- scoreTxt = new Text2('0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); timerTxt = new Text2('60', { size: 100, fill: 0xFFFFFF }); timerTxt.anchor.set(0.5, 0); LK.gui.topRight.addChild(timerTxt); // --- Timer --- function startTimer() { timeLeft = 60; timerTxt.setText(timeLeft); if (timerInterval) LK.clearInterval(timerInterval); timerInterval = LK.setInterval(function () { timeLeft--; timerTxt.setText(timeLeft); if (timeLeft <= 0) { LK.clearInterval(timerInterval); LK.showGameOver(); } }, 1000); } // --- Input handling --- var dragStartTile = null; var dragStartX = 0, dragStartY = 0; var dragMoved = false; function getTileAtPos(x, y) { var col = Math.floor((x - boardOffsetX) / tileSize); var row = Math.floor((y - boardOffsetY) / tileSize); if (row >= 0 && row < boardRows && col >= 0 && col < boardCols) { return board[row][col]; } return null; } game.down = function (x, y, obj) { if (isProcessing || anyTileMoving()) return; if (x < boardOffsetX || x > boardOffsetX + boardWidth || y < boardOffsetY || y > boardOffsetY + boardHeight) return; var tile = getTileAtPos(x, y); if (!tile) return; dragStartTile = tile; dragStartX = x; dragStartY = y; dragMoved = false; deselectTiles(); selectedTile = tile; tween(selectedTile.tileAsset, { scaleX: 1.2, scaleY: 1.2 }, { duration: 80, easing: tween.linear }); }; game.move = function (x, y, obj) { if (!dragStartTile || isProcessing || anyTileMoving()) return; var dx = x - dragStartX; var dy = y - dragStartY; if (!dragMoved && (Math.abs(dx) > tileSize / 3 || Math.abs(dy) > tileSize / 3)) { // Determine direction var dir = null; if (Math.abs(dx) > Math.abs(dy)) { dir = dx > 0 ? 'right' : 'left'; } else { dir = dy > 0 ? 'down' : 'up'; } var row = dragStartTile.row, col = dragStartTile.col; var targetRow = row, targetCol = col; if (dir === 'right') targetCol++; if (dir === 'left') targetCol--; if (dir === 'down') targetRow++; if (dir === 'up') targetRow--; if (targetRow >= 0 && targetRow < boardRows && targetCol >= 0 && targetCol < boardCols) { var targetTile = board[targetRow][targetCol]; dragMoved = true; isProcessing = true; deselectTiles(); swapTiles(dragStartTile, targetTile, true, function () { // After swap, check for matches var matches = findAllMatches(); if (matches.length > 0) { processMatches(function () { isProcessing = false; }); } else { // No match, swap back swapTiles(dragStartTile, targetTile, true, function () { isProcessing = false; }); } }); } } }; game.up = function (x, y, obj) { dragStartTile = null; dragMoved = false; deselectTiles(); }; // --- Match processing loop --- function processMatches(onFinish) { var matches = findAllMatches(); if (matches.length === 0) { if (onFinish) onFinish(); return; } removeMatches(matches, function () { dropTiles(function () { LK.setTimeout(function () { processMatches(onFinish); }, 80); }); }); } // --- Game update --- game.update = function () { // No per-frame logic needed for MVP }; // --- Start game --- function startGame() { // Reset state for (var i = 0; i < tiles.length; i++) { tiles[i].destroy(); } tiles = []; board = []; score = 0; scoreTxt.setText(score); deselectTiles(); fillBoardNoMatches(); startTimer(); } startGame();
===================================================================
--- original.js
+++ change.js
@@ -112,8 +112,13 @@
return dr + dc === 1;
}
// Swap two tiles in board and animate
function swapTiles(tileA, tileB, animate, onFinish) {
+ // Defensive: Only swap if both tiles are valid
+ if (!tileA || !tileB) {
+ if (onFinish) onFinish();
+ return;
+ }
// Swap in board array
var temp = board[tileA.row][tileA.col];
board[tileA.row][tileA.col] = board[tileB.row][tileB.col];
board[tileB.row][tileB.col] = temp;