oneshotlm
Compare all models on this prompt →

Tetris · deepseek-r1

Build a playable Tetris on a canvas with all seven tetrominoes, rotation, line clears, increasing speed, a next-piece preview, and score.

⚠ no runnable output

This one-shot run produced no runnable artifact.

agent produced no index.html

The transcript below shows what the model did — read it to see why the one shot fell short.

transcript · 2 messages
user

Build a playable Tetris on a canvas with all seven tetrominoes, rotation, line clears, increasing speed, a next-piece preview, and score.

assistant
thinking
We are going to build a Tetris game in a single HTML file (index.html)
 The game will include:
   - A canvas for drawing the game board
   - All seven tetrominoes (shapes: I, O, T, S, Z, J, L)
   - Rotation of pieces
   - Line clearing when a row is filled
   - Increasing speed as the game progresses (level up)
   - Next-piece preview
   - Score display

 Steps:
 1. We will create the index.html file with a basic structure.
 2. Inside, we'll have a <canvas> element for the main game board and another for the next piece preview.
 3. We'll write the CSS for layout and styling.
 4. We'll write the JavaScript for the game logic.

 Important considerations:
  - The game board: a grid of 10 columns and 20 rows (visible rows, plus a hidden top for spawning).
  - Each block is a square. Let's set the block size to 30 pixels.
  - The main canvas: width = 10 * 30 = 300, height = 20 * 30 = 600.
  - Next piece preview: a smaller canvas, maybe 4x4 blocks -> 120x120 pixels.

 Implementation plan:

 1. Define the tetromino shapes as arrays of their relative positions.
 2. Create a game board array (2D) to track which cells are occupied and by which color.
 3. Implement the game loop (using requestAnimationFrame) that drops the current piece at intervals.
 4. Handle user input for moving and rotating the piece.
 5. Check for completed lines and clear them, updating the score.
 6. Increase the speed (decrease the drop interval) every N lines cleared.
 7. Display the next piece in the preview canvas.
 8. Display the current score and level.

 We'll write the entire content to index.html.

 Let's break down the JavaScript:

  - Constants: block size, board width/height, etc.
  - Variables: current piece, next piece, game board, score, level, lines, drop interval, etc.
  - Functions:
      * init(): initialize the game state.
      * createPiece(): create a new tetromino of a given type (randomly selected if not specified).
      * drawBlock(ctx, x, y, color): draw a block at (x,y) with color.
      * drawBoard(): draw the entire board including the current piece and the grid.
      * drawNextPiece(): draw the next piece in the preview canvas.
      * movePiece(dx, dy): move the current piece if possible.
      * rotatePiece(): rotate the current piece if possible (with wall kicks).
      * checkCollision(): check if the current piece collides with the board boundaries or other blocks.
      * lockPiece(): lock the current piece to the board and check for line clears.
      * clearLines(): clear completed lines and update the board.
      * drop(): move the piece down one step (called by the game loop).
      * gameLoop(): the main game loop that handles dropping and rendering.
      * updateScore(): update the score display.
      * gameOver(): handle game over.

 We'll also set up keyboard event listeners.

 Since we are writing everything in one file, we'll put the CSS in a <style> tag and the JavaScript in a <script> tag.

 Let's write the content for index.html.

I'll create a self-contained Tetris game in a single HTML file. Here's the implementation: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Tetris</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c); color: white; overflow: hidden; } .game-container { display: flex; gap: 20px; padding: 20px; background: rgba(0, 0, 0, 0.7); border-radius: 10px; box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); } #game-board { border: 2px solid #3498db; background-color: #111; } .side-panel { display: flex; flex-direction: column; gap: 20px; width: 150px; } .panel-section { background: rgba(0, 0, 0, 0.5); padding: 15px; border-radius: 8px; border: 1px solid #3498db; } h2 { margin-top: 0; color: #3498db; text-align: center; font-size: 1.2em; } #next-piece { border: 2px solid #3498db; background-color: #111; margin: 0 auto; } #score, #level, #lines { font-size: 1.5em; text-align: center; margin: 10px 0; } .controls { margin-top: 20px; } button { width: 100%; padding: 10px; margin-bottom: 10px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; transition: background 0.3s; } button:hover { background: #2980b9; } .instructions { font-size: 0.9em; line-height: 1.4; } .game-over { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.85); display: flex; flex-direction: column; justify-content: center; align-items: center; z-index: 10; display: none; } .game-over h1 { font-size: 3em; color: #e74c3c; margin-bottom: 20px; } </style> </head> <body> <div class="game-container"> <canvas id="game-board" width="300" height="600"></canvas> <div class="side-panel"> <div class="panel-section"> <h2>Next Piece</h2> <canvas id="next-piece" width="120" height="120"></canvas> </div> <div class="panel-section"> <h2>Stats</h2> <div id="score">Score: 0</div> <div id="level">Level: 1</div> <div id="lines">Lines: 0</div> </div> <div class="panel-section controls"> <button id="start-btn">Start Game</button> <button id="pause-btn">Pause</button> <button id="reset-btn">Reset</button> </div> <div class="panel-section instructions"> <h2>Controls</h2> <p>← → : Move</p> <p>↑ : Rotate</p> <p>↓ : Soft Drop</p> <p>Space : Hard Drop</p> </div> </div> </div> <div class="game-over" id="game-over"> <h1>GAME OVER</h1> <button id="restart-btn">Play Again</button> </div> <script> // Game constants const COLS = 10; const ROWS = 20; const BLOCK_SIZE = 30; const COLORS = [ null, '#FF0D72', // I '#0DC2FF', // J '#0DFF72', // L '#F538FF', // O '#FF8E0D', // S '#FFE138', // T '#3877FF' // Z ]; // Tetromino shapes const SHAPES = [ [], [[0,0], [0,1], [0,2], [0,3]], // I [[0,0], [0,1], [0,2], [1,2]], // J [[0,0], [0,1], [0,2], [1,0]], // L [[0,0], [0,1], [1,0], [1,1]], // O [[0,1], [0,2], [1,0], [1,1]], // S [[0,1], [1,0], [1,1], [1,2]], // T [[0,0], [0,1], [1,1], [1,2]] // Z ]; // Game variables let canvas = document.getElementById('game-board'); let ctx = canvas.getContext('2d'); let nextCanvas = document.getElementById('next-piece'); let nextCtx = nextCanvas.getContext('2d'); let scoreDisplay = document.getElementById('score'); let levelDisplay = document.getElementById('level'); let linesDisplay = document.getElementById('lines'); let startBtn = document.getElementById('start-btn'); let pauseBtn = document.getElementById('pause-btn'); let resetBtn = document.getElementById('reset-btn'); let restartBtn = document.getElementById('restart-btn'); let gameOverScreen = document.getElementById('game-over'); let board = []; let currentPiece = null; let nextPiece = null; let score = 0; let level = 1; let lines = 0; let gameInterval = null; let dropSpeed = 1000; // ms let isPaused = false; let isGameOver = false; // Initialize game board function createBoard() { return Array.from({length: ROWS}, () => Array(COLS).fill(0)); } // Create a new tetromino function createPiece() { const typeId = Math.floor(Math.random() * 7) + 1; return { typeId: typeId, shape: SHAPES[typeId], x: Math.floor(COLS / 2) - 2, y: 0, color: COLORS[typeId] }; } // Draw a single block function drawBlock(ctx, x, y, color) { ctx.fillStyle = color; ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.strokeRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); // Add 3D effect ctx.fillStyle = 'rgba(255, 255, 255, 0.2)'; ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, 4); ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, 4, BLOCK_SIZE); ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE + BLOCK_SIZE - 4, BLOCK_SIZE, 4); ctx.fillRect(x * BLOCK_SIZE + BLOCK_SIZE - 4, y * BLOCK_SIZE, 4, BLOCK_SIZE); } // Draw the game board function drawBoard() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw grid ctx.strokeStyle = '#333'; ctx.lineWidth = 1; for (let r = 0; r < ROWS; r++) { for (let c = 0; c < COLS; c++) { ctx.strokeRect(c * BLOCK_SIZE, r * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); } } // Draw blocks for (let r = 0; r < ROWS; r++) { for (let c = 0; c < COLS; c++) { if (board[r][c]) { drawBlock(ctx, c, r, COLORS[board[r][c]]); } } } // Draw current piece if (currentPiece) { currentPiece.shape.forEach(([dx, dy]) => { const x = currentPiece.x + dx; const y = currentPiece.y + dy; if (y >= 0) { // Only draw if within visible area drawBlock(ctx, x, y, currentPiece.color); } }); } } // Draw next piece preview function drawNextPiece() { nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height); if (nextPiece) { // Center the piece in the preview canvas const offsetX = 2; const offsetY = 2; nextPiece.shape.forEach(([dx, dy]) => { drawBlock(nextCtx, dx + offsetX, dy + offsetY, nextPiece.color); }); } } // Move piece horizontally function movePiece(direction) { if (!currentPiece || isPaused || isGameOver) return; currentPiece.x += direction; if (checkCollision()) { currentPiece.x -= direction; // Revert if collision } else { drawBoard(); } } // Rotate piece function rotatePiece() { if (!currentPiece || isPaused || isGameOver) return; const originalShape = currentPiece.shape; // Rotate clockwise: (x, y) => (-y, x) currentPiece.shape = currentPiece.shape.map(([x, y]) => [-y, x]); // Wall kick: try moving left/right if rotation causes collision if (checkCollision()) { const kicks = [ {x: -1, y: 0}, {x: 1, y: 0}, {x: 0, y: -1} ]; for (const kick of kicks) { currentPiece.x += kick.x; currentPiece.y += kick.y; if (!checkCollision()) break; currentPiece.x -= kick.x; currentPiece.y -= kick.y; } // If still collision, revert rotation if (checkCollision()) { currentPiece.shape = originalShape; } } drawBoard(); } // Drop piece down function dropPiece() { if (!currentPiece || isPaused || isGameOver) return; currentPiece.y++; if (checkCollision()) { currentPiece.y--; lockPiece(); clearLines(); spawnPiece(); } drawBoard(); } // Hard drop (drop to bottom) function hardDrop() { if (!currentPiece || isPaused || isGameOver) return; while (!checkCollision()) { currentPiece.y++; } currentPiece.y--; lockPiece(); clearLines(); spawnPiece(); drawBoard(); } // Check for collisions function checkCollision() { return currentPiece.shape.some(([dx, dy]) => { const x = currentPiece.x + dx; const y = currentPiece.y + dy; return ( x < 0 || x >= COLS || y >= ROWS || (y >= 0 && board[y][x] !== 0) ); }); } // Lock piece on board function lockPiece() { currentPiece.shape.forEach(([dx, dy]) => { const x = currentPiece.x + dx; const y = currentPiece.y + dy; if (y >= 0) { board[y][x] = currentPiece.typeId; } }); } // Clear completed lines function clearLines() { let linesCleared = 0; for (let r = ROWS - 1; r >= 0; r--) { if (board[r].every(cell => cell !== 0)) { // Remove line board.splice(r, 1); // Add new empty line at top board.unshift(Array(COLS).fill(0)); linesCleared++; r++; // Recheck same row index } } if (linesCleared > 0) { // Update score const points = [0, 40, 100, 300, 1200]; // Points for 0-4 lines score += points[linesCleared] * level; // Update lines lines += linesCleared; // Level up every 10 lines level = Math.floor(lines / 10) + 1; // Increase speed dropSpeed = Math.max(100, 1000 - (level - 1) * 50); // Update UI updateStats(); // Reset interval with new speed if (gameInterval) { clearInterval(gameInterval); gameInterval = setInterval(dropPiece, dropSpeed); } } } // Spawn new piece function spawnPiece() { currentPiece = nextPiece || createPiece(); nextPiece = createPiece(); // Check for game over if (checkCollision()) { gameOver(); return; } drawBoard(); drawNextPiece(); } // Update stats display function updateStats() { scoreDisplay.textContent = `Score: ${score}`; levelDisplay.textContent = `Level: ${level}`; linesDisplay.textContent = `Lines: ${lines}`; } // Game over function gameOver() { isGameOver = true; clearInterval(gameInterval); gameOverScreen.style.display = 'flex'; } // Reset game function resetGame() { board = createBoard(); score = 0; level = 1; lines = 0; dropSpeed = 1000; isPaused = false; isGameOver = false; pauseBtn.textContent = 'Pause'; gameOverScreen.style.display = 'none'; nextPiece = createPiece(); spawnPiece(); updateStats(); if (gameInterval) clearInterval(gameInterval); gameInterval = setInterval(dropPiece, dropSpeed); } // Initialize game function init() { resetGame(); // Event listeners document.addEventListener('keydown', e => { if (isGameOver) return; switch (e.key) { case 'ArrowLeft': movePiece(-1); break; case 'ArrowRight': movePiece(1); break; case 'ArrowUp': rotatePiece(); break; case 'ArrowDown': dropPiece(); break; case ' ': hardDrop(); break; case 'p': togglePause(); break; } }); startBtn.addEventListener('click', () => { if (isGameOver) resetGame(); else if (isPaused) togglePause(); }); pauseBtn.addEventListener('click', togglePause); resetBtn.addEventListener('click', resetGame); restartBtn.addEventListener('click', resetGame); } // Toggle pause function togglePause() { if (isGameOver) return; isPaused = !isPaused; pauseBtn.textContent = isPaused ? 'Resume' : 'Pause'; if (isPaused) { clearInterval(gameInterval); } else { gameInterval = setInterval(dropPiece, dropSpeed); } } // Start the game window.onload = init; </script> </body> </html> ``` This implementation includes all requested features: 1. **Canvas-based rendering** for the game board 2. **All seven tetrominoes** with distinct colors 3. **Piece rotation** with wall kick mechanics 4. **Line clearing** with scoring 5. **Increasing speed** as levels progress 6. **Next-piece preview** display 7. **Score tracking** with level progression 8. **Game controls**: - Arrow keys for movement and rotation - Spacebar for hard drop - Start/Pause/Reset buttons The game is completely self-contained in a single HTML file with no external dependencies. It features a responsive design with a polished UI, including: - Gradient background - Game stats panel - Next-piece preview - Game controls - Game over screen - Visual effects for blocks To play: 1. Press "Start Game" 2. Use arrow keys to move and rotate pieces 3. Spacebar drops the piece immediately 4. Clear lines to earn points and level up 5. Game ends when pieces stack to the top The game automatically increases speed as you level up, making it progressively more challenging.

TAKE IT TO CODE

This run produced no artifact — reuse the prompt with a different model or your own pi-agent run.

RUN

modeldeepseek-r1
vendorDeepSeek
harnesspi-agent
generated2026-08-01

METRICS

runtime200.8s
tokens in1.9k
tokens out4.8k
cached1.8k
cost / run$0.0121