Code edit (17 edits merged)
Please save this source code
User prompt
If the AI gets pieces out of the screen as in the classical tetris, it should lose and the player wins. In that case, show you win
User prompt
Shwo above the PLAYER label, what is the next piece the player will get
User prompt
Play "moscow" music at start
User prompt
There is a bug when adding gray lines from the competitor, it seems the ghost pieces are being taken into account as existing and not ghosts?
User prompt
Implement a "ghost piece" tetris mechanism, where you show where the current piece will fall
Code edit (1 edits merged)
Please save this source code
Code edit (11 edits merged)
Please save this source code
User prompt
Make the colors of the tetrominos in a palette reminding of soviet union
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (22 edits merged)
Please save this source code
User prompt
Add a full width full height background on the back of everything
User prompt
Remove the Player: ... AI: ... score text elements. Instead, use the PLAYER and AI which are at -160 to also show the score like PLAYER(10) AI(2)
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
No sorry, I want two background assets - one for the AI background and one for the player background
Code edit (7 edits merged)
Please save this source code
User prompt
Add a background asset that is full screen covering the back of my screen
Code edit (1 edits merged)
Please save this source code
Code edit (3 edits merged)
Please save this source code
User prompt
Please fix: gray lines disappear. They should never nor for the player nor for the ai
User prompt
No, remove the controls of the pieces for the player (rotation, moving down, moving left, moving right). Instead, on the bottom of the game screen, place 4 widgets, 1 per rotate clockwise, one to move left, one to move right, one to move down. Full width, like on the bottom on the screen after the AI and PLayer screens.
User prompt
Change a little the controls for the player: - You rotate a piece if you click on it. - You move left a piece if you click on the left of a piece or left above the piece, but never left below. - You move right a piece if you click on the right of a piece or right above the piece, but never right below.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // --- Tetris Game Implementation --- // Board configuration var BOARD_COLS = 10; var BOARD_ROWS = 20; var CELL_SIZE = 80; // Larger cells for better visibility var BOARD_WIDTH = BOARD_COLS * CELL_SIZE; var BOARD_HEIGHT = BOARD_ROWS * CELL_SIZE; var PLAYER_BOARD_X = 1024 + 100; // Right side with more spacing var AI_BOARD_X = 1024 - BOARD_WIDTH - 100; // Left side with more spacing var BOARD_Y = (2732 - BOARD_HEIGHT) / 2; // Tetrimino definitions var TETROMINOS = [ // I { color: 0x00d4ff, blocks: [[0, 1], [1, 1], [2, 1], [3, 1]] }, // O { color: 0xffd700, blocks: [[1, 0], [2, 0], [1, 1], [2, 1]] }, // T { color: 0x9933ff, blocks: [[1, 0], [0, 1], [1, 1], [2, 1]] }, // S { color: 0x32cd32, blocks: [[1, 0], [2, 0], [0, 1], [1, 1]] }, // Z { color: 0xff4444, blocks: [[0, 0], [1, 0], [1, 1], [2, 1]] }, // J { color: 0x4169e1, blocks: [[0, 0], [0, 1], [1, 1], [2, 1]] }, // L { color: 0xff8c00, blocks: [[2, 0], [0, 1], [1, 1], [2, 1]] }]; // Board states for player and AI var playerBoard = []; var aiBoard = []; for (var r = 0; r < BOARD_ROWS; r++) { playerBoard[r] = []; aiBoard[r] = []; for (var c = 0; c < BOARD_COLS; c++) { playerBoard[r][c] = null; aiBoard[r][c] = null; } } // Score var playerScore = 0; var aiScore = 0; var scoreText = new Text2('Player: 0 | AI: 0', { size: 90, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); // Labels for boards var playerLabel = new Text2('PLAYER', { size: 70, fill: 0x00FF00 }); playerLabel.anchor.set(0.5, 0); playerLabel.x = PLAYER_BOARD_X + BOARD_WIDTH / 2; playerLabel.y = BOARD_Y - 60; game.addChild(playerLabel); var aiLabel = new Text2('AI', { size: 70, fill: 0xFF0000 }); aiLabel.anchor.set(0.5, 0); aiLabel.x = AI_BOARD_X + BOARD_WIDTH / 2; aiLabel.y = BOARD_Y - 60; game.addChild(aiLabel); // Add vertical divider line between AI and Player screens var dividerLine = LK.getAsset('character', { anchorX: 0.5, anchorY: 0, x: 1024, y: BOARD_Y - 80, width: 6, height: BOARD_HEIGHT + 160, tint: 0x666666 }); game.addChild(dividerLine); // Player board container and graphics var playerContainer = new Container(); playerContainer.x = PLAYER_BOARD_X; playerContainer.y = BOARD_Y; game.addChild(playerContainer); var playerCellGraphics = []; for (var r = 0; r < BOARD_ROWS; r++) { playerCellGraphics[r] = []; for (var c = 0; c < BOARD_COLS; c++) { var cell = LK.getAsset('character', { anchorX: 0, anchorY: 0 }); cell.width = CELL_SIZE - 2; cell.height = CELL_SIZE - 2; cell.x = c * CELL_SIZE + 1; cell.y = r * CELL_SIZE + 1; cell.visible = false; playerContainer.addChild(cell); playerCellGraphics[r][c] = cell; } } // AI board container and graphics var aiContainer = new Container(); aiContainer.x = AI_BOARD_X; aiContainer.y = BOARD_Y; game.addChild(aiContainer); var aiCellGraphics = []; for (var r = 0; r < BOARD_ROWS; r++) { aiCellGraphics[r] = []; for (var c = 0; c < BOARD_COLS; c++) { var cell = LK.getAsset('character', { anchorX: 0, anchorY: 0 }); cell.width = CELL_SIZE - 2; cell.height = CELL_SIZE - 2; cell.x = c * CELL_SIZE + 1; cell.y = r * CELL_SIZE + 1; cell.visible = false; aiContainer.addChild(cell); aiCellGraphics[r][c] = cell; } } // Active piece states var playerPiece = null; var playerX = 0; var playerY = 0; var aiPiece = null; var aiX = 0; var aiY = 0; // Helper: update board graphics function updateBoardGraphics(board, graphics) { for (var r = 0; r < BOARD_ROWS; r++) { for (var c = 0; c < BOARD_COLS; c++) { if (board[r][c]) { graphics[r][c].visible = true; graphics[r][c].tint = board[r][c]; } else { graphics[r][c].visible = false; } } } } // Helper: draw active piece function drawActivePiece(piece, x, y, graphics, show) { if (!piece) { return; } for (var i = 0; i < 4; i++) { var block = piece.blocks[i]; var r = y + block[1]; var c = x + block[0]; if (r >= 0 && r < BOARD_ROWS && c >= 0 && c < BOARD_COLS) { graphics[r][c].visible = show; if (show) { graphics[r][c].tint = piece.color; } } } } // Helper: rotate blocks function rotateBlocks(blocks) { var rotated = []; for (var i = 0; i < blocks.length; i++) { rotated.push([blocks[i][1], -blocks[i][0]]); } // Normalize to top-left var minX = Math.min(rotated[0][0], rotated[1][0], rotated[2][0], rotated[3][0]); var minY = Math.min(rotated[0][1], rotated[1][1], rotated[2][1], rotated[3][1]); for (var i = 0; i < rotated.length; i++) { rotated[i][0] -= minX; rotated[i][1] -= minY; } return rotated; } // Helper: check collision function collides(blocks, x, y, board) { for (var i = 0; i < 4; i++) { var bx = x + blocks[i][0]; var by = y + blocks[i][1]; if (bx < 0 || bx >= BOARD_COLS || by >= BOARD_ROWS) { return true; } if (by >= 0 && board[by][bx]) { return true; } } return false; } // Spawn a new piece function spawnPiece(isAI) { var shapeIdx = Math.floor(Math.random() * TETROMINOS.length); var shape = TETROMINOS[shapeIdx]; var piece = { color: shape.color, blocks: [[shape.blocks[0][0], shape.blocks[0][1]], [shape.blocks[1][0], shape.blocks[1][1]], [shape.blocks[2][0], shape.blocks[2][1]], [shape.blocks[3][0], shape.blocks[3][1]]] }; if (isAI) { aiPiece = piece; aiX = 3; aiY = -2; if (collides(aiPiece.blocks, aiX, aiY + 1, aiBoard)) { // AI lost, just reset AI board resetAIBoard(); } } else { playerPiece = piece; playerX = 3; playerY = -2; if (collides(playerPiece.blocks, playerX, playerY + 1, playerBoard)) { LK.showGameOver(); } } } // Reset AI board when AI loses function resetAIBoard() { for (var r = 0; r < BOARD_ROWS; r++) { for (var c = 0; c < BOARD_COLS; c++) { aiBoard[r][c] = null; } } aiScore = 0; scoreText.setText('Player: ' + playerScore + ' | AI: ' + aiScore); } // Lock the active piece into the board function lockPiece(piece, x, y, board) { for (var i = 0; i < 4; i++) { var block = piece.blocks[i]; var r = y + block[1]; var c = x + block[0]; if (r >= 0 && r < BOARD_ROWS && c >= 0 && c < BOARD_COLS) { board[r][c] = piece.color; } } } // Clear full lines function clearLines(board, isAI) { var lines = 0; var clearedLineData = []; // Store the cleared lines for transfer for (var r = BOARD_ROWS - 1; r >= 0; r--) { var full = true; for (var c = 0; c < BOARD_COLS; c++) { if (!board[r][c]) { full = false; break; } } if (full) { // Check if this line contains any transferred blocks (gray blocks) var isTransferredLine = true; for (var cc = 0; cc < BOARD_COLS; cc++) { if (board[r][cc] !== 0x808080) { isTransferredLine = false; break; } } // Skip clearing if this is a transferred line - gray lines stay forever if (isTransferredLine) { continue; // Don't clear gray lines, move to next row } lines++; // Store line data for transfer (only non-gray lines) var lineData = []; for (var cc = 0; cc < BOARD_COLS; cc++) { lineData[cc] = board[r][cc]; } clearedLineData.push(lineData); // Move all rows above down for (var rr = r; rr > 0; rr--) { for (var cc = 0; cc < BOARD_COLS; cc++) { board[rr][cc] = board[rr - 1][cc]; } } for (var cc = 0; cc < BOARD_COLS; cc++) { board[0][cc] = null; } r++; // Check this row again } } if (lines > 0) { if (isAI) { aiScore += lines * 100; // Only transfer lines that are not from previous transfers if (clearedLineData.length > 0) { transferLinesToBoard(clearedLineData, playerBoard); } } else { playerScore += lines * 100; // Only transfer lines that are not from previous transfers if (clearedLineData.length > 0) { transferLinesToBoard(clearedLineData, aiBoard); } } scoreText.setText('Player: ' + playerScore + ' | AI: ' + aiScore); } } // Try to move player piece function tryMovePlayer(dx, dy, rotate) { var newBlocks = []; if (rotate) { newBlocks = rotateBlocks(playerPiece.blocks); } else { for (var i = 0; i < 4; i++) { newBlocks[i] = [playerPiece.blocks[i][0], playerPiece.blocks[i][1]]; } } var nx = playerX + dx; var ny = playerY + dy; if (!collides(newBlocks, nx, ny, playerBoard)) { drawActivePiece(playerPiece, playerX, playerY, playerCellGraphics, false); playerX = nx; playerY = ny; if (rotate) { playerPiece.blocks = newBlocks; } drawActivePiece(playerPiece, playerX, playerY, playerCellGraphics, true); return true; } return false; } // AI logic functions function getHeight(board) { for (var r = 0; r < BOARD_ROWS; r++) { for (var c = 0; c < BOARD_COLS; c++) { if (board[r][c]) { return BOARD_ROWS - r; } } } return 0; } function countHoles(board) { var holes = 0; for (var c = 0; c < BOARD_COLS; c++) { var blockFound = false; for (var r = 0; r < BOARD_ROWS; r++) { if (board[r][c]) { blockFound = true; } else if (blockFound) { holes++; } } } return holes; } function evaluateBoard(board) { var height = getHeight(board); var holes = countHoles(board); var lines = 0; for (var r = 0; r < BOARD_ROWS; r++) { var full = true; for (var c = 0; c < BOARD_COLS; c++) { if (!board[r][c]) { full = false; break; } } if (full) { lines++; } } return lines * 1000 - height * 10 - holes * 30; } function simulateMove(piece, x, y, board) { var testBoard = []; for (var r = 0; r < BOARD_ROWS; r++) { testBoard[r] = []; for (var c = 0; c < BOARD_COLS; c++) { testBoard[r][c] = board[r][c]; } } // Drop piece to bottom while (!collides(piece.blocks, x, y + 1, testBoard)) { y++; } // Lock piece for (var i = 0; i < 4; i++) { var block = piece.blocks[i]; var r = y + block[1]; var c = x + block[0]; if (r >= 0 && r < BOARD_ROWS && c >= 0 && c < BOARD_COLS) { testBoard[r][c] = piece.color; } } return evaluateBoard(testBoard); } function findBestMove() { if (!aiPiece) { return null; } var bestScore = -99999; var bestX = aiX; var bestRotation = 0; // Try all rotations var testPiece = { color: aiPiece.color, blocks: [] }; for (var rot = 0; rot < 4; rot++) { // Copy blocks for (var i = 0; i < 4; i++) { testPiece.blocks[i] = [aiPiece.blocks[i][0], aiPiece.blocks[i][1]]; } // Apply rotation for (var r = 0; r < rot; r++) { testPiece.blocks = rotateBlocks(testPiece.blocks); } // Try all positions for (var x = -2; x < BOARD_COLS + 2; x++) { if (!collides(testPiece.blocks, x, aiY, aiBoard)) { var score = simulateMove(testPiece, x, aiY, aiBoard); if (score > bestScore) { bestScore = score; bestX = x; bestRotation = rot; } } } } return { x: bestX, rotation: bestRotation }; } // Add variable to track fast drop mode var fastDropMode = false; // Control widgets at bottom of screen var WIDGET_HEIGHT = 150; var WIDGET_Y = 2732 - WIDGET_HEIGHT - 20; var WIDGET_WIDTH = 2048 / 4; // Left button var leftButton = LK.getAsset('character', { anchorX: 0, anchorY: 0, x: 0, y: WIDGET_Y, width: WIDGET_WIDTH, height: WIDGET_HEIGHT, tint: 0x4169e1 }); game.addChild(leftButton); var leftLabel = new Text2('LEFT', { size: 50, fill: 0xFFFFFF }); leftLabel.anchor.set(0.5, 0.5); leftLabel.x = WIDGET_WIDTH / 2; leftLabel.y = WIDGET_HEIGHT / 2; leftButton.addChild(leftLabel); // Right button var rightButton = LK.getAsset('character', { anchorX: 0, anchorY: 0, x: WIDGET_WIDTH, y: WIDGET_Y, width: WIDGET_WIDTH, height: WIDGET_HEIGHT, tint: 0xff8c00 }); game.addChild(rightButton); var rightLabel = new Text2('RIGHT', { size: 50, fill: 0xFFFFFF }); rightLabel.anchor.set(0.5, 0.5); rightLabel.x = WIDGET_WIDTH / 2; rightLabel.y = WIDGET_HEIGHT / 2; rightButton.addChild(rightLabel); // Rotate button var rotateButton = LK.getAsset('character', { anchorX: 0, anchorY: 0, x: WIDGET_WIDTH * 2, y: WIDGET_Y, width: WIDGET_WIDTH, height: WIDGET_HEIGHT, tint: 0x9933ff }); game.addChild(rotateButton); var rotateLabel = new Text2('ROTATE', { size: 50, fill: 0xFFFFFF }); rotateLabel.anchor.set(0.5, 0.5); rotateLabel.x = WIDGET_WIDTH / 2; rotateLabel.y = WIDGET_HEIGHT / 2; rotateButton.addChild(rotateLabel); // Down button var downButton = LK.getAsset('character', { anchorX: 0, anchorY: 0, x: WIDGET_WIDTH * 3, y: WIDGET_Y, width: WIDGET_WIDTH, height: WIDGET_HEIGHT, tint: 0x32cd32 }); game.addChild(downButton); var downLabel = new Text2('DOWN', { size: 50, fill: 0xFFFFFF }); downLabel.anchor.set(0.5, 0.5); downLabel.x = WIDGET_WIDTH / 2; downLabel.y = WIDGET_HEIGHT / 2; downButton.addChild(downLabel); // Add click handlers for widgets leftButton.down = function (x, y, obj) { if (playerPiece) { tryMovePlayer(-1, 0, false); } }; rightButton.down = function (x, y, obj) { if (playerPiece) { tryMovePlayer(1, 0, false); } }; rotateButton.down = function (x, y, obj) { if (playerPiece) { tryMovePlayer(0, 0, true); } }; downButton.down = function (x, y, obj) { if (playerPiece) { fastDropMode = true; } }; downButton.up = function (x, y, obj) { fastDropMode = false; }; // Control widgets - no direct piece controls game.down = function (x, y, obj) { // Controls are now handled by widgets }; // Release fast drop mode when mouse/touch is released game.up = function (x, y, obj) { fastDropMode = false; }; // Main game update var dropCounter = 0; var aiMoveCounter = 0; var aiTargetX = null; var aiTargetRotation = 0; game.update = function () { dropCounter++; // Player update - use faster drop speed when fastDropMode is active var dropSpeed = fastDropMode ? 3 : 30; // Drop every 3 frames when fast, every 30 frames normally if (dropCounter >= dropSpeed) { dropCounter = 0; if (!tryMovePlayer(0, 1, false)) { drawActivePiece(playerPiece, playerX, playerY, playerCellGraphics, false); lockPiece(playerPiece, playerX, playerY, playerBoard); clearLines(playerBoard, false); updateBoardGraphics(playerBoard, playerCellGraphics); spawnPiece(false); drawActivePiece(playerPiece, playerX, playerY, playerCellGraphics, true); // Reset fast drop mode when piece locks fastDropMode = false; } } // AI update aiMoveCounter++; if (aiMoveCounter >= 5) { // AI moves faster aiMoveCounter = 0; // Get best move if not already calculated if (aiTargetX === null && aiPiece) { var bestMove = findBestMove(); if (bestMove) { aiTargetX = bestMove.x; aiTargetRotation = bestMove.rotation; } } // Execute AI moves if (aiPiece && aiTargetX !== null) { // Rotate first if (aiTargetRotation > 0) { var rotatedBlocks = rotateBlocks(aiPiece.blocks); if (!collides(rotatedBlocks, aiX, aiY, aiBoard)) { drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, false); aiPiece.blocks = rotatedBlocks; drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, true); aiTargetRotation--; } } // Then move horizontally else if (aiX < aiTargetX) { if (!collides(aiPiece.blocks, aiX + 1, aiY, aiBoard)) { drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, false); aiX++; drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, true); } } else if (aiX > aiTargetX) { if (!collides(aiPiece.blocks, aiX - 1, aiY, aiBoard)) { drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, false); aiX--; drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, true); } } // Drop when in position else { if (!collides(aiPiece.blocks, aiX, aiY + 1, aiBoard)) { drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, false); aiY++; drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, true); } else { // Lock piece drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, false); lockPiece(aiPiece, aiX, aiY, aiBoard); clearLines(aiBoard, true); updateBoardGraphics(aiBoard, aiCellGraphics); spawnPiece(true); aiTargetX = null; aiTargetRotation = 0; } } } } updateBoardGraphics(playerBoard, playerCellGraphics); updateBoardGraphics(aiBoard, aiCellGraphics); drawActivePiece(playerPiece, playerX, playerY, playerCellGraphics, true); drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, true); }; // Start game spawnPiece(false); // Player piece spawnPiece(true); // AI piece drawActivePiece(playerPiece, playerX, playerY, playerCellGraphics, true); drawActivePiece(aiPiece, aiX, aiY, aiCellGraphics, true); updateBoardGraphics(playerBoard, playerCellGraphics); updateBoardGraphics(aiBoard, aiCellGraphics); // Transfer lines to opponent's board (competitive mode) function transferLinesToBoard(lineData, targetBoard) { // Move all existing rows up by the number of transferred lines var numLines = lineData.length; for (var r = 0; r < BOARD_ROWS - numLines; r++) { for (var c = 0; c < BOARD_COLS; c++) { targetBoard[r][c] = targetBoard[r + numLines][c]; } } // Add the transferred lines at the bottom for (var i = 0; i < numLines; i++) { var targetRow = BOARD_ROWS - numLines + i; for (var c = 0; c < BOARD_COLS; c++) { // Use a neutral gray color for transferred lines to distinguish them targetBoard[targetRow][c] = 0x808080; } } }
===================================================================
--- original.js
+++ change.js
@@ -277,25 +277,27 @@
break;
}
}
if (full) {
- lines++;
// Check if this line contains any transferred blocks (gray blocks)
var isTransferredLine = true;
for (var cc = 0; cc < BOARD_COLS; cc++) {
if (board[r][cc] !== 0x808080) {
isTransferredLine = false;
break;
}
}
- // Only store line data for transfer if it's not a transferred line
- if (!isTransferredLine) {
- var lineData = [];
- for (var cc = 0; cc < BOARD_COLS; cc++) {
- lineData[cc] = board[r][cc];
- }
- clearedLineData.push(lineData);
+ // Skip clearing if this is a transferred line - gray lines stay forever
+ if (isTransferredLine) {
+ continue; // Don't clear gray lines, move to next row
}
+ lines++;
+ // Store line data for transfer (only non-gray lines)
+ var lineData = [];
+ for (var cc = 0; cc < BOARD_COLS; cc++) {
+ lineData[cc] = board[r][cc];
+ }
+ clearedLineData.push(lineData);
// Move all rows above down
for (var rr = r; rr > 0; rr--) {
for (var cc = 0; cc < BOARD_COLS; cc++) {
board[rr][cc] = board[rr - 1][cc];