feat(ecs): #80 entity-pool stats (Pool.live/free/reserved/capacity)
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 22s
ci / build-and-test (push) Successful in 2m27s
commit-lint / conventional-commits (push) Successful in 5s
docs / build-and-deploy (push) Successful in 29s

Ludic's ECS is already pool-based — the allocator recycles freed entity slots
through a freelist (L_alloc pops @L_freen before growing @L_entc), and component
storage is fixed per-entity arrays, so spawn/despawn churn (bullet-hell/horde)
does no per-spawn heap allocation and cannot fragment. Expose that with a Pool.*
namespace so a game can watch reuse: Pool.live (alive now), Pool.free (recycled
slots waiting), Pool.reserved (high-water — stays flat across a steady
spawn/despawn loop, proving reuse not reallocation), Pool.capacity (the fixed
cap). Zero-cost inline reads of the existing counters.

Example pool.ludic proves the key property: after despawn+respawn,
Pool.reserved() stays 3 (freed slot reused) — prints 0 0 3 3 0 2 1 3 0 3 1.
4 docs pages. Full suite 118/0, goldens byte-identical, fixpoint holds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-02 08:23:58 +03:00
parent f2cb3cd7e8
commit 347352cc4c
10 changed files with 25580 additions and 25382 deletions

View file

@ -0,0 +1,7 @@
---
id: pool
title: Pool
order: 35
---
Entity-pool statistics. Ludic's ECS is already pool-based: the allocator recycles freed entity slots through a freelist (a <a href="../structure/kw-model"><code>despawn</code></a>ed slot is reused by the next <code>spawn</code> before any new slot is taken), and component storage is fixed per-entity arrays — so spawning and despawning many entities per frame does no per-spawn heap allocation and cannot fragment. <a href="pool-live"><code>Pool.live</code></a> / <a href="pool-free"><code>Pool.free</code></a> / <a href="pool-reserved"><code>Pool.reserved</code></a> / <a href="pool-capacity"><code>Pool.capacity</code></a> read those counters so a bullet-hell or horde game can watch reuse and budget against the cap.

View file

@ -0,0 +1,14 @@
---
id: pool-capacity
name: Pool.capacity
category: pool
kind: namespace-method
tokens: Pool.capacity
sig: Pool.capacity() -> int
tip: The maximum number of entities.
order: 4
ns: Pool
member: capacity
---
Returns the maximum number of live entities (the fixed entity-array size). Budget spawns against it — a horde/bullet-hell game keeps <a href="pool-live"><code>Pool.live</code></a> under <code>Pool.capacity()</code>.

View file

@ -0,0 +1,14 @@
---
id: pool-free
name: Pool.free
category: pool
kind: namespace-method
tokens: Pool.free
sig: Pool.free() -> int
tip: How many freed slots are waiting to be reused.
order: 2
ns: Pool
member: free
---
Returns how many freed entity slots are on the freelist, waiting to be reused by the next <code>spawn</code>. This is the pool depth — despawning bullets/enemies raises it, and the next wave draws them back down with no allocation.

View file

@ -0,0 +1,14 @@
---
id: pool-live
name: Pool.live
category: pool
kind: namespace-method
tokens: Pool.live
sig: Pool.live() -> int
tip: How many entities are currently alive.
order: 1
ns: Pool
member: live
---
Returns the number of entities currently alive (reserved minus free). Despawning drops it and spawning raises it; a recycled slot does not change <a href="pool-reserved"><code>Pool.reserved</code></a>.

View file

@ -0,0 +1,14 @@
---
id: pool-reserved
name: Pool.reserved
category: pool
kind: namespace-method
tokens: Pool.reserved
sig: Pool.reserved() -> int
tip: High-water: how many slots have ever been allocated.
order: 3
ns: Pool
member: reserved
---
Returns the high-water mark — how many entity slots have ever been allocated. It only grows when a <code>spawn</code> finds the freelist empty; a spawn that reuses a freed slot leaves it unchanged, so a steady-state spawn/despawn loop keeps <code>reserved</code> flat (the proof that slots are pooled, not reallocated).