Audamir

part 31

Two Games on One Board: the Kit Behind Colony and Lines

The board's drawing, gestures, events and grid left the dice code within three days, and 204 original scenes are still drawn call for call the same. The second game, Lines, was built on them but still reached into 14 files of the app.

· 16 min read

Colony, a game without dice on a hexagonal board, was born on 26 October 2026 as a guest on the dice screen: a piece in the record looked like two dice, and an empty square like one (the article about Colony). For one game such a trick is enough; each next one would add its own branches to the shared code (the article about realms). The vision of 28 October reads: the board knows only the shape of the squares and the edge of the playing area, what stands on a square is data, and the rules belong to the game. Following it, between 28 and 30 October the drawing of the board, gestures, events and the grid were pulled out of the dice code into shared parts, which are called here the kit. On it, on 30 October, the second game of the realm Taflgard[1] was built: Lines (in Slovak Čiary, in the code chains).

Two things came out of it. The screen of the dice game did not change by a single canvas call when it moved: 204 original scenes are still drawn exactly as before. Colony changed once and on purpose, when a disc replaced its two dice, and all of today's 277 scenes are drawn exactly as the record says. And the rule "a new game adds only its own files" does not hold yet: Lines needed 91 lines in 14 files of the application outside its own package.

What the kit is and why the game needed it

Before the change, the board was geometry and state in one object. The renderer had 1,124 lines, asked the rules in four places and read 27 fields of one view bag that the app assembled in three places and kept in step by hand. Colony fitted into that with tricks: a piece was a square with two dice, and the renderer knew two words of Colony, cp and jp. The services (the record, the picture of the game, music, analysis) read a stream of events that only the dice game could produce, and the picture of the game built each square's owner from the attacks it had won, so it would not see a piece turning, a jump or a capture.

The design held to one word: thin. The board knows the shapes of the squares, what stands on them as data, and can draw it and take a click. Rules, AI, records and algorithms belong to the game. The dice engine is not rewritten: the board attaches to it through a read-only projection of its state, so the golden records stay the same. A member of the interface may exist only when a line of code needs it, or when the paper game failed without it.

The picture shows the kit at work: one move in Colony and in Lines. It is the same flow as in the article on architecture for the dice game, only doubled. The input of the board, the events, and the painters with the renderer are shared; the controller, the rules and the board description belong to each game.

One move in Colony and Lines on the shared kitThe scheme of one move in two games. The top row is Colony, the bottom row is Lines, the middle row is the kit that both use. A tap goes from the input (BoardInput) to the controller of the game and from the controller to the rules, which check the move and make it. Each game describes its own board; the painters and the renderer draw the picture on the canvas for both. Dashed arrows and the dashed frames show what is not connected yet: the events, the record and the event player of Colony and Lines. The end of the game is done.inputgame controllerrules and eventsboard and painterspictureBoardInputtap { cell }and the second tapColonyViewColony controller:picks the moveChainsViewLines controller:picks the moveRulesColony: checksand makes the moveBoardEventsact: pathset: before and afterRulesLines: checks,places the stoneColonyBoardview: descriptionof one momentPaintersdie, piece, glyphpaint the itemsBoard viewLines: its ownboard descriptionRendererdraws on the canvascalls no rulesevents and recordColony and Lines do not yethave a record or event playerend of gamefacts(): result and lengthexperience, coins, achievementskit: shared by both gamesgame: Colony or Linesplan: not connected yetdashed arrow: not connected yet
One move in Colony and Lines on the shared kit. The scheme is drawn from the code of both games (0.122.0, 6 Nov 2026); the flow of a move in the dice game is in the article about architecture. The solid frame is the kit, the plain frame belongs to a game, and the dashed one is not connected yet: Colony and Lines do not call events, the record or the player yet. The end of the game (facts) is done.

The parts the kit is made of (the grid Lattice, the view of the board, the painters, the renderer, the input BoardInput and the events BoardEvents) were taken out in the steps listed below.

On paper four games were walked through: Colony, a row-of-stones game (later Lines), checkers and a memory game with hidden cards. Each broke something, and the result was nine fixes. A move is a path, not a pair, because a checkers piece jumps over several squares; the fill may be flat, because a row game should not hatch neutral squares. On the eleven questions of the document, the answers of 29 October were: first the projection of Stacks, and only then the second game; the slot belongs to the game and its id is the game's id; neither the name nor a variant is a trademark; play from the keyboard, yes, but only after the first new game.

How it was shown that the screen did not change

The renderer refactor has one requirement: the player must not see a difference. Comparing pictures by eye is not enough, so step B1 (merged on 29 October at 13:09) recorded the look of the board before anything changed. The tool draws the scenes onto a fake canvas that remembers every call (for example c1.lineTo(850,285.72)), rounds the numbers to 0.01 and turns the order of the calls into a 53-bit fingerprint with a checkpoint every 256 lines. A scene is data, so it can be drawn the same way in Node and in the page.

The original record had 250 scenes (204 from the dice game and 46 from Colony) and 1.59 million canvas calls. The one real change (the shade of the fog 0.28 to 0.3) fell exactly on 5 scenes with fog. In a real Chromium the same counts and fingerprints gave 250 of 250 scenes, as in Node.

After each step the corpus was compared again:

StepCorpus after the stepWhat else
B2 (the renderer reads the view)250 of 250 after each of four commitsthree snapshots byte for byte the same
B3 (input)250 of 250 after each of three commits25 mutations of a file, each caught by at least one test
B4 (Colony on the view)204 dice scenes the same after each commit46 Colony scenes rewritten on purpose
B7 (player)277 of 277the golden record of the player from the code before the change
B8 (Lines)277 of 27729 new scenes of Lines

The change of Colony to discs was on purpose. After the mock-ups of three looks from 29 October the choice fell on a disc, and the Colony scenes were rewritten in a commit. The chart shows what that gained. The budget for the other boards was at most +2 ms or +15 %, whichever is more; the default board came out 0.3 ms (+18 %) slower at B2, so it was within budget.

Colony after the change to discs: fewer calls and faster drawingAfter the change of Colony to discs (step B4, 30 Oct 2026): the piece takes 7 calls instead of 19, the start of the board hex58 fell from 5,483 to 2,668 calls and the redraw of Colony from 1.3 to 0.9 ms. The length of a bar is the value after the change in percent of the value before it.piece: canvas calls7 instead of 19 (−63%)start of the board hex58: calls2,668 instead of 5,483 (−51%)redraw of Colony: ms0.9 ms instead of 1.3 (−31%)bar length = value after the change in percent of the value before it (100%)
Colony before and after the change to discs (step B4, 30 Oct 2026). The length of a bar is the value after the change in percent of the value before it, so 100 % would be no change.

Apart from the canvas, it was checked that the engine did not move: none of the 14 files that decide the game is in the change, the rules version stayed 2 and the generator version 1. There are 188 baseline fingerprints, and the golden records (12 games and 3 maps) match byte for byte.

Where the corpus was not enough

The first version of the Stacks projection answered for the object it made, not for the one the controllers hold. The corpus did not see it; only the test puzzle-ui did.

The second case is a circle. The test of event composition compared its result with the record player, and once the player began to read exactly those events, the test stopped proving anything. The proof became the golden record of the player made by the old code: 15 records and 4,111 steps, with each step's fingerprint of the board, players, move, arrow, sentence and controllers. Added to it were 32 damaged records (6,264 steps) and 60 random games (33,462 steps) that the old code recorded itself.

Steps in order

StepMergedWhat moved
B129 Oct, 13:09the fixed record of the look
B529 OctLattice: row, column, and the step in a direction for the square and the hexagon
B229 Oct, 20:41the renderer reads the view of the board, 1,124 → 553 lines
B329 Oct, 22:22BoardInput, does not know Colony
B430 Oct, 00:37Colony on the view, the disc
B630 Oct, 11:05BoardEvents, nothing loads it yet
R2b-130 Oct, 11:21Colony as a game of the app
B730 Oct, 13:25the player reads the events of the board
B830 Oct, 14:49Lines, the second game on the board

Three remarks. The grid Lattice came about because checkers, the row game and the table of cards need "one to the right", which neighbours by edge do not know. A new file of the core is written into six lists, and the error shows late: the first full run had 9 failures in the build tests. BoardInput sends the controller a tap with the number of the square and a flag saying whether a second tap is needed, so the second copy of the rule "confirm with a second tap", which lived in Colony itself, disappeared.

BoardEvents knows the verbs start, turn, round, pass, over and act. The verbs place, remove, move, convert, count, reveal and hide the game does not send; one pure function derives them from the content of the squares before and after. For Colony, 72 games of AI opponents and 192 games of a random player were tried. Nobody loaded the files on purpose until the first reader (B7) showed whether the dictionary carried everything the player wanted from the board. It does.

A game that is more than a board

Step R2b-1 (30 October) made Colony a game of the app: it is saved after every move, the rule "one saved game, one browser tab" holds, and the game has a Restart and a statistics tab. A finished game leaves a line in the history and a count in the realm's container. The keys were written for the first time, so they were checked with data from the old code: the saved games and the profile are byte for byte the same. The tutorial was not built, because the lessons are dice from the ground up.

A second game on the kit

Lines are the first game that was written on the board, not moved onto it. Players place stones in turn, and the first to have len stones in a row wins. The variants are data of the game: tic (3 × 3, a row of 3), gomoku (15 × 15, a row of 5) and drop (7 × 6, a row of 4, the stone falls). The game is described in the article on three in a row and the article on the length of a row. The first commit of Lines is from 30 October at 11:56 and the last was merged at 14:49. That is the time of commits, not of work: two days of building the kit and a paper test came before it.

The paper test predicted five things that did not fit. Four were solved: a flat fill, a stone that follows the pointer, the search for a move written in the game and not in the kit, and a status line as the text of the controller. The paper test did not see four things. The fall of a stone in drop needs a height and time, and its shadow lies on the square where it lands. A touch screen must ask for a second tap, because a stone cannot be taken back. The most important was the price of the application's wrapper.

What the second game needed outside its package: 590 lines590 lines outside the package of Lines: 378 lines of translations in nine catalogues, 121 of the growth of the renderer and 91 of the wrapper of the app in 14 files. Of the wrapper, 40 lines are decision code (app.js 19, describe.js 10, rulesets.js 11) and 51 are tables and names.translations in nine catalogues378growth of the renderer121wrapper: tables and names51wrapper: decision code40lines outside the package chains, DESIGN §80.6
Lines outside the package of Lines according to DESIGN §80.6 (the merge of step B8, 30 Oct 2026). The wrapper is 91 lines in 14 files, of which the decision code is 40 lines in three files.

Only three files of the wrapper carry decision code: app.js (19 lines), describe.js (10) and rulesets.js (11). The rest of the wrapper is tables and names in lists, for example the lists of files in index.html, sw.js and the compatibility page.

The rule "a new game adds only its own files" is therefore a goal, not a state. A third game would touch the same 14 files, and so B9 has to begin with the game's wrapper: one entry in the manifest for the screen, services, description, names, tile and files. Today app.js lists Colony and Lines by hand in several places: the list of screens, the table of games, the "again" and "back" buttons, and the start.

Compared file by file (§80.6), Colony and Lines have the same game loop (147 and 142 lines), the same worker (21 and 21) and about 70 % of the view. Together that is about 500 of the 1,536 lines of code of the package. Moves, their order and the value of a position differ.

One piece of the kit proved unnecessary the same day. The painter glyph was made for the letters X and O in tic, but on 30 October a cross and a circle on the 3 × 3 board did not suit, and the commit of 17:22 gave tic discs like the other variants. The painter stayed in the renderer with its unit tests, but no product code asks for it.

What is done and what is only decided

WhatStatus
the fixed look, Lattice, the view of the board, BoardInput, BoardEvents, Lines (B1 to B3, B5 to B8)done
Colony on the view of the board (the disc) and as a game of the app (B4, R2b-1)done
stone and dice as looks of the piecein the code, not offered until Rokgard has a wardrobe
keyboard (B11), Colony tutorial (R2b-1b), the marks of the controller and one map of animations (B4b)decided or planned, not built
the record and the player of Colony (R2b-2), events of Linesnot built
B9: the game's wrapper, then search and BotClient; B10 checkers, B12 memory gamenot built, estimate M to L
the payment for a finished gameColony pays XP from R3b, Lines does not

"Decided" is not "done". This holds also for the marks of the player's shapes (shapes): according to DESIGN §71.4 it is a decision that follows the recommendation and still has to be confirmed.

What follows from it

  • Proof is written before the change. The list of canvas calls was made with the old renderer, and every step was compared with it. Without it one could only say "it looks the same".
  • Proof that reads the same thing stops being proof. The test of event composition became a circle when the player began to read it. The proof had to move to the golden record from the code before the change.
  • A paper test catches part of it. The fall with a height and the touch confirmation showed up only in the code, and the price of the wrapper only with the second game. The painter glyph survived in the product until 17:22, although the rule says "no member without a user".
  • Two copies are the base for pulling out. Colony and Lines have about 500 shared lines out of 1,536, and it is visible which ones are the same. Pulling them out before the second game would have fixed an unmeasured shape.

What remains

The next step is B9: its first half is the wrapper of the game from the manifest, which removes those 14 files. Then comes the kit for search, the record and the player of games on the board, the keyboard, and only then checkers and the memory game. The economy of realms (level, tokens, wardrobe, home screen) is another topic and waits for steps R3c1, R3e and R5.

Sources and notes

  • Design and decisions: docs/BOARD-KIT.md (chapters 0 to 2, table 6.5 with the nine fixes, steps B0 to B12, the eleven decisions of 29 Oct 2026). Vision: docs/ROADMAP.md, chapter 26.
  • Steps and numbers: docs/DESIGN.md §65 to §80 (B1 to B8, §80.6 for the table of the files of Lines), §87 (the painter glyph) and §82 (payment).
  • Checked on 31 Oct 2026 (game 0.65.6): the tools for the scenes (277 and 29 the same), the fixtures, baseline.json (188), the golden records and the rules version 2. No world data here. Not checked: pixels beyond three snapshots from B2, speed on a phone (the measurements ran in a hidden pane), the file counts of §80.6, "about 70 %" and "about 500 lines" (taken from DESIGN without a new diff), and the confirmation of the shape marks (§71.4). No test was run again.

Notes

  1. Taflgard was the name of the realm of board games from 28 October 2026. From 31 October 2026 this realm is called Rokgard (today js/realms/rokgard/). ↩︎