sugarcraft / candy-tetris
Tetris clone built on the SugarCraft stack — SugarCraft runtime, CandySprinkles styling. Standard SRS rules (7-bag RNG, ghost piece, hard drop, hold, level/speed curve, line-clear scoring). A real demo of the runtime, not a toy.
Requires
- php: >=8.3
- sugarcraft/candy-buffer: dev-master
- sugarcraft/candy-core: dev-master
- sugarcraft/candy-sprinkles: dev-master
Requires (Dev)
- phpunit/phpunit: ^10.0
- sugarcraft/candy-testing: dev-master
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-03 15:32:37 UTC
README
CandyTetris
Tetris built on the SugarCraft stack. SugarCraft runtime, CandySprinkles for the rounded borders and per-piece colours, deterministic 7-bag RNG, ghost piece, hard drop, hold, level-driven gravity ramp, line-clear scoring.
Run it
composer install ./bin/tetris # Single player mode ./bin/tetris -v # VS Computer mode ./bin/tetris --vs # VS Computer mode (long form)
VS Computer Mode
Compete against an AI opponent in split-screen mode. When you clear lines, garbage rows are sent to the computer. When the computer clears lines, garbage rows are sent to you. Last player standing wins!
VS Mode Controls
| Key | Action |
|---|---|
| ← / → | Move left / right |
| ↑ / x | Rotate clockwise |
| z | Rotate counter-cw |
| ↓ | Soft drop |
| Space | Hard drop |
| p | Pause / resume |
| q | Quit |
The computer opponent uses weighted heuristics (board height, holes, gaps, lines) to make optimal moves.
Controls
| Key | Action |
|---|---|
| ← / → | Move left / right |
| ↑ / x | Rotate clockwise |
| z | Rotate counter-cw |
| ↓ | Soft drop |
| Space | Hard drop |
| c | Hold piece |
| p | Pause / resume |
| q | Quit |
Hold
Press c to stash the falling piece into the hold slot; press it again on the next piece to swap it back in. You can only hold once per drop — the slot dims until the current piece locks.
Lock delay
A grounded piece does not lock instantly — you get a real-time window
(~500 ms by default, independent of the gravity speed, so it stays a constant
half-second even at level 29) to slide or rotate it before it settles. The
binary arms it via Game::startWithLockDelay(). Every successful move or
rotation while grounded re-arms the full window, up to the SRS-style 15-reset
cap — once the budget is spent the open window locks on expiry and further
inputs no longer extend it. Moving the piece back off a surface closes the
window and hands timing back to gravity; a hard drop bypasses it and locks
immediately. Pausing extends the window instead of locking a frozen game.
Scoring
Line-clear scoring
| Lines cleared | Base points × (level + 1) |
|---|---|
| 1 | 40 |
| 2 | 100 |
| 3 | 300 |
| 4 (Tetris) | 1200 |
Level rises every 10 lines. Gravity speeds up at every level — by level 9 pieces fall every 6 frames, by level 29+ they fall every frame. The frame-rate-agnostic Score::framesPerRow() is what the gravity tick consults.
T-Spin scoring
When the last successful input before a T piece locks was a rotation (moves, drops and the fall itself reset that flag — settling under gravity keeps it) and two or more of the piece's four diagonal corner cells are already filled (walls count as filled), it scores as a T-Spin:
| Type | Base points |
|---|---|
| T-Spin | 400 × (level + 1) |
| T-Spin Mini | 100 × (level + 1) |
T-Spin Mini is detected when exactly two front corners are filled (the side the piece entered from).
Last-input rule: A T-Spin is active when the piece's last successful action
before locking was a rotation (Game::$lastActionWasRotation, set by rotate,
cleared by move/soft drop/hard drop — gravity steps leave it alone, so a
rotate-then-settle spin is legal), AND at least two of the four diagonal corner
cells around the T are occupied (wall/out-of-bounds = occupied). With exactly
two filled corners and they are the front pair, it scores as a mini. See
Scoring\TSpin::detect().
Back-to-Back (B2B) bonus
Consecutive Tetris clears or full T-Spins (non-mini) carry a 1.5× multiplier on the line-clear base points:
- 4-line Tetris after a prior Tetris → 1200 × 1.5 × (level + 1)
- Full T-Spin after a prior full T-Spin → 400 × 1.5 × (level + 1)
B2B resets when a 1–3 line clear or a T-Spin Mini breaks the streak.
Combo bonus
Consecutive line clears (regardless of type) build a combo counter — each combo step adds combo × 10 × (level + 1) bonus points. The counter resets to 0 on any clean (zero-line) piece placement.
Perfect clear bonus
When all lines are cleared at once and the board becomes completely empty, an additional +5000 × (level + 1) bonus is awarded.
Shared foundations
The single-player playfield is painted into a canonical candy-buffer Buffer cell grid (10×20 visible); per-tetromino background style and ghost-piece foreground style are applied per-cell. The hold/next/score sidebar and the VS split-screen panels are CandySprinkles cards joined with CandyLayout (Layout::joinVertical / Layout::joinHorizontal), composited alongside the buffered playfield. Snapshot tests via candy-testing pin canonical game states (starting board, T-spin completion, pause overlay).


