Tetris · deepseek-v4-flash
Build a playable Tetris on a canvas with all seven tetrominoes, rotation, line clears, increasing speed, a next-piece preview, and score.
transcript · 6 messages
Build a playable Tetris on a canvas with all seven tetrominoes, rotation, line clears, increasing speed, a next-piece preview, and score.
thinking
The user wants me to build a complete Tetris game in a single HTML file with a canvas element. Let me plan out all the features: 1. All 7 tetrominoes (I, O, T, S, Z, J, L) 2. Rotation (with wall kicks) 3. Line clears 4. Increasing speed 5. Next piece preview 6. Score display 7. Canvas-based rendering Let me build this comprehensively.
/workspace/index.html (13706 bytes)Successfully wrote 13706 bytes to /workspace/index.html
thinking
Let me verify the file was written correctly and check for any issues.
Let me verify the file is present and well-formed:
{"command":"wc -l /workspace/index.html && head -5 /workspace/index.html && echo \"---\" && tail -5 /workspace/index.html"}453 /workspace/index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> --- resetGame(); })(); </script> </body> </html>
thinking
The file is 453 lines and looks well-formed. Let me double-check the game logic once more quickly by reviewing the key parts to make sure everything is correct.
Actually, let me also check if there's a subtle issue: when the game starts, `resetGame()` calls `spawnNext()`, which sets `current` and `next`. But `spawnNext()` checks `if (!next)` - on the first call, `next` is null, so it assigns `next = randomPiece()`. Then it sets `current = spawnPiece(next)` and `next = randomPiece()`. That's correct.
Wait, there's a bug in `spawnNext()`: the first time it's called, `next` is null, so it sets `next = randomPiece()`. Then `current = spawnPiece(next)` and `next = randomPiece()`. That's fine - current gets a piece, next gets a new random piece.
But wait, let me re-read:
```
function spawnNext() {
if (!next) {
next = randomPiece();
}
current = spawnPiece(next);
next = randomPiece();
...
}
```
First call: next is null, so next = randomPiece(). Then current = spawnPiece(next). Then next = randomPiece(). Good.
Second call: next is not null (it was set in the previous call). So we skip the if. current = spawnPiece(next). Then next = randomPiece(). Good.
But actually, wait. In `resetGame()`, we call `spawnNext()`. But we also set `next = null` before that. So on the first call:
- next is null, so next = randomPiece() (first piece)
- current = spawnPiece(next) (current gets the piece)
- next = randomPiece() (next gets a new piece)
That's correct.
In `lockPiece()`, we call `spawnNext()` again. At that point, next has a piece from the previous call. So:
- next is not null, skip the if
- current = spawnPiece(next) (current gets the waiting piece)
- next = randomPiece() (next gets a new piece)
That's correct! The bag randomizer works well.
One thing I notice: I should also check that the animation loop keeps running. The `render()` function calls `requestAnimationFrame(render)` at the end, so it will keep running once started. The `resetGame()` function calls `render()` only if `animationId` is falsy, which it will be on the first call. After that, the loop continues.
Let me also think about the `valid` function for the ghost piece - when the current piece is at the top and some rows are above the board (y < 0), the `isValid` function checks `if (by < 0) continue;` which is fine for collision detection.
The game looks correct. Let me also verify the file is complete by reading it.
Actually, I think the file is good. The game has all the requested features:
- All 7 tetrominoes ✓
- Rotation (with wall kicks for I piece and others) ✓
- Line clears ✓
- Increasing speed (level up every 10 lines, drop interval decreases) ✓
- Next-piece preview ✓
- Score (100/300/500/800 points for 1-4 lines, multiplied by level, +2 per hard drop row) ✓
- Ghost piece ✓
- Bag randomizer for fair piece distribution ✓
- Game over detection ✓
- Restart button ✓
I'm satisfied with the implementation.The file is complete at 453 lines. Here's what's implemented: **Tetris — all in one `index.html` file** | Feature | Details | |---|---| | **7 tetrominoes** | I, O, T, S, Z, J, L — each with distinct colors | | **Rotation** | 90° CW with wall kicks (8 kick offsets, including I-piece adjustments) | | **Line clears** | 1–4 lines at once with scoring: 100/300/500/800 × level | | **Increasing speed** | Level up every 10 lines; drop interval goes from 1000ms → min 100ms | | **Next-piece preview** | 120×120 canvas showing the upcoming piece | | **Score** | Displayed alongside level and lines cleared | | **Ghost piece** | Translucent outline showing where the piece will land | | **Hard drop** | Spacebar — locks instantly, +2 pts per row dropped | | **Soft drop** | ↓ key — move down faster | | **Bag randomizer** | 7-bag system ensures fair piece distribution | | **Game over** | Detected when a new piece can't spawn; overlay + restart button | | **Controls** | ← → ↓ ↑ Space, with on-screen key legend | To play, open `index.html` in any browser — no network or dependencies needed.
TAKE IT TO CODE
A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.
RUN
EVALUATION
Functional Tetris with score, level, lines, next-piece preview; pieces fall and respond to keyboard controls.
Vision grade · anthropic/claude-sonnet-5
MEASUREMENTS
Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.