Skip to content

Own backend: unit events (step 11, part 3) - #57

Merged
jeany55 merged 3 commits into
trigscript-4from
events
Oct 23, 2026
Merged

jeany55 merged 3 commits into
trigscript-4from
events

Conversation

@jeany55

@jeany55 jeany55 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Step 11 of the way off eudplib, part 3: unit events on a map of our own. The ledger's 15 rows of on.created,
on.died, on.killed, on.order and on.morphed are taken, so step 11 is done: 50 of 50. No row of another step
moved.

What it does

  • The map's events pass runs once for the map, before every program's turn. It watches the game's unit places: a
    trigger for each place, whose conditions compare the place's record with what the trigger saw there last, held in
    its own conditions' numbers. That is the uniqueness byte, and, as the events need them, whether the unit is alive or
    which order it has, and its type.
  • A place that hasn't changed costs that one trigger: its conditions hold, and its two actions put the trigger
    before it back and go on to the next. A place that changed goes through a stub of its own to the handler, laid out
    once. If the uniqueness byte differs and the unit is alive, a unit came on. If it was seen alive and isn't now, it
    died. If it took up another order or turned into another type, that is reported too. Then what it sees now goes back
    into the trigger's numbers.
  • A place whose unit is dead or gone is watched by its uniqueness byte alone. The handler writes 0 into the kind of
    the second condition, and the game reads no further. That costs 48 ns a place, against 60 for a living unit.
  • The places are walked newest first, as the simulator hands units on. The walk starts at the place the game will
    hand out next, goes up to 1,699, then does place 0. The game hands places out in that order, never-used ones last, so
    the starting point only moves down. When the handler sees that place handed out, the walk starts one further on from
    then on.
  • A listener is a task that loops over what the pass found. A handler that waits starts a task for each unit.
  • Replaced before this merge: the first commit walked the game's lists by their pointers, as eudplib's build does.
    It was slower than eudplib when units were dying (146 µs against 89) and with on.order (221 against 164). The table
    of plain dwords 336 bytes apart and pointAt went with it.
  • Found on the way: an action whose number comes from a variable is found by its index in its trigger, and a write
    earlier in the same trigger moved it. A task's call (start) with a unit among its parameters was rewritten as an
    expression instead of as statements. An empty trigger whose next pointer gets written has to be kept.

Weighed (probes/own-events.ts, and the same probe without on.order; lifted game, Remastered's weights, µs a
frame)

Ours eudplib
nothing coming or going, on.order among the events 7.2 164
forty-six units dying, on.order among the events 9.0 224
forty units made / killed in a frame, on.order among the events 227 / 228 506 / 528
without on.order: forty-six units dying 8.7 89
without on.order: forty units made / killed in a frame 188 / 180 248 / 392
without on.order: nothing coming or going, about 80 places handed out 6.7 5.1
without on.order: the same, 128 places handed out 9.3 5.2
the first frame (the listeners started) 249 1,272
the map (CHK), on.order among the events / without 1.96 / 1.84 MB 1.78 / 1.27 MB

Still behind eudplib, listed in the plan's Open list

  • Frames where nothing comes on and nothing is dying, without on.order. eudplib's build looks only at the heads
    of its lists then. The watch costs 48–60 ns for every place the game has handed out: 9.3 µs against 5.2 at 128
    places, and about 90 against 5 with the whole table in use. The way out is a gate that skips the watch when nothing
    can have changed: the head of the game's free list (it moves when a unit gets a place or one is freed) plus a count
    that moves when a unit starts to die. That has to be read in both games first, since Remastered links its lists
    differently.
  • The map is larger: 1,700 places at two triggers each. A stub with fewer actions would pack tighter.
  • The handler reads a changed place a bit at a time, about 4 µs for each unit made.

Checked

  • tests/emitter.test.ts, "a map of our own, unit events": a unit table laid out as the game keeps it, changed between
    frames: a unit handed a place, an order taken up, a type changed, a unit dying in a fight, and its place handed to
    another unit. The payload is walked from its bytes frame after frame, each line checked at its frame. Also a handler
    that waits: a task each.
  • Both probes built both ways in the lifted game: every line the same at the same frame, and "ok".
  • Full suite: 1339 passed (72 files); the ledger unchanged (every row still takes). probes/own-tasks.ts and
    probes/own-errors.ts build byte for byte as before. The emitter's random-program tests take as long as before.

Played in Remastered: the first commit's map (recording 21.59.29): all 7 lines in order, each at the lifted game's
frame. To play: Maps/TrigScript/own-step11-events-watch.scx, the merged version, with its answer sheet.

🤖 Generated with Claude Code

https://claude.ai/code/session_01En4SHqafb1QFJ4xgbUkAe5

jeany55 and others added 3 commits October 22, 2026 21:55
Step 11, part 3 of the way off eudplib: the ledger's 15 rows of on.created, on.died,
on.killed, on.order and on.morphed are taken, and step 11 is done (50 of 50). The map's
events pass, once before every program's turn as the Python side's: the units linked in
behind the head of the game's list since the pass before, each not seen before marked
and listed; the dying list both ways from its head; with on.order or on.morphed, every
unit's order and type against the pass before's. What it keeps of a unit place is a table
of plain dwords 336 bytes apart, as the places are, which the layout lays out before the
triggers so that they fill the room between; the current player is pointed at a unit's
dword of it (pointAt). A listener is a task looping over what the pass found.

probes/own-events.ts, lifted game, Remastered's weights, µs a frame (ours / eudplib):
nothing coming or going 4.7 / 5.1, forty units made 210 / 248, forty-six dying 146 / 89,
with on.order 221 / 164; the map 1.05 / 1.78 MB. The walks are slower: the plan says how
to be past eudplib at all of it (a trigger a unit place holding what it last saw).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01En4SHqafb1QFJ4xgbUkAe5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01En4SHqafb1QFJ4xgbUkAe5
The events pass no longer walks the game's lists by their pointers: it is a trigger for
each unit place, whose conditions compare the place's record with what the trigger saw
there last, held in its own conditions' numbers (the uniqueness byte, whether the unit is
alive or its order, its type). A place as it was costs that one trigger; one that changed
goes through its stub to the handler, which lists what came on, died, took up an order or
turned into another type, and writes what it sees back into the trigger. A place whose
unit is dead or gone is watched by its uniqueness byte alone: the handler switches the
second condition off. The places are walked newest first, from the one the game hands out
next. The 336-byte tables and pointAt are gone.

Lifted game, Remastered's weights, µs a frame (ours / eudplib): with on.order, nothing
happening 7.2 / 164, forty-six dying 9.0 / 224; without it, forty-six dying 8.7 / 89,
forty made 188 / 248, forty killed 180 / 392, nothing happening 9.3 / 5.2 with 128 places
handed out (the one frame eudplib's build is ahead on: in the plan's Open list).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01En4SHqafb1QFJ4xgbUkAe5
@jeany55
jeany55 merged commit dd5c1a3 into trigscript-4 Oct 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant