ludic/packages/ludic.things/tests/things_test.ludic

320 lines
13 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
edge: float = 1000000.0 # a morning reaches x below this (the game's zones)
}
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 }
function in_reach(things_test_st: ThingsTestState, x: float, z: float) -> bool { return x < things_test_st.edge }
bind ThingsWorld { restocks: fn in_reach, 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: 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, 1)
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)
}
test "removals keep the indexes true: nearest, find, uid and the columns agree" (things_st: mut ThingsState) {
things_clear(things_st)
var keep: Thing = null
for i in 0 .. 400 {
let t = put(things_st, TH_BRANCH + (i % 2), float(i) * 3.0 - 600.0, float(i % 17) * 5.0)
if i == 250 { keep = t }
}
let gone = thing_find(things_st, TH_BRANCH, -1)
let uid = gone.uid
thing_remove(things_st, gone)
expect(thing_by_uid(things_st, uid) == null)
expect(thing_by_uid(things_st, keep.uid) == keep)
expect_eq(things_count(things_st), 399)
expect_eq(things_count_of(things_st, TH_BRANCH), 199)
thing_move(keep, 5000.0, 5000.0, 0.0)
expect(thing_nearest(things_st, -1, 4990.0, 4990.0) == keep)
thing_hide(keep)
expect(thing_nearest(things_st, -1, 4990.0, 4990.0) != keep)
expect_eq(things_verify(things_st), 0)
keep.x = 1.0
expect_eq(things_verify(things_st), 1)
}
test "a chunk's Things are counted and let go together; a move carries a Thing to its new chunk" (things_st: mut ThingsState, things_test_st: ThingsTestState) {
things_clear(things_st)
put(things_st, TH_BRANCH, 10.0, 10.0)
put(things_st, TH_BRANCH, 20.0, 30.0)
let far = put(things_st, TH_BRANCH, 600.0, 10.0)
let here = things_chunk(10.0, 10.0)
expect_eq(things_chunk_count(things_st, here), 2)
expect_eq(things_chunk_count(things_st, things_chunk(600.0, 10.0)), 1)
thing_move(far, 15.0, 15.0, 0.0)
expect_eq(things_chunk_count(things_st, here), 3)
facts(things_st)
let gone = things_test_st.gone
expect_eq(things_drop_chunk(things_st, here), 3)
expect_eq(things_count(things_st), 0)
expect_eq(things_test_st.gone, gone + 3)
expect_eq(len(facts(things_st)), 3)
expect_eq(things_verify(things_st), 0)
}
test "change detection names what a verb wrote since the tick a consumer kept" (things_st: mut ThingsState) {
things_clear(things_st)
let a = put(things_st, TH_BRANCH, 1.0, 0.0)
let b = put(things_st, TH_BRANCH, 2.0, 0.0)
let seen = things_now(things_st)
let rows = words(0)
let out = new []Thing
expect_eq(things_changed(things_st, seen, rows, out), 0)
thing_hide(b)
expect_eq(things_changed(things_st, seen, rows, out), 1)
expect(out[0] == b)
let later = things_now(things_st)
thing_move(a, 9.0, 9.0, 0.0)
expect_eq(things_changed(things_st, later, rows, out), 1)
expect(out[0] == a)
expect_eq(things_changed(things_st, seen, rows, out), 2)
}
test "a removed Thing's record is placed again once enough wait, as new, with a new uid" (things_st: mut ThingsState) {
things_reset(things_st)
let first = thing_put(things_st, TH_LURE, "lure", 1.0, 1.0, 0.0, 0.0, 2.0, 7)
first.used = 5
first.owner = 3
thing_hide(first)
let old_uid = first.uid
thing_remove(things_st, first)
for i in 0 .. 32 { thing_remove(things_st, thing_put(things_st, TH_BRANCH, "branch", float(i), 0.0, 0.0, 0.0, 1.5, 0)) }
expect_eq(things_spare_count(things_st), 33)
let again = thing_put(things_st, TH_SIGN, "sign", 4.0, 4.0, 0.0, 0.0, 2.0, 1)
let ap: pointer = again
let fp: pointer = first
expect(ap == fp)
expect(again.uid != old_uid)
expect_eq(again.kind, TH_SIGN)
expect_eq(again.used, 0)
expect_eq(again.owner, -1)
expect(again.active)
expect_eq(thing_nearest(things_st, TH_SIGN, 4.0, 4.0).uid, again.uid)
expect_eq(things_verify(things_st), 0)
for i in 0 .. 500 { thing_remove(things_st, thing_put(things_st, TH_BRANCH, "branch", 0.0, float(i), 0.0, 0.0, 1.5, 0)) }
expect(things_spare_count(things_st) <= 33)
things_reserve(things_st, 5000)
expect_eq(things_verify(things_st), 0)
things_spare_warm(things_st, 96)
expect_eq(things_spare_count(things_st), 96)
let warm = thing_put(things_st, TH_SIGN, "sign", 9.0, 9.0, 0.0, 0.0, 2.0, 2)
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)
}
test "a Thing's seats: taken one at a time, left, saved without who sits in them" (things_st: mut ThingsState) {
things_clear(things_st)
let l = put(things_st, TH_LURE, 3.0, 4.0)
thing_set_seats(l, 7, 2)
expect_eq(thing_seat_free(l), 0)
expect(thing_seat_take(l, 0))
expect(not thing_seat_take(l, 0))
expect(not thing_seat_take(l, 2))
expect_eq(thing_seat_free(l), 1)
expect(thing_seat_take(l, 1))
expect_eq(thing_seat_free(l), -1)
thing_seat_leave(l, 0)
thing_seat_leave(l, 0)
expect_eq(thing_seat_free(l), 0)
let root = save_tree()
save_section(root, "things", 1, things_save(things_st))
let text = save_encode(root)
things_clear(things_st)
let n = load_section(save_decode(text), "things")
things_load(things_st, n.data, n.version)
let t = thing_find(things_st, TH_LURE, -1)
expect_eq(t.seat, 7)
expect_eq(t.seats, 2)
expect_eq(t.taken, 0)
}
test "a Thing a morning missed is restocked once a morning would reach it, and only once" (things_st: mut ThingsState, things_test_st: mut ThingsTestState) {
things_clear(things_st)
let near = put(things_st, TH_BRANCH, 0.0, 0.0)
let far = put(things_st, TH_BRANCH, 500.0, 0.0)
thing_use(things_st, near)
thing_use(things_st, far)
things_test_st.edge = 100.0
things_restock(things_st, 3)
expect(near.active)
expect_eq(near.dawn, 3)
expect(not far.active)
let out = new []Thing
expect_eq(things_catch_up(things_st, things_chunk(500.0, 0.0), 3, out), 0)
expect(not far.active)
things_test_st.edge = 1000000.0
expect_eq(things_catch_up(things_st, things_chunk(500.0, 0.0), 3, out), 1)
expect(far.active)
expect_eq(far.dawn, 3)
expect(out[0] == far)
expect_eq(things_catch_up(things_st, things_chunk(500.0, 0.0), 3, out), 0)
expect_eq(things_catch_up(things_st, things_chunk(0.0, 0.0), 3, out), 0)
}
}