DAEDALUS
LOCAL TIME --:--:--LOCATION NOVGOROD STATIONWEATHER 15°C · CLEAR ☼

PROJECT 09 // FALLING-BLOCK PUZZLE & COMPILER PROVING GROUND

Tetris

The obligatory Tetris clone -- a complete, playable game written entirely in ArcoBASIC and compiled by ArcoFission straight to a self-contained WebAssembly page, with a matching native desktop build from the exact same source.

STATUS ActivePROGRESS 100%

WEB CAPSULE // RUNS ENTIRELY IN YOUR BROWSER, NO SERVER INVOLVED

PLAY TETRIS

OPEN FULL SCREEN ↗

The obligatory first game

Every new game engine, every new language, every new graphics stack eventually has to prove it can do the ordinary thing: draw a board, move a shape, clear a line. Tetris is that proof for ArcoBASIC and ArcoFission -- a real, complete, playable game with nothing borrowed from an existing engine.

The whole thing is one ArcoBASIC source file. ArcoFission compiles it two ways from that same file: a native Linux binary linking GLFW and Cairo for a real desktop window, and a WebAssembly capsule that runs the identical bytecode behind an HTML5 canvas, requiring nothing but a browser -- not even a server, since the wasm module is embedded directly in the page rather than fetched separately.

Rotation without a rotation matrix

Each of the seven pieces is stored as four rotation states, each state a list of four cell offsets inside a 4x4 box. Turning a piece is nothing more elaborate than switching which offset list is active -- no trigonometry, no matrix multiplication, just a different row of a lookup table.

ArcoBASICArcoBASIC: a lookup-table rotation, not a rotation matrix
' Every piece is 4 rotation states, each a list of 4 [x, y] cell offsets inside a
' 4x4 box -- turning a piece is just switching which offset list is active.
I_PIECE_ROTATIONS = [
    [[0, 1], [1, 1], [2, 1], [3, 1]],
    [[2, 0], [2, 1], [2, 2], [2, 3]],
    [[0, 2], [1, 2], [2, 2], [3, 2]],
    [[1, 0], [1, 1], [1, 2], [1, 3]]
]

FUNCTION AbsoluteCells(piece, rotations)
    cells = rotations[piece.Rotation]
    result = []
    FOR i = 0 TO LEN(cells) - 1
        cell = cells[i]
        ignored = Array.Add(result, [piece.X + cell[0], piece.Y + cell[1]])
    NEXT i
    RETURN result
END FUNCTION

piece = {X: 3, Y: 5, Rotation: 0}
PRINT "Flat I-piece cells: " + STRING(AbsoluteCells(piece, I_PIECE_ROTATIONS))

piece.Rotation = 1
PRINT "Standing I-piece cells: " + STRING(AbsoluteCells(piece, I_PIECE_ROTATIONS))

What counts as a valid move

A falling piece is allowed to have cells above the visible board -- that is just a piece still emerging at spawn. Anything at or past the floor, off either side, or already occupied by a locked piece is not. That one rule, checked against every cell of a proposed position, is the entire physics engine: movement, rotation, soft drop, and hard drop are all just "try a new position, keep it only if it is valid."

ArcoBASICArcoBASIC: the one rule the entire game is built on
' A cell above the board (y < 0) is fine -- that is just a piece still emerging
' at spawn. Anything at or past the floor, off either side, or already occupied
' is not.
BOARD_W = 10
BOARD_H = 20

FUNCTION CellsValid(cells, board)
    FOR i = 0 TO LEN(cells) - 1
        cell = cells[i]
        cx = cell[0]
        cy = cell[1]
        IF cx < 0 OR cx >= BOARD_W THEN RETURN FALSE
        IF cy >= BOARD_H THEN RETURN FALSE
        IF cy >= 0 THEN
            row = board[cy]
            IF row[cx] <> 0 THEN RETURN FALSE
        END IF
    NEXT i
    RETURN TRUE
END FUNCTION

board = []
FOR y = 0 TO BOARD_H - 1
    row = []
    FOR x = 0 TO BOARD_W - 1
        ignored = Array.Add(row, 0)
    NEXT x
    ignored = Array.Add(board, row)
NEXT y
board[19][4] = 3

PRINT "Still emerging above the board: " + STRING(CellsValid([[3, -2], [4, -1]], board))
PRINT "Landing on an empty cell: " + STRING(CellsValid([[5, 19]], board))
PRINT "Landing on an occupied cell: " + STRING(CellsValid([[4, 19]], board))
PRINT "Past the floor: " + STRING(CellsValid([[4, 20]], board))

A real bug the game found

Building an actual playable thing surfaces problems a synthetic test suite does not. The browser build's hard-drop key silently did nothing until it became clear the shared web GUI backend was mapping the spacebar's key-name incorrectly -- a length check ran before the lookup table that would have caught it, so the one-character space character never reached the branch that renames it "space". Fixed once, at the source, for every future ArcoBASIC browser game that binds Space to anything.

A lookup table beats a formula you have to trust

The rotation system above is the clearest example of a bigger habit: reach for a lookup table over a computed formula whenever the space of valid states is small and fixed. Four rotation states times seven pieces is twenty-eight known-good offset lists, checked once, then never wrong again. That trade generalizes well past falling blocks.

  • When the set of valid configurations is small and enumerable, store them rather than deriving them at runtime. A lookup table cannot introduce a trigonometry rounding error or a sign mistake, because there is no computation left to get wrong at the point of use.
  • Reduce a whole feature to one predicate wherever possible, the way movement, rotation, soft drop, and hard drop here are all just "try a new position, keep it only if CellsValid says yes." One well-tested rule is easier to trust than four separately-implemented ones that are each supposed to agree with it.
  • Trust a synthetic test suite for logic, but still put the real thing in front of a real user before calling it done. The spacebar bug was invisible to unit tests and only surfaced because someone actually tried to hard-drop a piece in a browser; playing the thing is still a test synthetic coverage cannot replace.

DAEDALUS_OS TERMINAL

DAEDALUS_OS v3.8.0

CONNECTED.

How can I help?

Technology should adapt to people.

Choose a perspective above or type help for commands.