dreamstack/examples/game-tetris.ds

412 lines
38 KiB
Text
Raw Normal View History

-- DreamStack Tetris
-- Blocks fall. They collide. They freeze. Lines clear.
--
-- Run:
-- Tab 1: cargo run -p ds-stream (relay)
-- Tab 2: dreamstack dev examples/game-tetris.ds (player)
import { Badge } from "../registry/components/badge"
-- ================================================================
-- GRID: flat 200-element array (20 rows × 10 cols)
-- grid[row * 10 + col] = 0 (empty) or 1-7 (piece color)
-- ================================================================
let grid = [0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0]
-- Active piece: 1=I, 2=J, 3=L, 4=O, 5=S, 6=T, 7=Z
let piece = 6
let rotation = 0
let px = 3
let py = 0
let nextPiece = 1
-- Game state
let score = 0
let lines = 0
let level = 1
let gameOver = 0
let paused = 0
let gravityTick = 0
let dropInterval = 24
let lockTick = 0
let clearDelay = 0
let ghostY = 0
let showGhost = 1
-- ================================================================
-- PIECE SHAPES: array lookup — sd[cell][x/y][shapeIndex]
-- Index = (piece - 1) * 4 + rotation (28 entries, pieces 1-7 × rot 0-3)
-- SRS standard rotations for all 7 tetrominoes
-- ================================================================
-- Cell 0 offsets (dx, dy)
let sd0x = [0, 2, 0, 1, 0, 1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 2, 0, 1]
let sd0y = [1, 0, 2, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0]
-- Cell 1 offsets
let sd1x = [1, 2, 1, 1, 0, 2, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 0, 0, 1, 1, 0, 1, 1, 1, 0]
let sd1y = [1, 1, 2, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1]
-- Cell 2 offsets
let sd2x = [2, 2, 2, 1, 1, 1, 2, 0, 1, 1, 2, 1, 0, 0, 0, 0, 0, 2, 0, 1, 1, 2, 2, 1, 1, 2, 1, 1]
let sd2y = [1, 2, 2, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1]
-- Cell 3 offsets
let sd3x = [3, 2, 3, 1, 2, 1, 2, 1, 2, 2, 0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 0]
let sd3y = [1, 3, 2, 3, 1, 2, 2, 2, 1, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2]
-- Color arrays for piece-type rendering (index 0=empty, 1-7=piece)
let colorL = ["#171717", "#22d3ee", "#60a5fa", "#fb923c", "#facc15", "#4ade80", "#c084fc", "#f87171"]
let colorD = ["#171717", "#06b6d4", "#3b82f6", "#f97316", "#eab308", "#22c55e", "#a855f7", "#ef4444"]
-- ================================================================
-- DERIVED: cell offsets from shape arrays
-- ================================================================
let si = 0
let c0dx = 0
let c0dy = 0
let c1dx = 0
let c1dy = 0
let c2dx = 0
let c2dy = 0
let c3dx = 0
let c3dy = 0
every 33 -> si = (piece - 1) * 4 + rotation
every 33 -> c0dx = sd0x[si]
every 33 -> c0dy = sd0y[si]
every 33 -> c1dx = sd1x[si]
every 33 -> c1dy = sd1y[si]
every 33 -> c2dx = sd2x[si]
every 33 -> c2dy = sd2y[si]
every 33 -> c3dx = sd3x[si]
every 33 -> c3dy = sd3y[si]
-- ================================================================
-- COLLISION: can the piece move down?
-- Check floor bounds + grid occupancy for all 4 cells
-- ================================================================
let blocked = 0
every 33 -> blocked = if py + c0dy + 1 >= 20 then 1 else (if py + c1dy + 1 >= 20 then 1 else (if py + c2dy + 1 >= 20 then 1 else (if py + c3dy + 1 >= 20 then 1 else (if grid[(py + c0dy + 1) * 10 + px + c0dx] > 0 then 1 else (if grid[(py + c1dy + 1) * 10 + px + c1dx] > 0 then 1 else (if grid[(py + c2dy + 1) * 10 + px + c2dx] > 0 then 1 else (if grid[(py + c3dy + 1) * 10 + px + c3dx] > 0 then 1 else 0)))))))
-- Left collision: wall + grid
let leftOk = 0
every 33 -> leftOk = if px + c0dx - 1 < 0 then 0 else (if px + c1dx - 1 < 0 then 0 else (if px + c2dx - 1 < 0 then 0 else (if px + c3dx - 1 < 0 then 0 else (if grid[(py + c0dy) * 10 + px + c0dx - 1] > 0 then 0 else (if grid[(py + c1dy) * 10 + px + c1dx - 1] > 0 then 0 else (if grid[(py + c2dy) * 10 + px + c2dx - 1] > 0 then 0 else (if grid[(py + c3dy) * 10 + px + c3dx - 1] > 0 then 0 else 1)))))))
-- Right collision: wall + grid
let rightOk = 0
every 33 -> rightOk = if px + c0dx + 1 >= 10 then 0 else (if px + c1dx + 1 >= 10 then 0 else (if px + c2dx + 1 >= 10 then 0 else (if px + c3dx + 1 >= 10 then 0 else (if grid[(py + c0dy) * 10 + px + c0dx + 1] > 0 then 0 else (if grid[(py + c1dy) * 10 + px + c1dx + 1] > 0 then 0 else (if grid[(py + c2dy) * 10 + px + c2dx + 1] > 0 then 0 else (if grid[(py + c3dy) * 10 + px + c3dx + 1] > 0 then 0 else 1)))))))
-- Rotation collision: check if next rotation fits
let nrot = 0
let nsi = 0
let nc0dx = 0
let nc0dy = 0
let nc1dx = 0
let nc1dy = 0
let nc2dx = 0
let nc2dy = 0
let nc3dx = 0
let nc3dy = 0
let rotOk = 0
every 33 -> nrot = (rotation + 1) % 4
every 33 -> nsi = (piece - 1) * 4 + nrot
every 33 -> nc0dx = sd0x[nsi]
every 33 -> nc0dy = sd0y[nsi]
every 33 -> nc1dx = sd1x[nsi]
every 33 -> nc1dy = sd1y[nsi]
every 33 -> nc2dx = sd2x[nsi]
every 33 -> nc2dy = sd2y[nsi]
every 33 -> nc3dx = sd3x[nsi]
every 33 -> nc3dy = sd3y[nsi]
every 33 -> rotOk = if px + nc0dx < 0 then 0 else (if px + nc0dx >= 10 then 0 else (if py + nc0dy >= 20 then 0 else (if px + nc1dx < 0 then 0 else (if px + nc1dx >= 10 then 0 else (if py + nc1dy >= 20 then 0 else (if px + nc2dx < 0 then 0 else (if px + nc2dx >= 10 then 0 else (if py + nc2dy >= 20 then 0 else (if px + nc3dx < 0 then 0 else (if px + nc3dx >= 10 then 0 else (if py + nc3dy >= 20 then 0 else (if grid[(py + nc0dy) * 10 + px + nc0dx] > 0 then 0 else (if grid[(py + nc1dy) * 10 + px + nc1dx] > 0 then 0 else (if grid[(py + nc2dy) * 10 + px + nc2dx] > 0 then 0 else (if grid[(py + nc3dy) * 10 + px + nc3dx] > 0 then 0 else 1)))))))))))))))
-- ================================================================
-- GHOST: compute landing position (try to push ghostY down 19 times)
-- ================================================================
every 33 -> ghostY = py
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
every 33 -> ghostY = if ghostY + c0dy + 1 >= 20 then ghostY else (if ghostY + c1dy + 1 >= 20 then ghostY else (if ghostY + c2dy + 1 >= 20 then ghostY else (if ghostY + c3dy + 1 >= 20 then ghostY else (if grid[(ghostY + c0dy + 1) * 10 + px + c0dx] > 0 then ghostY else (if grid[(ghostY + c1dy + 1) * 10 + px + c1dx] > 0 then ghostY else (if grid[(ghostY + c2dy + 1) * 10 + px + c2dx] > 0 then ghostY else (if grid[(ghostY + c3dy + 1) * 10 + px + c3dx] > 0 then ghostY else ghostY + 1)))))))
-- ================================================================
-- INPUT: keyboard controls
-- ================================================================
on keydown(ev) -> px = if gameOver then px else (if paused then px else (if ev.key == "ArrowLeft" then (if leftOk then px - 1 else px) else px))
on keydown(ev) -> px = if gameOver then px else (if paused then px else (if ev.key == "ArrowRight" then (if rightOk then px + 1 else px) else px))
on keydown(ev) -> rotation = if gameOver then rotation else (if paused then rotation else (if ev.key == "ArrowUp" then (if rotOk then nrot else rotation) else rotation))
on keydown(ev) -> py = if gameOver then py else (if paused then py else (if ev.key == "ArrowDown" then (if blocked then py else py + 1) else (if ev.key == " " then ghostY else py)))
on keydown(ev) -> paused = if ev.key == "p" then (if paused then 0 else 1) else paused
on keydown(ev) -> showGhost = if ev.key == "g" then (if showGhost then 0 else 1) else showGhost
-- ================================================================
-- PHYSICS: gravity pulls pieces down, contact freezes them
-- ================================================================
-- Gravity tick
every 33 -> gravityTick = if paused then gravityTick else (if gameOver then gravityTick else gravityTick + 1)
-- Auto-drop: gravity pulls piece down
every 33 -> py = if paused then py else (if gameOver then py else (if blocked then py else (if gravityTick > dropInterval then py + 1 else py)))
every 33 -> gravityTick = if gravityTick > dropInterval then 0 else gravityTick
-- Lock: when blocked (contact!), count frames then freeze
every 33 -> lockTick = if blocked then lockTick + 1 else 0
-- ================================================================
-- FREEZE: on contact, write piece cells into grid
-- ================================================================
every 33 -> grid[(py + c0dy) * 10 + px + c0dx] = if lockTick == 3 then piece else grid[(py + c0dy) * 10 + px + c0dx]
every 33 -> grid[(py + c1dy) * 10 + px + c1dx] = if lockTick == 3 then piece else grid[(py + c1dy) * 10 + px + c1dx]
every 33 -> grid[(py + c2dy) * 10 + px + c2dx] = if lockTick == 3 then piece else grid[(py + c2dy) * 10 + px + c2dx]
every 33 -> grid[(py + c3dy) * 10 + px + c3dx] = if lockTick == 3 then piece else grid[(py + c3dy) * 10 + px + c3dx]
-- Spawn new piece after freeze
every 33 -> piece = if lockTick == 3 then nextPiece else piece
every 33 -> nextPiece = if lockTick == 3 then (gravityTick % 7 + 1) else nextPiece
every 33 -> py = if lockTick == 3 then 0 else py
every 33 -> px = if lockTick == 3 then 3 else px
every 33 -> rotation = if lockTick == 3 then 0 else rotation
every 33 -> score = if lockTick == 3 then score + 10 else score
-- ================================================================
-- LINE CLEAR: detect full rows, cascade with slice/concat
-- ================================================================
-- Row fullness: check all 10 cells per row
let f0 = 0
let f1 = 0
let f2 = 0
let f3 = 0
let f4 = 0
let f5 = 0
let f6 = 0
let f7 = 0
let f8 = 0
let f9 = 0
let f10 = 0
let f11 = 0
let f12 = 0
let f13 = 0
let f14 = 0
let f15 = 0
let f16 = 0
let f17 = 0
let f18 = 0
let f19 = 0
every 33 -> f0 = if grid[0] > 0 then (if grid[1] > 0 then (if grid[2] > 0 then (if grid[3] > 0 then (if grid[4] > 0 then (if grid[5] > 0 then (if grid[6] > 0 then (if grid[7] > 0 then (if grid[8] > 0 then (if grid[9] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f1 = if grid[10] > 0 then (if grid[11] > 0 then (if grid[12] > 0 then (if grid[13] > 0 then (if grid[14] > 0 then (if grid[15] > 0 then (if grid[16] > 0 then (if grid[17] > 0 then (if grid[18] > 0 then (if grid[19] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f2 = if grid[20] > 0 then (if grid[21] > 0 then (if grid[22] > 0 then (if grid[23] > 0 then (if grid[24] > 0 then (if grid[25] > 0 then (if grid[26] > 0 then (if grid[27] > 0 then (if grid[28] > 0 then (if grid[29] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f3 = if grid[30] > 0 then (if grid[31] > 0 then (if grid[32] > 0 then (if grid[33] > 0 then (if grid[34] > 0 then (if grid[35] > 0 then (if grid[36] > 0 then (if grid[37] > 0 then (if grid[38] > 0 then (if grid[39] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f4 = if grid[40] > 0 then (if grid[41] > 0 then (if grid[42] > 0 then (if grid[43] > 0 then (if grid[44] > 0 then (if grid[45] > 0 then (if grid[46] > 0 then (if grid[47] > 0 then (if grid[48] > 0 then (if grid[49] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f5 = if grid[50] > 0 then (if grid[51] > 0 then (if grid[52] > 0 then (if grid[53] > 0 then (if grid[54] > 0 then (if grid[55] > 0 then (if grid[56] > 0 then (if grid[57] > 0 then (if grid[58] > 0 then (if grid[59] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f6 = if grid[60] > 0 then (if grid[61] > 0 then (if grid[62] > 0 then (if grid[63] > 0 then (if grid[64] > 0 then (if grid[65] > 0 then (if grid[66] > 0 then (if grid[67] > 0 then (if grid[68] > 0 then (if grid[69] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f7 = if grid[70] > 0 then (if grid[71] > 0 then (if grid[72] > 0 then (if grid[73] > 0 then (if grid[74] > 0 then (if grid[75] > 0 then (if grid[76] > 0 then (if grid[77] > 0 then (if grid[78] > 0 then (if grid[79] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f8 = if grid[80] > 0 then (if grid[81] > 0 then (if grid[82] > 0 then (if grid[83] > 0 then (if grid[84] > 0 then (if grid[85] > 0 then (if grid[86] > 0 then (if grid[87] > 0 then (if grid[88] > 0 then (if grid[89] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f9 = if grid[90] > 0 then (if grid[91] > 0 then (if grid[92] > 0 then (if grid[93] > 0 then (if grid[94] > 0 then (if grid[95] > 0 then (if grid[96] > 0 then (if grid[97] > 0 then (if grid[98] > 0 then (if grid[99] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f10 = if grid[100] > 0 then (if grid[101] > 0 then (if grid[102] > 0 then (if grid[103] > 0 then (if grid[104] > 0 then (if grid[105] > 0 then (if grid[106] > 0 then (if grid[107] > 0 then (if grid[108] > 0 then (if grid[109] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f11 = if grid[110] > 0 then (if grid[111] > 0 then (if grid[112] > 0 then (if grid[113] > 0 then (if grid[114] > 0 then (if grid[115] > 0 then (if grid[116] > 0 then (if grid[117] > 0 then (if grid[118] > 0 then (if grid[119] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f12 = if grid[120] > 0 then (if grid[121] > 0 then (if grid[122] > 0 then (if grid[123] > 0 then (if grid[124] > 0 then (if grid[125] > 0 then (if grid[126] > 0 then (if grid[127] > 0 then (if grid[128] > 0 then (if grid[129] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f13 = if grid[130] > 0 then (if grid[131] > 0 then (if grid[132] > 0 then (if grid[133] > 0 then (if grid[134] > 0 then (if grid[135] > 0 then (if grid[136] > 0 then (if grid[137] > 0 then (if grid[138] > 0 then (if grid[139] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f14 = if grid[140] > 0 then (if grid[141] > 0 then (if grid[142] > 0 then (if grid[143] > 0 then (if grid[144] > 0 then (if grid[145] > 0 then (if grid[146] > 0 then (if grid[147] > 0 then (if grid[148] > 0 then (if grid[149] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f15 = if grid[150] > 0 then (if grid[151] > 0 then (if grid[152] > 0 then (if grid[153] > 0 then (if grid[154] > 0 then (if grid[155] > 0 then (if grid[156] > 0 then (if grid[157] > 0 then (if grid[158] > 0 then (if grid[159] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f16 = if grid[160] > 0 then (if grid[161] > 0 then (if grid[162] > 0 then (if grid[163] > 0 then (if grid[164] > 0 then (if grid[165] > 0 then (if grid[166] > 0 then (if grid[167] > 0 then (if grid[168] > 0 then (if grid[169] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f17 = if grid[170] > 0 then (if grid[171] > 0 then (if grid[172] > 0 then (if grid[173] > 0 then (if grid[174] > 0 then (if grid[175] > 0 then (if grid[176] > 0 then (if grid[177] > 0 then (if grid[178] > 0 then (if grid[179] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f18 = if grid[180] > 0 then (if grid[181] > 0 then (if grid[182] > 0 then (if grid[183] > 0 then (if grid[184] > 0 then (if grid[185] > 0 then (if grid[186] > 0 then (if grid[187] > 0 then (if grid[188] > 0 then (if grid[189] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
every 33 -> f19 = if grid[190] > 0 then (if grid[191] > 0 then (if grid[192] > 0 then (if grid[193] > 0 then (if grid[194] > 0 then (if grid[195] > 0 then (if grid[196] > 0 then (if grid[197] > 0 then (if grid[198] > 0 then (if grid[199] > 0 then 1 else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0) else 0
-- Find bottommost full row
let clearRow = -1
every 33 -> clearRow = if f19 then 19 else (if f18 then 18 else (if f17 then 17 else (if f16 then 16 else (if f15 then 15 else (if f14 then 14 else (if f13 then 13 else (if f12 then 12 else (if f11 then 11 else (if f10 then 10 else (if f9 then 9 else (if f8 then 8 else (if f7 then 7 else (if f6 then 6 else (if f5 then 5 else (if f4 then 4 else (if f3 then 3 else (if f2 then 2 else (if f1 then 1 else (if f0 then 0 else -1)))))))))))))))))))
-- Clear row: slice out the full row, prepend empty row
every 33 -> clearDelay = if clearRow >= 0 then clearDelay + 1 else 0
every 33 -> grid = if clearDelay == 2 then concat([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], concat(slice(grid, 0, clearRow * 10), slice(grid, (clearRow + 1) * 10, 200))) else grid
every 33 -> score = if clearDelay == 2 then score + 100 else score
every 33 -> lines = if clearDelay == 2 then lines + 1 else lines
every 33 -> clearDelay = if clearDelay == 2 then 0 else clearDelay
-- Level and speed
every 33 -> level = if lines > 0 then (lines / 10 + 1) else 1
every 33 -> dropInterval = if level > 9 then 3 else (27 - level * 3)
-- Game over: spawn area occupied
every 33 -> gameOver = if grid[3] > 0 then 1 else (if grid[4] > 0 then 1 else (if grid[5] > 0 then 1 else (if grid[6] > 0 then 1 else gameOver)))
-- ================================================================
-- SOUND
-- ================================================================
every 33 -> play_tone(if lockTick == 3 then 220 else 0, 80)
every 33 -> play_tone(if gameOver then 110 else 0, 500, "sawtooth")
-- ================================================================
-- STREAM: spectator broadcast
-- ================================================================
stream tetris on "ws://localhost:9100/peer/tetris" {
mode: signal,
output: piece, rotation, px, py, nextPiece, score, lines, level, gameOver, paused, grid
}
-- ================================================================
-- VIEW
-- ================================================================
view tetris_game = column [
text "DreamStack Tetris" { variant: "title" }
row [
Badge { label: "Score: {score}", variant: "success" }
Badge { label: "Lines: {lines}", variant: "info" }
Badge { label: "Level: {level}", variant: "warning" }
]
row [
-- Main Board: 300x600
stack { style: "position:relative; width:300px; height:600px; background:linear-gradient(180deg,#0a0a1a,#111133); border:2px solid #334155; border-radius:12px; overflow:hidden; box-shadow:0 0 60px rgba(99,102,241,0.15)" } [
-- Grid lines
for r in [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19] ->
column { style: "position:absolute; width:300px; height:1px; top:{r * 30}px; background:rgba(99,102,241,0.08)" } []
for k in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:1px; height:600px; left:{k * 30}px; background:rgba(99,102,241,0.08)" } []
-- Frozen grid: row 0
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:1px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[c]]}; opacity:{if grid[c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 1
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:31px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[10 + c]]}; opacity:{if grid[10 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 2
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:61px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[20 + c]]}; opacity:{if grid[20 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 3
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:91px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[30 + c]]}; opacity:{if grid[30 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 4
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:121px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[40 + c]]}; opacity:{if grid[40 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 5
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:151px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[50 + c]]}; opacity:{if grid[50 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 6
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:181px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[60 + c]]}; opacity:{if grid[60 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 7
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:211px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[70 + c]]}; opacity:{if grid[70 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 8
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:241px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[80 + c]]}; opacity:{if grid[80 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 9
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:271px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[90 + c]]}; opacity:{if grid[90 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 10
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:301px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[100 + c]]}; opacity:{if grid[100 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 11
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:331px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[110 + c]]}; opacity:{if grid[110 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 12
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:361px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[120 + c]]}; opacity:{if grid[120 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 13
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:391px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[130 + c]]}; opacity:{if grid[130 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 14
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:421px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[140 + c]]}; opacity:{if grid[140 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 15
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:451px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[150 + c]]}; opacity:{if grid[150 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 16
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:481px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[160 + c]]}; opacity:{if grid[160 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 17
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:511px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[170 + c]]}; opacity:{if grid[170 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 18
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:541px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[180 + c]]}; opacity:{if grid[180 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Row 19
for c in [0,1,2,3,4,5,6,7,8,9] ->
column { style: "position:absolute; width:28px; height:28px; top:571px; left:{c * 30 + 1}px; border-radius:4px; background:{colorD[grid[190 + c]]}; opacity:{if grid[190 + c] > 0 then 1 else 0}; transition:opacity 0.15s; box-shadow:0 0 8px rgba(99,102,241,0.3)" } []
-- Ghost piece: shows where piece will land
column { style: "position:absolute; width:28px; height:28px; border-radius:4px; border:2px dashed {colorD[piece]}; opacity:{if showGhost then 0.3 else 0}; top:{(ghostY + c0dy) * 30 + 1}px; left:{(px + c0dx) * 30 + 1}px" } []
column { style: "position:absolute; width:28px; height:28px; border-radius:4px; border:2px dashed {colorD[piece]}; opacity:{if showGhost then 0.3 else 0}; top:{(ghostY + c1dy) * 30 + 1}px; left:{(px + c1dx) * 30 + 1}px" } []
column { style: "position:absolute; width:28px; height:28px; border-radius:4px; border:2px dashed {colorD[piece]}; opacity:{if showGhost then 0.3 else 0}; top:{(ghostY + c2dy) * 30 + 1}px; left:{(px + c2dx) * 30 + 1}px" } []
column { style: "position:absolute; width:28px; height:28px; border-radius:4px; border:2px dashed {colorD[piece]}; opacity:{if showGhost then 0.3 else 0}; top:{(ghostY + c3dy) * 30 + 1}px; left:{(px + c3dx) * 30 + 1}px" } []
-- Active piece: 4 cells positioned by offsets
column { style: "position:absolute; width:28px; height:28px; border-radius:4px; background:{colorL[piece]}; box-shadow:0 0 12px rgba(168,85,247,0.5); transition:left 0.05s,top 0.05s; top:{(py + c0dy) * 30 + 1}px; left:{(px + c0dx) * 30 + 1}px" } []
column { style: "position:absolute; width:28px; height:28px; border-radius:4px; background:{colorL[piece]}; box-shadow:0 0 12px rgba(168,85,247,0.5); transition:left 0.05s,top 0.05s; top:{(py + c1dy) * 30 + 1}px; left:{(px + c1dx) * 30 + 1}px" } []
column { style: "position:absolute; width:28px; height:28px; border-radius:4px; background:{colorL[piece]}; box-shadow:0 0 12px rgba(168,85,247,0.5); transition:left 0.05s,top 0.05s; top:{(py + c2dy) * 30 + 1}px; left:{(px + c2dx) * 30 + 1}px" } []
column { style: "position:absolute; width:28px; height:28px; border-radius:4px; background:{colorL[piece]}; box-shadow:0 0 12px rgba(168,85,247,0.5); transition:left 0.05s,top 0.05s; top:{(py + c3dy) * 30 + 1}px; left:{(px + c3dx) * 30 + 1}px" } []
]
-- Side panel
column [
text "NEXT" { variant: "subtitle" }
stack { style: "position:relative; width:120px; height:90px; background:#0f0f23; border-radius:8px; border:1px solid #334155" } [
column { style: "position:absolute; width:24px; height:24px; border-radius:3px; top:{sd0y[(nextPiece - 1) * 4] * 26 + 10}px; left:{sd0x[(nextPiece - 1) * 4] * 26 + 10}px; background:{colorL[nextPiece]}" } []
column { style: "position:absolute; width:24px; height:24px; border-radius:3px; top:{sd1y[(nextPiece - 1) * 4] * 26 + 10}px; left:{sd1x[(nextPiece - 1) * 4] * 26 + 10}px; background:{colorL[nextPiece]}" } []
column { style: "position:absolute; width:24px; height:24px; border-radius:3px; top:{sd2y[(nextPiece - 1) * 4] * 26 + 10}px; left:{sd2x[(nextPiece - 1) * 4] * 26 + 10}px; background:{colorL[nextPiece]}" } []
column { style: "position:absolute; width:24px; height:24px; border-radius:3px; top:{sd3y[(nextPiece - 1) * 4] * 26 + 10}px; left:{sd3x[(nextPiece - 1) * 4] * 26 + 10}px; background:{colorL[nextPiece]}" } []
]
text "STATS" { variant: "subtitle" }
text "Score: {score}"
text "Lines: {lines}"
text "Level: {level}"
text "Speed: {dropInterval}" { variant: "muted" }
text "DEBUG" { variant: "subtitle" }
text "piece:{piece} rot:{rotation}" { variant: "muted" }
text "px:{px} py:{py}" { variant: "muted" }
text "blk:{blocked} lock:{lockTick}" { variant: "muted" }
text "si:{si} clr:{clearRow}" { variant: "muted" }
]
]
-- Controls
row [
button (if paused then "Resume" else "Pause") {
click: paused = if paused then 0 else 1,
variant: "primary"
}
button "Left" { click: px = if leftOk then px - 1 else px, variant: "secondary" }
button "Right" { click: px = if rightOk then px + 1 else px, variant: "secondary" }
button "Rotate" { click: rotation = if rotOk then nrot else rotation, variant: "secondary" }
button "Drop" { click: py = ghostY, variant: "secondary" }
button (if showGhost then "Ghost: ON" else "Ghost: OFF") { click: showGhost = if showGhost then 0 else 1, variant: "outline" }
button "Reset" {
click: score = 0; lines = 0; level = 1; gameOver = 0; paused = 0; piece = 6; nextPiece = 1; px = 3; py = 0; rotation = 0; grid = [0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0],
variant: "destructive"
}
]
when gameOver > 0 -> text "GAME OVER — Score: {score}" { variant: "title" }
]