320 lines
13 KiB
Text
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)
|
|
}
|
|
}
|