User prompt
Fix bugs
User prompt
- Players receive a random colored block to place on a 7x9 grid. - Drag and drop the block onto any empty cell. - When three or more adjacent blocks of the same color connect, they merge into a new color and clear, scoring points. - Merges can trigger chain reactions for bonus points. - The game ends when the grid is full and no moves remain. - Aim for high scores by planning merges and creating cascades.
User prompt
Can not see the game, fix it
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of null (reading 'grid')' in or related to this line: 'var bdata = prevState.grid[c] && prevState.grid[c][r] ? prevState.grid[c][r] : null;' Line Number: 252
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of null (reading 'grid')' in or related to this line: 'var bdata = prevState.grid[c][r];' Line Number: 249
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Fix the game
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 210
User prompt
Reset game codes to its functional
User prompt
Fix errors
User prompt
Undo deleted asset
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 209
User prompt
Please fix the bug: 'Uncaught ReferenceError: copyNextBlocksState is not defined' in or related to this line: 'return arr;' Line Number: 209
/**** * Classes ****/ // --- Block Class --- var Block = Container.expand(function () { var self = Container.call(this); self.colorId = null; self.gridX = null; self.gridY = null; self.setColor = function (colorId) { self.colorId = colorId; if (self.asset) self.removeChild(self.asset); self.asset = self.attachAsset(colorId, { anchorX: 0.5, anchorY: 0.5 }); self.asset.width = CELL_SIZE; self.asset.height = CELL_SIZE; }; self.setGridPos = function (gx, gy) { self.gridX = gx; self.gridY = gy; self.x = GRID_OFFSET_X + gx * CELL_SIZE + CELL_SIZE / 2; self.y = GRID_OFFSET_Y + gy * CELL_SIZE + CELL_SIZE / 2; }; return self; }); /**** * Initialize Game ****/ // --- Draw Board Background --- var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // --- Game Constants --- var GRID_COLS = 7; var GRID_ROWS = 9; var CELL_SIZE = 200; // Each block is 200x200 px as per asset size var GRID_OFFSET_X = Math.floor((2048 - GRID_COLS * CELL_SIZE) / 2); var GRID_OFFSET_Y = Math.floor((2732 - GRID_ROWS * CELL_SIZE) / 2); var COLORS = [{ id: 'face_red', next: 'face_yellow', points: 10 }, { id: 'face_yellow', next: 'face_green', points: 20 }, { id: 'face_green', next: 'face_blue', points: 30 }, { id: 'face_blue', next: 'face_rainbow', points: 40 }, { id: 'face_rainbow', next: null, points: 100 }]; // --- Game State --- var grid = []; for (var y = 0; y < GRID_ROWS; y++) { grid[y] = []; for (var x = 0; x < GRID_COLS; x++) { grid[y][x] = null; } } var currentBlock = null; var dragging = false; var dragSprite = null; var dragGridX = null; var dragGridY = null; var scoreText = null; // --- Helper Functions --- function getRandomColor() { var idx = Math.floor(Math.random() * 4); // Only up to face_blue for random return COLORS[idx].id; } function getColorData(id) { for (var i = 0; i < COLORS.length; i++) { if (COLORS[i].id === id) return COLORS[i]; } return null; } function isInsideGrid(x, y) { return x >= 0 && x < GRID_COLS && y >= 0 && y < GRID_ROWS; } function getNeighbors(x, y) { var n = []; if (isInsideGrid(x - 1, y)) n.push({ x: x - 1, y: y }); if (isInsideGrid(x + 1, y)) n.push({ x: x + 1, y: y }); if (isInsideGrid(x, y - 1)) n.push({ x: x, y: y - 1 }); if (isInsideGrid(x, y + 1)) n.push({ x: x, y: y + 1 }); return n; } function findCluster(x, y, colorId, visited) { if (!isInsideGrid(x, y)) return []; if (visited[y * GRID_COLS + x]) return []; var block = grid[y][x]; if (!block || block.colorId !== colorId) return []; visited[y * GRID_COLS + x] = true; var cluster = [{ x: x, y: y }]; var neighbors = getNeighbors(x, y); for (var i = 0; i < neighbors.length; i++) { var nx = neighbors[i].x, ny = neighbors[i].y; cluster = cluster.concat(findCluster(nx, ny, colorId, visited)); } return cluster; } function clearBlock(x, y) { if (grid[y][x]) { grid[y][x].destroy(); grid[y][x] = null; } } function updateScoreText() { if (scoreText) scoreText.setText(LK.getScore()); } // --- Draw Board Background --- for (var y = 0; y < GRID_ROWS; y++) { for (var x = 0; x < GRID_COLS; x++) { var bg = LK.getAsset('block_gray', { anchorX: 0.5, anchorY: 0.5 }); bg.width = CELL_SIZE; bg.height = CELL_SIZE; bg.x = GRID_OFFSET_X + x * CELL_SIZE + CELL_SIZE / 2; bg.y = GRID_OFFSET_Y + y * CELL_SIZE + CELL_SIZE / 2; game.addChild(bg); } } // --- Score Display --- scoreText = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); updateScoreText(); // --- Spawn New Block --- function spawnBlock() { if (currentBlock) { currentBlock.destroy(); currentBlock = null; } var colorId = getRandomColor(); currentBlock = new Block(); currentBlock.setColor(colorId); currentBlock.x = 2048 / 2; currentBlock.y = 300; currentBlock.asset.width = CELL_SIZE; currentBlock.asset.height = CELL_SIZE; game.addChild(currentBlock); dragging = false; dragSprite = null; dragGridX = null; dragGridY = null; } spawnBlock(); // --- Drag and Drop Handlers --- game.down = function (x, y, obj) { if (!currentBlock) return; // Only start drag if touch is on the current block var dx = x - currentBlock.x; var dy = y - currentBlock.y; if (Math.abs(dx) < CELL_SIZE / 2 && Math.abs(dy) < CELL_SIZE / 2) { dragging = true; dragSprite = currentBlock; } }; game.move = function (x, y, obj) { if (!dragging || !dragSprite) return; // Snap to nearest grid cell var gx = Math.floor((x - GRID_OFFSET_X) / CELL_SIZE); var gy = Math.floor((y - GRID_OFFSET_Y) / CELL_SIZE); if (isInsideGrid(gx, gy)) { dragSprite.x = GRID_OFFSET_X + gx * CELL_SIZE + CELL_SIZE / 2; dragSprite.y = GRID_OFFSET_Y + gy * CELL_SIZE + CELL_SIZE / 2; dragGridX = gx; dragGridY = gy; } else { // Follow finger dragSprite.x = x; dragSprite.y = y; dragGridX = null; dragGridY = null; } }; game.up = function (x, y, obj) { if (!dragging || !dragSprite) return; if (dragGridX !== null && dragGridY !== null && !grid[dragGridY][dragGridX]) { // Place block dragSprite.setGridPos(dragGridX, dragGridY); grid[dragGridY][dragGridX] = dragSprite; // Check for merges handleMerge(dragGridX, dragGridY); // Check for game over if (isBoardFull()) { LK.showGameOver(); return; } spawnBlock(); } else { // Return to top dragSprite.x = 2048 / 2; dragSprite.y = 300; } dragging = false; dragSprite = null; dragGridX = null; dragGridY = null; }; // --- Merge Logic --- function handleMerge(gx, gy) { var block = grid[gy][gx]; if (!block) return; var colorId = block.colorId; var visited = []; var cluster = findCluster(gx, gy, colorId, visited); if (cluster.length >= 3) { // Merge! var colorData = getColorData(colorId); var nextColor = colorData.next; var points = colorData.points * cluster.length; // Remove all blocks in cluster for (var i = 0; i < cluster.length; i++) { var cx = cluster[i].x, cy = cluster[i].y; if (cx !== gx || cy !== gy) clearBlock(cx, cy); } // Upgrade main block if (nextColor) { block.setColor(nextColor); } LK.setScore(LK.getScore() + points); updateScoreText(); // Chain reaction handleMerge(gx, gy); } } // --- Board Full Check --- function isBoardFull() { for (var y = 0; y < GRID_ROWS; y++) { for (var x = 0; x < GRID_COLS; x++) { if (!grid[y][x]) return false; } } return true; } // --- Game Update (not used, but required for LK) --- game.update = function () {};
===================================================================
--- original.js
+++ change.js
@@ -1,871 +1,275 @@
/****
-* Plugins
-****/
-var tween = LK.import("@upit/tween.v1");
-
-/****
* Classes
****/
-// --- Game Over / Reset handled by LK engine ---
// --- Block Class ---
var Block = Container.expand(function () {
var self = Container.call(this);
- // Default color index (0=red, 1=orange, 2=yellow, 3=green, 4=blue, 5=purple, 6=pink, 7=white, 8=rainbow)
- self.colorIndex = 0;
- self.isLocked = false;
- self.isOnGrid = false;
- self.gridX = -1;
- self.gridY = -1;
- // Attach block asset (default, will be set by setColor)
- self.blockAsset = self.attachAsset('Hd', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- self.scaleX = 1;
- self.scaleY = 1;
- // Set color and locked state
- self.setColor = function (colorIdx) {
- self.colorIndex = colorIdx;
- self.isLocked = false; // No more locked blocks
- // Remove old asset if exists
- if (self.blockAsset) {
- self.removeChild(self.blockAsset);
- }
- self.blockAsset = self.attachAsset('Hd', {
+ self.colorId = null;
+ self.gridX = null;
+ self.gridY = null;
+ self.setColor = function (colorId) {
+ self.colorId = colorId;
+ if (self.asset) self.removeChild(self.asset);
+ self.asset = self.attachAsset(colorId, {
anchorX: 0.5,
anchorY: 0.5
});
- self.scaleX = 1;
- self.scaleY = 1;
- // Assign each block a unique, visually distinct color using the Hd asset for all
- // 0=yellow, 1=red, 2=blue, 3=green, 4=purple, 5=rainbow/white, 6=pink, 7=black, 8=orange
- var tints = [0xFFF600,
- // 0 Yellow
- 0xFF6666,
- // 1 Red
- 0x3ec1ff,
- // 2 Blue
- 0x7cff6b,
- // 3 Green (matches face_green)
- 0xd08cff,
- // 4 Purple
- 0xffffff,
- // 5 Rainbow/white (special)
- 0xffb7e5,
- // 6 Pink
- 0x222222,
- // 7 Black
- 0xffa500 // 8 Orange
- ];
- // Map colorIndex to correct tint
- if (colorIdx === 0) {
- self.blockAsset.tint = tints[0]; // yellow
- } else if (colorIdx === 1) {
- self.blockAsset.tint = tints[1]; // red
- } else if (colorIdx === 2) {
- self.blockAsset.tint = tints[2]; // blue
- } else if (colorIdx === 3) {
- self.blockAsset.tint = tints[3]; // green
- } else if (colorIdx === 4) {
- self.blockAsset.tint = tints[4]; // purple
- } else if (colorIdx === 5) {
- self.blockAsset.tint = tints[5]; // rainbow/white
- } else if (colorIdx === 6) {
- self.blockAsset.tint = tints[6]; // pink
- } else if (colorIdx === 7) {
- self.blockAsset.tint = tints[7]; // black
- } else if (colorIdx === 8) {
- self.blockAsset.tint = tints[8]; // orange
- } else {
- self.blockAsset.tint = tints[0]; // fallback to yellow
- }
- // Add a lightful flash effect when color is set
- LK.effects.flashObject(self.blockAsset, 0xffffff, 220);
+ self.asset.width = CELL_SIZE;
+ self.asset.height = CELL_SIZE;
};
- // Animate merge (scale up and back)
- self.animateMerge = function (_onFinish) {
- tween(self, {
- scaleX: 1.25,
- scaleY: 1.25
- }, {
- duration: 80,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- tween(self, {
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 100,
- easing: tween.easeIn,
- onFinish: function onFinish() {
- if (_onFinish) {
- _onFinish();
- }
- }
- });
- }
- });
+ self.setGridPos = function (gx, gy) {
+ self.gridX = gx;
+ self.gridY = gy;
+ self.x = GRID_OFFSET_X + gx * CELL_SIZE + CELL_SIZE / 2;
+ self.y = GRID_OFFSET_Y + gy * CELL_SIZE + CELL_SIZE / 2;
};
return self;
});
/****
* Initialize Game
****/
+// --- Draw Board Background ---
var game = new LK.Game({
- backgroundColor: 0x222233
+ backgroundColor: 0x000000
});
/****
* Game Code
****/
-// Flower and honey jar assets
-// Face assets in different colors
-// Removed block_gray asset initialization (no more grey squares)
-// locked block
// --- Game Constants ---
-// Define block shapes for each color and merged color
-// We'll use 4 colors: red, blue, yellow, purple (no green)
-// Merged block (final color)
-// Sound for merge
-// visually white, but will be rainbow in logic
-// More defined, fun, and clear block shapes for each color
-// Cheerful blue
-// Cheerful green
-// Cheerful purple
-// Bright yellow for rainbow
-// Cheerful red
-// Pure white
-// Cheerful yellow
-// Cheerful pink
-// Fill nextBlocks with random colors (original mechanic)
-var GRID_COLS = 6;
-var GRID_ROWS = 7;
-var CELL_SIZE = 260; // px (was 200, now bigger for larger squares)
+var GRID_COLS = 7;
+var GRID_ROWS = 9;
+var CELL_SIZE = 200; // Each block is 200x200 px as per asset size
var GRID_OFFSET_X = Math.floor((2048 - GRID_COLS * CELL_SIZE) / 2);
-var GRID_OFFSET_Y = 350;
-var COLORS = [0, 1, 2, 3, 4, 6, 7, 8]; // 0=yellow, 1=red, 2=blue, 3=green, 4=purple, 6=pink, 7=black, 8=orange
-var COLOR_NAMES = ['yellow', 'red', 'blue', 'green', 'purple', 'rainbow', 'pink', 'black', 'orange'];
-var MAX_COLOR_INDEX = 8; // 0-8 normal, 5=rainbow (special, can't merge further)
-var RAINBOW_COLOR_INDEX = 5; // special rainbow block (if needed)
-var BLOCK_DROP_Y = 2200; // y position for blocks that fall off
+var GRID_OFFSET_Y = Math.floor((2732 - GRID_ROWS * CELL_SIZE) / 2);
+var COLORS = [{
+ id: 'face_red',
+ next: 'face_yellow',
+ points: 10
+}, {
+ id: 'face_yellow',
+ next: 'face_green',
+ points: 20
+}, {
+ id: 'face_green',
+ next: 'face_blue',
+ points: 30
+}, {
+ id: 'face_blue',
+ next: 'face_rainbow',
+ points: 40
+}, {
+ id: 'face_rainbow',
+ next: null,
+ points: 100
+}];
// --- Game State ---
-var grid = []; // 2D array [col][row] of Block or null
-for (var c = 0; c < GRID_COLS; c++) {
- grid[c] = [];
- for (var r = 0; r < GRID_ROWS; r++) {
- grid[c][r] = null;
+var grid = [];
+for (var y = 0; y < GRID_ROWS; y++) {
+ grid[y] = [];
+ for (var x = 0; x < GRID_COLS; x++) {
+ grid[y][x] = null;
}
}
-var draggingBlock = null;
-var dragOffsetX = 0;
-var dragOffsetY = 0;
-var nextBlocks = []; // Array of next blocks to place
-var score = 0;
-var scoreTxt = null;
-var isProcessing = false; // Prevent input during merges
-// --- Undo/Föregående State ---
-var prevState = null;
-// Helper: Deep copy grid state (only colorIndex, isLocked, isOnGrid, gridX, gridY)
-function copyGridState(srcGrid) {
- var arr = [];
- for (var c = 0; c < GRID_COLS; c++) {
- arr[c] = [];
- for (var r = 0; r < GRID_ROWS; r++) {
- var b = srcGrid[c][r];
- if (b) {
- // Remove pink and violet cubes if present
- if (b.colorIndex === 4 || b.colorIndex === 6) {
- arr[c][r] = {
- colorIndex: b.colorIndex,
- isLocked: b.isLocked,
- isOnGrid: b.isOnGrid,
- gridX: b.gridX,
- gridY: b.gridY
- };
- } else {
- arr[c][r] = null;
- }
- }
- }
- }
- // Helper: Deep copy nextBlocks state (only colorIndex)
- function copyNextBlocksState(srcNextBlocks) {
- var arr = [];
- for (var i = 0; i < srcNextBlocks.length; i++) {
- // Defensive: handle both Block objects and plain objects
- var colorIndex = srcNextBlocks[i] && typeof srcNextBlocks[i].colorIndex !== "undefined" ? srcNextBlocks[i].colorIndex : null;
- arr[i] = {
- colorIndex: colorIndex
- };
- }
- return arr;
- }
- // Save current state for undo
- function savePrevState() {
- prevState = {
- grid: copyGridState(grid),
- nextBlocks: copyNextBlocksState(nextBlocks),
- score: score
- };
- }
- // Restore previous state (undo)
- function restorePrevState() {
- if (!prevState) {
- return;
- }
- // Remove all blocks from game
- for (var c = 0; c < GRID_COLS; c++) {
- for (var r = 0; r < GRID_ROWS; r++) {
- if (grid[c][r]) {
- grid[c][r].destroy();
- grid[c][r] = null;
- }
- }
- }
- }
- // Restore grid
- if (!prevState || !prevState.grid) {
- // Defensive: If prevState or prevState.grid is null, skip resto
- }
- for (var c = 0; c < GRID_COLS; c++) {
- for (var r = 0; r < GRID_ROWS; r++) {
- var bdata = prevState.grid && prevState.grid[c] && prevState.grid[c][r] ? prevState.grid[c][r] : null;
- if (bdata) {
- var b = new Block();
- b.setColor(bdata.colorIndex);
- b.isOnGrid = bdata.isOnGrid;
- b.gridX = bdata.gridX;
- b.gridY = bdata.gridY;
- var pos = getPosForCell(c, r);
- b.x = pos.x;
- b.y = pos.y;
- grid[c][r] = b;
- game.addChild(b);
- } else {
- grid[c][r] = null;
- }
- }
- }
- // Remove all nextBlocks from game
- for (var i = 0; i < nextBlocks.length; i++) {
- if (nextBlocks[i]) {
- nextBlocks[i].destroy();
- }
- }
- nextBlocks = [];
- // Restore nextBlocks
- for (var i = 0; i < prevState.nextBlocks.length; i++) {
- var nb = new Block();
- nb.setColor(prevState.nextBlocks[i].colorIndex);
- nb.x = 2048 / 2 + (nextBlocks.length - 1) * 220;
- nb.y = 220;
- nb.scaleX = nb.scaleY = 1;
- nb.isOnGrid = false;
- nb.gridX = -1;
- nb.gridY = -1;
- game.addChild(nb);
- nextBlocks.push(nb);
- }
- // Reposition nextBlocks
- for (var i = 0; i < nextBlocks.length; i++) {
- var bx = 2048 / 2 + (i - 1) * 220;
- nextBlocks[i].x = bx;
- nextBlocks[i].y = 220;
- }
- // Restore score
- score = prevState.score;
- scoreTxt.setText(score);
- prevState = null;
+var currentBlock = null;
+var dragging = false;
+var dragSprite = null;
+var dragGridX = null;
+var dragGridY = null;
+var scoreText = null;
+// --- Helper Functions ---
+function getRandomColor() {
+ var idx = Math.floor(Math.random() * 4); // Only up to face_blue for random
+ return COLORS[idx].id;
}
-// Add a button for undo/föregående
-var undoBtn = new Text2('⟲', {
- size: 110,
- fill: "#fff"
-});
-undoBtn.anchor.set(0.5, 0.5);
-undoBtn.x = 2048 - 120;
-undoBtn.y = 120;
-undoBtn.interactive = true;
-undoBtn.buttonMode = true;
-undoBtn.down = function () {
- restorePrevState();
-};
-LK.gui.top.addChild(undoBtn);
-// --- GUI Elements ---
-scoreTxt = new Text2('0', {
- size: 120,
- fill: 0xFFFFFF
-});
-scoreTxt.anchor.set(0.5, 0);
-LK.gui.top.addChild(scoreTxt);
-// --- Draw Background Image ---
-var background = LK.getAsset('Background', {
- width: 2048,
- height: 2732,
- anchorX: 0,
- anchorY: 0,
- x: 0,
- y: 0,
- alpha: 1
-});
-game.addChild(background);
-// --- Draw Grid Background ---
-for (var c = 0; c < GRID_COLS; c++) {
- for (var r = 0; r < GRID_ROWS; r++) {
- var cell = LK.getAsset('block_white', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: GRID_OFFSET_X + c * CELL_SIZE + CELL_SIZE / 2,
- y: GRID_OFFSET_Y + r * CELL_SIZE + CELL_SIZE / 2,
- alpha: 0.08
- });
- game.addChild(cell);
+function getColorData(id) {
+ for (var i = 0; i < COLORS.length; i++) {
+ if (COLORS[i].id === id) return COLORS[i];
}
+ return null;
}
-// --- Draw Grid Lines ---
-for (var c = 0; c <= GRID_COLS; c++) {
- var x = GRID_OFFSET_X + c * CELL_SIZE;
- var y1 = GRID_OFFSET_Y;
- var y2 = GRID_OFFSET_Y + GRID_ROWS * CELL_SIZE;
- // Draw vertical line as a thick, high-contrast rectangle
- var vline = LK.getAsset('block_black', {
- width: 14,
- height: GRID_ROWS * CELL_SIZE,
- anchorX: 0.5,
- anchorY: 0,
+function isInsideGrid(x, y) {
+ return x >= 0 && x < GRID_COLS && y >= 0 && y < GRID_ROWS;
+}
+function getNeighbors(x, y) {
+ var n = [];
+ if (isInsideGrid(x - 1, y)) n.push({
+ x: x - 1,
+ y: y
+ });
+ if (isInsideGrid(x + 1, y)) n.push({
+ x: x + 1,
+ y: y
+ });
+ if (isInsideGrid(x, y - 1)) n.push({
x: x,
- y: y1,
- alpha: 0.38
+ y: y - 1
});
- game.addChild(vline);
-}
-for (var r = 0; r <= GRID_ROWS; r++) {
- var y = GRID_OFFSET_Y + r * CELL_SIZE;
- var x1 = GRID_OFFSET_X;
- var x2 = GRID_OFFSET_X + GRID_COLS * CELL_SIZE;
- // Draw horizontal line as a thick, high-contrast rectangle
- var hline = LK.getAsset('block_black', {
- width: GRID_COLS * CELL_SIZE,
- height: 14,
- anchorX: 0,
- anchorY: 0.5,
- x: x1,
- y: y,
- alpha: 0.38
+ if (isInsideGrid(x, y + 1)) n.push({
+ x: x,
+ y: y + 1
});
- game.addChild(hline);
+ return n;
}
-// --- Helper Functions ---
-// Get grid cell from x,y (game coordinates)
-function getGridCellFromPos(x, y) {
- var gx = Math.floor((x - GRID_OFFSET_X) / CELL_SIZE);
- var gy = Math.floor((y - GRID_OFFSET_Y) / CELL_SIZE);
- if (gx < 0 || gx >= GRID_COLS || gy < 0 || gy >= GRID_ROWS) {
- return null;
- }
- return {
- col: gx,
- row: gy
- };
-}
-// Get position (x,y) for grid cell
-function getPosForCell(col, row) {
- return {
- x: GRID_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2,
- y: GRID_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2
- };
-}
-// Generate a random color index (0-4)
-function randomColorIndex() {
- return COLORS[Math.floor(Math.random() * COLORS.length)];
-}
-// Create a new block for the "next" area
-function createNextBlock(idx) {
- var block = new Block();
- block.setColor(idx);
- block.x = 2048 / 2 + (nextBlocks.length - 1) * 220;
- block.y = 220;
- block.scaleX = block.scaleY = 1;
- block.isOnGrid = false;
- block.gridX = -1;
- block.gridY = -1;
- game.addChild(block);
- return block;
-}
-// Fill nextBlocks to always have 3 blocks
-function refillNextBlocks() {
- // Limit: Only allow new blocks if there is at least one empty cell on the grid
- var emptyCells = 0;
- for (var c = 0; c < GRID_COLS; c++) {
- for (var r = 0; r < GRID_ROWS; r++) {
- if (!grid[c][r]) {
- emptyCells++;
- }
- }
- }
- // Only allow up to 3 next blocks, but never more than empty cells
- var maxNext = Math.min(3, emptyCells);
- // Fill nextBlocks with random colors (original mechanic)
- while (nextBlocks.length < maxNext) {
- // 5% chance to spawn a rainbow block, otherwise random color
- var idx;
- var rand = Math.random();
- if (rand < 0.05) {
- idx = RAINBOW_COLOR_INDEX; // rainbow
- } else {
- // Allow all defined COLORS except rainbow (5) as next blocks
- var allowedColors = COLORS.filter(function (cidx) {
- return cidx !== RAINBOW_COLOR_INDEX;
- });
- idx = allowedColors[Math.floor(Math.random() * allowedColors.length)];
- }
- var block = createNextBlock(idx);
- nextBlocks.push(block);
- }
- // Remove excess nextBlocks if grid is almost full
- while (nextBlocks.length > maxNext) {
- var b = nextBlocks.pop();
- if (b) {
- b.destroy();
- }
- }
- // Position them nicely
- for (var i = 0; i < nextBlocks.length; i++) {
- var bx = 2048 / 2 + (i - 1) * 220;
- tween(nextBlocks[i], {
- x: bx,
- y: 220
- }, {
- duration: 180,
- easing: tween.easeInOut
- });
- }
-}
-// Remove a block from nextBlocks and shift others
-function popNextBlock(block) {
- var idx = nextBlocks.indexOf(block);
- if (idx >= 0) {
- nextBlocks.splice(idx, 1);
- }
- refillNextBlocks();
-}
-// Place block on grid
-function placeBlockOnGrid(block, col, row) {
- block.isOnGrid = true;
- block.gridX = col;
- block.gridY = row;
- grid[col][row] = block;
- var pos = getPosForCell(col, row);
- tween(block, {
- x: pos.x,
- y: pos.y,
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 120,
- easing: tween.easeOut
- });
-}
-// Remove block from grid
-function removeBlockFromGrid(col, row) {
- var block = grid[col][row];
- if (block) {
- grid[col][row] = null;
- }
-}
-// Find all connected blocks of the same color (DFS)
-function findConnectedBlocks(col, row, colorIndex, visited) {
- if (col < 0 || col >= GRID_COLS || row < 0 || row >= GRID_ROWS) {
- return [];
- }
- if (visited[col + "," + row]) {
- return [];
- }
- var block = grid[col][row];
- if (!block || block.colorIndex !== colorIndex) {
- return [];
- }
- visited[col + "," + row] = true;
- var result = [{
- col: col,
- row: row,
- block: block
+function findCluster(x, y, colorId, visited) {
+ if (!isInsideGrid(x, y)) return [];
+ if (visited[y * GRID_COLS + x]) return [];
+ var block = grid[y][x];
+ if (!block || block.colorId !== colorId) return [];
+ visited[y * GRID_COLS + x] = true;
+ var cluster = [{
+ x: x,
+ y: y
}];
- // 4 directions
- var dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
- for (var i = 0; i < dirs.length; i++) {
- var nc = col + dirs[i][0];
- var nr = row + dirs[i][1];
- result = result.concat(findConnectedBlocks(nc, nr, colorIndex, visited));
+ var neighbors = getNeighbors(x, y);
+ for (var i = 0; i < neighbors.length; i++) {
+ var nx = neighbors[i].x,
+ ny = neighbors[i].y;
+ cluster = cluster.concat(findCluster(nx, ny, colorId, visited));
}
- return result;
+ return cluster;
}
-// Check if any moves are possible (empty cell exists)
-function hasMoves() {
- for (var c = 0; c < GRID_COLS; c++) {
- for (var r = 0; r < GRID_ROWS; r++) {
- if (!grid[c][r]) {
- return true;
- }
- }
+function clearBlock(x, y) {
+ if (grid[y][x]) {
+ grid[y][x].destroy();
+ grid[y][x] = null;
}
- return false;
}
-// --- Game Logic ---
-// Handle block drop onto grid
-function tryPlaceBlock(block, x, y) {
- if (isProcessing) {
- return false;
- }
- var cell = getGridCellFromPos(x, y);
- if (!cell) {
- return false;
- }
- var col = cell.col,
- row = cell.row;
- if (grid[col][row]) {
- return false;
- } // occupied
- // No locked blocks, so no need to check
- if (grid[col][row]) {
- return false;
- } // cell is occupied
- if (block.colorIndex === RAINBOW_COLOR_INDEX) {
- // Rainbow block special checks
- }
- // Save state for undo before placing
- function savePrevState() {
- prevState = {
- grid: copyGridState(grid),
- nextBlocks: copyNextBlocksState(nextBlocks),
- score: score
- };
- }
- savePrevState();
- if (block.colorIndex === RAINBOW_COLOR_INDEX) {
- // Place rainbow block and trigger effect
- placeBlockOnGrid(block, col, row);
- block.isOnGrid = true;
- popNextBlock(block);
- // Pick a random color present on the grid (not locked, not white, not rainbow)
- var presentColors = {};
- for (var c2 = 0; c2 < GRID_COLS; c2++) {
- for (var r2 = 0; r2 < GRID_ROWS; r2++) {
- var b2 = grid[c2][r2];
- if (b2 && (b2.colorIndex === 0 || b2.colorIndex === 1 || b2.colorIndex === 2 || b2.colorIndex === 4)) {
- presentColors[b2.colorIndex] = true;
- }
- }
- }
- var colorList = [];
- for (var k in presentColors) {
- if (presentColors.hasOwnProperty(k)) {
- colorList.push(parseInt(k));
- }
- }
- if (colorList.length > 0) {
- var colorToClear = colorList[Math.floor(Math.random() * colorList.length)];
- // Animate and remove all blocks of that color
- for (var c2 = 0; c2 < GRID_COLS; c2++) {
- for (var r2 = 0; r2 < GRID_ROWS; r2++) {
- var b2 = grid[c2][r2];
- if (b2 && b2.colorIndex === colorToClear) {
- var tmpBlock = b2; // Store reference before nulling the grid position
- grid[c2][r2] = null;
- (function (bref) {
- tween(bref, {
- scaleX: 0,
- scaleY: 0,
- alpha: 0
- }, {
- duration: 180,
- easing: tween.easeIn,
- onFinish: function onFinish() {
- bref.destroy();
- }
- });
- })(tmpBlock);
- }
- }
- }
- // Add bonus score for rainbow clear
- score += 200;
- scoreTxt.setText(score);
- }
- // Play a sound for rainbow effect
- LK.getSound('merge').play();
- // After effect, check for game over
- LK.setTimeout(function () {
- if (!hasMoves()) {
- LK.showGameOver();
- }
- }, 220);
- return true;
- }
- // Place block
- placeBlockOnGrid(block, col, row);
- popNextBlock(block);
- // After placement, check for merges
- processMerges(col, row, function () {
- // After all merges and cascades, check for game over
- if (!hasMoves()) {
- LK.showGameOver();
- }
- });
- return true;
+function updateScoreText() {
+ if (scoreText) scoreText.setText(LK.getScore());
}
-// Process merges and cascades starting from (col,row)
-function processMerges(col, row, onFinish) {
- if (isProcessing) {
- return;
- }
- isProcessing = true;
- var block = grid[col][row];
- if (!block || block.isLocked) {
- isProcessing = false;
- if (onFinish) {
- onFinish();
- }
- return;
- }
- var colorIdx = block.colorIndex;
- if (colorIdx >= MAX_COLOR_INDEX) {
- isProcessing = false;
- if (onFinish) {
- onFinish();
- }
- return;
- }
- // Remove pink (6) and violet (4) cubes immediately if present at this position
- if (block.colorIndex === 4 || block.colorIndex === 6) {
- removeBlockFromGrid(col, row);
- tween(block, {
- scaleX: 0,
- scaleY: 0,
- alpha: 0
- }, {
- duration: 120,
- easing: tween.easeIn,
- onFinish: function onFinish() {
- block.destroy();
- isProcessing = false;
- if (onFinish) {
- onFinish();
- }
- }
+// --- Draw Board Background ---
+for (var y = 0; y < GRID_ROWS; y++) {
+ for (var x = 0; x < GRID_COLS; x++) {
+ var bg = LK.getAsset('block_gray', {
+ anchorX: 0.5,
+ anchorY: 0.5
});
- return;
+ bg.width = CELL_SIZE;
+ bg.height = CELL_SIZE;
+ bg.x = GRID_OFFSET_X + x * CELL_SIZE + CELL_SIZE / 2;
+ bg.y = GRID_OFFSET_Y + y * CELL_SIZE + CELL_SIZE / 2;
+ game.addChild(bg);
}
- // Find all connected blocks of same color
- var connected = findConnectedBlocks(col, row, colorIdx, {});
- if (connected.length >= 3) {
- // Play happy merge sound only on actual merge
- LK.getSound('merge').play();
- // Find center position for merged block
- var sumX = 0,
- sumY = 0;
- for (var i = 0; i < connected.length; i++) {
- sumX += connected[i].col;
- sumY += connected[i].row;
- }
- var centerCol = Math.round(sumX / connected.length);
- var centerRow = Math.round(sumY / connected.length);
- // Pick a new color for the merged block: must be a color not present in the merged group
- var presentColors = {};
- for (var i = 0; i < connected.length; i++) {
- presentColors[connected[i].block.colorIndex] = true;
- }
- // Try to find a color not present in the merged group
- var allPossibleColors = [0, 1, 2, 3, 4, 6, 7, 8, 5]; // all colors, rainbow last
- var newColorIdx = -1;
- for (var i = 0; i < allPossibleColors.length; i++) {
- if (!presentColors[allPossibleColors[i]]) {
- newColorIdx = allPossibleColors[i];
- break;
- }
- }
- // If all colors are present, fallback to rainbow/white
- if (newColorIdx === -1) {
- newColorIdx = RAINBOW_COLOR_INDEX;
- }
- // Remove all blocks in connected, but delay the destruction until after the merged block is created
- var blocksToRemove = [];
- for (var i = 0; i < connected.length; i++) {
- var b = connected[i].block;
- blocksToRemove.push(b);
- removeBlockFromGrid(b.gridX, b.gridY);
- }
- // Animate and destroy the old blocks, then create the merged block after all are gone
- var destroyCount = 0;
- for (var i = 0; i < blocksToRemove.length; i++) {
- (function (bref) {
- tween(bref, {
- scaleX: 0,
- scaleY: 0,
- alpha: 0
- }, {
- duration: 120,
- easing: tween.easeIn,
- onFinish: function onFinish() {
- bref.destroy();
- destroyCount++;
- // When all blocks are destroyed, create the merged block and animate it
- if (destroyCount === blocksToRemove.length) {
- // Add delay for purple (4) and pink (6) merges
- var doTransform = function doTransform() {
- // Create merged block at center position
- var mergedBlock = new Block();
- mergedBlock.setColor(newColorIdx);
- mergedBlock.isOnGrid = true;
- mergedBlock.gridX = centerCol;
- mergedBlock.gridY = centerRow;
- var pos = getPosForCell(centerCol, centerRow);
- mergedBlock.x = pos.x;
- mergedBlock.y = pos.y;
- grid[centerCol][centerRow] = mergedBlock;
- game.addChild(mergedBlock);
- mergedBlock.animateMerge(function () {
- // Update score
- var points = 10 * connected.length * (colorIdx + 1);
- score += points;
- scoreTxt.setText(score);
- // After animation, finish processing
- isProcessing = false;
- // After merge, check if the new merged block itself can merge again (chain reaction)
- processMerges(centerCol, centerRow, onFinish);
- });
- };
- // If merging to purple (4) or pink (6), add a delay before transform
- if (newColorIdx === 4 || newColorIdx === 6) {
- LK.setTimeout(doTransform, 320);
- } else {
- doTransform();
- }
- }
- }
- });
- })(blocksToRemove[i]);
- }
- // If no blocks to remove (should not happen), just finish
- if (blocksToRemove.length === 0) {
- mergedBlock.animateMerge(function () {
- var points = 10 * connected.length * (colorIdx + 1);
- score += points;
- scoreTxt.setText(score);
- isProcessing = false;
- processMerges(centerCol, centerRow, onFinish);
- });
- }
- } else {
- isProcessing = false;
- if (onFinish) {
- onFinish();
- }
+}
+// --- Score Display ---
+scoreText = new Text2('0', {
+ size: 120,
+ fill: 0xFFFFFF
+});
+scoreText.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreText);
+updateScoreText();
+// --- Spawn New Block ---
+function spawnBlock() {
+ if (currentBlock) {
+ currentBlock.destroy();
+ currentBlock = null;
}
+ var colorId = getRandomColor();
+ currentBlock = new Block();
+ currentBlock.setColor(colorId);
+ currentBlock.x = 2048 / 2;
+ currentBlock.y = 300;
+ currentBlock.asset.width = CELL_SIZE;
+ currentBlock.asset.height = CELL_SIZE;
+ game.addChild(currentBlock);
+ dragging = false;
+ dragSprite = null;
+ dragGridX = null;
+ dragGridY = null;
}
-// --- Input Handling ---
-// Only allow dragging from nextBlocks
+spawnBlock();
+// --- Drag and Drop Handlers ---
game.down = function (x, y, obj) {
- if (isProcessing) {
- return;
+ if (!currentBlock) return;
+ // Only start drag if touch is on the current block
+ var dx = x - currentBlock.x;
+ var dy = y - currentBlock.y;
+ if (Math.abs(dx) < CELL_SIZE / 2 && Math.abs(dy) < CELL_SIZE / 2) {
+ dragging = true;
+ dragSprite = currentBlock;
}
- for (var i = 0; i < nextBlocks.length; i++) {
- var block = nextBlocks[i];
- // Check if touch is inside block
- var dx = x - block.x;
- var dy = y - block.y;
- if (Math.abs(dx) < 90 && Math.abs(dy) < 90 && !block.isLocked && !block.isOnGrid) {
- draggingBlock = block;
- dragOffsetX = dx;
- dragOffsetY = dy;
- // Play a sound when picking up a block
- LK.getSound('merge').play();
- // Bring to front
- game.addChild(block);
- tween(block, {
- scaleX: 1.15,
- scaleY: 1.15
- }, {
- duration: 80,
- easing: tween.easeOut
- });
- break;
- }
- }
};
game.move = function (x, y, obj) {
- if (!draggingBlock) {
- return;
+ if (!dragging || !dragSprite) return;
+ // Snap to nearest grid cell
+ var gx = Math.floor((x - GRID_OFFSET_X) / CELL_SIZE);
+ var gy = Math.floor((y - GRID_OFFSET_Y) / CELL_SIZE);
+ if (isInsideGrid(gx, gy)) {
+ dragSprite.x = GRID_OFFSET_X + gx * CELL_SIZE + CELL_SIZE / 2;
+ dragSprite.y = GRID_OFFSET_Y + gy * CELL_SIZE + CELL_SIZE / 2;
+ dragGridX = gx;
+ dragGridY = gy;
+ } else {
+ // Follow finger
+ dragSprite.x = x;
+ dragSprite.y = y;
+ dragGridX = null;
+ dragGridY = null;
}
- draggingBlock.x = x - dragOffsetX;
- draggingBlock.y = y - dragOffsetY;
};
game.up = function (x, y, obj) {
- if (!draggingBlock) {
- return;
- }
- // Try to place on grid
- var placed = tryPlaceBlock(draggingBlock, draggingBlock.x, draggingBlock.y);
- if (!placed) {
- // Snap back to next area
- var idx = nextBlocks.indexOf(draggingBlock);
- var bx = 2048 / 2 + (idx - 1) * 220;
- tween(draggingBlock, {
- x: bx,
- y: 220,
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 120,
- easing: tween.easeInOut
- });
+ if (!dragging || !dragSprite) return;
+ if (dragGridX !== null && dragGridY !== null && !grid[dragGridY][dragGridX]) {
+ // Place block
+ dragSprite.setGridPos(dragGridX, dragGridY);
+ grid[dragGridY][dragGridX] = dragSprite;
+ // Check for merges
+ handleMerge(dragGridX, dragGridY);
+ // Check for game over
+ if (isBoardFull()) {
+ LK.showGameOver();
+ return;
+ }
+ spawnBlock();
} else {
- // Play a sound when block is placed on the grid
- LK.getSound('merge').play();
- // Block is now on grid, don't need to do anything
+ // Return to top
+ dragSprite.x = 2048 / 2;
+ dragSprite.y = 300;
}
- draggingBlock = null;
+ dragging = false;
+ dragSprite = null;
+ dragGridX = null;
+ dragGridY = null;
};
-// --- Game Update Loop ---
-game.update = function () {
- // Animate blocks falling off (if any)
- for (var c = 0; c < GRID_COLS; c++) {
- for (var r = 0; r < GRID_ROWS; r++) {
- var block = grid[c][r];
- // Remove pink and violet cubes if present
- if (block && (block.colorIndex === 4 || block.colorIndex === 6)) {
- block.destroy();
- grid[c][r] = null;
- } else if (block && block.y > BLOCK_DROP_Y) {
- block.destroy();
- grid[c][r] = null;
- }
+// --- Merge Logic ---
+function handleMerge(gx, gy) {
+ var block = grid[gy][gx];
+ if (!block) return;
+ var colorId = block.colorId;
+ var visited = [];
+ var cluster = findCluster(gx, gy, colorId, visited);
+ if (cluster.length >= 3) {
+ // Merge!
+ var colorData = getColorData(colorId);
+ var nextColor = colorData.next;
+ var points = colorData.points * cluster.length;
+ // Remove all blocks in cluster
+ for (var i = 0; i < cluster.length; i++) {
+ var cx = cluster[i].x,
+ cy = cluster[i].y;
+ if (cx !== gx || cy !== gy) clearBlock(cx, cy);
}
+ // Upgrade main block
+ if (nextColor) {
+ block.setColor(nextColor);
+ }
+ LK.setScore(LK.getScore() + points);
+ updateScoreText();
+ // Chain reaction
+ handleMerge(gx, gy);
}
- // Occasionally spawn a locked block in a random empty cell (every 10 turns)
- if (typeof game.turnsSinceLock === "undefined") {
- game.turnsSinceLock = 0;
- }
- if (typeof game.lastBlocksOnGrid === "undefined") {
- game.lastBlocksOnGrid = 0;
- }
- var blocksOnGrid = 0;
- for (var c = 0; c < GRID_COLS; c++) {
- for (var r = 0; r < GRID_ROWS; r++) {
- if (grid[c][r]) {
- blocksOnGrid++;
- }
+}
+// --- Board Full Check ---
+function isBoardFull() {
+ for (var y = 0; y < GRID_ROWS; y++) {
+ for (var x = 0; x < GRID_COLS; x++) {
+ if (!grid[y][x]) return false;
}
}
- // Removed logic for spawning gray/locked blocks every 10 turns (no more grey squares)
- if (blocksOnGrid > game.lastBlocksOnGrid) {
- game.turnsSinceLock++;
- game.lastBlocksOnGrid = blocksOnGrid;
- // No locked block spawn
- }
-};
-// --- Game Start ---
-score = 0;
-scoreTxt.setText(score);
-refillNextBlocks();
\ No newline at end of file
+ return true;
+}
+// --- Game Update (not used, but required for LK) ---
+game.update = function () {};
\ No newline at end of file