examples/chess-board.umsg
%unimsg 0 board-presentation/chess/v0 · 134 lines · 4927 bytes of text · 1438 bytes canonical · the raw file
Point at anything below — a sigil, a literal, a brace — and the card says what it is. Keyboard: tab through the document. Every explanation is also printed in full beneath the source.
%unimsg 0 board-presentation/chess/v0
-- How to draw a position of @ruleset/chess/v0, in @vocab/board-presentation/v0.
--
-- THE THIRD DOCUMENT, AND THE SMALLEST. A ruleset says what is legal and never
-- what it looks like; a graphic is a resolved picture and cannot change when a
-- piece moves. What is missing between them is the function from a position to
-- a scene — and for a game played on a grid that function is not a program, it
-- is this table.
--
-- SO NOTHING HERE IS CHESS-SPECIFIC MACHINERY. A square size, two fills, a
-- glyph per piece kind, and where a baseline sits. tools/boardview reads it
-- beside the ruleset and draws any position:
--
-- (cd ../unimsg && go run ./tools/boardview -board examples/chess-board.umsg \
-- -ruleset examples/chess-ruleset.umsg -o /tmp/scene.umsg)
--
-- and the same table inverts, so a point on the picture resolves to a square
-- and a square to the moves the ruleset allows from it. Drawing and pointing
-- are one binding read in two directions.
board-presentation @board-presentation/chess/v0 {
title "A chess board"
summary "Eight files by eight ranks, dark on a1, pieces as the Unicode chess
glyphs, and a frame carrying the file and rank names. It is a
presentation of one ruleset and it knows none of its rules: nothing
here mentions a legal move, a check or a capture, because none of
those is a fact about what the board looks like."
status :draft
at 2026-08-26
presents "@ruleset/chess/v0"
geometry {
square 64
margin 28
y-axis :up
}
-- PARITY IS (file + rank), and a1 is file 0 rank 0, so :even is the dark
-- square. Getting this backwards is the classic board bug and it is one row
-- here rather than a condition somewhere in a renderer.
squares [
| parity fill
| :even "#b58863"
| :odd "#f0d9b5"
]
frame {
fill "#312a24"
stroke "#1a1613"
width 1
}
coordinates {
font [ "Inter", :sans ]
size 12
fill "#e6ddd1"
}
-- THE GLYPHS ARE TEXT, WHICH IS A CHOICE WITH A COST. Six paths per side
-- would be exact and would not depend on a font being present; two columns of
-- one character each are twelve rows a person can read and correct. The cost
-- is declared rather than hidden: :font names the stack, and a host without
-- any of them draws the wrong thing.
glyphs [
| piece white black
| :king "♔" "♚"
| :queen "♕" "♛"
| :rook "♖" "♜"
| :bishop "♗" "♝"
| :knight "♘" "♞"
| :pawn "♙" "♟"
]
glyph-style {
font [ "Segoe UI Symbol", "Noto Sans Symbols 2", "DejaVu Sans", :sans ]
size 46
fill "#141414"
baseline 16
-- A PIECE ON A MARKED SQUARE IS DRAWN LARGER, which is how a person sees
-- that the piece they touched is the one that will move. Fifteen percent is
-- enough to read at a glance and small enough that the glyph stays inside
-- its cell -- 46 becomes 53, against a square of 64.
emphasis 115
}
-- WHERE THE PIECES THAT ARE NO LONGER ON THE BOARD GO. A band above and
-- below, so the picture is the board plus what it has cost. The size is well
-- under a square's: a tray is read as a count and never pointed at, and a
-- captured piece drawn at playing size reads as a piece that could move.
tray {
height 40
size 26
spacing 24
baseline 9
-- The light square's colour. A captured piece is drawn in :glyph-style's
-- near-black, as a piece on the board is, so the band it stands on must be
-- the ground that colour was chosen against -- see
-- a-tray-glyph-keeps-its-own-colour.
ground "#f0d9b5"
}
-- HOW A FINISHED GAME IS DECLARED. Over the board rather than beside it,
-- because the board is what a person is looking at when it happens, and
-- translucent so the position that produced the result is still readable
-- underneath.
outcome {
height 84
size 38
ground "#1a1613"
opacity 0.82
fill "#f0d9b5"
baseline 13
-- THE SIDE NAME IS THE RULESET'S AND ARRIVES LOWERCASE, so the wording
-- puts it where a lowercase word belongs. Capitalising it would be a rule
-- about a language, and the module that substitutes it knows none.
won "winner: {side}"
drawn "drawn — no winner"
}
-- WHAT A HIGHLIGHT LOOKS LIKE, and the reason a presentation has any say in
-- it: the squares that can be highlighted come from the RULESET — the moves
-- legal from the square a person touched — and only how they are drawn is a
-- presentation decision. The list is computed; the colour is declared.
marks [
| mark fill opacity
| :selected "#f4f169" 0.55
| :move "#6f9f4f" 0.55
| :capture "#c05a3e" 0.6
| :last "#c9c26a" 0.45
]
}
The canonical encoding
Produced by the reference encoder when this page was built. Decoding these bytes and printing them in the text form returns the document above, byte for byte — that round trip is the claim the whole format rests on.
a1 72 62 6f 61 72 64 2d 70 72 65 73 65 6e 74 61 74 69 6f 6e da 00 01 11
d5 82 81 da 00 01 11 d6 78 1b 62 6f 61 72 64 2d 70 72 65 73 65 6e 74 61
74 69 6f 6e 2f 63 68 65 73 73 2f 76 30 ae 62 61 74 da 00 01 11 da 6a 32
30 32 36 2d 30 38 2d 32 36 64 74 72 61 79 a5 64 73 69 7a 65 18 1a 66 67
72 6f 75 6e 64 67 23 66 30 64 39 62 35 66 68 65 69 67 68 74 18 28 67 73
70 61 63 69 6e 67 18 18 68 62 61 73 65 6c 69 6e 65 09 65 66 72 61 6d 65
a3 64 66 69 6c 6c 67 23 33 31 32 61 32 34 65 77 69 64 74 68 01 66 73 74
72 6f 6b 65 67 23 31 61 31 36 31 33 65 6d 61 72 6b 73 84 a3 64 66 69 6c
6c 67 23 66 34 66 31 36 39 64 6d 61 72 6b da 00 01 11 d4 68 73 65 6c 65
63 74 65 64 67 6f 70 61 63 69 74 79 c4 82 21 18 37 a3 64 66 69 6c 6c 67
23 36 66 39 66 34 66 64 6d 61 72 6b da 00 01 11 d4 64 6d 6f 76 65 67 6f
70 61 63 69 74 79 c4 82 21 18 37 a3 64 66 69 6c 6c 67 23 63 30 35 61 33
65 64 6d 61 72 6b da 00 01 11 d4 67 63 61 70 74 75 72 65 67 6f 70 61 63
69 74 79 c4 82 20 06 a3 64 66 69 6c 6c 67 23 63 39 63 32 36 61 64 6d 61
72 6b da 00 01 11 d4 64 6c 61 73 74 67 6f 70 61 63 69 74 79 c4 82 21 18
2d 65 74 69 74 6c 65 6d 41 20 63 68 65 73 73 20 62 6f 61 72 64 66 67 6c
79 70 68 73 86 a3 65 62 6c 61 63 6b 63 e2 99 9a 65 70 69 65 63 65 da 00
01 11 d4 64 6b 69 6e 67 65 77 68 69 74 65 63 e2 99 94 a3 65 62 6c 61 63
6b 63 e2 99 9b 65 70 69 65 63 65 da 00 01 11 d4 65 71 75 65 65 6e 65 77
68 69 74 65 63 e2 99 95 a3 65 62 6c 61 63 6b 63 e2 99 9c 65 70 69 65 63
65 da 00 01 11 d4 64 72 6f 6f 6b 65 77 68 69 74 65 63 e2 99 96 a3 65 62
6c 61 63 6b 63 e2 99 9d 65 70 69 65 63 65 da 00 01 11 d4 66 62 69 73 68
6f 70 65 77 68 69 74 65 63 e2 99 97 a3 65 62 6c 61 63 6b 63 e2 99 9e 65
70 69 65 63 65 da 00 01 11 d4 66 6b 6e 69 67 68 74 65 77 68 69 74 65 63
e2 99 98 a3 65 62 6c 61 63 6b 63 e2 99 9f 65 70 69 65 63 65 da 00 01 11
d4 64 70 61 77 6e 65 77 68 69 74 65 63 e2 99 99 66 73 74 61 74 75 73 da
00 01 11 d4 65 64 72 61 66 74 67 6f 75 74 63 6f 6d 65 a8 63 77 6f 6e 6e
77 69 6e 6e 65 72 3a 20 7b 73 69 64 65 7d 64 66 69 6c 6c 67 23 66 30 64
39 62 35 64 73 69 7a 65 18 26 65 64 72 61 77 6e 73 64 72 61 77 6e 20 e2
80 94 20 6e 6f 20 77 69 6e 6e 65 72 66 67 72 6f 75 6e 64 67 23 31 61 31
36 31 33 66 68 65 69 67 68 74 18 54 67 6f 70 61 63 69 74 79 c4 82 21 18
52 68 62 61 73 65 6c 69 6e 65 0d 67 73 71 75 61 72 65 73 82 a2 64 66 69
6c 6c 67 23 62 35 38 38 36 33 66 70 61 72 69 74 79 da 00 01 11 d4 64 65
76 65 6e a2 64 66 69 6c 6c 67 23 66 30 64 39 62 35 66 70 61 72 69 74 79
da 00 01 11 d4 63 6f 64 64 67 73 75 6d 6d 61 72 79 79 01 68 45 69 67 68
74 20 66 69 6c 65 73 20 62 79 20 65 69 67 68 74 20 72 61 6e 6b 73 2c 20
64 61 72 6b 20 6f 6e 20 61 31 2c 20 70 69 65 63 65 73 20 61 73 20 74 68
65 20 55 6e 69 63 6f 64 65 20 63 68 65 73 73 0a 20 20 20 20 20 20 20 20
20 20 20 20 67 6c 79 70 68 73 2c 20 61 6e 64 20 61 20 66 72 61 6d 65 20
63 61 72 72 79 69 6e 67 20 74 68 65 20 66 69 6c 65 20 61 6e 64 20 72 61
6e 6b 20 6e 61 6d 65 73 2e 20 49 74 20 69 73 20 61 0a 20 20 20 20 20 20
20 20 20 20 20 20 70 72 65 73 65 6e 74 61 74 69 6f 6e 20 6f 66 20 6f 6e
65 20 72 75 6c 65 73 65 74 20 61 6e 64 20 69 74 20 6b 6e 6f 77 73 20 6e
6f 6e 65 20 6f 66 20 69 74 73 20 72 75 6c 65 73 3a 20 6e 6f 74 68 69 6e
67 0a 20 20 20 20 20 20 20 20 20 20 20 20 68 65 72 65 20 6d 65 6e 74 69
6f 6e 73 20 61 20 6c 65 67 61 6c 20 6d 6f 76 65 2c 20 61 20 63 68 65 63
6b 20 6f 72 20 61 20 63 61 70 74 75 72 65 2c 20 62 65 63 61 75 73 65 20
6e 6f 6e 65 20 6f 66 0a 20 20 20 20 20 20 20 20 20 20 20 20 74 68 6f 73
65 20 69 73 20 61 20 66 61 63 74 20 61 62 6f 75 74 20 77 68 61 74 20 74
68 65 20 62 6f 61 72 64 20 6c 6f 6f 6b 73 20 6c 69 6b 65 2e 68 67 65 6f
6d 65 74 72 79 a3 66 6d 61 72 67 69 6e 18 1c 66 73 71 75 61 72 65 18 40
66 79 2d 61 78 69 73 da 00 01 11 d4 62 75 70 68 70 72 65 73 65 6e 74 73
71 40 72 75 6c 65 73 65 74 2f 63 68 65 73 73 2f 76 30 6b 63 6f 6f 72 64
69 6e 61 74 65 73 a3 64 66 69 6c 6c 67 23 65 36 64 64 64 31 64 66 6f 6e
74 82 65 49 6e 74 65 72 da 00 01 11 d4 64 73 61 6e 73 64 73 69 7a 65 0c
6b 67 6c 79 70 68 2d 73 74 79 6c 65 a5 64 66 69 6c 6c 67 23 31 34 31 34
31 34 64 66 6f 6e 74 84 6f 53 65 67 6f 65 20 55 49 20 53 79 6d 62 6f 6c
73 4e 6f 74 6f 20 53 61 6e 73 20 53 79 6d 62 6f 6c 73 20 32 6b 44 65 6a
61 56 75 20 53 61 6e 73 da 00 01 11 d4 64 73 61 6e 73 64 73 69 7a 65 18
2e 68 62 61 73 65 6c 69 6e 65 10 68 65 6d 70 68 61 73 69 73 18 73
examples/chess-board.umsg 1438 bytesEvery construct on this page
The formal columns are read from the specification when this page is generated. The last column is this site's plain-language gloss and is not normative — where they differ, the specification is right.
| construct | glyph | carries | example | gloss |
|---|---|---|---|---|
| :annotation | — | — | — | A bare name qualifying the value after it—for example a language tag or a unit such as kg. It is not a value itself; a bare name with nothing after it is an error, not a guess. |
| :array | ^ | type, or type:shape; then [ elements ] | ^f64 [ 1.5, 2.5 ] or ^i16:2x3 [ … ] | A packed, homogeneous numeric array. The ^ marker gives every element its type and may also give the row-major shape: ^f64:2x3 [ … ]. |
| :array-element | — | — | — | One element of a typed array. Its type comes from the ^ marker, so 1.5 inside ^f64 is binary64 rather than an exact decimal. |
| :array-missing | — | — | — | A missing position in a typed array. null here is not an element value: it adds a validity mask while preserving the array's shape. |
| :block | — | — | — | A block: named pairs, in braces. Its keys are sorted in the encoding and shown here as written. |
| :bytes | ~ | encoding:data | ~hex:ff00 or ~b64:iVBORw0KGgo= | Raw octets, carried as octets. No base64 tax, no pretending they are text. |
| :comment | -- | to end of line | -- a note | A note for people. It travels with the pair it precedes and never reaches the bytes. |
| :decimal | — | — | — | An exact decimal. 19.99 is nineteen and ninety-nine hundredths, not the nearest double to it. |
| :extension | ! | name, then value | !acme/hint { zone 4 } | A qualifier this reader may not know. It survives the round trip intact, and stays legible on the way past — the rule that separates an extension from a private convention. |
| :float | — | — | — | A binary float, marked so it can never be mistaken for the exact decimal beside it. |
| :hash | # | algorithm:hex | #sha256:deadbeef | The digest of some content, with the algorithm said out loud so it is still meaningful in ten years. |
| :header | % | unimsg only | %unimsg 0 label | Declares the format version and what this document is. Optional, and useful exactly when a file arrives without context. |
| :identifier | @ | word | @ord-88213 | Gives this value a name, so another document can point at it without copying it. |
| :integer | — | — | — | A whole number of any size. Past 2^53 it is still exact, which is the whole reason this is not JSON. |
| :key | — | — | — | A field name: the first thing in a pair. It is bare when possible and quoted when needed, and names the content without becoming part of the value. |
| :reference | -> | an identifier or a path | -> @cust-4471, -> @lexicon.entries.kitāb | Points at a name. Nothing follows it while reading — resolution is somebody else's job, deliberately. |
| :reserved-literal | — | — | — | One of six bare words that are values rather than names. The list is fixed, so no value is coerced by what it looks like. |
| :sequence | — | — | — | An ordered list. Order is preserved and is meaning. |
| :string | — | — | — | Text. May span lines, so prose does not have to be escaped into one. |
| :symbol | : | word | :dispatched | A name from a fixed vocabulary, not free text. :high is a value someone chose; "high" is a phrase someone typed. |
| :table-row | | | cells | | sku qty | One record of a table. A table is sugar for a sequence of blocks with the same keys, and encodes identically to the longhand. |
| — | — | — | A temporal value at the precision written. 2026-08 stays a month, and 03:52 stays a wall-clock time; neither is widened into an invented instant. |