Array Languages #6: X_eTaL-games, Rules That Are Arrays
2247 words • 12 min read • Abstract

| Resource | Link |
|---|---|
| The repo | softwarewrighter/X_eTaL-games — one sub-project per game, each with its rules as a library |
| Live catalog | all twenty games in the browser · the code, cross-referenced |
| The originals | TBT #1: a horse race in APL, 1972 · TBT #8: the COR24 BASIC games |
| Prior post | Array Languages #5: X_eTaL-ML |
| Comments | Discord |
Twenty games, live
Each game’s rules are a small X_eTaL library, and its page shows only what X_eTaL prints or draws: the game in a terminal, its scripted run as a notebook, and the source. Each tile links to its page:
Each game, one idea
This is the point of the repo: a game is chosen because its rules are array-shaped, and each one makes a concept concrete. Read down the middle column and it is a short course in array programming.
| Game | The array idea | Terms it exercises |
|---|---|---|
| Horse race | the whole field as one vector, advanced by one expression | vector, random roll, reduction |
| Guess the number | every candidate against every guess, so every game at once | outer product (table), comparison, mask |
| Robot chase | all robots move at once; collisions as one product | coordinate matrix, sign as a fork, inner product |
| Trek adventure | the story as a table of rows chosen by masks | table, mask, state machine as data |
| Star Trek | the galaxy as planes; a course as every step at once | rank-3 array, path as a matrix, windows by selection |
| Tic-tac-toe | lines by indexing; every move rated at once | indexing, inner product, minimax |
| Shut the box | masks over all 512 subsets at once | Boolean mask, subset sums by inner product |
| Minesweeper | a cell’s count as eight rotations added | rotate, reduce, flood fill to a fixed point |
| 2048 | a move as compress, merge, compress; the board turned to reuse it | compress, train, power of a function |
| Sudoku | every cell’s candidates as one array, every unit counted at once | rank-3 tensor, sums over units, fixed point |
| Lights out | a press as a row of a matrix; solved by elimination | matrix product mod 2, GF(2) |
| Capitals | every distance at once, the nearest by grade | outer product, grade, SVG as text |
| Stargazer | every star’s brightness band by one table | table lookup, 2,887 points placed at once |
| Dungeon | you, the monsters and the items as rows of one matrix | entity-component system as arrays |
| Maze | a maze carved by a scan; a first-person view looked up at every depth at once | scan, flood to a fixed point, lookup |
| Falling blocks | a piece turned by reversing and transposing; full rows by an and-reduction | reverse, transpose, compress |
| Side-scroller | gravity and collision for every body at once, ticked by the clock | whole-column systems, a mask used as a number |
| Racer | every car against every track segment; places by comparing every pair | outer product, reduction |
| Asteroids | shots and rocks spawned and despawned in the free slots | masks, outer product of hits, replicate |
| Tower defense | every tower against every creep in one table, every shot against every creep in another | outer product, flood for the road, a linear map for the board and its inverse for clicks |
A few of those ideas, in the games’ own code:
places ← 1 + '+ r̲/₂ 0 + p '< t̲able p ⍝ racer: each car's place, every pair compared
piece ← o̲\ r̲ev piece ⍝ falling blocks: a quarter turn
full ← '∧ r̲/₂ W ⍝ falling blocks: which rows are full
W ← (n̲ot full) r̲eplicate W ⍝ ... and the board without them
x ← x × (1 − hit) + snap × hit ⍝ side-scroller: a mask used as a number
runs ← ('+ s̲\ ends) − ends ⍝ maze: runs of a row, by one scan
The same few shapes recur across games that look nothing alike: rotate and reduce for a neighborhood, a table of every pair, compress to keep what survives, a fixed point to finish a flood. That recurrence is what the shared libraries capture, Board, Play, Text, State, Svg and Ecs, each game using the ones it needs.
An entity-component system as arrays
The newest games, the dungeon, side-scroller, racer, asteroids and tower defense, share one idea from game engines, written array first. A world is one matrix: a row per entity, a column for each component’s fields, and a column saying whether the slot is alive. A system is a whole-column expression over every entity with the components it needs, at once. Spawning is writing into the free slots a mask finds. A macro library writes a world’s layout, its getters, setters and masks, from one line:
"h" ᵉᶜc̲omponents< "pos:2 hp atk kind chase heal gold player goal"
It is the series’ two meanings of Extensible again: a library for the systems, and a macro for the layout.
Games in the other repos
Three more games live outside this repo, next to the machinery they show off:
| Game | Where | The array idea |
|---|---|---|
| Gem Hunt | X_eTaL-extensions, a native 3D window | a voxel world as arrays: faces found by six rotations, sunlight by one scan down each column, a dig recorded as a row |
| The voxel Rubik’s cube | X_eTaL-extensions | each quarter turn a permutation of 54 stickers computed from the geometry, with buttons to turn, scramble and solve |
| Eigencube | X_eTaL-demos, in the browser; its solver now a library in X_eTaL-libraries | cubelets as vectors and turns as rotation matrices; a batch of cubes as one array, every turn of every cube one product |
Tower defense’s pieces, made by Blender
The newest game is also a case of two repos building one thing together. Tower defense wanted 3D towers and creeps, so X_eTaL-games filed an ask with X_eTaL-extensions, and the Blender support there answered it.
The pieces are described in X_eTaL, as parts that the Bpy library turns into Blender’s Python: an arrow, a cannon and a frost tower, each at three levels, and three creeps, a runner, a brute and a shield. Each sits on one tile, from about a hundred triangles to about twelve hundred. Each carries its animations as named clips: an idle loop for every piece, a fire for the towers that kicks back or swells, and a walk and a hit for the creeps. The td-models demo writes them as .glb files and reads every clip back.
A second demo, game assets, makes them in earnest. Blender renders each piece alone on a transparent background and the twelve lined up as a short video. X_eTaL then hashes every file with the digest extension into a manifest and writes a page with each piece in a 3D viewer and its clips on buttons. The result is published where the game can fetch it.
The game does the rest. Its X_eTaL program writes the whole 3D scene into the picture it draws: the camera, the tiles, and every piece’s model, place, heading, clip and moment, so a tower fires just after its shot and a creep walks facing along the road. The page draws that scene with three.js from the published .glb files. Without WebGL the flat picture stays, and the labels, toolbar and clicks are the program’s own SVG either way. So one game draws on two repos: its rules from X_eTaL-games, and its pieces from X_eTaL-extensions, made by the Blender extension and checked by the digest extension.
Both have posts of their own: the voxel world and Gem Hunt, and the Eigencube, whose solver also drives the voxel cube. And TTTML, the tic-tac-toe machine that learns by playing itself, is a demo in the language repo.
Dogfooding the language
The games are also how the language gets tested on ordinary programs. A demo computes and draws. A game keeps state between moves, reads a player’s input, answers clicks, keeps time, loads its data from files, and has to stay fast enough to play. Each of those has pushed on the language:
- What the games brought in. Clicks as events for the map games, a clock for the side-scroller, game data in TOML files, and a terminal on every page where the game reads your moves. The terminal is being built in the language as its own saga.
- What they still ask for. A functional update that returns an array with a few items changed, which every turn-based game wants and which the voxel world in the extensions post asked for too. Functions defined inside a lambda. Named trains of two arguments. Numbers, not just text, from data files. And speed: an inner product cost about 100 ns per multiply-add when last measured, and a game notices.
- What they check. Every scripted game states its guarantees with
a̲ssert<, every page is played in headless Chrome against the command line’s output, and a benchmark fails if any game gets more than 15% slower.
When an ask lands, the game drops its workaround. When it does not, the game shows exactly what the language is missing.
The series so far
That is the tour: the language, the demos, the libraries, the extensions, the machine learning, and the games. Six repositories, each with one job, and one language under all of them. Two more, for GPUs and FPGAs, are planned and will join the series when they exist.
Part 6 of the Array Languages series. View all parts
Comments or questions? SW Lab Discord or YouTube @SoftwareWrighter.



















