ludic/packages/ludic.things/tests/things_test.ludic
Orkuncakilkaya 71735b10a2 feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune
ludic.base's Queue<T> carries a QueueTag (its name and pending count): queue_new(name), q_push(q, v),
q_drain(q), q_clear(q) take no BaseState, and core_undrained(tags) names the given queues still holding
facts. A reducer on a package's state can now call that package's verbs (wallet_earn(wallet_st, n)).

ludic migrate state --prune (ludicc --migrate-prune) takes out each state parameter a function no
longer uses, nor anything it calls, and the argument that fills it - including an argument for a
parameter the callee has dropped, which is taken out before the call is checked, so a generic's T is
told by the argument that says it. A reducer keeps its state. --dry-run now counts the edits it would
make. Every package was moved with it: 608 base_st parameters and their arguments, 1889 edits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 04:19:56 +03:00

158 lines
6.3 KiB
Text

# things_test.ludic - a toy camp: kinds as data with their behaviour as function values, the
# verbs and what the port hears of them, the spatial questions, the facts, and the save by key
import "ludic.things"
import "ludic.base"
program ThingsTest {
numbers float
state ThingsTestState {
drawn: int = 0
gone: int = 0
shows: int = 0
back: int = 0
wood: int = 0
}
function lit_prompt(t: Thing) -> string {
if t.used != 0 { return "Add wood" }
return "Light it"
}
function lit_use(t: Thing) -> void { t.used = 1 }
function tent_name(t: Thing) -> string {
if t.owner == 0 { return "Your tent" }
return ""
}
function branch_use(things_test_st: mut ThingsTestState, t: Thing) -> void {
things_test_st.wood += 1
thing_hide(t)
}
function lure_tick(t: Thing, hours: float) -> void {
t.timer -= hours
if t.timer < 0.0 { thing_hide(t) }
}
function far_reach(t: Thing) -> float { return 9.0 }
def ThingKinds tent { name: "A tent", name_of: fn tent_name }
def ThingKinds fire { name: "The fire", prompt: fn lit_prompt, use: fn lit_use, keeps: true }
def ThingKinds branch { name: "Firewood", reach: 1.5, use: fn branch_use }
def ThingKinds lure { name: "A lure", tick: fn lure_tick, put_down: true, saved: true }
def ThingKinds sign { name: "A sign", reach_of: fn far_reach }
function hill(x: float, z: float) -> float { return x * 0.5 }
function on_placed(things_test_st: mut ThingsTestState, t: Thing) -> void { things_test_st.drawn += 1 }
function on_removed(things_test_st: mut ThingsTestState, t: Thing) -> void { things_test_st.gone += 1 }
function on_shown(things_test_st: mut ThingsTestState, t: Thing) -> void { things_test_st.shows += 1 }
function on_restored(things_test_st: mut ThingsTestState, t: Thing) -> void { things_test_st.back += 1 }
bind ThingsWorld { ground: fn hill, placed: fn on_placed, removed: fn on_removed, shown: fn on_shown, restored: fn on_restored }
function put(things_st: mut ThingsState, kind: int, x: float, z: float) -> Thing { return thing_spawn(things_st, kind, "m", x, 0.0, z, 0.0, 0) }
function facts(things_st: mut ThingsState) -> []ThingFact { return q_drain(things_facts(things_st)) }
test "a spawn is drawn, numbered and said" (things_st: mut ThingsState, things_test_st: ThingsTestState) {
things_clear(things_st)
let a = put(things_st, TH_TENT, 1.0, 1.0)
let b = put(things_st, TH_FIRE, 2.0, 2.0)
expect_eq(things_test_st.drawn, 2)
expect(a.uid != b.uid)
expect_eq(things_count(things_st), 2)
let fs = facts(things_st)
expect_eq(len(fs), 2)
expect_eq(fs[1].what, THING_PLACED)
expect_eq(fs[1].kind, TH_FIRE)
expect(thing_by_uid(things_st, b.uid) == b)
}
test "the spatial questions see only what is active" (things_st: mut ThingsState, things_test_st: ThingsTestState) {
things_clear(things_st)
let near = put(things_st, TH_BRANCH, 1.0, 0.0)
let far = put(things_st, TH_BRANCH, 5.0, 0.0)
put(things_st, TH_FIRE, 3.0, 0.0)
expect(thing_nearest(things_st, TH_BRANCH, 0.0, 0.0) == near)
expect(thing_nearest(things_st, -1, 3.2, 0.0).kind == TH_FIRE)
expect_eq(len(things_within(things_st, 0.0, 0.0, 3.5)), 2)
expect(things_within(things_st, 6.0, 0.0, 10.0)[0] == far)
thing_hide(near)
expect(thing_nearest(things_st, TH_BRANCH, 0.0, 0.0) == far)
expect_eq(len(things_each(things_st, TH_BRANCH)), 1)
expect_eq(len(things_each_all(things_st, TH_BRANCH)), 2)
expect(thing_find(things_st, TH_BRANCH, -1) == far)
expect(thing_find(things_st, TH_TENT, -1) == null)
expect_eq(things_test_st.shows, 1)
}
test "remove and move tell the port" (things_st: mut ThingsState, things_test_st: ThingsTestState) {
things_clear(things_st)
let a = put(things_st, TH_TENT, 1.0, 1.0)
put(things_st, TH_TENT, 2.0, 2.0)
facts(things_st)
thing_remove(things_st, a)
expect_eq(things_test_st.gone, 1)
expect_eq(things_count(things_st), 1)
expect_eq(facts(things_st)[0].what, THING_REMOVED)
let b = thing_find(things_st, TH_TENT, -1)
thing_move(b, 4.0, 0.0, 1.0)
expect_near(b.y, 2.0, 0.001)
let c = thing_put(things_st, TH_BRANCH, "stick", 2.0, 0.0, 0.0, 0.25, 1.9, 7)
expect_near(c.y, 0.75, 0.001)
expect_near(thing_reach(c), 1.9, 0.001)
expect_eq(c.id, 7)
}
test "the kind answers: name, prompt, use, reach" (things_st: mut ThingsState) {
things_clear(things_st)
let t = put(things_st, TH_TENT, 0.0, 0.0)
expect(thing_name(t) == "A tent")
t.owner = 0
expect(thing_name(t) == "Your tent")
let f = put(things_st, TH_FIRE, 0.0, 0.0)
expect(thing_prompt(f) == "Light it")
thing_use(things_st, f)
expect(thing_prompt(f) == "Add wood")
expect(thing_prompt(t) == "")
expect(not thing_can_use(t))
expect_near(thing_reach(f), 2.2, 0.001)
expect_near(thing_reach(put(things_st, TH_BRANCH, 0.0, 0.0)), 1.5, 0.001)
expect_near(thing_reach(put(things_st, TH_SIGN, 0.0, 0.0)), 9.0, 0.001)
let fs = facts(things_st)
expect_eq(fs[len(fs) - 3].what, THING_USED)
}
test "ticks and a restock" (things_st: mut ThingsState, things_test_st: ThingsTestState) {
things_clear(things_st)
let l = put(things_st, TH_LURE, 0.0, 0.0)
l.timer = 1.0
let b = put(things_st, TH_BRANCH, 0.0, 0.0)
let f = put(things_st, TH_FIRE, 0.0, 0.0)
thing_use(things_st, b)
thing_use(things_st, f)
expect_eq(things_test_st.wood, 1)
things_tick(things_st, 0.6)
expect(l.active)
things_tick(things_st, 0.6)
expect(not l.active)
things_restock(things_st)
expect(b.active)
expect(not l.active)
expect_eq(f.used, 1)
}
test "the save keeps what the port keeps, by the kind's key" (things_st: mut ThingsState, things_test_st: ThingsTestState) {
things_clear(things_st)
let l = put(things_st, TH_LURE, 3.0, 4.0)
l.owner = 0
l.timer = 2.5
put(things_st, TH_TENT, 0.0, 0.0)
let root = save_tree()
save_section(root, "things", 1, things_save(things_st))
let text = save_encode(root)
things_clear(things_st)
put(things_st, TH_LURE, 9.0, 9.0)
let n = load_section(save_decode(text), "things")
things_load(things_st, n.data, n.version)
expect_eq(things_count(things_st), 1)
let t = thing_find(things_st, TH_LURE, -1)
expect_near(t.x, 3.0, 0.001)
expect_near(t.timer, 2.5, 0.001)
expect_eq(t.owner, 0)
expect_eq(things_test_st.back, 1)
}
}