ludic.things: a fact's record kept, in two halves swapped when a fact finds the queue empty

th_fact made a ThingFact per placement, removal and use, and the valley places and removes Things
all day. The records come back two drains after they were handed out, so a reader placing a Thing
while it reads the last drain's list never sees one change (the test holds exactly that).
thing_use takes ThingsState mut, since it pushes a fact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 12:33:44 +03:00
parent 22d1e2d66a
commit 8a4f3f5ad6
6 changed files with 52 additions and 12 deletions

View file

@ -52,6 +52,9 @@ away Things all day, so `thing_remove` keeps the record and `thing_put` / `thing
oldest one again - set back as new, with a new `uid` - once 32 are waiting. Hold a Thing across a
removal only by its `uid` (`thing_by_uid`), never by the record.
A fact's record (`things_facts()`) is kept too: it comes back two drains after it was handed out, so
read a drained list before the drain after next, and copy what must last longer.
## The port
Every member has a default, so any may be left out of the `bind`:

View file

@ -21,7 +21,7 @@ export function thing_prompt(t: Thing) -> string {
}
# the kind does what using it does; the use is a fact whatever the kind made of it
export function thing_use(things_st: ThingsState, t: Thing) -> void {
export function thing_use(things_st: mut ThingsState, t: Thing) -> void {
if t == null or not thing_kind_ok(t.kind) { return }
let k = ThingKinds[t.kind]
th_fact(things_st, THING_USED, t)

View file

@ -0,0 +1,23 @@
# facts.ludic - what happened to a Thing, pushed on the package's queue for the game to drain
# a fact's record is one kept for it, not a new one: the halves swap whenever a fact finds the queue
# empty (everything before it drained or cleared), so a record comes back two drains after it was
# handed out - a reader placing a Thing while it reads the last drain's list never sees one change
function th_fact(things_st: mut ThingsState, what: int, t: Thing) -> void {
if q_len(things_st.th_facts) == 0 and things_st.th_fact_n > 0 {
things_st.th_fact_on_b = not things_st.th_fact_on_b
things_st.th_fact_n = 0
}
var pool = things_st.th_fact_a
if things_st.th_fact_on_b { pool = things_st.th_fact_b }
if things_st.th_fact_n >= len(pool) { push(pool, new ThingFact) }
let f = pool[things_st.th_fact_n]
things_st.th_fact_n += 1
f.what = what
f.uid = t.uid
f.kind = t.kind
f.id = t.id
f.owner = t.owner
f.x = t.x
f.z = t.z
q_push(things_facts(things_st), f)
}

View file

@ -6,6 +6,7 @@ import "ludic.base"
import "kinds.ludic"
import "port.ludic"
import "store.ludic"
import "facts.ludic"
import "verbs.ludic"
import "moves.ludic"
import "queries.ludic"

View file

@ -26,6 +26,10 @@ export state ThingsState {
th_hs: words = words(0) # things_tick's handles, taken before any tick runs
th_spare: []Thing = new []Thing # removed records, made again oldest first (spare.ludic)
th_spare_at: int = 0
th_fact_a: []ThingFact = new []ThingFact # the facts' records, two halves (th_fact)
th_fact_b: []ThingFact = new []ThingFact
th_fact_n: int = 0
th_fact_on_b: bool = false
}
function th_facts__new() -> Queue<ThingFact> { return queue_new("things.facts") }
@ -45,17 +49,6 @@ export function things_facts(things_st: ThingsState) -> Queue<ThingFact> {
return things_st.th_facts
}
function th_fact(things_st: ThingsState, what: int, t: Thing) -> void {
let f = new ThingFact
f.what = what
f.uid = t.uid
f.kind = t.kind
f.id = t.id
f.owner = t.owner
f.x = t.x
f.z = t.z
q_push(things_facts(things_st), f)
}
# whether a world has been set out (things_clear) since the program began
export function things_ready(things_st: ThingsState) -> bool { return things_st.th.tab != null }

View file

@ -246,4 +246,24 @@ program ThingsTest {
expect_eq(things_spare_count(things_st), 95)
expect_eq(thing_nearest(things_st, TH_SIGN, 9.0, 9.0).uid, warm.uid)
}
test "a fact's record is kept: a drained list holds still while its reader places a Thing, and comes back two drains on" (things_st: mut ThingsState) {
things_reset(things_st)
q_clear(things_facts(things_st))
let a = thing_put(things_st, TH_SIGN, "sign", 1.0, 0.0, 0.0, 0.0, 2.0, 1)
thing_put(things_st, TH_SIGN, "sign", 2.0, 0.0, 0.0, 0.0, 2.0, 2)
let first = q_drain(things_facts(things_st))
expect_eq(len(first), 2)
let r0: pointer = first[0]
thing_put(things_st, TH_BRANCH, "branch", 3.0, 0.0, 0.0, 0.0, 1.5, 0)
expect_eq(first[0].uid, a.uid)
expect_eq(first[0].what, THING_PLACED)
let second = q_drain(things_facts(things_st))
expect_eq(len(second), 1)
expect_eq(second[0].kind, TH_BRANCH)
thing_put(things_st, TH_LURE, "lure", 4.0, 0.0, 0.0, 0.0, 2.0, 0)
let third = q_drain(things_facts(things_st))
let r2: pointer = third[0]
expect(r2 == r0)
expect_eq(third[0].kind, TH_LURE)
}
}