# 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: 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) } 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) } }