Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
288 commits
Select commit Hold shift + click to select a range
71a21a7
TrigScript 4, B1: generators, Promise.all's values, .then((value) => …
jeany55 Oct 16, 2026
9186ae4
TrigScript 4: B1's second part as built
jeany55 Oct 16, 2026
2737f71
Merge pull request #9 from scm-js/b1-tasks
jeany55 Oct 17, 2026
31e0398
TrigScript 4, S2: the UI spike, where a board of text can be drawn
jeany55 Oct 17, 2026
b301a75
TrigScript 4, S2: the UI spike played, the board goes in the text lines
jeany55 Oct 17, 2026
9afb4c2
TrigScript 4, S2: the UI spike's second round, a board's colours
jeany55 Oct 17, 2026
4c54c57
TrigScript 4, S2: the second round played, only grey sticks
jeany55 Oct 17, 2026
cda1f2d
TrigScript 4, S2: JSX texts and ui.lines (IR 17)
jeany55 Oct 17, 2026
5e7cc4d
TrigScript 4, S2: Tetris in the text lines, and S2 as built
jeany55 Oct 17, 2026
60de286
TrigScript: && and || stop at the first item that settles them, in th…
jeany55 Oct 17, 2026
b09f04d
TrigScript: keyPressed() is once a tap, not every frame the key is down
jeany55 Oct 17, 2026
bb6b807
TrigScript: keyHeld(), and the held-key probe
jeany55 Oct 17, 2026
d9ab17a
TrigScript 4: held keys played, keyHeld() works in Remastered
jeany55 Oct 17, 2026
45c90d8
TrigScript 4: the unit-pixel probe; Tetris slides when held; centerLo…
jeany55 Oct 17, 2026
96d7730
TrigScript: keyHeld() rides out a missing or late message
jeany55 Oct 17, 2026
659ab27
TrigScript: with a held key in the game, a press is worked out from it
jeany55 Oct 17, 2026
36071ab
TrigScript 4: pixels made of units, played (steps A-D)
jeany55 Oct 17, 2026
3405d3a
TrigScript 4, S2: Tetris on the minimap too - the board drawn with Ov…
jeany55 Oct 17, 2026
d48820f
Tests: the Tetris build fixture takes the library it imports
jeany55 Oct 17, 2026
c3a8d86
TrigScript 4, S2: Tetris's Overlords in the pieces' own colours, away…
jeany55 Oct 17, 2026
68ce0f4
TrigScript 4, S2: Tetris keeps its computer; the S piece is yours
jeany55 Oct 17, 2026
ac84290
TrigScript 4: a class from any file is the program's, no game() needed
jeany55 Oct 17, 2026
13c7133
TrigScript 4, S2: the unit screen is a class
jeany55 Oct 17, 2026
8ede2f7
Merge pull request #10 from scm-js/s2-ui
jeany55 Oct 17, 2026
fb8e5bb
TrigScript 4, B2: events — on.created, on.died, on.chat, on.key
jeany55 Oct 17, 2026
65a6a6b
TrigScript 4, B2: walk the dying list both ways
jeany55 Oct 17, 2026
47c9c9a
TrigScript 4, B2: the events probe gives minerals for its SCV; on.cha…
jeany55 Oct 17, 2026
df38a74
TrigScript 4: F2 to F5 are keys Remastered keeps, as F6 is
jeany55 Oct 17, 2026
d3a7c7d
Merge pull request #11 from scm-js/b2-events
jeany55 Oct 17, 2026
010e5bc
TrigScript 4, B2: on.killed, on.order, on.morphed, listeners at the top
jeany55 Oct 17, 2026
e96c5d2
TrigScript 4, B2: the second events probe's A and B, played in Remast…
jeany55 Oct 17, 2026
04c1e37
TrigScript 4, B2: the second events probe's C and D, played in Remast…
jeany55 Oct 17, 2026
0c04c19
TrigScript 4, B2: the events probe's Zerglings to storm stand where t…
jeany55 Oct 17, 2026
68f642a
TrigScript 4, B2: the Zerglings to storm are the player's own
jeany55 Oct 17, 2026
6c4066c
TrigScript 4, B2: a unit killed by its own player has no killer
jeany55 Oct 17, 2026
11284cf
TrigScript 4, B2: a Drone into a Hatchery, played in Remastered
jeany55 Oct 17, 2026
fd33cdd
TrigScript 4, B2: the second events probe played through in Remastered
jeany55 Oct 17, 2026
d94d169
Merge pull request #12 from scm-js/b2-events-2
jeany55 Oct 17, 2026
22b5430
TrigScript 4: Find an EUD Error, from Remastered's code back to the line
jeany55 Oct 17, 2026
e7e096e
Merge pull request #13 from scm-js/eud-error-line
jeany55 Oct 17, 2026
7696363
TrigScript 4, S4: Bad Apple drawn with fog of war, played in Remastered
jeany55 Oct 17, 2026
dcda029
TrigScript 4, S4: the board on the minimap's whitest tile
jeany55 Oct 17, 2026
e75f753
TrigScript 4, B7: memory, cells of the game with a type and Remastere…
jeany55 Oct 17, 2026
201b11b
TrigScript 4: the standard library, and fog as its first module
jeany55 Oct 17, 2026
429758e
TrigScript 4: the plan's As built for memory and the fog library
jeany55 Oct 17, 2026
fdeddef
TrigScript 4: fog.light, one verb for a disc and a picture, and dark ink
jeany55 Oct 17, 2026
f55b1dd
Merge pull request #14 from scm-js/b7-memory
jeany55 Oct 17, 2026
cfbab58
TrigScript 4, B8: videos a script imports, drawn in the fog
jeany55 Oct 17, 2026
d2a9f72
TrigScript 4, B8: Import Video… in the editor, decoded with WebCodecs
jeany55 Oct 17, 2026
9407cbd
TrigScript 4, B8: Bad Apple in TrigScript played in Remastered
jeany55 Oct 17, 2026
c0202d0
Merge pull request #15 from scm-js/b8-imports
jeany55 Oct 17, 2026
c6fa7e6
TrigScript 4, B6: fixed numbers and Math, the same bits in the game a…
jeany55 Oct 17, 2026
a5e653e
TrigScript 4, S5: a raycaster in the fog, and fog.columns to draw it
jeany55 Oct 17, 2026
fddcac9
TrigScript 4, S5: the raycaster as a level, the simulator draws the fog
jeany55 Oct 17, 2026
6b4027c
TrigScript 4: the plan's As built for B6, and S5
jeany55 Oct 17, 2026
9f671e7
TrigScript 4, S5: panelled walls, fog.show, the raycaster after its f…
jeany55 Oct 17, 2026
335eb80
TrigScript 4, S5: outline, the walls as their edges
jeany55 Oct 17, 2026
56cf80c
TrigScript 4: the plan's S5, as played
jeany55 Oct 17, 2026
757454f
TrigScript 4: S5's third round, played
jeany55 Oct 17, 2026
7d29393
Merge pull request #16 from scm-js/b6-fixed
jeany55 Oct 17, 2026
f5f587c
TrigScript 4, B3: functions as values
jeany55 Oct 18, 2026
9682696
Merge pull request #17 from scm-js/b3-function-values
jeany55 Oct 18, 2026
f4c11a4
TrigScript 4, B4: try / catch / finally and throw
jeany55 Oct 18, 2026
625e1cf
TrigScript 4, B4: generators' finally, caught errors' methods, stack,…
jeany55 Oct 18, 2026
8f0cd0a
Merge pull request #18 from scm-js/b4-try-catch
jeany55 Oct 18, 2026
925c08a
TrigScript 4, B5: the local-reads probe
jeany55 Oct 18, 2026
4d159e7
TrigScript 4, B5 probe: F8 selects a Marine, not MSQC's Valkyrie
jeany55 Oct 18, 2026
23ebe5c
TrigScript 4, B5 probe: line 3 shows the selection the game holds for…
jeany55 Oct 18, 2026
00a35e4
TrigScript 4, B5: the local-reads probe's answers in the plan
jeany55 Oct 18, 2026
b503695
TrigScript 4, B5: local values and sync()
jeany55 Oct 18, 2026
ba59262
TrigScript 4, B5: the first part as built, and a map to play
jeany55 Oct 18, 2026
dc52e12
TrigScript 4, B5: the local-values map, played
jeany55 Oct 18, 2026
229507d
TrigScript 4, B5: the local-values map, played in multiplayer
jeany55 Oct 18, 2026
c102ba2
TrigScript 4, B5: sync() of several values, the selection and select()
jeany55 Oct 18, 2026
9d60bff
TrigScript 4, B5: the second part as built, and a map to play
jeany55 Oct 18, 2026
cd020c8
TrigScript 4, B5: the selection map, played
jeany55 Oct 18, 2026
3381390
TrigScript 4, B5: the selection map, played in multiplayer
jeany55 Oct 18, 2026
d7e0cef
TrigScript 4, B5: IR 23 in docs/ir.md
jeany55 Oct 18, 2026
f45665d
Merge pull request #19 from scm-js/b5-local
jeany55 Oct 18, 2026
4a4add3
TrigScript 4: closures over locals, increments as values, functions t…
jeany55 Oct 18, 2026
2d07a52
Tests: put back two values the IR version change caught
jeany55 Oct 18, 2026
90e1ae4
TrigScript 4: text and array methods, JSX components, functions from …
jeany55 Oct 18, 2026
b90c11c
TrigScript 4: console tests, and the plan as built
jeany55 Oct 18, 2026
c5dac32
Standard library: trigscript/console is trigscript/screen
jeany55 Oct 18, 2026
30d54ac
TrigScript 4: generics; trigscript/timers, signals, random and math
jeany55 Oct 18, 2026
b350133
Screen.reveal, and ui.lines fills the text lines
jeany55 Oct 18, 2026
cd1f2be
Snake on a 64 x 64 map; the screen's colours in the text lines' order…
jeany55 Oct 18, 2026
3dc1362
Snake: the length meter out of 60, so it fills as the snake grows
jeany55 Oct 18, 2026
f558d9f
Screen: a solid look, each pixel a Pylon
jeany55 Oct 18, 2026
c3b7163
Plan: the solid screen played; dots stay the default
jeany55 Oct 18, 2026
68ec388
Merge pull request #20 from scm-js/closures-text
jeany55 Oct 18, 2026
a1fe218
Plan: what is still missing, and B9, references and values
jeany55 Oct 18, 2026
d915e0b
References (B9): classes kept by reference as rows of the object table
jeany55 Oct 18, 2026
714d85c
References: counting sees past a call standing as a statement; tests …
jeany55 Oct 18, 2026
94440df
References: the sweep asks a row's class once and clears only its col…
jeany55 Oct 18, 2026
ba48c25
Values (B9): records and instances of classes nothing changes in plac…
jeany55 Oct 18, 2026
b165ddf
Values (B9): an array of a union of records holds every kind's fields
jeany55 Oct 18, 2026
9581e07
Plan: B9 as built
jeany55 Oct 18, 2026
2e54adb
Generators finished, and async functions in libraries
jeany55 Oct 18, 2026
bb39cea
trigscript/cutscene, and what it needed of the language
jeany55 Oct 18, 2026
523bd85
Plan: generators finished, async functions in libraries, trigscript/c…
jeany55 Oct 18, 2026
eae2bcd
typeof narrowing in c ? a : b, and Date.now()/Math.random() refused i…
jeany55 Oct 18, 2026
8a6608f
Merge pull request #21 from scm-js/refs-values
jeany55 Oct 18, 2026
aa579d2
Plan: a second sweep, and what is left
jeany55 Oct 18, 2026
d25dc80
The second sweep's small gaps, and its fault
jeany55 Oct 19, 2026
e55af1c
Merge pull request #22 from scm-js/small-gaps
jeany55 Oct 19, 2026
2489953
Cost step 1: the weights probe, and what Remastered charges
jeany55 Oct 19, 2026
70bafa8
Cost step 1: the weights as played
jeany55 Oct 19, 2026
07cf6ba
Cost in Remastered's time: the weights in A4
jeany55 Oct 19, 2026
847b342
Merge pull request #23 from scm-js/frame-weights
jeany55 Oct 19, 2026
c99f404
The lowering bench: what a turn of each kind of statement costs Remas…
jeany55 Oct 19, 2026
ab4f47f
Cheaper lowering: adds in place, loop counters, ANDs in one trigger
jeany55 Oct 19, 2026
bf4dfda
Cheaper lowering: cells of their own, and a[i] -= v wraps
jeany55 Oct 19, 2026
bc3a847
Cheaper lowering: & and | with a constant as a masked write, stores c…
jeany55 Oct 19, 2026
1e5d945
Merge pull request #24 from scm-js/cheaper-lowering
jeany55 Oct 19, 2026
1c67d69
A view put together only when it changes: calls followed, and the ref…
jeany55 Oct 19, 2026
39df005
Merge pull request #25 from scm-js/view-calls
jeany55 Oct 19, 2026
5d08bbe
Screen: present() looks only at what was drawn; arrays of cells fille…
jeany55 Oct 19, 2026
aa2251d
Cheaper numbers: fixed products, quotients and roots, shifts, product…
jeany55 Oct 19, 2026
afb159c
Merge pull request #26 from scm-js/screen-present
jeany55 Oct 19, 2026
8515430
Merge pull request #27 from scm-js/fixed-math
jeany55 Oct 19, 2026
ff40ef0
A loop over units costs what the map holds: the table walked as the g…
jeany55 Oct 19, 2026
34c1653
Merge pull request #28 from scm-js/unit-lists
jeany55 Oct 19, 2026
1512caf
Plan: arrays at their cost — the bullets bench, what an array that gr…
jeany55 Oct 19, 2026
83df7ac
Arrays at their cost: growing arrays in cells of their own, an index …
jeany55 Oct 19, 2026
4875c78
The fast-cell probe: the neutral players' ore, gas and score, and Pla…
jeany55 Oct 19, 2026
52d185f
A division whose result is small, as comparisons: Math.min(H, Math.fl…
jeany55 Oct 19, 2026
dd62972
Fast cells played: tables outside the emulator 2.5x cheaper an action…
jeany55 Oct 19, 2026
45ac0d7
Merge pull request #29 from scm-js/cost-abstractions
jeany55 Oct 19, 2026
654bd83
Plan: the limits, and a backend of our own — two probes, value ranges…
jeany55 Oct 19, 2026
e0e1461
Two probes for the limits: a map that is not EUD (classic weights) an…
jeany55 Oct 19, 2026
85a84db
Value ranges: the values each number can take, and a lowering that ta…
jeany55 Oct 19, 2026
5a5c913
Backend, part 1: static routing — a variable read at one site and tou…
jeany55 Oct 19, 2026
83b5743
Backend, part 1b: routing set once in front of a loop, and copies for…
jeany55 Oct 19, 2026
7670971
Backend, part 2: record loops — reads and writes at a loop's counter …
jeany55 Oct 19, 2026
eb7b7eb
Plan: the backend's next steps; a loop's test turned round tried and …
jeany55 Oct 19, 2026
9570013
Plan: the two probes played — a classic map wins nothing; a native qu…
jeany55 Oct 19, 2026
ca08356
Probe: does Bring read a moved location's old counts in the same pass
jeany55 Oct 19, 2026
26e7a11
Plan: the stale-location probe played — writing a location's sides or…
jeany55 Oct 19, 2026
86a7788
A count after a location moves or units are made in it counts again: …
jeany55 Oct 19, 2026
e0f854d
Backend, part 3: fixed products and quotients in loops over cells of …
jeany55 Oct 19, 2026
08bd187
Images a script imports: .image files and a build-time Image
jeany55 Oct 19, 2026
bf82d2a
Halftone: greys in the fog, and a probe to play them
jeany55 Oct 19, 2026
78e6fc7
The emitter, E1: loops of numbers lowered to triggers of our own, eve…
jeany55 Oct 19, 2026
0e6c52c
Plan: the halftone probe as played
jeany55 Oct 19, 2026
824008e
Tile survey: each tile's minimap colour lit and in fog, for a recordi…
jeany55 Oct 19, 2026
c00c075
The emitter: islands — a statement it does not lower stays in a taken…
jeany55 Oct 19, 2026
a97d298
Plan: Remastered's minimap surveyed — snow, and fog at 0.64 of lit
jeany55 Oct 19, 2026
b3bc723
Fog maps on snow for Remastered graphics: BOARD=snow in the base map
jeany55 Oct 19, 2026
2053bf3
Plan: snow played in Remastered
jeany55 Oct 19, 2026
219f98e
The fog library, faster: brushes placed in pixels, a location's sides…
jeany55 Oct 19, 2026
f4339e3
Snow is the fog maps' board; the fog library says why
jeany55 Oct 19, 2026
055aaf6
Merge pull request #30 from scm-js/image-import
jeany55 Oct 19, 2026
90d96c2
Sound probe: timing, unlisted sounds, restart, channels, copies, loud…
jeany55 Oct 19, 2026
39267d3
Plan: the sound probe as played
jeany55 Oct 19, 2026
4728d24
The emitter, E3: more of a loop its own — calls inlined, cells of kno…
jeany55 Oct 19, 2026
0377596
Merge emitter-e3: more of a loop the emitter's own
jeany55 Oct 19, 2026
8828c19
Sounds a script imports or makes: .sound files and a build-time Clip
jeany55 Oct 19, 2026
d560f22
Sound demo: an import, a scale, a melody, drums, a chord, made at bui…
jeany55 Oct 19, 2026
711ae11
A tone ADPCM would make harsh goes in the map as 16-bit; square and s…
jeany55 Oct 19, 2026
f03c9a2
The emitter on by default: its probe played in Remastered prints what…
jeany55 Oct 19, 2026
51c331f
Plan: the emitter's heading back in place, and its probe played in Re…
jeany55 Oct 19, 2026
fb369e6
Merge trigscript-4 (images, halftone, snow) into backend-limits
jeany55 Oct 19, 2026
dd7510e
The standard library's "trigscript/music": instruments, patterns, songs
jeany55 Oct 19, 2026
5a410ef
Plan: the music demo as played
jeany55 Oct 19, 2026
9bf279b
Merge pull request #31 from scm-js/backend-limits
jeany55 Oct 19, 2026
f0ba2ca
Merge pull request #32 from scm-js/sound-probe
jeany55 Oct 19, 2026
59b22d6
Probe: what Remastered offers past eudplib — native registers, done t…
jeany55 Oct 19, 2026
4372cc2
The emitter, E4: stores into arrays, a counter's offsets as its place…
jeany55 Oct 19, 2026
d67ba48
A map of our own: no eudplib, no Python — our own layout after the st…
jeany55 Oct 19, 2026
fb748f4
Plan: the own-loader map played in Remastered — all eight lines ok
jeany55 Oct 19, 2026
5d1c18d
Plan: the own-loader map quits cleanly in Remastered
jeany55 Oct 19, 2026
8b06dd3
The emitter's own reciprocal, whole-number division in cells, each ac…
jeany55 Oct 19, 2026
1e3a4f2
The emitter: switches, loops the front end unrolled, booleans, cells …
jeany55 Oct 19, 2026
a396fb5
The emitter: shifts and copies after the Python side's statements by …
jeany55 Oct 19, 2026
50296fe
The emitter: a statement that reads a value computed ahead of the loo…
jeany55 Oct 19, 2026
3bd784d
Plan: the emitter's second round as built, with its numbers and what …
jeany55 Oct 19, 2026
d8c384e
The graph's scratch cells without an initial value: every read of one…
jeany55 Oct 19, 2026
3acb395
Merge emitter-e4
jeany55 Oct 19, 2026
44dc4c7
Merge primitives
jeany55 Oct 19, 2026
10309e1
Merge own-loader
jeany55 Oct 19, 2026
847ef70
Plan: the primitives probe as played — resources a sixth of an EUD ce…
jeany55 Oct 19, 2026
e089327
Plan: the emitter's second probe played in Remastered — stores and re…
jeany55 Oct 19, 2026
028396a
Merge pull request #33 from scm-js/beyond-eudplib
jeany55 Oct 19, 2026
2f56d81
Fixture maps' round trip: a text or a sound in a field the action doe…
jeany55 Oct 19, 2026
4b3010c
Merge pull request #34 from scm-js/wakka-roundtrip
jeany55 Oct 19, 2026
3084a6d
Plan: past eudplib — the machine the measurements describe, eighteen …
jeany55 Oct 19, 2026
75633e0
Calls in a map of our own: arguments into the callee's places, back b…
jeany55 Oct 19, 2026
e729e24
Calls cost less than eudplib's: results through their cells, copies r…
jeany55 Oct 19, 2026
e5dd335
Plan: calls on our own backend as built — the first measure, the thre…
jeany55 Oct 19, 2026
520ff0b
Plan: the own-calls probe as played in Remastered — every line ok, ro…
jeany55 Oct 19, 2026
0064bdb
Merge pull request #35 from scm-js/calls
jeany55 Oct 19, 2026
7eb4951
Ranges: the debug switch read only where there is a process
jeany55 Oct 19, 2026
eca3d5c
Sounds, step 1: two channels, a limiter, sinc resampling, Ogg Vorbis …
jeany55 Oct 19, 2026
993770e
Plan: the Korobeiniki A/B as played: the Ogg on time, whole, both sid…
jeany55 Oct 19, 2026
07168d6
Sounds, step 2: filters, drive, echo, plate reverb, chorus, compresso…
jeany55 Oct 19, 2026
5c83ab1
Plan: step 2 as played
jeany55 Oct 19, 2026
c9d7e5b
Music: songs of sections, chords by name, accents, swing, risers
jeany55 Oct 19, 2026
9d847e4
Music: import a MIDI file as notes, and music.midi makes a song of them
jeany55 Oct 19, 2026
d7d3eaa
Sound: the limiter keeps the wave between samples under its ceiling
jeany55 Oct 19, 2026
33d67c0
Plan: step 3 as played
jeany55 Oct 19, 2026
01f0547
Music: songs cut into bars and layers that the game steers bar by bar
jeany55 Oct 19, 2026
ead7eab
Sound: an Ogg goes in with two channels, and stingers fit under the bars
jeany55 Oct 20, 2026
41c873a
Music: stingers on the next beat, and songs cut every few beats
jeany55 Oct 20, 2026
8229c69
Sounds: the tools and the recipe to make the Korobeiniki work again
jeany55 Oct 20, 2026
02dec78
Tests: stems.playthrough is the pieces added up at their times
jeany55 Oct 20, 2026
dfb9dcf
Merge pull request #36 from scm-js/music-quality
jeany55 Oct 20, 2026
3a62c91
Recursion in a map of our own: a stack of cell triggers for each func…
jeany55 Oct 20, 2026
e5d8a2f
Plan: recursion on our own backend, as built and as played; the rando…
jeany55 Oct 20, 2026
e6dfa9c
Merge pull request #37 from scm-js/recursion
jeany55 Oct 20, 2026
3cd0064
Texts with numbers in them, in a map of our own: a template a print, …
jeany55 Oct 20, 2026
c599ffb
Plan: the own-print probe as played in Remastered — every line as the…
jeany55 Oct 20, 2026
e617cd7
Merge pull request #38 from scm-js/digits
jeany55 Oct 20, 2026
a6472f4
Arrays of a fixed length in maps of our own
jeany55 Oct 20, 2026
3d53581
Plan: the arrays probe played in Remastered
jeany55 Oct 20, 2026
1b8e1a8
Merge pull request #39 from scm-js/arrays
jeany55 Oct 20, 2026
490f3dc
A map of our own refuses what it built wrong without a word: a text f…
jeany55 Oct 20, 2026
7407c88
Plan: the way off eudplib, audited — what the nine steps left out, se…
jeany55 Oct 20, 2026
4e642bf
Merge pull request #40 from scm-js/off-eudplib-audit
jeany55 Oct 20, 2026
08cc73a
A map of our own says what it refuses: the part the emitter never tak…
jeany55 Oct 20, 2026
d87284f
A field read straight off a pick is the game's, not undefined when th…
jeany55 Oct 20, 2026
9b9c3da
The action order() compiles inside a program: it is not the event on.…
jeany55 Oct 20, 2026
de89495
music.loop(song) compiles: the wait at the end of each turn is the lo…
jeany55 Oct 20, 2026
6d3bf90
The ledger: a script for each feature, its step off eudplib, and what…
jeany55 Oct 20, 2026
90372f1
Plan: the ledger as built, by step, and what writing it found
jeany55 Oct 20, 2026
43b68a3
Merge pull request #41 from scm-js/ledger
jeany55 Oct 20, 2026
e4ab2db
The action order() gives orders.Move, orders.Patrol and orders.Attack…
jeany55 Oct 20, 2026
40d481d
A function that keeps a state is the program's, not worked out once w…
jeany55 Oct 20, 2026
569e549
Products, quotients and remainders in a map of our own: step 4, part 1
jeany55 Oct 20, 2026
e6cb5d1
Bits and shifts in a map of our own: step 4, part 2
jeany55 Oct 20, 2026
8f102d4
Own backend, numbers part 3 and 4: fixed point and random numbers
jeany55 Oct 20, 2026
b631877
Own backend, numbers part 5: square roots, sines, arc tangents, u8 an…
jeany55 Oct 20, 2026
9bdb136
Emitter tests: four heavy operations a random program, and five minutes
jeany55 Oct 20, 2026
9a0fa08
Merge pull request #42 from scm-js/wrong-builds
jeany55 Oct 20, 2026
f4d7394
Merge pull request #43 from scm-js/numbers
jeany55 Oct 20, 2026
d35a1c3
Own backend, the game's conditions and actions: step 5, part 1
jeany55 Oct 20, 2026
7ba9ac2
Own backend, fields from values and made texts: step 5, part 2
jeany55 Oct 20, 2026
a1d3e7c
Merge pull request #44 from scm-js/game-actions
jeany55 Oct 20, 2026
c8909e7
Own backend, the game read as a value: step 5, part 3
jeany55 Oct 20, 2026
ac002c0
Own backend: a game number compared with a value is the condition itself
jeany55 Oct 20, 2026
75bb069
Own backend, locations, players and latches: step 5, part 4
jeany55 Oct 20, 2026
2717d36
Own backend, prints at the centre of the screen: step 5 done but Wait
jeany55 Oct 20, 2026
70a910b
Merge pull request #45 from scm-js/game-reads
jeany55 Oct 20, 2026
8929ad0
Own backend, the game's tables: step 6
jeany55 Oct 20, 2026
c3532f0
Merge pull request #46 from scm-js/game-memory
jeany55 Oct 20, 2026
cab8a08
Own backend, programs, owners and players: step 7
jeany55 Oct 20, 2026
d2a1ad8
Step 7's test: the declaration's place, which the type-check wants
jeany55 Oct 20, 2026
1eb057e
Merge pull request #47 from scm-js/programs
jeany55 Oct 20, 2026
f188002
Own backend, texts known when built chosen while the game runs: step …
jeany55 Oct 20, 2026
1a901a1
Merge pull request #48 from scm-js/texts
jeany55 Oct 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ jobs:
# `dist/testing.js` and its declarations are what another repository's tests import at
# a tag to run triggers, so they are held to the same rule.
with:
artifacts: dist/compiler.js dist/testing.js dist/testing.d.ts
artifacts: dist/compiler.js dist/testing.js dist/testing.d.ts dist/gametest.js
secrets: inherit
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ node_modules/
*.tsbuildinfo
# Blizzard's maps, copied in for the round-trip test; never committed.
fixtures/
# Videos a probe imports (probes/bad-apple.ts): not ours, made on the spot with scripts/import-video.mts.
probes/*.video
# The halftone probe's pictures and walks: made on the spot by probes/lib/halftone-gen.mts.
probes/*.image
# The sound demo's imported sound: made on the spot by probes/lib/sound-demo-gen.mts.
probes/*.sound
33 changes: 18 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ import { test, expect, program, keyPressed, chatted, setResources, print, sleep,
program(() => {
let shop = false;
while (true) {
if (keyPressed(CurrentPlayer, "F2")) { shop = !shop; print(shop ? "Shop open" : "Shop closed"); }
if (keyPressed(CurrentPlayer, "F7")) { shop = !shop; print(shop ? "Shop open" : "Shop closed"); }
const give = chatted(CurrentPlayer, "-give {n}");
if (give && shop) setResources(CurrentPlayer, "add", give.n, "ore");
sleep(frames(1));
Expand All @@ -287,7 +287,7 @@ program(() => {
test("-give pays only while the shop is open", (sim) => {
sim.type("-give 100").frames(2);
expect(sim.resources(0 as Player).ore).toBe(0);
sim.press("F2").frames(1);
sim.press("F7").frames(1);
expect(sim.program("shop").shop).toBe(true);
sim.type("-give 100").frames(2);
expect(sim.resources(0 as Player).ore).toBe(100);
Expand Down Expand Up @@ -417,7 +417,7 @@ with a message, never passed over in silence.
| `boolean` | Same | Assigned with `=` only: no `\|\|=`, `&&=`. |
| `string` | Mostly | A value with no length to declare. `length`, `s[i]` and `slice` count characters (code points), where JavaScript counts UTF-16 units: they differ only past U+FFFF. A made text holds 1 023 bytes. The methods are a subset: no `split`, `replace`, `trim`, `toUpperCase`, `parseInt`, regular expressions. |
| `null`, `undefined` | Units only | `Unit \| null` is real (`first(…)`, `if (u)`, `u?.kill()`). A number, a boolean or a text is never either: where JavaScript would give `undefined` — `pop()`, `find()`, `get()`, `at()` — say what it is then (`xs.pop() ?? 0`). No optional fields or parameters (`y?: number`); a parameter's default (`y = 1`) is there. |
| Operators: `+ - * / %`, comparisons, `&&` `\|\|` `!`, `c ? a : b`, `& \| ^ << >> >>>`, `??`, `?.`, `++` `--`, the compound assignments, the comma | Same | `==` and `===` are one thing, since nothing is coerced. A shift by 32 or more leaves nothing, where JavaScript shifts by the remainder. Missing: `**`, `typeof`, `in`, `delete`. |
| Operators: `+ - * / %`, comparisons, `&&` `\|\|` `!`, `c ? a : b`, `& \| ^ << >> >>>`, `??`, `?.`, `++` `--`, the compound assignments, the comma | Same | `==` and `===` are one thing, since nothing is coerced. A shift by 32 or more leaves nothing, where JavaScript shifts by the remainder. `typeof x === "…"` and `"key" in x` work where the answer is known when the script is built — a function written for a number or a text is inlined with the side its argument takes — and not as tests in the game. Missing: `**`, `delete`. |
| `if`, `while`, `do`, `for`, `for…of`, `switch`, `break`, `continue` | Same | A loop runs all its rounds within one frame of the game, so one that never ends must `sleep()` on every path; `while (true)` left only by a `break` needs the sleep too. A `for` over bounds the script knows is unrolled. No `for…in`, no labels. |
| Functions: declarations, parameters by value, defaults, rest parameters, recursion, generics | Same | The compiler inlines a function or calls it; the meaning is the same. One that calls itself cannot `sleep()` and has a depth limit. A function declared inside another does not see the outer one's locals (it sees the program's). It returns a number, a boolean, a text, a unit or an instance — not a record or an array it made. |
| Arrow functions, closures, functions as values | Callbacks only | An arrow is written where a method takes it (`xs.map((x) => x + bonus)`), and sees every variable in reach. It cannot be kept: not in a variable, an array, a field or a return value. |
Expand All @@ -430,7 +430,7 @@ with a message, never passed over in silence.
| `try` / `catch` / `throw` | Missing | The game has no exceptions. What is always a mistake — an index past the end, a full heap, a stack overflow — is said in the game in red, or listed by Simulate. |
| `async` / `await`, promises, generators, timers | Missing | `sleep()` is how a program waits: it gives the frame back and carries on later, and may stand anywhere but in a function that calls itself or a loop over units. Something on its own clock is another `program()`. |
| Modules | Same | `import` between the script's files and from `"trigscript"`; nothing from npm. A function another file's program calls is made with `game()`. |
| The standard library | A little | `Math.min`, `Math.max`, `Math.abs` (and the rounding ones, which change nothing), `String(n)`, `n.toString()`, `Array.from`, `new Array(n).fill(v)`. For `console.log` there is `print()`, for `Math.random()` there is `random(n)`. No `JSON`, `Date`, `RegExp`, `Object.*`, and no `Math.sqrt` or `**` on a variable. |
| The standard library | A little | `Math.min`, `Math.max`, `Math.abs` (and the rounding ones, which change nothing), `String(n)`, `n.toString()`, `Array.from`, `new Array(n).fill(v)`. For `console.log` there is `print()`, for `Math.random()` there is `random(n)`. Inside a program `Math.random()`, `Date.now()` and `new Date()` are refused, since they would be worked out once when the map is built; game time is `elapsed()` or `memory.game.frame`. No `JSON`, `Date`, `RegExp`, `Object.*`, and no `Math.sqrt` or `**` on a variable. |
| — | Added | What TypeScript has no word for: `sleep`, `rose` and `once`, reads of the game (`minerals(p)`), `Unit` objects and loops over units, `stats()`, `keyPressed` / `clicked` / `mouse` / `chatted`, `shared()`, per-player programs. |

The sections below say each of these in full, and [What a program does not
Expand Down Expand Up @@ -1197,8 +1197,11 @@ if (target) target.order("move", locations.Exit);
`randomUnit(filter?)` pick one unit or `null`, and TypeScript wants the `if (target)` before
the unit is used (or `target?.kill()`). A filter is `{ type, owner, at }`, every part known
when you build; `units.Men`, `units.Buildings` and `units.Factories` work as a type, and
`CurrentPlayer` as an owner. Units come in the order of the game's unit table; a dying unit
is passed over, so `first()` after a `kill()` is the next one.
`CurrentPlayer` as an owner. Units come oldest first: the game's unit table in the order the
game hands its places out (slot 0, then 1699 downwards), the walk stopping once it has met
every unit in use, so a loop costs what the map holds. A unit made in the place of one that
died, once the game has freed it, comes where the dead one did. A dying unit is passed over,
so `first()` after a `kill()` is the next one.

A unit has `hp`, `shields` and `energy` in whole points, `maxHp` / `maxShields` of its
type, `owner`, `type`, `x`, `y`, `kills`, `orderId`, `cooldown`, `resources` (a mineral
Expand Down Expand Up @@ -1292,8 +1295,9 @@ program(() => {

`keyPressed(player, key)` is true when the press arrives — once per press, not while the
key is held (the game reports no held key), and not while the player is typing a message.
Keys are the letters and digits, `"F1"` … `"F12"` without `"F6"` (the game keeps it to
itself and reports no press of it — played and seen — so it is an error to ask), `"Space"`, `"Enter"`, `"Escape"`,
Keys are the letters and digits, `"F1"`, `"F7"` … `"F12"` (the game keeps F2 to F6 to
itself and reports no press of them — played and seen; F2 to F4 move the camera — so it is
an error to ask), `"Space"`, `"Enter"`, `"Escape"`,
`"Tab"`, `"Shift"`, `"Ctrl"`, `"Alt"`, the arrows (`"Left"` …), `"Backspace"`, `"Delete"`,
`"Insert"`, `"Home"`, `"End"`, `"PageUp"`, `"PageDown"` and `"Numpad0"` … `"Numpad9"`; the
game's own use of a key still happens. `clicked(player, "left" | "right" | "middle")` is
Expand Down Expand Up @@ -1479,14 +1483,12 @@ a message that says so, none is passed over in silence:
| --- | --- |
| Fractions: every number is whole | work in a smaller unit (pixels, frames, hundredths) |
| `**`, `Math.sqrt`, `Math.sign` and the rest of `Math` on a variable — `min`, `max` and `abs` are there, and the rounding ones around a division | a loop, or a list the script worked out (`const roots = Array.from({ length: 100 }, (_, n) => Math.floor(Math.sqrt(n)))`, then `roots[n]`) |
| A function kept as a value: `const f = (x: number) => x + k`, a function in an array, one returned | `function f(x: number) { … }` in the program; an arrow is fine where a method takes one |
| A function that returns a record or an array it made | hand it the record or the array to fill in; an instance of a class is the exception (*Classes*) |
| `try`, `catch`, `throw` — the game has no exceptions | return a number that says what happened |
| `async`, generators, `for…in`, a labelled `break`, `typeof` | — |
| A function that returns an array it made (a record or an instance it made is fine) | hand it the array to fill in |
| `for…in`, a labelled `break` | `for…of`, a flag tested after the inner loop |
| An `enum` declared inside the program | declare it above the program: its members are numbers the script has, and a variable holds one |
| On an array: `shift`, `unshift`, `splice`, `at`, `lastIndexOf`, `join` of numbers | `pop` and `push`, a place kept in a variable, `xs[xs.length - 1]`, a template in a loop |
| What `splice` takes out, as a value | read it first: `xs.slice(start, end)` |
| `map` into texts or records | `push` in a `for…of` |
| On a made text: `split`, `trim`, `replace`, `toUpperCase`, `lastIndexOf`, `parseInt`, regular expressions | keep the number beside the text and make the text from it |
| On a made text: regular expressions, and `replace` with a function | `replace` / `replaceAll` / `split` with a text |
| A `Map` whose values are texts or records, or whose keys are texts | the place of a row of an array of records as the value |
| Writing a unit's position, cloak or tint | the game refuses these itself (*Units*) |

Expand Down Expand Up @@ -1753,7 +1755,8 @@ stored label. Arithmetic is 32 bits that wrap, and each comparison, division, sh
right, `min`, `max` and printed number reads them signed or unsigned as `numbers.ts`
marked it. An array that grows is a block of one heap the programs share — sizes double,
a block given back serves the next array of its size — and a made text is such a block
of bytes. A function that is called is one body with its parameters as variables; one
of bytes; an array's own cells sit beside the heap's bytes, a variable each, so that a
loop's counter reads and writes them in a few triggers. A function that is called is one body with its parameters as variables; one
that calls itself keeps what a call would overwrite on a stack of its own, an array
beside the heap whose depth the script's Settings give. `docs/ir.md` says what each node
means to both backends. A per-player program loops over the
Expand Down
40 changes: 34 additions & 6 deletions compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ export const libUrl = (dist: string = DEFAULT_DIST) => `${dist.replace(/\/+$/, "
export const compilerUrl = (dist: string = DEFAULT_DIST) =>
dist === DEFAULT_DIST ? `https://cdn.jsdelivr.net/gh/scm-js/plugin-trigscript@v${VERSION}/dist/compiler.js` : `${dist.replace(/\/+$/, "")}/compiler.js`;

/**
* What runs a script's tests beside the lifted game (`dist/gametest.js`, from `compiler/gameTestEntry.ts`), at this
* release's own tag like the compiler: its text is handed to the engine's page (TrigScript 4, A2).
*/
export const gametestUrl = (dist: string = DEFAULT_DIST) =>
dist === DEFAULT_DIST ? `https://cdn.jsdelivr.net/gh/scm-js/plugin-trigscript@v${VERSION}/dist/gametest.js` : `${dist.replace(/\/+$/, "")}/gametest.js`;

/** What Remastered is known to allow (`data/allowlist.json`, copied into `dist/` by the build), for Find EUD Error: fetched only then. */
export const allowlistUrl = (dist: string = DEFAULT_DIST) =>
dist === DEFAULT_DIST ? `https://cdn.jsdelivr.net/gh/scm-js/plugin-trigscript@v${VERSION}/dist/allowlist.json` : `${dist.replace(/\/+$/, "")}/allowlist.json`;

/** The Ogg Vorbis encoder (`compiler/vorbis.ts`, `dist/vorbis.wasm`), at this release's own tag: fetched only by a build that has a long clip to put in the map. */
export const vorbisUrl = (dist: string = DEFAULT_DIST) =>
dist === DEFAULT_DIST ? `https://cdn.jsdelivr.net/gh/scm-js/plugin-trigscript@v${VERSION}/dist/vorbis.wasm` : `${dist.replace(/\/+$/, "")}/vorbis.wasm`;

/**
* What the worker is told to import. The editor's loader turns a fetched plugin into
* `blob:` modules, and the compiler's own blob URL is the compiler the main thread would
Expand All @@ -51,18 +66,23 @@ export const compilerUrl = (dist: string = DEFAULT_DIST) =>
export const workerModuleUrl = (dist: string = DEFAULT_DIST) => (ENTRY_URL.startsWith("blob:") && !blobRefused ? ENTRY_URL : compilerUrl(dist));

export const COMPILE_TIMEOUT_MS = 15_000;
/** A build's compile makes the script's sounds too — a long song mixed and encoded — and gets longer. */
export const BUILD_TIMEOUT_MS = 60_000;

export interface CompileInput {
files: ScriptFiles;
names: ScriptNames;
/** Run the script's tests after the compile: the world they start from, and which of them. */
tests?: TestRunOptions;
/** Encode the sounds the map is to get (a build): a check leaves an Ogg Vorbis one as its name (`compiler/vorbis.ts`). */
sounds?: boolean;
}

interface CompileRequest extends CompileInput {
id: number;
moduleUrl: string;
libUrl: string;
vorbisUrl?: string;
}

interface CompileResponse {
Expand All @@ -78,7 +98,7 @@ importScripts(${JSON.stringify(TS_URL)});
let loading = null;
let lib = null;
self.onmessage = async (e) => {
const { id, moduleUrl, libUrl, files, names, tests } = e.data;
const { id, moduleUrl, libUrl, vorbisUrl, files, names, tests } = e.data;
try {
if (!loading) loading = import(moduleUrl);
let mod;
Expand All @@ -90,7 +110,14 @@ self.onmessage = async (e) => {
if (!r.ok) throw new Error("Could not load the standard library from " + libUrl + " (" + r.status + ").");
lib = await r.text();
}
postMessage({ id, result: mod.compileScript(self.ts, files, names, { lib, tests }) });
// A check names the script's clips and makes none of them; a build makes them, the long ones encoded below.
const result = mod.compileScript(self.ts, files, names, { lib, tests, sounds: Boolean(vorbisUrl) });
// A build's long clips encoded as Ogg Vorbis; a check's left as names, their samples not sent.
if (typeof mod.finishSounds === "function") {
const encoder = vorbisUrl && result.sounds.some((s) => s.vorbis) ? await mod.loadVorbis(vorbisUrl) : null;
mod.finishSounds(result.sounds, encoder);
}
postMessage({ id, result });
} catch (err) {
postMessage({ id, error: String((err && err.message) || err) });
}
Expand Down Expand Up @@ -188,14 +215,14 @@ function breakWorker(reason: string) {
}

/** The script ran too long: drop this worker (the next request starts another) and say so. */
function timeOut(id: number) {
function timeOut(id: number, ms: number) {
const p = pending.get(id);
if (!p) return;
pending.delete(id);
worker?.terminate();
worker = null;
rejectAll("The compile was stopped.");
p.reject(new Error(`The script did not finish in ${COMPILE_TIMEOUT_MS / 1000} seconds. Is there an endless loop outside program()?`));
p.reject(new Error(`The script did not finish in ${ms / 1000} seconds. Is there an endless loop outside program()?`));
}

/** The worker, started if there is none. Throws `CompilerUnavailable` when one cannot be made, or could not a moment ago. */
Expand Down Expand Up @@ -247,8 +274,9 @@ export function compileInBackground(input: CompileInput, dist: string = DEFAULT_
p.reject(new CompileSuperseded());
}
return new Promise<CompileResult>((resolve, reject) => {
pending.set(id, { resolve, reject, timer: setTimeout(() => timeOut(id), COMPILE_TIMEOUT_MS) });
const req: CompileRequest = { id, moduleUrl: workerModuleUrl(dist), libUrl: libUrl(dist), files: input.files, names: input.names, ...(input.tests ? { tests: input.tests } : {}) };
const ms = input.sounds ? BUILD_TIMEOUT_MS : COMPILE_TIMEOUT_MS;
pending.set(id, { resolve, reject, timer: setTimeout(() => timeOut(id, ms), ms) });
const req: CompileRequest = { id, moduleUrl: workerModuleUrl(dist), libUrl: libUrl(dist), files: input.files, names: input.names, ...(input.tests ? { tests: input.tests } : {}), ...(input.sounds ? { vorbisUrl: vorbisUrl(dist) } : {}) };
w.postMessage(req);
}).catch((err: Error) => {
if (err.message === RETRY) return compileInBackground(input, dist);
Expand Down
Loading
Loading