User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'var placedBlock = gameArea.attachAsset(blockColor, {' Line Number: 419
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'var placedBlock = gameArea.attachAsset(blockColor, {' Line Number: 419
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'var placedBlock = gameArea.attachAsset(blockColor, {' Line Number: 414
Code edit (1 edits merged)
Please save this source code
User prompt
Block Tower Challenge
Initial prompt
I want a good game like tetris, but in that game you have 10 levels max. In level five the level is harder then level 1, 2, 3 and four. Level ten is the hardest.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, level: 1 }); /**** * Classes ****/ var Block = Container.expand(function () { var self = Container.call(this); self.init = function (type, size) { self.blockType = type; self.blockSize = size || 80; self.blocks = []; // Create block shapes based on type // Each block type has a different pattern var color; var pattern; switch (type) { case 'I': // I block (4 blocks in a line) color = 'blockCyan'; pattern = [[0, 0], [0, 1], [0, 2], [0, 3]]; break; case 'J': // J block color = 'blockBlue'; pattern = [[0, 0], [0, 1], [0, 2], [-1, 2]]; break; case 'L': // L block color = 'blockOrange'; pattern = [[0, 0], [0, 1], [0, 2], [1, 2]]; break; case 'O': // O block (2x2 square) color = 'blockYellow'; pattern = [[0, 0], [1, 0], [0, 1], [1, 1]]; break; case 'S': // S block color = 'blockGreen'; pattern = [[0, 0], [1, 0], [0, 1], [-1, 1]]; break; case 'Z': // Z block color = 'blockRed'; pattern = [[0, 0], [-1, 0], [0, 1], [1, 1]]; break; case 'T': // T block color = 'blockPurple'; pattern = [[0, 0], [-1, 1], [0, 1], [1, 1]]; break; default: color = 'blockBlue'; pattern = [[0, 0]]; } // Create block parts based on pattern for (var i = 0; i < pattern.length; i++) { var blockPart = self.attachAsset(color, { anchorX: 0, anchorY: 0, x: pattern[i][0] * self.blockSize, y: pattern[i][1] * self.blockSize, width: self.blockSize, height: self.blockSize }); // Add a small border to each block by reducing its size a bit blockPart.scale.set(0.95); blockPart.position.x += self.blockSize * 0.025; blockPart.position.y += self.blockSize * 0.025; self.blocks.push({ sprite: blockPart, relX: pattern[i][0], relY: pattern[i][1] }); } return self; }; self.rotate = function (direction) { // Rotate the block (1 = clockwise, -1 = counter-clockwise) if (self.blockType === 'O') { return; } // O block doesn't need rotation for (var i = 0; i < self.blocks.length; i++) { var block = self.blocks[i]; var newRelX, newRelY; if (direction === 1) { // Clockwise rotation newRelX = -block.relY; newRelY = block.relX; } else { // Counter-clockwise rotation newRelX = block.relY; newRelY = -block.relX; } block.relX = newRelX; block.relY = newRelY; block.sprite.x = newRelX * self.blockSize; block.sprite.y = newRelY * self.blockSize; } // Play rotation sound LK.getSound('blockRotate').play(); }; self.getBlockPositions = function (offsetX, offsetY) { // Get the global positions of each block part var positions = []; for (var i = 0; i < self.blocks.length; i++) { var block = self.blocks[i]; positions.push({ x: Math.floor((self.x + block.relX * self.blockSize) / self.blockSize) + offsetX, y: Math.floor((self.y + block.relY * self.blockSize) / self.blockSize) + offsetY }); } return positions; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Game constants var BLOCK_SIZE = 80; // Block size in pixels var GRID_WIDTH = 10; // Width of the game grid var GRID_HEIGHT = 20; // Height of the game grid var GAME_AREA_WIDTH = GRID_WIDTH * BLOCK_SIZE; var GAME_AREA_HEIGHT = GRID_HEIGHT * BLOCK_SIZE; var BLOCK_TYPES = ['I', 'J', 'L', 'O', 'S', 'Z', 'T']; var LEVEL_SPEEDS = [800, 700, 600, 500, 400, 300, 250, 200, 150, 100, 50]; // Fall speed in ms per level // Game state variables var gameGrid = []; // 2D grid to track placed blocks var currentBlock = null; // Current falling block var nextBlock = null; // Next block to fall var gameTimer = null; // Timer for block movement var gameOver = false; var score = 0; var level = storage.level || 1; var linesCleared = 0; var linesToNextLevel = 10; // Lines needed to clear to advance a level var fallSpeed = LEVEL_SPEEDS[level - 1] || LEVEL_SPEEDS[0]; var touchStartX = 0; var touchStartY = 0; var isSwipeDown = false; // Create UI elements var scoreText = new Text2('Score: 0', { size: 50, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); LK.gui.topRight.addChild(scoreText); scoreText.position.x = -scoreText.width - 20; scoreText.position.y = 20; var levelText = new Text2('Level: ' + level, { size: 50, fill: 0xFFFFFF }); levelText.anchor.set(0, 0); LK.gui.topRight.addChild(levelText); levelText.position.x = -levelText.width - 20; levelText.position.y = 80; var nextBlockText = new Text2('Next:', { size: 40, fill: 0xFFFFFF }); nextBlockText.anchor.set(0, 0); LK.gui.topRight.addChild(nextBlockText); nextBlockText.position.x = -nextBlockText.width - 160; nextBlockText.position.y = 140; // Create game area container var gameArea = new Container(); game.addChild(gameArea); // Center the game area in the screen gameArea.x = (2048 - GAME_AREA_WIDTH) / 2; gameArea.y = (2732 - GAME_AREA_HEIGHT) / 2; // Create game area border var gameBorder = gameArea.attachAsset('gameAreaBorder', { anchorX: 0, anchorY: 0, width: GAME_AREA_WIDTH + 10, height: GAME_AREA_HEIGHT + 10, x: -5, y: -5 }); // Create next block display area var nextBlockArea = new Container(); game.addChild(nextBlockArea); nextBlockArea.x = gameArea.x + GAME_AREA_WIDTH + 100; nextBlockArea.y = gameArea.y + 200; var nextBlockBorder = nextBlockArea.attachAsset('nextBlockArea', { anchorX: 0, anchorY: 0, width: 200, height: 200, x: -5, y: -5 }); // Initialize game grid function initializeGrid() { gameGrid = []; for (var y = 0; y < GRID_HEIGHT; y++) { gameGrid[y] = []; for (var x = 0; x < GRID_WIDTH; x++) { gameGrid[y][x] = null; } } } // Create a random block function createRandomBlock() { var type = BLOCK_TYPES[Math.floor(Math.random() * BLOCK_TYPES.length)]; return new Block().init(type, BLOCK_SIZE); } // Start the game function startGame() { initializeGrid(); score = 0; linesCleared = 0; linesToNextLevel = 10; gameOver = false; // Update UI updateScoreDisplay(); updateLevelDisplay(); // Create initial blocks currentBlock = createRandomBlock(); nextBlock = createRandomBlock(); // Position the current block currentBlock.x = GAME_AREA_WIDTH / 2; currentBlock.y = 0; gameArea.addChild(currentBlock); // Display next block displayNextBlock(); // Start game timer if (gameTimer) { LK.clearInterval(gameTimer); } gameTimer = LK.setInterval(moveBlockDown, fallSpeed); // Play game music LK.playMusic('gameplayMusic'); } // Display the next block in the next block area function displayNextBlock() { // Clear previous next block display while (nextBlockArea.children.length > 1) { // Keep the border nextBlockArea.removeChild(nextBlockArea.children[1]); } // Add next block to the display area var displayBlock = createRandomBlock(); displayBlock.init(nextBlock.blockType, BLOCK_SIZE); // Center the block in the display area displayBlock.x = 100; displayBlock.y = 100; nextBlockArea.addChild(displayBlock); } // Check if a position is valid for the current block function isPositionValid(offsetX, offsetY) { var positions = currentBlock.getBlockPositions(offsetX, offsetY); for (var i = 0; i < positions.length; i++) { var pos = positions[i]; // Check if out of bounds if (pos.x < 0 || pos.x >= GRID_WIDTH || pos.y < 0 || pos.y >= GRID_HEIGHT) { return false; } // Check if position is already occupied if (pos.y >= 0 && gameGrid[pos.y][pos.x] !== null) { return false; } } return true; } // Move the current block down one position function moveBlockDown() { if (gameOver) { return; } if (isPositionValid(0, 1)) { currentBlock.y += BLOCK_SIZE; } else { // Block has reached the bottom or hit another block placeBlock(); // Check for completed lines checkLines(); // Create new block currentBlock = nextBlock; nextBlock = createRandomBlock(); // Position the new block currentBlock.x = GAME_AREA_WIDTH / 2; currentBlock.y = 0; gameArea.addChild(currentBlock); // Display next block displayNextBlock(); // Check if game over if (!isPositionValid(0, 0)) { handleGameOver(); } // Play drop sound LK.getSound('blockDrop').play(); } } // Move the block horizontally function moveBlockHorizontal(direction) { if (gameOver) { return; } if (isPositionValid(direction, 0)) { currentBlock.x += BLOCK_SIZE * direction; // Play move sound LK.getSound('blockMove').play(); } } // Rotate the current block function rotateBlock() { if (gameOver) { return; } // Backup current positions var originalPositions = currentBlock.getBlockPositions(0, 0); // Try to rotate currentBlock.rotate(1); // Check if rotation is valid if (!isPositionValid(0, 0)) { // If not valid, try with wall kicks (adjustments) var validPosition = false; // Try different offsets for wall kicks var wallKickOffsets = [{ x: -1, y: 0 }, { x: 1, y: 0 }, { x: -2, y: 0 }, { x: 2, y: 0 }, { x: 0, y: -1 }, { x: 0, y: -2 }]; for (var i = 0; i < wallKickOffsets.length; i++) { var offset = wallKickOffsets[i]; if (isPositionValid(offset.x, offset.y)) { currentBlock.x += offset.x * BLOCK_SIZE; currentBlock.y += offset.y * BLOCK_SIZE; validPosition = true; break; } } // If still not valid, revert the rotation if (!validPosition) { currentBlock.rotate(-1); // Rotate back } } } // Drop the block instantly to the bottom function dropBlock() { if (gameOver) { return; } // Find how far the block can drop var dropDistance = 0; while (isPositionValid(0, dropDistance + 1)) { dropDistance++; } if (dropDistance > 0) { currentBlock.y += dropDistance * BLOCK_SIZE; // Force placement moveBlockDown(); // Add bonus points for hard drop score += dropDistance * 2; updateScoreDisplay(); } } // Place the current block on the grid function placeBlock() { var positions = currentBlock.getBlockPositions(0, 0); for (var i = 0; i < positions.length; i++) { var pos = positions[i]; if (pos.y >= 0) { // Create a static block on the grid var blockColor = currentBlock.blocks[i].sprite.color || 'blockBlue'; gameGrid[pos.y][pos.x] = blockColor; // Create visual representation of placed block var placedBlock = gameArea.attachAsset(blockColor, { anchorX: 0, anchorY: 0, x: pos.x * BLOCK_SIZE, y: pos.y * BLOCK_SIZE, width: BLOCK_SIZE, height: BLOCK_SIZE }); // Add a small border to each block placedBlock.scale.set(0.95); placedBlock.position.x += BLOCK_SIZE * 0.025; placedBlock.position.y += BLOCK_SIZE * 0.025; // Tag it for identification placedBlock.gridX = pos.x; placedBlock.gridY = pos.y; } } // Remove the falling block gameArea.removeChild(currentBlock); } // Check for completed lines function checkLines() { var linesCompleted = 0; var linesToClear = []; for (var y = 0; y < GRID_HEIGHT; y++) { var lineComplete = true; for (var x = 0; x < GRID_WIDTH; x++) { if (gameGrid[y][x] === null) { lineComplete = false; break; } } if (lineComplete) { linesCompleted++; linesToClear.push(y); } } if (linesCompleted > 0) { // Clear the completed lines clearLines(linesToClear); // Update score var linePoints = 0; switch (linesCompleted) { case 1: linePoints = 100; break; case 2: linePoints = 300; break; case 3: linePoints = 500; break; case 4: linePoints = 800; break; // Tetris! } // Apply level multiplier score += linePoints * level; linesCleared += linesCompleted; linesToNextLevel -= linesCompleted; // Check for level up if (linesToNextLevel <= 0) { levelUp(); } updateScoreDisplay(); // Play line clear sound LK.getSound('lineClear').play(); } } // Clear the completed lines function clearLines(lines) { // First, remove blocks from the display for completed lines for (var i = 0; i < lines.length; i++) { var lineY = lines[i]; // Remove blocks in this line from display for (var j = gameArea.children.length - 1; j >= 0; j--) { var child = gameArea.children[j]; if (child.gridY !== undefined && child.gridY === lineY) { gameArea.removeChild(child); } } } // Update the grid and shift blocks down for (var i = 0; i < lines.length; i++) { var lineY = lines[i]; // Shift grid down for (var y = lineY; y > 0; y--) { for (var x = 0; x < GRID_WIDTH; x++) { gameGrid[y][x] = gameGrid[y - 1][x]; } } // Clear top line for (var x = 0; x < GRID_WIDTH; x++) { gameGrid[0][x] = null; } // Update visual blocks for (var j = 0; j < gameArea.children.length; j++) { var child = gameArea.children[j]; if (child.gridY !== undefined) { if (child.gridY < lineY) { child.gridY++; child.y += BLOCK_SIZE; } } } // Adjust remaining lines to clear for (var j = i + 1; j < lines.length; j++) { if (lines[j] < lineY) { lines[j]++; } } } } // Level up function levelUp() { level++; if (level > 10) { level = 10; } // Cap at level 10 // Update level display updateLevelDisplay(); // Reset lines to next level linesToNextLevel = 10; // Update speed fallSpeed = LEVEL_SPEEDS[level - 1] || LEVEL_SPEEDS[0]; if (gameTimer) { LK.clearInterval(gameTimer); } gameTimer = LK.setInterval(moveBlockDown, fallSpeed); // Play level up sound LK.getSound('levelUp').play(); // Flash screen for level up LK.effects.flashScreen(0x33cc33, 500); } // Update the score display function updateScoreDisplay() { scoreText.setText('Score: ' + score); scoreText.position.x = -scoreText.width - 20; } // Update the level display function updateLevelDisplay() { levelText.setText('Level: ' + level + ' (' + linesToNextLevel + ' lines)'); levelText.position.x = -levelText.width - 20; } // Handle game over function handleGameOver() { gameOver = true; // Clear game timer if (gameTimer) { LK.clearInterval(gameTimer); } // Update high score if needed if (score > storage.highScore) { storage.highScore = score; } // Save current level storage.level = level; // Show game over screen LK.showGameOver(); } // Touch handling for the game game.down = function (x, y, obj) { if (gameOver) { return; } touchStartX = x; touchStartY = y; isSwipeDown = false; }; game.move = function (x, y, obj) { if (gameOver) { return; } if (touchStartX !== null) { var deltaX = x - touchStartX; var deltaY = y - touchStartY; // Detect horizontal swipe if (Math.abs(deltaX) > 50 && Math.abs(deltaY) < 50) { if (deltaX < 0) { moveBlockHorizontal(-1); // Move left } else { moveBlockHorizontal(1); // Move right } touchStartX = x; touchStartY = y; } // Detect downward swipe if (deltaY > 100 && !isSwipeDown) { dropBlock(); // Hard drop isSwipeDown = true; } } }; game.up = function (x, y, obj) { if (gameOver) { return; } var deltaX = x - touchStartX; var deltaY = y - touchStartY; // Tap detection (little movement) if (Math.abs(deltaX) < 30 && Math.abs(deltaY) < 30) { rotateBlock(); } touchStartX = null; touchStartY = null; }; game.update = function () { // This function is called every frame // Not needed for core gameplay as we use timers for block movement }; // Start the game startGame();
===================================================================
--- original.js
+++ change.js
@@ -393,9 +393,9 @@
for (var i = 0; i < positions.length; i++) {
var pos = positions[i];
if (pos.y >= 0) {
// Create a static block on the grid
- var blockColor = currentBlock.blocks[i].sprite.color;
+ var blockColor = currentBlock.blocks[i].sprite.color || 'blockBlue';
gameGrid[pos.y][pos.x] = blockColor;
// Create visual representation of placed block
var placedBlock = gameArea.attachAsset(blockColor, {
anchorX: 0,