From 58f0dbbc84e11a5032f9373468bb5296080bae80 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 15:53:27 +0300 Subject: [PATCH 1/5] fix(ludic.npc): a name picked and a name let go build nothing - the body's names counted in place, the last eight who left in a ring made with the state ludic deps --allocs (25.2) found npc_pick_name making a list of the body's names at every spawn and np_left making a new list of the recent names at every retire. Co-Authored-By: Claude Opus 5.5 --- packages/ludic.npc/names.ludic | 39 +++++++++++++++++++++++---------- packages/ludic.npc/person.ludic | 3 ++- packages/ludic.npc/tick.ludic | 2 +- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/packages/ludic.npc/names.ludic b/packages/ludic.npc/names.ludic index 48582954..81d6699c 100644 --- a/packages/ludic.npc/names.ludic +++ b/packages/ludic.npc/names.ludic @@ -7,30 +7,47 @@ export function npc_name_used(npc_st: NpcState, n: string) -> bool { for i in 0 .. len(all) { if all[i].active and all[i].name == n { return true } } let posts = npc_posts(npc_st) for i in 0 .. len(posts) { if posts[i].active and posts[i].name == n { return true } } - if npc_st.np_recent != null { for i in 0 .. len(npc_st.np_recent) { if npc_st.np_recent[i] == n { return true } } } + for i in 0 .. len(npc_st.np_recent) { if npc_st.np_recent[i] != "" and npc_st.np_recent[i] == n { return true } } return false } # a free name from the body's pool, from a place in it the dice choose ("" when the pool is empty) export function npc_pick_name(npc_st: NpcState, body: int) -> string { - let pool = new []string - for i in 0 .. NPCN_COUNT { if NpcNames[i].body == body { push(pool, NpcNames[i].name) } } - let n = len(pool) + var n = 0 + for i in 0 .. NPCN_COUNT { if NpcNames[i].body == body { n += 1 } } if n == 0 { return "" } let start = npc_between(npc_st, 0, n - 1) for k in 0 .. n { - let name = pool[(start + k) % n] + let name = np_name_nth(body, (start + k) % n) if not npc_name_used(npc_st, name) { return name } } - return pool[start] + return np_name_nth(body, start) +} +# the body's j-th name in the table, counted in place rather than gathered into a list +function np_name_nth(body: int, j: int) -> string { + var seen = 0 + for i in 0 .. NPCN_COUNT { + if NpcNames[i].body == body { + if seen == j { return NpcNames[i].name } + seen += 1 + } + } + return "" } +# the last eight who left, in a ring of slots made with the state function np_left(npc_st: mut NpcState, name: string) -> void { - push(npc_st.np_recent, name) - if len(npc_st.np_recent) <= 8 { return } - let keep = new []string - for i in len(npc_st.np_recent) - 8 .. len(npc_st.np_recent) { push(keep, npc_st.np_recent[i]) } - npc_st.np_recent = keep + npc_st.np_recent[npc_st.np_rnext] = name + npc_st.np_rnext = (npc_st.np_rnext + 1) % len(npc_st.np_recent) +} +function np_recent_new() -> []string { + let l = new []string + for i in 0 .. 8 { push(l, "") } + return l +} +function np_recent_clear(npc_st: mut NpcState) -> void { + for i in 0 .. len(npc_st.np_recent) { npc_st.np_recent[i] = "" } + npc_st.np_rnext = 0 } # how many pairs share a name, the place's own names included diff --git a/packages/ludic.npc/person.ludic b/packages/ludic.npc/person.ludic index f6f90360..7615671c 100644 --- a/packages/ludic.npc/person.ludic +++ b/packages/ludic.npc/person.ludic @@ -6,7 +6,8 @@ export state NpcState { np_wx: float = 0.0 np_wz: float = 0.0 np_clock: float = 0.0 - np_recent: []string = new []string # the last few names that left + np_recent: []string = np_recent_new() # the last few names that left, a ring + np_rnext: int = 0 np_walkers: []NpcPerson = new []NpcPerson np_posts: []NpcPerson = new []NpcPerson np_max: int = 10 # walkers at most diff --git a/packages/ludic.npc/tick.ludic b/packages/ludic.npc/tick.ludic index 1e9da316..ae7c8a48 100644 --- a/packages/ludic.npc/tick.ludic +++ b/packages/ludic.npc/tick.ludic @@ -85,7 +85,7 @@ export function npc_reset(npc_st: mut NpcState) -> void { npc_st.np_n = 0 npc_st.np_next_h = 0.0 npc_st.np_hold = false - npc_st.np_recent = new []string + np_recent_clear(npc_st) q_clear(npc_facts(npc_st)) } From 7f973f9a4f69a2ebc06ea45783426d10413b55fc Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 15:56:12 +0300 Subject: [PATCH 2/5] fix(ludic.physics): buoyant bodies in a table made with the state (256), a sink taking the last row - floating and sinking push nothing ludic deps --allocs (25.2) found phys_float's five pushes per drop reaching the water and phys_sink's filtered copies per removal. Co-Authored-By: Claude Opus 5.5 --- packages/ludic.physics/shapes.ludic | 3 +- packages/ludic.physics/state.ludic | 7 ++--- packages/ludic.physics/water.ludic | 44 +++++++++++++---------------- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/packages/ludic.physics/shapes.ludic b/packages/ludic.physics/shapes.ludic index f72ae383..c09444df 100644 --- a/packages/ludic.physics/shapes.ludic +++ b/packages/ludic.physics/shapes.ludic @@ -35,8 +35,7 @@ export function phys_close(physics_st: mut PhysicsState) -> void { } if physics_st.ph_shapes != null { List.clear(physics_st.ph_shapes) } List.clear(physics_st.ph_spare) - List.clear(physics_st.ph_floats) - List.clear(physics_st.ph_float_k) + physics_st.ph_nf = 0 if physics_st.ph_facts != null { q_clear(physics_st.ph_facts) } } diff --git a/packages/ludic.physics/state.ludic b/packages/ludic.physics/state.ludic index 5cbdc201..1ef9edb2 100644 --- a/packages/ludic.physics/state.ludic +++ b/packages/ludic.physics/state.ludic @@ -46,10 +46,9 @@ export state PhysicsState { ph_steps: int = 0 ph_gen: int = 0 # how many worlds have been opened: an id from an older one is stale ph_hard: float = 2.0 # metres a second: a contact under this is not a fact - ph_floats: []int = new []int # buoyant bodies' ids - ph_float_k: []float = new []float # and theirs: buoyancy, linear drag, angular drag, wet - ph_floats2: []int = new []int # the spare pair phys_sink filters into - ph_float_k2: []float = new []float + ph_floats: words = words(256) # buoyant bodies' ids, the first ph_nf of them + ph_float_k: floats = floats(256 * 4) # and theirs: buoyancy, linear drag, angular drag, wet + ph_nf: int = 0 ph_rx: float = 0.0 # where the last phys_resolve put a body ph_rz: float = 0.0 ph_walkers: []pointer = new []pointer # a walker id is its place here; null once removed diff --git a/packages/ludic.physics/water.ludic b/packages/ludic.physics/water.ludic index 7d73c34c..651e6637 100644 --- a/packages/ludic.physics/water.ludic +++ b/packages/ludic.physics/water.ludic @@ -2,46 +2,42 @@ # carried by its current, and slowed by its drags. Reaching the water is a PHYS_SPLASH fact. const PH_K: int = 4 -# buoyancy 1 floats level with the surface, more rides higher; drags in 1/s +# buoyancy 1 floats level with the surface, more rides higher; drags in 1/s. At most 256 float at +# once (a table made with the state): past that a body is simply not buoyed export function phys_float(physics_st: mut PhysicsState, id: int, buoyancy: float, linear_drag: float, angular_drag: float) -> void { ph_buffers() phys_sink(physics_st, id) - push(physics_st.ph_floats, id) - push(physics_st.ph_float_k, buoyancy) - push(physics_st.ph_float_k, linear_drag) - push(physics_st.ph_float_k, angular_drag) - push(physics_st.ph_float_k, 0.0) + if physics_st.ph_nf >= len(physics_st.ph_floats) { return } + let i = physics_st.ph_nf + physics_st.ph_floats[i] = id + physics_st.ph_float_k[i * PH_K] = buoyancy + physics_st.ph_float_k[i * PH_K + 1] = linear_drag + physics_st.ph_float_k[i * PH_K + 2] = angular_drag + physics_st.ph_float_k[i * PH_K + 3] = 0.0 + physics_st.ph_nf += 1 } -# no longer floats (it is still a body); every removal asks, so the lists are filtered into a kept -# spare pair and swapped, never made anew +# no longer floats (it is still a body): the last one takes its row export function phys_sink(physics_st: mut PhysicsState, id: int) -> void { - if physics_st.ph_floats == null { return } - let ids = physics_st.ph_floats2 - let ks = physics_st.ph_float_k2 - List.clear(ids) - List.clear(ks) - for i in 0 .. len(physics_st.ph_floats) { - if physics_st.ph_floats[i] != id { - push(ids, physics_st.ph_floats[i]) - for k in 0 .. PH_K { push(ks, physics_st.ph_float_k[i * PH_K + k]) } + for i in 0 .. physics_st.ph_nf { + if physics_st.ph_floats[i] == id { + let last = physics_st.ph_nf - 1 + physics_st.ph_floats[i] = physics_st.ph_floats[last] + for k in 0 .. PH_K { physics_st.ph_float_k[i * PH_K + k] = physics_st.ph_float_k[last * PH_K + k] } + physics_st.ph_nf = last + return } } - physics_st.ph_floats2 = physics_st.ph_floats - physics_st.ph_float_k2 = physics_st.ph_float_k - physics_st.ph_floats = ids - physics_st.ph_float_k = ks } export function phys_floating(physics_st: PhysicsState, id: int) -> bool { - if physics_st.ph_floats == null { return false } - for i in 0 .. len(physics_st.ph_floats) { if physics_st.ph_floats[i] == id { return true } } + for i in 0 .. physics_st.ph_nf { if physics_st.ph_floats[i] == id { return true } } return false } function ph_buoy_all(physics_st: mut PhysicsState) -> void { let dt = physics_st.ph_step - for i in 0 .. len(physics_st.ph_floats) { + for i in 0 .. physics_st.ph_nf { let id = physics_st.ph_floats[i] if jph_body_read(physics_st.ph_world, id, physics_st.ph_vals) == 0 { ph_buoy_one(physics_st, i, id, physics_st.ph_vals[0], physics_st.ph_vals[2], dt) From c8c230449ee5ad442cfd4aaabd73e86d56b12da5 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 15:57:22 +0300 Subject: [PATCH 3/5] fix(character, physics, vehicles, wildlife): fact records made at the start (64) with a free list of room for all, as ludic.npc's A frame holding a new peak of facts made a record and grew the free list; now nothing is made until 64 are held at once. The two record-count tests hold the 64. Co-Authored-By: Claude Opus 5.5 --- packages/ludic.character/fact_pool.ludic | 14 ++++++++++++++ packages/ludic.character/index.ludic | 1 + packages/ludic.character/ports.ludic | 4 ++-- packages/ludic.physics/fact_pool.ludic | 14 ++++++++++++++ packages/ludic.physics/index.ludic | 1 + packages/ludic.physics/state.ludic | 4 ++-- packages/ludic.vehicles/fact_pool.ludic | 14 ++++++++++++++ packages/ludic.vehicles/index.ludic | 1 + packages/ludic.vehicles/ports.ludic | 4 ++-- packages/ludic.vehicles/tests/vehicles_test.ludic | 4 ++-- packages/ludic.wildlife/fact_pool.ludic | 14 ++++++++++++++ packages/ludic.wildlife/index.ludic | 1 + packages/ludic.wildlife/species.ludic | 4 ++-- packages/ludic.wildlife/tests/crowd_test.ludic | 4 ++-- 14 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 packages/ludic.character/fact_pool.ludic create mode 100644 packages/ludic.physics/fact_pool.ludic create mode 100644 packages/ludic.vehicles/fact_pool.ludic create mode 100644 packages/ludic.wildlife/fact_pool.ludic diff --git a/packages/ludic.character/fact_pool.ludic b/packages/ludic.character/fact_pool.ludic new file mode 100644 index 00000000..fcb7dfa5 --- /dev/null +++ b/packages/ludic.character/fact_pool.ludic @@ -0,0 +1,14 @@ +# ludic.character/fact_pool.ludic - the facts' records made at the start (CHARACTERFACTS of them) with a free list +# of room for all, so a frame holding a new peak of facts makes nothing until that many are held +const CHR_FACTS: int = 64 + +function chr_fpool_new() -> []CharacterFact { + let l = new []CharacterFact + for i in 0 .. CHR_FACTS { push(l, new CharacterFact) } + return l +} +function chr_ffree_new() -> []CharacterFact { + let l = chr_fpool_new() + List.clear(l) + return l +} diff --git a/packages/ludic.character/index.ludic b/packages/ludic.character/index.ludic index b145db18..e14df30c 100644 --- a/packages/ludic.character/index.ludic +++ b/packages/ludic.character/index.ludic @@ -6,6 +6,7 @@ numbers float import "ludic.base" import "rules.ludic" import "ports.ludic" +import "fact_pool.ludic" import "state.ludic" import "queries.ludic" import "ground.ludic" diff --git a/packages/ludic.character/ports.ludic b/packages/ludic.character/ports.ludic index 306c5dd2..a53a55a0 100644 --- a/packages/ludic.character/ports.ludic +++ b/packages/ludic.character/ports.ludic @@ -104,8 +104,8 @@ export state CharacterState { chr_safe_x: float = 0.0 chr_safe_z: float = 0.0 chr_fact_q: Queue = chr_fact_q__new() - chr_fpool: []CharacterFact = new []CharacterFact # every fact record made, reused once no drain holds it - chr_ffree: []CharacterFact = new []CharacterFact + chr_fpool: []CharacterFact = chr_fpool_new() # every fact record made, reused once no drain holds it + chr_ffree: []CharacterFact = chr_ffree_new() chr_fnext: int = 0 } function chr__top_plain(x: float, z: float, r: float, from_y: float, step: float, base: float) -> float { return base } diff --git a/packages/ludic.physics/fact_pool.ludic b/packages/ludic.physics/fact_pool.ludic new file mode 100644 index 00000000..77bdfc79 --- /dev/null +++ b/packages/ludic.physics/fact_pool.ludic @@ -0,0 +1,14 @@ +# ludic.physics/fact_pool.ludic - the facts' records made at the start (PHYSFACTS of them) with a free list +# of room for all, so a frame holding a new peak of facts makes nothing until that many are held +const PH_FACTS: int = 64 + +function ph_fpool_new() -> []PhysFact { + let l = new []PhysFact + for i in 0 .. PH_FACTS { push(l, new PhysFact) } + return l +} +function ph_ffree_new() -> []PhysFact { + let l = ph_fpool_new() + List.clear(l) + return l +} diff --git a/packages/ludic.physics/index.ludic b/packages/ludic.physics/index.ludic index df3b930b..4de40a96 100644 --- a/packages/ludic.physics/index.ludic +++ b/packages/ludic.physics/index.ludic @@ -5,6 +5,7 @@ numbers float import "ludic.base" import "native.ludic" import "state.ludic" +import "fact_pool.ludic" import "shape_ids.ludic" import "owned.ludic" import "shapes.ludic" diff --git a/packages/ludic.physics/state.ludic b/packages/ludic.physics/state.ludic index 1ef9edb2..ba01b327 100644 --- a/packages/ludic.physics/state.ludic +++ b/packages/ludic.physics/state.ludic @@ -55,8 +55,8 @@ export state PhysicsState { ph_ids: []int = words(PH_CAP * 2) ph_vals: []float = floats(PH_CAP * 4) ph_facts: Queue = ph_facts__new() - ph_fpool: []PhysFact = new []PhysFact # every fact record made, reused once no drain holds it - ph_ffree: []PhysFact = new []PhysFact + ph_fpool: []PhysFact = ph_fpool_new() # every fact record made, reused once no drain holds it + ph_ffree: []PhysFact = ph_ffree_new() ph_fnext: int = 0 } diff --git a/packages/ludic.vehicles/fact_pool.ludic b/packages/ludic.vehicles/fact_pool.ludic new file mode 100644 index 00000000..84e6d0e9 --- /dev/null +++ b/packages/ludic.vehicles/fact_pool.ludic @@ -0,0 +1,14 @@ +# ludic.vehicles/fact_pool.ludic - the facts' records made at the start (VEHICLEFACTS of them) with a free list +# of room for all, so a frame holding a new peak of facts makes nothing until that many are held +const VE_FACTS: int = 64 + +function ve_fpool_new() -> []VehicleFact { + let l = new []VehicleFact + for i in 0 .. VE_FACTS { push(l, new VehicleFact) } + return l +} +function ve_ffree_new() -> []VehicleFact { + let l = ve_fpool_new() + List.clear(l) + return l +} diff --git a/packages/ludic.vehicles/index.ludic b/packages/ludic.vehicles/index.ludic index 4f51ef00..6e641dda 100644 --- a/packages/ludic.vehicles/index.ludic +++ b/packages/ludic.vehicles/index.ludic @@ -7,6 +7,7 @@ import "ludic.base" import "kinds.ludic" import "table.ludic" import "ports.ludic" +import "fact_pool.ludic" import "state.ludic" import "places.ludic" import "call.ludic" diff --git a/packages/ludic.vehicles/ports.ludic b/packages/ludic.vehicles/ports.ludic index 3e1c04a4..4682596c 100644 --- a/packages/ludic.vehicles/ports.ludic +++ b/packages/ludic.vehicles/ports.ludic @@ -63,8 +63,8 @@ export state VehiclesState { ve_ly: float = 0.0 ve_lz: float = 0.0 ve_fact_q: Queue = ve_fact_q__new() - ve_fpool: []VehicleFact = new []VehicleFact # every fact record made, reused once no drain holds it - ve_ffree: []VehicleFact = new []VehicleFact + ve_fpool: []VehicleFact = ve_fpool_new() # every fact record made, reused once no drain holds it + ve_ffree: []VehicleFact = ve_ffree_new() ve_fnext: int = 0 } function ve__dry(x: float, z: float) -> float { return -10000.0 } diff --git a/packages/ludic.vehicles/tests/vehicles_test.ludic b/packages/ludic.vehicles/tests/vehicles_test.ludic index 71e4cba7..7ff6263e 100644 --- a/packages/ludic.vehicles/tests/vehicles_test.ludic +++ b/packages/ludic.vehicles/tests/vehicles_test.ludic @@ -376,7 +376,7 @@ program VehiclesTest { expect(vehicle_by_nid(vehicles_st, b.nid) == null) } - test "a fed fact a turn, drained each turn, makes a few records and then reuses them" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) { + test "a fed fact a turn, drained each turn, makes no record past the ones made at the start" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) { fresh(vehicles_st, vehicles_test_st) vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 0) let h = vehicle_find(vehicles_st, VEHICLE_HORSE) @@ -386,6 +386,6 @@ program VehiclesTest { fed += count(facts(vehicles_st), VEHICLE_FED) } expect_eq(fed, 1000) - expect(vehicle_fact_records(vehicles_st) <= 4) + expect_eq(vehicle_fact_records(vehicles_st), 64) } } diff --git a/packages/ludic.wildlife/fact_pool.ludic b/packages/ludic.wildlife/fact_pool.ludic new file mode 100644 index 00000000..bf300903 --- /dev/null +++ b/packages/ludic.wildlife/fact_pool.ludic @@ -0,0 +1,14 @@ +# ludic.wildlife/fact_pool.ludic - the facts' records made at the start (WILDFACTS of them) with a free list +# of room for all, so a frame holding a new peak of facts makes nothing until that many are held +const WL_FACTS: int = 64 + +function wl_fpool_new() -> []WildFact { + let l = new []WildFact + for i in 0 .. WL_FACTS { push(l, new WildFact) } + return l +} +function wl_ffree_new() -> []WildFact { + let l = wl_fpool_new() + List.clear(l) + return l +} diff --git a/packages/ludic.wildlife/index.ludic b/packages/ludic.wildlife/index.ludic index 6ce99a14..2b5347db 100644 --- a/packages/ludic.wildlife/index.ludic +++ b/packages/ludic.wildlife/index.ludic @@ -4,6 +4,7 @@ module ludic_wildlife uses ludic_base numbers float import "ludic.base" import "species.ludic" +import "fact_pool.ludic" import "animal.ludic" import "places.ludic" import "port.ludic" diff --git a/packages/ludic.wildlife/species.ludic b/packages/ludic.wildlife/species.ludic index c563dfd3..66834e3b 100644 --- a/packages/ludic.wildlife/species.ludic +++ b/packages/ludic.wildlife/species.ludic @@ -35,8 +35,8 @@ export property WildSpecies { export state WildlifeState { wl_facts: Queue = wl_facts__new() - wl_fpool: []WildFact = new []WildFact # every fact record made, reused once no drain holds it - wl_ffree: []WildFact = new []WildFact # of those, the ones free at the last look + wl_fpool: []WildFact = wl_fpool_new() # every fact record made, reused once no drain holds it + wl_ffree: []WildFact = wl_ffree_new() # of those, the ones free at the last look wl_fnext: []int = words(1) # the next of them to hand out wl_own: WildTable = null # a guest's own animals, put away whole while it draws the host's wl_px: float = 0.0 diff --git a/packages/ludic.wildlife/tests/crowd_test.ludic b/packages/ludic.wildlife/tests/crowd_test.ludic index 97805767..3d896957 100644 --- a/packages/ludic.wildlife/tests/crowd_test.ludic +++ b/packages/ludic.wildlife/tests/crowd_test.ludic @@ -84,13 +84,13 @@ program WildCrowdTest { expect(not wildlife_listed(wildlife_st, a)) } - test "a fact a frame, drained each frame, makes a few records and then reuses them" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + test "a fact a frame, drained each frame, makes no record past the ones made at the start" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { let a = walker(wildlife_st, wildlife_tests_fake_st) for k in 1 .. 3001 { wildlife_moved(wildlife_st, a, 0.0, -3.0 * float(k), 0.0, -1.2) let fs = q_drain(wildlife_facts(wildlife_st)) for i in 0 .. len(fs) { expect_eq(fs[i].what, WILD_PRINT) } } - expect(wildlife_fact_records(wildlife_st) <= 4) + expect_eq(wildlife_fact_records(wildlife_st), 64) } } From 082fd26bf1e6ade82a313bddc061f712603e46cd Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 15:58:10 +0300 Subject: [PATCH 4/5] frame allocs (25.2): a fact record past the 64 made at the start is @alloc_ok, in its own function, in npc, character, physics, vehicles and wildlife Co-Authored-By: Claude Opus 5.5 --- packages/ludic.character/fact_pool.ludic | 7 +++++++ packages/ludic.character/state.ludic | 4 +--- packages/ludic.npc/pool.ludic | 7 +++++++ packages/ludic.npc/way.ludic | 4 +--- packages/ludic.physics/fact_pool.ludic | 7 +++++++ packages/ludic.physics/system.ludic | 4 +--- packages/ludic.vehicles/fact_pool.ludic | 7 +++++++ packages/ludic.vehicles/state.ludic | 4 +--- packages/ludic.wildlife/fact_pool.ludic | 7 +++++++ packages/ludic.wildlife/facts.ludic | 4 +--- 10 files changed, 40 insertions(+), 15 deletions(-) diff --git a/packages/ludic.character/fact_pool.ludic b/packages/ludic.character/fact_pool.ludic index fcb7dfa5..2b9b2d9d 100644 --- a/packages/ludic.character/fact_pool.ludic +++ b/packages/ludic.character/fact_pool.ludic @@ -12,3 +12,10 @@ function chr_ffree_new() -> []CharacterFact { List.clear(l) return l } +# past them: a record for the new peak, kept in the pool for good +@alloc_ok("a new peak of facts held at once, past the 64 made at the start; kept and reused") +function chr_fact_more(character_st: mut CharacterState) -> CharacterFact { + let f = new CharacterFact + push(character_st.chr_fpool, f) + return f +} diff --git a/packages/ludic.character/state.ludic b/packages/ludic.character/state.ludic index f70e3e7f..99f21200 100644 --- a/packages/ludic.character/state.ludic +++ b/packages/ludic.character/state.ludic @@ -25,9 +25,7 @@ function chr_fact_record(character_st: mut CharacterState) -> CharacterFact { character_st.chr_fnext += 1 return f } - let f = new CharacterFact - push(character_st.chr_fpool, f) - return f + return chr_fact_more(character_st) } function chr_say(character_st: mut CharacterState, what: int, v: float) -> void { let f = chr_fact_record(character_st) diff --git a/packages/ludic.npc/pool.ludic b/packages/ludic.npc/pool.ludic index b33c43db..4c9dc012 100644 --- a/packages/ludic.npc/pool.ludic +++ b/packages/ludic.npc/pool.ludic @@ -24,3 +24,10 @@ function np_queue_sized(q: Queue) -> Queue { List.clear(q.spare) return q } +# past them: a record for the new peak, kept in the pool for good +@alloc_ok("a new peak of facts held at once, past the 64 made at the start; kept and reused") +function np_fact_more(npc_st: mut NpcState) -> NpcFact { + let f = new NpcFact + push(npc_st.np_fpool, f) + return f +} diff --git a/packages/ludic.npc/way.ludic b/packages/ludic.npc/way.ludic index dd50e908..c6256992 100644 --- a/packages/ludic.npc/way.ludic +++ b/packages/ludic.npc/way.ludic @@ -94,9 +94,7 @@ function np_fact_record(npc_st: mut NpcState) -> NpcFact { f.z = 0.0 return f } - let f = new NpcFact - push(npc_st.np_fpool, f) - return f + return np_fact_more(npc_st) } # how many fact records have ever been made: the peak held at once, not the facts pushed export function npc_fact_records(npc_st: NpcState) -> int { return len(npc_st.np_fpool) } diff --git a/packages/ludic.physics/fact_pool.ludic b/packages/ludic.physics/fact_pool.ludic index 77bdfc79..990de92a 100644 --- a/packages/ludic.physics/fact_pool.ludic +++ b/packages/ludic.physics/fact_pool.ludic @@ -12,3 +12,10 @@ function ph_ffree_new() -> []PhysFact { List.clear(l) return l } +# past them: a record for the new peak, kept in the pool for good +@alloc_ok("a new peak of facts held at once, past the 64 made at the start; kept and reused") +function ph_fact_more(physics_st: mut PhysicsState) -> PhysFact { + let f = new PhysFact + push(physics_st.ph_fpool, f) + return f +} diff --git a/packages/ludic.physics/system.ludic b/packages/ludic.physics/system.ludic index e0c2e682..c2a06a95 100644 --- a/packages/ludic.physics/system.ludic +++ b/packages/ludic.physics/system.ludic @@ -45,9 +45,7 @@ function ph_fact_record(physics_st: mut PhysicsState) -> PhysFact { physics_st.ph_fnext += 1 return f } - let f = new PhysFact - push(physics_st.ph_fpool, f) - return f + return ph_fact_more(physics_st) } function ph_contacts(physics_st: mut PhysicsState) -> void { let n = jph_contacts_drain(physics_st.ph_world, physics_st.ph_ids, physics_st.ph_vals, PH_CAP) diff --git a/packages/ludic.vehicles/fact_pool.ludic b/packages/ludic.vehicles/fact_pool.ludic index 84e6d0e9..f57b3276 100644 --- a/packages/ludic.vehicles/fact_pool.ludic +++ b/packages/ludic.vehicles/fact_pool.ludic @@ -12,3 +12,10 @@ function ve_ffree_new() -> []VehicleFact { List.clear(l) return l } +# past them: a record for the new peak, kept in the pool for good +@alloc_ok("a new peak of facts held at once, past the 64 made at the start; kept and reused") +function ve_fact_more(vehicles_st: mut VehiclesState) -> VehicleFact { + let f = new VehicleFact + push(vehicles_st.ve_fpool, f) + return f +} diff --git a/packages/ludic.vehicles/state.ludic b/packages/ludic.vehicles/state.ludic index e5606dc7..8e4aa8b3 100644 --- a/packages/ludic.vehicles/state.ludic +++ b/packages/ludic.vehicles/state.ludic @@ -18,9 +18,7 @@ function ve_fact_record(vehicles_st: mut VehiclesState) -> VehicleFact { vehicles_st.ve_fnext += 1 return f } - let f = new VehicleFact - push(vehicles_st.ve_fpool, f) - return f + return ve_fact_more(vehicles_st) } # how many fact records have ever been made: the peak held at once, not the facts pushed export function vehicle_fact_records(vehicles_st: VehiclesState) -> int { return len(vehicles_st.ve_fpool) } diff --git a/packages/ludic.wildlife/fact_pool.ludic b/packages/ludic.wildlife/fact_pool.ludic index bf300903..99cd472a 100644 --- a/packages/ludic.wildlife/fact_pool.ludic +++ b/packages/ludic.wildlife/fact_pool.ludic @@ -12,3 +12,10 @@ function wl_ffree_new() -> []WildFact { List.clear(l) return l } +# past them: a record for the new peak, kept in the pool for good +@alloc_ok("a new peak of facts held at once, past the 64 made at the start; kept and reused") +function wl_fact_more(wildlife_st: mut WildlifeState) -> WildFact { + let f = new WildFact + push(wildlife_st.wl_fpool, f) + return f +} diff --git a/packages/ludic.wildlife/facts.ludic b/packages/ludic.wildlife/facts.ludic index 32330109..fe393914 100644 --- a/packages/ludic.wildlife/facts.ludic +++ b/packages/ludic.wildlife/facts.ludic @@ -48,9 +48,7 @@ function wl_fact_record(wildlife_st: mut WildlifeState) -> WildFact { f.diet = 0 return f } - let f = new WildFact - push(wildlife_st.wl_fpool, f) - return f + return wl_fact_more(wildlife_st) } # how many fact records have ever been made: the peak held at once, not the facts pushed export function wildlife_fact_records(wildlife_st: WildlifeState) -> int { return len(wildlife_st.wl_fpool) } From 34664e867f55a5799fa5800ff5c749446809c362 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 16:01:30 +0300 Subject: [PATCH 5/5] frame allocs (25.2): physics, npc, vehicles and wildlife's bounded growth declared - id tables to their most, a walker slot, a guest's copies, a player's own vehicle, a table at a load Co-Authored-By: Claude Opus 5.5 --- packages/ludic.npc/census.ludic | 1 + packages/ludic.npc/mirror.ludic | 1 + packages/ludic.physics/owned.ludic | 1 + packages/ludic.physics/shape_ids.ludic | 2 ++ packages/ludic.physics/walker.ludic | 1 + packages/ludic.vehicles/call.ludic | 1 + packages/ludic.wildlife/table.ludic | 1 + 7 files changed, 8 insertions(+) diff --git a/packages/ludic.npc/census.ludic b/packages/ludic.npc/census.ludic index 5b48a5b6..fabcb699 100644 --- a/packages/ludic.npc/census.ludic +++ b/packages/ludic.npc/census.ludic @@ -9,6 +9,7 @@ export function npc_spawn(npc_st: mut NpcState, kind: int) -> NpcPerson { break } } + @alloc_ok("a walker for each of np_max at most; one who left is reused") if p == null { if len(npc_st.np_walkers) >= npc_st.np_max { return null } p = new NpcPerson diff --git a/packages/ludic.npc/mirror.ludic b/packages/ludic.npc/mirror.ludic index a2026cf7..02f8dd8d 100644 --- a/packages/ludic.npc/mirror.ludic +++ b/packages/ludic.npc/mirror.ludic @@ -14,6 +14,7 @@ export function npc_mirroring(npc_st: NpcState) -> bool { return npc_st.np_mirro # the host's slot: somebody new in it (born), or nobody (gone) export function npc_put(npc_st: mut NpcState, slot: int, id: int, kind: int, look: int, seed: float, name: string, hx: float, hz: float) -> NpcPerson { if slot < 0 or slot >= npc_st.np_max or kind < 0 or kind >= NPCK_COUNT { return null } + @alloc_ok("a guest's copy of the host's slots, no more than np_max") while len(npc_st.np_walkers) <= slot { let q = new NpcPerson q.slot = len(npc_st.np_walkers) diff --git a/packages/ludic.physics/owned.ludic b/packages/ludic.physics/owned.ludic index 78f7c879..68b49eb8 100644 --- a/packages/ludic.physics/owned.ludic +++ b/packages/ludic.physics/owned.ludic @@ -4,6 +4,7 @@ const PH_INDEX: int = 8388607 # a body id's low 23 bits are its index (Jolt's BodyID) # every body owns nothing: at a world's opening, the table made once at its size and kept after +@alloc_ok("the body table made at its size when a world opens, kept after") function ph_own_reset(physics_st: mut PhysicsState, max_bodies: int) -> void { if len(physics_st.ph_own) < max_bodies { List.clear(physics_st.ph_own) diff --git a/packages/ludic.physics/shape_ids.ludic b/packages/ludic.physics/shape_ids.ludic index 199bc482..eaacf08a 100644 --- a/packages/ludic.physics/shape_ids.ludic +++ b/packages/ludic.physics/shape_ids.ludic @@ -10,6 +10,7 @@ function ph_keep(physics_st: mut PhysicsState, s: pointer) -> int { physics_st.ph_shapes[id] = s return id } + @alloc_ok("the id table grows to the most shapes held at once; a freed id is handed out again") push(physics_st.ph_shapes, s) return len(physics_st.ph_shapes) - 1 } @@ -26,6 +27,7 @@ export function phys_shape_free(physics_st: mut PhysicsState, id: int) -> void { if s == null { return } jph_shape_free(s) physics_st.ph_shapes[id] = null + @alloc_ok("the spare ids, no more than the most shapes ever held") push(physics_st.ph_spare, id) } diff --git a/packages/ludic.physics/walker.ludic b/packages/ludic.physics/walker.ludic index c77c5776..dd381101 100644 --- a/packages/ludic.physics/walker.ludic +++ b/packages/ludic.physics/walker.ludic @@ -35,6 +35,7 @@ export function phys_walker_add(physics_st: mut PhysicsState, r: float, h: float return i } } + @alloc_ok("a slot for the most walkers at once; a removed one's slot is reused") push(physics_st.ph_walkers, k) return len(physics_st.ph_walkers) - 1 } diff --git a/packages/ludic.vehicles/call.ludic b/packages/ludic.vehicles/call.ludic index 12ed5764..bb39b736 100644 --- a/packages/ludic.vehicles/call.ludic +++ b/packages/ludic.vehicles/call.ludic @@ -1,5 +1,6 @@ # call.ludic - a player's boat or horse brought to a place: made there the first time, fetched there # after; gone when its player leaves +@alloc_ok("a player's own boat or horse, made the first time it is called and fetched after") function ve_add(vehicles_st: VehiclesState, kind: int, owner: int, x: float, z: float, yaw: float) -> Vehicle { if kind != VEHICLE_HORSE and kind != VEHICLE_BOAT { return null } let v = new Vehicle diff --git a/packages/ludic.wildlife/table.ludic b/packages/ludic.wildlife/table.ludic index 46782ddb..032aea8d 100644 --- a/packages/ludic.wildlife/table.ludic +++ b/packages/ludic.wildlife/table.ludic @@ -14,6 +14,7 @@ export property WildTable { nids: IntMap = null } +@alloc_ok("a table of animals: a load, or a guest taking the host's") function wl__new() -> WildTable { let w = new WildTable let tb: Table = table_new(2, 2)