feat(ludic.base): the vocabulary mechanic packages share - Tick, phases, Queue<T>, rng streams, the save tree, the system runner
A mechanic package depends on ludic.base and nothing else: ports (records of function values for now) for questions, queues for facts, verbs for changes, phases for order and its own versioned save section. The runner inits, resets, saves and loads systems in the order added and ticks them phase by phase; a missing save section is a reset. Tests for each piece and a worked route between two toy mechanics live under tests/. The old ludic.core (engine ECS components) is used by examples/library and stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ec9a650e65
commit
dd6a449921
16 changed files with 928 additions and 0 deletions
105
packages/ludic.base/tests/system_test.ludic
Normal file
105
packages/ludic.base/tests/system_test.ludic
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# system_test.ludic - the runner: init, reset, save and load in the order added, tick phase by
|
||||
# phase, each system its own save section, and a missing section is a reset
|
||||
import "ludic.base"
|
||||
program SystemTest {
|
||||
numbers float
|
||||
var trail: string = ""
|
||||
var apples: int = 0
|
||||
var hours: float = 0.0
|
||||
|
||||
function draw_tick(t: Tick) -> void { trail = trail + "d" }
|
||||
function think_tick(t: Tick) -> void { trail = trail + "t" }
|
||||
function input_tick(t: Tick) -> void { trail = trail + "i" }
|
||||
function commit_tick(t: Tick) -> void { trail = trail + "c" }
|
||||
function apples_init() -> void { trail = trail + "A" }
|
||||
function apples_reset() -> void { apples = 0 }
|
||||
function apples_tick(t: Tick) -> void { apples += 1 }
|
||||
function apples_save() -> Val {
|
||||
let v = Value.object()
|
||||
sv_put_int(v, "n", apples)
|
||||
return v
|
||||
}
|
||||
function apples_load(v: Val, version: int) -> void { apples = sv_int(v, "n", 0) * version }
|
||||
function clock_reset() -> void { hours = 6.0 }
|
||||
function clock_tick(t: Tick) -> void { hours = hours + t.hours }
|
||||
function clock_save() -> Val {
|
||||
let v = Value.object()
|
||||
sv_put_float(v, "h", hours)
|
||||
return v
|
||||
}
|
||||
function clock_load(v: Val, version: int) -> void { hours = sv_float(v, "h", 6.0) }
|
||||
|
||||
function sys(key: string, phase: int, tick: fn(Tick) -> void) -> System {
|
||||
let s = system_new(key, phase)
|
||||
s.tick = tick
|
||||
return s
|
||||
}
|
||||
|
||||
function setup() -> void {
|
||||
core_clear()
|
||||
trail = ""
|
||||
core_add(sys("draw", PH_PRESENT, fn draw_tick))
|
||||
core_add(sys("think", PH_SIMULATE, fn think_tick))
|
||||
let a = sys("apples", PH_SIMULATE, fn apples_tick)
|
||||
a.init = fn apples_init
|
||||
a.reset = fn apples_reset
|
||||
a.save = fn apples_save
|
||||
a.load = fn apples_load
|
||||
a.version = 2
|
||||
core_add(a)
|
||||
core_add(sys("input", PH_INPUT, fn input_tick))
|
||||
core_add(sys("commit", PH_COMMIT, fn commit_tick))
|
||||
let c = sys("clock", PH_SIMULATE, fn clock_tick)
|
||||
c.reset = fn clock_reset
|
||||
c.save = fn clock_save
|
||||
c.load = fn clock_load
|
||||
core_add(c)
|
||||
}
|
||||
|
||||
test "tick runs phase by phase, in the order added within a phase" {
|
||||
setup()
|
||||
core_tick_all(tick_new(1.0 / 60.0, 0, 0.0))
|
||||
expect(trail == "itcd")
|
||||
expect_eq(apples, 1)
|
||||
}
|
||||
|
||||
test "init and reset run in the order added and skip a null verb" {
|
||||
setup()
|
||||
core_init_all()
|
||||
expect(trail == "A")
|
||||
apples = 9
|
||||
core_reset_all()
|
||||
expect_eq(apples, 0)
|
||||
expect(hours == 6.0)
|
||||
expect_eq(core_count(), 6)
|
||||
}
|
||||
|
||||
test "save writes each system under its key with its version, and load reads it back" {
|
||||
setup()
|
||||
core_reset_all()
|
||||
apples = 4
|
||||
core_tick_all(tick_new(0.016, 1, 0.5))
|
||||
let root = core_save_all()
|
||||
expect_eq(Value.count(root), 2)
|
||||
expect_eq(load_section(root, "apples").version, 2)
|
||||
expect_eq(load_section(root, "clock").version, 1)
|
||||
let text = save_encode(root)
|
||||
core_reset_all()
|
||||
core_load_all(save_decode(text))
|
||||
expect_eq(apples, 10)
|
||||
expect(hours == 6.5)
|
||||
}
|
||||
|
||||
test "a system with no section in the save is reset" {
|
||||
setup()
|
||||
apples = 3
|
||||
hours = 11.0
|
||||
let root = save_tree()
|
||||
let v = Value.object()
|
||||
sv_put_float(v, "h", 20.25)
|
||||
save_section(root, "clock", 1, v)
|
||||
core_load_all(root)
|
||||
expect_eq(apples, 0)
|
||||
expect(hours == 20.25)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue