diff --git a/packages/ludic.wildlife/tests/approach_pins_test.ludic b/packages/ludic.wildlife/tests/approach_pins_test.ludic new file mode 100644 index 00000000..d98cdf64 --- /dev/null +++ b/packages/ludic.wildlife/tests/approach_pins_test.ludic @@ -0,0 +1,83 @@ +# approach_pins_test.ludic - phase 27.2a: APPROACH as it stands. Its lure gone, IDLE 2 s; within +# 1.3 m it EATs - 14 s at food, 9 at a lure - or a snare holds it (TRAPPED where the snare is, a +# CAUGHT fact); no roll on any of it +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program ApproachPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function coming(wildlife_st: mut WildlifeState, kind: int, lure_x: float) -> WildAnimal { + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_APPROACH, 5.0) + a.lure = 50 + a.lure_kind = kind + a.lure_x = lure_x + a.lure_z = 0.0 + a.lure_owner = 3 + wildlife_seed(wildlife_st, 13) + pn_step(wildlife_st, a) + return a + } + + test "APPROACH, its lure gone: IDLE 2 s, the lure let go" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + wildlife_tests_fake_st.fk_gone = 50 + let a = coming(wildlife_st, WILD_FEED, 20.0) + expect_eq(a.state, WILD_IDLE) + expect_near(a.timer, 2.0, 0.0001) + expect_eq(a.lure, 0) + expect_eq(pn_drew(wildlife_st, 13), 0) + } + + test "APPROACH on its way: walks at its walk, the timer untouched by arriving" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = coming(wildlife_st, WILD_FEED, 20.0) + expect_eq(a.state, WILD_APPROACH) + expect_near(a.speed, 1.0, 0.0001) + expect_near(a.timer, 4.95, 0.0001) + expect_eq(pn_drew(wildlife_st, 13), 0) + } + + test "APPROACH within 1.3 m: EAT 14 s at food, 9 s at a lure" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = coming(wildlife_st, WILD_FEED, 1.0) + expect_eq(a.state, WILD_EAT) + expect_near(a.timer, 14.0, 0.0001) + expect_near(a.speed, 0.0, 0.00001) + let b = coming(wildlife_st, WILD_LURE, 1.0) + expect_eq(b.state, WILD_EAT) + expect_near(b.timer, 9.0, 0.0001) + expect_eq(pn_count(pn_drain(wildlife_st), WILD_FED), 0) + expect_eq(pn_drew(wildlife_st, 13), 0) + } + + test "APPROACH within 1.3 m of a snare: TRAPPED on the snare, CAUGHT with the snare and its owner" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = coming(wildlife_st, WILD_SNARE, 1.0) + expect_eq(a.state, WILD_TRAPPED) + expect_near(a.x, 1.0, 0.0001) + expect_near(a.z, 0.0, 0.0001) + expect_near(a.y, 10.0, 0.0001) + let fs = pn_drain(wildlife_st) + expect_eq(pn_count(fs, WILD_CAUGHT), 1) + expect_eq(fs[0].lure, 50) + expect_eq(fs[0].owner, 3) + expect_eq(pn_drew(wildlife_st, 13), 0) + } +} diff --git a/packages/ludic.wildlife/tests/bird_pins_test.ludic b/packages/ludic.wildlife/tests/bird_pins_test.ludic new file mode 100644 index 00000000..6d5df26a --- /dev/null +++ b/packages/ludic.wildlife/tests/bird_pins_test.ludic @@ -0,0 +1,83 @@ +# bird_pins_test.ludic - phase 27.2a: a bird aloft as it stands (the jay: comes down whenever it can; +# the crow: never on its own). Its timer up with nobody within its land_shy it rolls a spot (x, z) and +# its land_chance, and comes down (LAND, 30 s); else it moves its home by two rolls and circles 30 s +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program BirdPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function aloft(wildlife_st: mut WildlifeState, sp: int, timer: float) -> WildAnimal { + let a = pn_at(wildlife_st, sp, 0.0, 0.0, WILD_FLY, timer) + wildlife_seed(wildlife_st, 16) + pn_fly(wildlife_st, a) + return a + } + + test "FLY before its time: circles on, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = aloft(wildlife_st, 3, 5.0) + expect_eq(a.state, WILD_FLY) + expect_near(a.timer, 4.95, 0.0001) + expect_eq(pn_drew(wildlife_st, 16), 0) + } + + test "FLY, its time up, nobody near: LAND at (x + 24 (r0 - 0.5), z + 24 (r1 - 0.5)) for 30 s, r2 its chance" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = aloft(wildlife_st, 3, 0.01) + expect_eq(a.state, WILD_LAND) + expect_near(a.timer, 30.0, 0.0001) + expect_near(a.tx, a.x + (pn_roll(16, 0) - 0.5) * 24.0, 0.001) + expect_near(a.tz, a.z + (pn_roll(16, 1) - 0.5) * 24.0, 0.001) + expect_eq(pn_drew(wildlife_st, 16), 3) + } + + test "FLY, its time up, somebody within land_shy: no landing roll; home moves by 120 (r0 - 0.5), 30 s" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_player(wildlife_tests_fake_st, 0.0, 0.0) + let a = aloft(wildlife_st, 3, 0.01) + expect_eq(a.state, WILD_FLY) + expect_near(a.timer, 30.0, 0.0001) + expect_near(a.home_x, (pn_roll(16, 0) - 0.5) * 120.0, 0.001) + expect_near(a.home_z, (pn_roll(16, 1) - 0.5) * 120.0, 0.001) + expect_eq(pn_drew(wildlife_st, 16), 2) + } + + test "FLY, its time up, the chance failed: five rolls - spot, spot, chance, then home by r3, r4" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = aloft(wildlife_st, 5, 0.01) + expect_eq(a.state, WILD_FLY) + expect_near(a.timer, 30.0, 0.0001) + expect_near(a.home_x, (pn_roll(16, 3) - 0.5) * 120.0, 0.001) + expect_near(a.home_z, (pn_roll(16, 4) - 0.5) * 120.0, 0.001) + expect_eq(pn_drew(wildlife_st, 16), 5) + } + + test "a bird made as given starts IDLE and circles on as IDLE" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = wildlife_make(wildlife_st, 3, 0.0, 0.0, false, 0.0, 0, 1.0, 1) + expect_eq(a.state, WILD_IDLE) + a.timer = 5.0 + wildlife_seed(wildlife_st, 16) + pn_fly(wildlife_st, a) + expect_eq(a.state, WILD_IDLE) + expect(a.fly_h > 0.0) + expect_eq(pn_drew(wildlife_st, 16), 0) + } +} diff --git a/packages/ludic.wildlife/tests/born_pins_test.ludic b/packages/ludic.wildlife/tests/born_pins_test.ludic new file mode 100644 index 00000000..ab06e4d8 --- /dev/null +++ b/packages/ludic.wildlife/tests/born_pins_test.ludic @@ -0,0 +1,91 @@ +# born_pins_test.ludic - phase 27.2a: where an animal's day starts, and what gates its tick, as they +# stand. A newborn walker is IDLE, a newborn bird FLY, off seven and eight rolls in a set order; the +# step (wildlife_tick) leaves alone one lying up, gone, pinned, held, or 900 m from everyone +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program BornPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + test "a newborn walker: IDLE; yaw, coat, size, thirst, hunger, droppings, timer - seven rolls" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + wildlife_seed(wildlife_st, 19) + let a = wildlife_new(wildlife_st, 2, 0.0, 0.0, false) + let r = rng_new(19) + rng_next(r) + expect_eq(a.state, WILD_IDLE) + expect_near(a.yaw, pn_roll(19, 0) * 6.2831853, 0.00001) + expect_eq(a.variant, rng_next(r) % 12) + expect_near(a.scale, 0.88 + pn_roll(19, 2) * 0.24, 0.00001) + expect_near(a.thirst, pn_roll(19, 3) * 50.0, 0.0001) + expect_near(a.hunger, pn_roll(19, 4) * 50.0, 0.0001) + expect_near(a.scat_t, 40.0 + pn_roll(19, 5) * 180.0, 0.001) + expect_near(a.timer, pn_roll(19, 6) * 6.0, 0.0001) + expect_eq(pn_drew(wildlife_st, 19), 7) + } + + test "a newborn bird: FLY, its height 10 + 20 r6 before the timer - eight rolls" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + wildlife_seed(wildlife_st, 19) + let a = wildlife_new(wildlife_st, 3, 0.0, 0.0, false) + expect_eq(a.state, WILD_FLY) + expect_near(a.fly_h, 10.0 + 20.0 * pn_roll(19, 6), 0.0001) + expect_near(a.timer, pn_roll(19, 7) * 6.0, 0.0001) + expect_eq(pn_drew(wildlife_st, 19), 8) + } + + test "wildlife_tick within 900 m steps it (IDLE, time up: WANDER); beyond, nothing moves and nothing rolls" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_IDLE, 0.0) + wildlife_seed(wildlife_st, 20) + wildlife_tick(wildlife_st, 0.05, 0.05 / 60.0) + expect_eq(a.state, WILD_IDLE) + expect_near(a.timer, 0.0, 0.00001) + pn_player(wildlife_tests_fake_st, 100.0, 0.0) + wildlife_tick(wildlife_st, 0.05, 0.05 / 60.0) + expect_eq(a.state, WILD_WANDER) + expect_eq(pn_drew(wildlife_st, 20), 3) + } + + test "lying up, pinned or held: its state and timer kept, no roll; gone: removed with a WENT fact" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_player(wildlife_tests_fake_st, 100.0, 0.0) + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_IDLE, 0.0) + a.speed = 3.0 + wildlife_seed(wildlife_st, 20) + wildlife_tests_fake_st.fk_about = WILD_AWAY + wildlife_tick(wildlife_st, 0.05, 0.05 / 60.0) + expect(not a.shown) + wildlife_tests_fake_st.fk_about = WILD_ABOUT + wildlife_hold(wildlife_st, true) + wildlife_tick(wildlife_st, 0.05, 0.05 / 60.0) + expect_near(a.speed, 3.0, 0.00001) + wildlife_hold(wildlife_st, false) + a.pinned = true + wildlife_tick(wildlife_st, 0.05, 0.05 / 60.0) + expect_near(a.speed, 0.0, 0.00001) + expect_eq(a.state, WILD_IDLE) + expect_near(a.timer, 0.0, 0.00001) + wildlife_tests_fake_st.fk_about = WILD_GONE + wildlife_tick(wildlife_st, 0.05, 0.05 / 60.0) + expect(not a.alive) + expect_eq(pn_count(pn_drain(wildlife_st), WILD_WENT), 1) + expect_eq(pn_drew(wildlife_st, 20), 0) + } +} diff --git a/packages/ludic.wildlife/tests/charge_pins_test.ludic b/packages/ludic.wildlife/tests/charge_pins_test.ludic new file mode 100644 index 00000000..a132ee04 --- /dev/null +++ b/packages/ludic.wildlife/tests/charge_pins_test.ludic @@ -0,0 +1,93 @@ +# charge_pins_test.ludic - phase 27.2a: CHARGE as it stands (the bear). Anyone within the charge +# reach (9 m) of a charger that is not calm is charged from any state but TRAPPED, before the alert +# is read; within 2.6 m it shoves and wanders off calm; its 2.5 s up it wanders and may charge again +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program ChargePins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function bear(wildlife_st: mut WildlifeState, state: int, timer: float) -> WildAnimal { + let a = pn_at(wildlife_st, 1, 0.0, 0.0, state, timer) + wildlife_seed(wildlife_st, 4) + pn_step(wildlife_st, a) + return a + } + + test "IDLE, FLEE and EAT, somebody within 9 m: CHARGE for 2.5 s, a CHARGED fact, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_player(wildlife_tests_fake_st, 5.0, 0.0) + let states = [WILD_IDLE, WILD_FLEE, WILD_EAT] + for i in 0 .. 3 { + let a = bear(wildlife_st, states[i], 5.0) + expect_eq(a.state, WILD_CHARGE) + expect_near(a.timer, 2.5, 0.0001) + expect_near(a.speed, 6.0, 0.0001) + let fs = pn_drain(wildlife_st) + expect_eq(pn_count(fs, WILD_CHARGED), 1) + expect_eq(fs[0].player, 0) + expect_eq(pn_drew(wildlife_st, 4), 0) + } + } + + test "CHARGE within 2.6 m: SHOVED, WANDER 8 s toward (r0, r1), calm 30 min" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_player(wildlife_tests_fake_st, 2.0, 0.0) + let a = bear(wildlife_st, WILD_CHARGE, 2.0) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 8.0, 0.0001) + expect_near(a.calm, 30.0, 0.0001) + expect_near(a.tx, pn_tx(a, 4, 0, 60.0), 0.001) + let fs = pn_drain(wildlife_st) + expect_eq(pn_count(fs, WILD_SHOVED), 1) + expect_eq(pn_count(fs, WILD_CHARGED), 0) + expect_eq(pn_drew(wildlife_st, 4), 2) + } + + test "CHARGE, its 2.5 s up short of anyone: WANDER 6 s, two rolls, not calm - and the next tick charges again" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_player(wildlife_tests_fake_st, 5.0, 0.0) + let a = bear(wildlife_st, WILD_CHARGE, 0.01) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 6.0, 0.0001) + expect_near(a.calm, 0.0, 0.0001) + expect_near(a.tx, pn_tx(a, 4, 0, 60.0), 0.001) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_CHARGE) + expect_eq(pn_count(pn_drain(wildlife_st), WILD_CHARGED), 1) + expect_eq(pn_drew(wildlife_st, 4), 2) + } + + test "no charge from a calm bear, from beyond 9 m, or out of a snare; no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_player(wildlife_tests_fake_st, 5.0, 0.0) + let a = pn_at(wildlife_st, 1, 0.0, 0.0, WILD_IDLE, 5.0) + a.calm = 30.0 + wildlife_seed(wildlife_st, 4) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_IDLE) + let t = bear(wildlife_st, WILD_TRAPPED, 5.0) + expect_eq(t.state, WILD_TRAPPED) + pn_player(wildlife_tests_fake_st, 9.5, 0.0) + let b = bear(wildlife_st, WILD_IDLE, 5.0) + expect_eq(b.state, WILD_IDLE) + expect_eq(pn_count(pn_drain(wildlife_st), WILD_CHARGED), 0) + expect_eq(pn_drew(wildlife_st, 4), 0) + } +} diff --git a/packages/ludic.wildlife/tests/drink_pins_test.ludic b/packages/ludic.wildlife/tests/drink_pins_test.ludic new file mode 100644 index 00000000..c851ed9a --- /dev/null +++ b/packages/ludic.wildlife/tests/drink_pins_test.ludic @@ -0,0 +1,80 @@ +# drink_pins_test.ludic - phase 27.2a: DRINK as it stands. It drinks (a water spot) or grazes at +# 600 a game hour, its timer run down twice a tick; its time up - or EITHER need under 1, not only +# the one it is meeting - it takes home where it stands and wanders 6 s toward (r0, r1) +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program DrinkPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function drinking(wildlife_st: mut WildlifeState, water: bool, thirst: float, hunger: float, timer: float) -> WildAnimal { + let a = pn_at(wildlife_st, 2, 3.0, 0.0, WILD_DRINK, timer) + a.spot = 900 + a.spot_water = water + a.thirst = thirst + a.hunger = hunger + wildlife_seed(wildlife_st, 10) + pn_step(wildlife_st, a) + return a + } + + test "DRINK at water: thirst down 600 a game hour, the timer 2 dt a tick, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = drinking(wildlife_st, true, 50.0, 50.0, 5.0) + expect_eq(a.state, WILD_DRINK) + expect_near(a.timer, 4.9, 0.0001) + expect_near(a.speed, 0.0, 0.00001) + expect_near(a.thirst, 50.0 + 12.0 * 0.05 / 60.0 - 0.5, 0.0001) + expect_near(a.hunger, 50.0 + 5.0 * 0.05 / 60.0, 0.0001) + expect_eq(pn_drew(wildlife_st, 10), 0) + } + + test "DRINK at forage grazes hunger down instead" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = drinking(wildlife_st, false, 50.0, 50.0, 5.0) + expect_near(a.hunger, 50.0 + 5.0 * 0.05 / 60.0 - 0.5, 0.0001) + expect_near(a.thirst, 50.0 + 12.0 * 0.05 / 60.0, 0.0001) + } + + test "DRINK, its time up: home where it stands, WANDER 6 s toward (r0, r1), two rolls" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let b = pn_at(wildlife_st, 2, 3.0, 0.0, WILD_DRINK, 0.05) + b.spot = 900 + b.spot_water = true + b.home_x = 50.0 + b.thirst = 50.0 + b.hunger = 50.0 + wildlife_seed(wildlife_st, 10) + pn_step(wildlife_st, b) + expect_eq(b.state, WILD_WANDER) + expect_near(b.timer, 6.0, 0.0001) + expect_near(b.home_x, 3.0, 0.0001) + expect_near(b.tx, pn_tx(b, 10, 0, 60.0), 0.001) + expect_near(b.tz, pn_tz(b, 10, 0, 60.0), 0.001) + expect_eq(pn_drew(wildlife_st, 10), 2) + } + + test "DRINK at water with hunger under 1: it leaves thirsty, WANDER 6 s, two rolls" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = drinking(wildlife_st, true, 80.0, 0.0, 5.0) + expect_eq(a.state, WILD_WANDER) + expect(a.thirst > 79.0) + expect_eq(pn_drew(wildlife_st, 10), 2) + } +} diff --git a/packages/ludic.wildlife/tests/eat_pins_test.ludic b/packages/ludic.wildlife/tests/eat_pins_test.ludic new file mode 100644 index 00000000..6248c1b7 --- /dev/null +++ b/packages/ludic.wildlife/tests/eat_pins_test.ludic @@ -0,0 +1,94 @@ +# eat_pins_test.ludic - phase 27.2a: EAT and TRAPPED as they stand. Eating done, food still there is +# credited (FED, calm 180 game minutes) and it wanders 4 s toward (r0, r1); a snare holds it through +# anything the tick asks, and only a release lets it go - FLEE 4 s, holding nothing against anyone +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program EatPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function eating(wildlife_st: mut WildlifeState, kind: int, timer: float) -> WildAnimal { + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_EAT, timer) + a.lure = 50 + a.lure_kind = kind + a.lure_owner = 3 + wildlife_seed(wildlife_st, 14) + pn_step(wildlife_st, a) + return a + } + + test "EAT at food, its time up: FED with the food and its owner, calm 180, WANDER 4 s, two rolls" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = eating(wildlife_st, WILD_FEED, 0.01) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 4.0, 0.0001) + expect_near(a.calm, 180.0, 0.0001) + expect_eq(a.fed, 1) + expect_eq(a.lure, 0) + expect_near(a.tx, pn_tx(a, 14, 0, 60.0), 0.001) + let fs = pn_drain(wildlife_st) + expect_eq(pn_count(fs, WILD_FED), 1) + expect_eq(fs[0].lure, 50) + expect_eq(fs[0].owner, 3) + expect_eq(fs[0].diet, WILD_PLANTS) + expect_eq(pn_drew(wildlife_st, 14), 2) + } + + test "EAT done at a lure, or the food gone: WANDER 4 s, nothing credited" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = eating(wildlife_st, WILD_LURE, 0.01) + expect_eq(a.state, WILD_WANDER) + expect_near(a.calm, 0.0, 0.0001) + wildlife_tests_fake_st.fk_gone = 50 + let b = eating(wildlife_st, WILD_FEED, 0.01) + expect_eq(b.state, WILD_WANDER) + expect_eq(b.fed, 0) + expect_eq(pn_count(pn_drain(wildlife_st), WILD_FED), 0) + expect_eq(pn_drew(wildlife_st, 14), 2) + } + + test "EAT before its time: stands eating, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = eating(wildlife_st, WILD_FEED, 5.0) + expect_eq(a.state, WILD_EAT) + expect_near(a.timer, 4.95, 0.0001) + expect_eq(pn_drew(wildlife_st, 14), 0) + } + + test "TRAPPED holds through an alert over 95 and a thirst over 60; a release: FLEE 4 s, calm 0, lure 0" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_TRAPPED, 0.01) + a.lure = 60 + a.alert = 97.0 + a.thirst = 90.0 + a.calm = 10.0 + a.speed = 2.0 + wildlife_seed(wildlife_st, 14) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_TRAPPED) + expect_near(a.speed, 0.0, 0.00001) + expect(wildlife_in_snare(wildlife_st, 60) == a) + expect_eq(pn_drew(wildlife_st, 14), 0) + wildlife_release(a) + expect_eq(a.state, WILD_FLEE) + expect_near(a.timer, 4.0, 0.0001) + expect_near(a.calm, 0.0, 0.0001) + expect_eq(a.lure, 0) + } +} diff --git a/packages/ludic.wildlife/tests/flee_pins_test.ludic b/packages/ludic.wildlife/tests/flee_pins_test.ludic new file mode 100644 index 00000000..5a6e9a27 --- /dev/null +++ b/packages/ludic.wildlife/tests/flee_pins_test.ludic @@ -0,0 +1,85 @@ +# flee_pins_test.ludic - phase 27.2a: FLEE as it stands. Over 95 it bolts (3 + 3 r0 s, spooked for +# 120 + 180 r1 game minutes, the alert dropped to 70, a FLED fact); fleeing it is not re-entered; its +# time up it takes home where it stands and wanders 5 s; a fresh rise over 25 looks up mid-flight +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program FleePins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + test "IDLE, alert over 95: FLEE, then the spook; two rolls in that order" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 10.0, 0.0, WILD_IDLE, 5.0) + a.alert = 97.0 + wildlife_seed(wildlife_st, 5) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_FLEE) + expect_near(a.timer, 3.0 + 3.0 * pn_roll(5, 0), 0.0001) + expect_near(a.spook, 120.0 + 180.0 * pn_roll(5, 1), 0.001) + expect_near(a.alert, 70.0, 0.0001) + let fs = pn_drain(wildlife_st) + expect_eq(pn_count(fs, WILD_FLED), 1) + expect_eq(fs[0].player, 0) + expect_eq(pn_drew(wildlife_st, 5), 2) + } + + test "FLEE, alert still over 95: no second flight, no fact, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 10.0, 0.0, WILD_FLEE, 5.0) + a.alert = 97.0 + wildlife_seed(wildlife_st, 5) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_FLEE) + expect_near(a.timer, 4.95, 0.0001) + expect(a.alert > 95.0) + expect_eq(pn_count(pn_drain(wildlife_st), WILD_FLED), 0) + expect_eq(pn_drew(wildlife_st, 5), 0) + } + + test "FLEE, its time up: home where it stands, WANDER 5 s toward (r0, r1) about it" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 10.0, 0.0, WILD_FLEE, 0.01) + wildlife_seed(wildlife_st, 5) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 5.0, 0.0001) + expect_near(a.home_x, a.x, 0.0001) + expect_near(a.home_z, a.z, 0.0001) + expect_near(a.tx, pn_tx(a, 5, 0, 60.0), 0.001) + expect_near(a.tz, pn_tz(a, 5, 0, 60.0), 0.001) + expect_eq(pn_drew(wildlife_st, 5), 2) + } + + test "FLEE, a fresh alert between 25 and 60: ALERT mid-flight, one roll (WARY never interrupts it)" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 10.0, 0.0, WILD_FLEE, 5.0) + a.alert = 40.0 + wildlife_seed(wildlife_st, 5) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_ALERT) + expect_near(a.timer, 1.5 + 2.5 * pn_roll(5, 0), 0.0001) + expect_eq(pn_drew(wildlife_st, 5), 1) + let b = pn_at(wildlife_st, 2, 10.0, 0.0, WILD_FLEE, 5.0) + b.alert = 70.0 + wildlife_seed(wildlife_st, 5) + pn_step(wildlife_st, b) + expect_eq(b.state, WILD_FLEE) + expect_eq(pn_drew(wildlife_st, 5), 0) + } +} diff --git a/packages/ludic.wildlife/tests/go_pins_test.ludic b/packages/ludic.wildlife/tests/go_pins_test.ludic new file mode 100644 index 00000000..8b0a2960 --- /dev/null +++ b/packages/ludic.wildlife/tests/go_pins_test.ludic @@ -0,0 +1,89 @@ +# go_pins_test.ludic - phase 27.2a: GO_DRINK / GO_FEED as they stand. Within 2 m of the spot it +# DRINKs for 6 + 10 r0 s; the spot gone, IDLE 2 s; its timer runs down twice a tick and gives up at +# -600 (IDLE 3 s); a hungry one turns to food of its diet nearer - unless a nearer snare wins the roll +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program GoPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function going(wildlife_st: mut WildlifeState, state: int, spot_x: float, timer: float) -> WildAnimal { + let a = pn_at(wildlife_st, 2, 0.0, 0.0, state, timer) + a.spot = 900 + a.spot_x = spot_x + a.spot_water = state == WILD_GO_DRINK + wildlife_seed(wildlife_st, 8) + pn_step(wildlife_st, a) + return a + } + + test "GO_DRINK within 2 m: DRINK for 6 + 10 r0 s, less one more tick taken at once" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = going(wildlife_st, WILD_GO_DRINK, 1.0, 120.0) + expect_eq(a.state, WILD_DRINK) + expect_near(a.timer, 6.0 + 10.0 * pn_roll(8, 0) - 0.05, 0.0001) + expect_eq(pn_drew(wildlife_st, 8), 1) + } + + test "on the way: the timer runs down 2 dt a tick; the spot gone: IDLE 2 s; past -600: IDLE 3 s" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = going(wildlife_st, WILD_GO_DRINK, 30.0, 10.0) + expect_eq(a.state, WILD_GO_DRINK) + expect_near(a.timer, 9.9, 0.0001) + let b = going(wildlife_st, WILD_GO_FEED, 30.0, -599.95) + expect_eq(b.state, WILD_IDLE) + expect_near(b.timer, 3.0, 0.0001) + wildlife_tests_fake_st.fk_gone = 900 + let c = going(wildlife_st, WILD_GO_DRINK, 30.0, 10.0) + expect_eq(c.state, WILD_IDLE) + expect_near(c.timer, 2.0, 0.0001) + expect_eq(c.spot, 0) + expect_eq(pn_drew(wildlife_st, 8), 0) + } + + test "GO_FEED, food of its diet nearer: APPROACH it (the spot kept); GO_DRINK does not turn" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 50, WILD_FEED, WILD_PLANTS, 5.0, 0.0) + let a = going(wildlife_st, WILD_GO_FEED, 30.0, 100.0) + expect_eq(a.state, WILD_APPROACH) + expect_eq(a.lure, 50) + expect_eq(a.lure_kind, WILD_FEED) + expect_eq(a.lure_owner, 3) + expect_eq(a.spot, 900) + let b = going(wildlife_st, WILD_GO_DRINK, 30.0, 100.0) + expect_eq(b.state, WILD_GO_DRINK) + expect_eq(b.lure, 0) + expect_eq(pn_drew(wildlife_st, 8), 0) + } + + test "GO_FEED, a snare nearer than the food: r0 > 0.5 draws it to the snare, which masks the food" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 60, WILD_SNARE, 0, 3.0, 0.0) + pn_lure(wildlife_tests_fake_st, 50, WILD_FEED, WILD_PLANTS, 5.0, 0.0) + for seed in 1 .. 17 { + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_GO_FEED, 100.0) + a.spot = 900 + a.spot_x = 30.0 + wildlife_seed(wildlife_st, seed) + pn_step(wildlife_st, a) + if pn_roll(seed, 0) > 0.5 { expect_eq(a.state, WILD_GO_FEED) } else { expect_eq(a.lure, 50) } + expect_eq(pn_drew(wildlife_st, seed), 1) + } + } +} diff --git a/packages/ludic.wildlife/tests/guard_pins_test.ludic b/packages/ludic.wildlife/tests/guard_pins_test.ludic new file mode 100644 index 00000000..9f9952d7 --- /dev/null +++ b/packages/ludic.wildlife/tests/guard_pins_test.ludic @@ -0,0 +1,80 @@ +# guard_pins_test.ludic - phase 27.2a: which states the alert reaches as it stands. EATING and a +# tame kind ignore it; going to water, drinking and walking to food do not - they bolt with the spot +# or the lure still held +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program GuardPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function spooked(wildlife_st: mut WildlifeState, sp: int, state: int, alert: float) -> WildAnimal { + let a = pn_at(wildlife_st, sp, 10.0, 0.0, state, 5.0) + a.alert = alert + a.spot = 900 + a.spot_x = 40.0 + a.spot_water = true + a.lure = 50 + a.lure_kind = WILD_FEED + a.lure_x = 30.0 + a.thirst = 50.0 + a.hunger = 50.0 + wildlife_seed(wildlife_st, 2) + pn_step(wildlife_st, a) + return a + } + + test "EAT, alert over 95: still eating, no fact, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = spooked(wildlife_st, 2, WILD_EAT, 97.0) + expect_eq(a.state, WILD_EAT) + expect_near(a.timer, 4.95, 0.0001) + expect_eq(pn_count(pn_drain(wildlife_st), WILD_FLED), 0) + expect_eq(pn_drew(wildlife_st, 2), 0) + } + + test "a tame kind, alert over 95: IDLE still, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = spooked(wildlife_st, 4, WILD_IDLE, 97.0) + expect_eq(a.state, WILD_IDLE) + expect_eq(pn_drew(wildlife_st, 2), 0) + } + + test "DRINK, APPROACH, GO_DRINK and GO_FEED, alert over 95: FLEE, two rolls, the spot and the lure kept" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let states = [WILD_DRINK, WILD_APPROACH, WILD_GO_DRINK, WILD_GO_FEED] + for i in 0 .. 4 { + let a = spooked(wildlife_st, 2, states[i], 97.0) + expect_eq(a.state, WILD_FLEE) + expect_near(a.timer, 3.0 + 3.0 * pn_roll(2, 0), 0.0001) + expect_eq(a.spot, 900) + expect_eq(a.lure, 50) + expect_eq(pn_drew(wildlife_st, 2), 2) + } + } + + test "GO_DRINK and DRINK, a fresh alert over 25: ALERT, one roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = spooked(wildlife_st, 2, WILD_GO_DRINK, 40.0) + expect_eq(a.state, WILD_ALERT) + expect_eq(pn_drew(wildlife_st, 2), 1) + let b = spooked(wildlife_st, 2, WILD_DRINK, 40.0) + expect_eq(b.state, WILD_ALERT) + expect_eq(pn_drew(wildlife_st, 2), 1) + } +} diff --git a/packages/ludic.wildlife/tests/idle_pins_test.ludic b/packages/ludic.wildlife/tests/idle_pins_test.ludic new file mode 100644 index 00000000..eb8f0f00 --- /dev/null +++ b/packages/ludic.wildlife/tests/idle_pins_test.ludic @@ -0,0 +1,94 @@ +# idle_pins_test.ludic - phase 27.2a: IDLE's way out as it stands. Its time up it goes WANDER for +# 6 + 10 r s toward a spot about home (an angle, a distance): a tracked walker first rolls for a bed +# when the sit was short, and the droppings' roll comes before all of them +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program IdlePins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + test "IDLE, its time up: WANDER for 6 + 10 r0 s toward home at (r1, r2); a walker leaving no sign rolls three" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_IDLE, 0.0) + wildlife_seed(wildlife_st, 11) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 6.0 + 10.0 * pn_roll(11, 0), 0.0001) + expect_near(a.tx, pn_tx(a, 11, 1, 60.0), 0.001) + expect_near(a.tz, pn_tz(a, 11, 1, 60.0), 0.001) + expect_eq(pn_count(pn_drain(wildlife_st), WILD_BED), 0) + expect_eq(pn_drew(wildlife_st, 11), 3) + } + + test "a tracked walker after a short sit rolls r0 for a bed (under 0.06 leaves one), then the same three" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let seeds = [25, 11] + for i in 0 .. 2 { + let a = pn_at(wildlife_st, 0, 0.0, 0.0, WILD_IDLE, 0.0) + wildlife_seed(wildlife_st, seeds[i]) + pn_step(wildlife_st, a) + var bed = 0 + if pn_roll(seeds[i], 0) < 0.06 { bed = 1 } + expect_eq(pn_count(pn_drain(wildlife_st), WILD_BED), bed) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 6.0 + 10.0 * pn_roll(seeds[i], 1), 0.0001) + expect_near(a.tx, pn_tx(a, seeds[i], 2, 60.0), 0.001) + expect_eq(pn_drew(wildlife_st, seeds[i]), 4) + } + } + + test "a sit of 6 s or more rolls no bed: the three alone" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 0, 0.0, 0.0, WILD_IDLE, -10.0) + wildlife_seed(wildlife_st, 25) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 6.0 + 10.0 * pn_roll(25, 0), 0.0001) + expect_eq(pn_count(pn_drain(wildlife_st), WILD_BED), 0) + expect_eq(pn_drew(wildlife_st, 25), 3) + } + + test "droppings due roll first (scat_t = 120 + 300 r0, a fact within 500 m), then bed, time, target" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_player(wildlife_tests_fake_st, 100.0, 0.0) + let a = pn_at(wildlife_st, 0, 0.0, 0.0, WILD_IDLE, 0.0) + a.scat_t = 0.0 + wildlife_seed(wildlife_st, 15) + pn_step(wildlife_st, a) + let fs = pn_drain(wildlife_st) + expect_eq(pn_count(fs, WILD_SCAT), 1) + expect_near(a.scat_t, 120.0 + 300.0 * pn_roll(15, 0), 0.001) + var bed = 0 + if pn_roll(15, 1) < 0.06 { bed = 1 } + expect_eq(pn_count(fs, WILD_BED), bed) + expect_near(a.timer, 6.0 + 10.0 * pn_roll(15, 2), 0.0001) + expect_near(a.tx, pn_tx(a, 15, 3, 60.0), 0.001) + expect_eq(pn_drew(wildlife_st, 15), 5) + } + + test "IDLE before its time stays, and rolls nothing" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_IDLE, 5.0) + wildlife_seed(wildlife_st, 11) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_IDLE) + expect_near(a.timer, 4.95, 0.0001) + expect_eq(pn_drew(wildlife_st, 11), 0) + } +} diff --git a/packages/ludic.wildlife/tests/lure_pins_test.ludic b/packages/ludic.wildlife/tests/lure_pins_test.ludic new file mode 100644 index 00000000..16414590 --- /dev/null +++ b/packages/ludic.wildlife/tests/lure_pins_test.ludic @@ -0,0 +1,81 @@ +# lure_pins_test.ludic - phase 27.2a: IDLE and WANDER drawn to what is set out, as they stand. The +# nearest within 70 m (food at 0.4 of its distance while feeding is taught) of its diet, or a snare +# it fits, which rolls r > 0.5 each tick it is looked at: APPROACH, whatever its timer +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program LurePins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function drawn(wildlife_st: mut WildlifeState, sp: int, state: int) -> WildAnimal { + let a = pn_at(wildlife_st, sp, 0.0, 0.0, state, 5.0) + wildlife_seed(wildlife_st, 12) + pn_step(wildlife_st, a) + return a + } + + test "IDLE and WANDER, food of its diet within 70 m: APPROACH it, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 50, WILD_FEED, WILD_PLANTS, 20.0, 0.0) + let a = drawn(wildlife_st, 2, WILD_IDLE) + expect_eq(a.state, WILD_APPROACH) + expect_eq(a.lure, 50) + expect_eq(a.lure_kind, WILD_FEED) + expect_eq(a.lure_owner, 3) + expect_near(a.lure_x, 20.0, 0.0001) + let b = drawn(wildlife_st, 2, WILD_WANDER) + expect_eq(b.state, WILD_APPROACH) + expect_eq(pn_drew(wildlife_st, 12), 0) + } + + test "food of another diet, or 80 m off, is left alone; taught, 80 m is near enough" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState, wild_pin_st: mut WildPinState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 51, WILD_FEED, WILD_FISH, 20.0, 0.0) + pn_lure(wildlife_tests_fake_st, 50, WILD_FEED, WILD_PLANTS, 80.0, 0.0) + let a = drawn(wildlife_st, 2, WILD_IDLE) + expect_eq(a.state, WILD_IDLE) + expect_eq(a.lure, 0) + expect_eq(pn_drew(wildlife_st, 12), 0) + wild_pin_st.pn_taught = true + let b = drawn(wildlife_st, 2, WILD_IDLE) + expect_eq(b.state, WILD_APPROACH) + expect_eq(b.lure, 50) + } + + test "a snare it fits: one roll, over 0.5 and it APPROACHes the snare" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 60, WILD_SNARE, 0, 10.0, 0.0) + for seed in 1 .. 17 { + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_IDLE, 5.0) + wildlife_seed(wildlife_st, seed) + pn_step(wildlife_st, a) + if pn_roll(seed, 0) > 0.5 { expect_eq(a.state, WILD_APPROACH) } else { expect_eq(a.state, WILD_IDLE) } + if pn_roll(seed, 0) > 0.5 { expect_eq(a.lure_kind, WILD_SNARE) } + expect_eq(pn_drew(wildlife_st, seed), 1) + } + } + + test "a snare too small for it (a deer, size 1): no roll, left alone" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 60, WILD_SNARE, 0, 10.0, 0.0) + let a = drawn(wildlife_st, 0, WILD_IDLE) + expect_eq(a.state, WILD_IDLE) + expect_eq(pn_drew(wildlife_st, 12), 0) + } +} diff --git a/packages/ludic.wildlife/tests/perch_pins_test.ludic b/packages/ludic.wildlife/tests/perch_pins_test.ludic new file mode 100644 index 00000000..520ebdb7 --- /dev/null +++ b/packages/ludic.wildlife/tests/perch_pins_test.ludic @@ -0,0 +1,82 @@ +# perch_pins_test.ludic - phase 27.2a: a bird coming down and on the ground as it stands. Gliding in, +# down and within 2 m it PERCHes for 8 + 16 r0 s; anyone within its flee, or 20 s over its time, +# lifts it (FLY, 20 + 40 r0 s); on the ground its time up or anyone within its flee lifts it too +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program PerchPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function bird(wildlife_st: mut WildlifeState, state: int, timer: float, fly_h: float) -> WildAnimal { + let a = pn_at(wildlife_st, 3, 0.0, 0.0, state, timer) + a.fly_h = fly_h + wildlife_seed(wildlife_st, 17) + pn_fly(wildlife_st, a) + return a + } + + test "LAND, down and at its spot: PERCH for 8 + 16 r0 s, one roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = bird(wildlife_st, WILD_LAND, 20.0, 0.1) + expect_eq(a.state, WILD_PERCH) + expect_near(a.timer, 8.0 + 16.0 * pn_roll(17, 0), 0.0001) + expect_near(a.fly_h, 0.0, 0.00001) + expect_eq(pn_drew(wildlife_st, 17), 1) + } + + test "LAND still high: gliding on, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = bird(wildlife_st, WILD_LAND, 20.0, 5.0) + expect_eq(a.state, WILD_LAND) + expect_near(a.fly_h, 5.0 - 0.05 * 3.2, 0.0001) + expect_eq(pn_drew(wildlife_st, 17), 0) + } + + test "LAND with somebody within its flee, or 20 s past its time: FLY for 20 + 40 r0 s at the height it had" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let b = bird(wildlife_st, WILD_LAND, -19.99, 5.0) + expect_eq(b.state, WILD_FLY) + expect_near(b.fly_h, 5.0 - 0.05 * 3.2, 0.0001) + pn_player(wildlife_tests_fake_st, 5.0, 0.0) + let a = bird(wildlife_st, WILD_LAND, 20.0, 5.0) + expect_eq(a.state, WILD_FLY) + expect_near(a.timer, 20.0 + 40.0 * pn_roll(17, 0), 0.0001) + expect_eq(pn_drew(wildlife_st, 17), 1) + } + + test "PERCH, its time up or somebody within its flee: FLY for 20 + 40 r0 s, 0.5 m up" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = bird(wildlife_st, WILD_PERCH, 0.01, 0.0) + expect_eq(a.state, WILD_FLY) + expect_near(a.timer, 20.0 + 40.0 * pn_roll(17, 0), 0.0001) + expect_near(a.fly_h, 0.5, 0.0001) + pn_player(wildlife_tests_fake_st, 5.0, 0.0) + let b = bird(wildlife_st, WILD_PERCH, 5.0, 0.0) + expect_eq(b.state, WILD_FLY) + expect_eq(pn_drew(wildlife_st, 17), 1) + } + + test "PERCH before its time, nobody near: stays, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = bird(wildlife_st, WILD_PERCH, 5.0, 0.0) + expect_eq(a.state, WILD_PERCH) + expect_near(a.timer, 4.95, 0.0001) + expect_eq(pn_drew(wildlife_st, 17), 0) + } +} diff --git a/packages/ludic.wildlife/tests/pin/dice.ludic b/packages/ludic.wildlife/tests/pin/dice.ludic new file mode 100644 index 00000000..2c003ead --- /dev/null +++ b/packages/ludic.wildlife/tests/pin/dice.ludic @@ -0,0 +1,46 @@ +# dice.ludic - what the package's dice drew since a seed, read off a newborn's yaw (the first draw +# a newborn takes), and the facts and lures the pins count and set out +numbers float +const PN_TAU: float = 6.2831853 + +# the k-th draw (from 0) of a stream seeded as wildlife_seed(seed) seeds the package's +function pn_roll(seed: int, k: int) -> float { + let r = rng_new(seed) + for i in 0 .. k { rng_next(r) } + return rng_float(r) +} + +# how many draws the package has taken since wildlife_seed(seed): the next one is a newborn's yaw; +# -1 when it is none of the first 64 +function pn_drew(wildlife_st: mut WildlifeState, seed: int) -> int { + let yaw = wildlife_new(wildlife_st, 2, 9000.0, 9000.0, false).yaw + let r = rng_new(seed) + for k in 0 .. 64 { + if Math.abs(rng_float(r) * PN_TAU - yaw) < 0.00001 { return k } + } + return -1 +} + +# a wander's target from draws k (the angle) and k + 1 (the distance) about home, radius `home` +function pn_tx(a: WildAnimal, seed: int, k: int, home: float) -> float { return a.home_x + Math.cos(pn_roll(seed, k) * PN_TAU) * pn_roll(seed, k + 1) * home } +function pn_tz(a: WildAnimal, seed: int, k: int, home: float) -> float { return a.home_z + Math.sin(pn_roll(seed, k) * PN_TAU) * pn_roll(seed, k + 1) * home } + +# the facts waiting, drained, and how many of them are of a kind +function pn_drain(wildlife_st: WildlifeState) -> []WildFact { return q_drain(wildlife_facts(wildlife_st)) } +function pn_count(fs: []WildFact, what: int) -> int { + var n = 0 + for i in 0 .. len(fs) { if fs[i].what == what { n += 1 } } + return n +} + +function pn_lure(wildlife_tests_fake_st: mut WildlifeTestsFakeState, uid: int, kind: int, diet: int, x: float, z: float) -> WildLure { + let l = new WildLure + l.uid = uid + l.kind = kind + l.diet = diet + l.x = x + l.z = z + l.owner = 3 + push(wildlife_tests_fake_st.fk_lures, l) + return l +} diff --git a/packages/ludic.wildlife/tests/pin/index.ludic b/packages/ludic.wildlife/tests/pin/index.ludic new file mode 100644 index 00000000..6738d6f7 --- /dev/null +++ b/packages/ludic.wildlife/tests/pin/index.ludic @@ -0,0 +1,60 @@ +# pin - what the transition pins share (phase 27.2a): one step at a fixed rate, an animal set in a +# state, a spot and a teaching switch of the test's own, and two species the fake has not +numbers float +import "dice.ludic" +export state WildPinState { + pn_no_spot: bool = false # the world offers no water or forage + pn_taught: bool = false # feeding is being taught +} + +function pn_spot(wild_pin_st: WildPinState, a: WildAnimal, water: bool, s: WildSpot) -> bool { + if wild_pin_st.pn_no_spot { return false } + s.uid = 900 + s.x = a.x + 30.0 + s.z = a.z + s.water = water + return true +} + +function pn_taught(wild_pin_st: WildPinState) -> bool { return wild_pin_st.pn_taught } + +# after fk_setup: 4 a horse (tame), 5 a crow (flies, never comes down on its own) +function pn_species(wildlife_st: WildlifeState) -> void { + let h = new WildSpecies + h.key = "horse" + h.tame = true + h.size = 2 + h.diet = WILD_HAY + wildlife_species_add(wildlife_st, h) + let c = new WildSpecies + c.key = "crow" + c.flies = true + c.walk = 4.0 + c.flee = 8.0 + c.land_shy = 0.0 + c.land_chance = 0.0 + wildlife_species_add(wildlife_st, c) +} + +# one of species sp at (x, z), in `state` with `timer` left; no droppings due for a long while +function pn_at(wildlife_st: mut WildlifeState, sp: int, x: float, z: float, state: int, timer: float) -> WildAnimal { + let a = wildlife_make(wildlife_st, sp, x, z, false, 0.0, 0, 1.0, 1) + a.state = state + a.timer = timer + a.scat_t = 100000.0 + return a +} + +# one tick of 50 ms, a walker's and a bird's +function pn_step(wildlife_st: mut WildlifeState, a: WildAnimal) -> void { wildlife_tick_ground(wildlife_st, a, 0.05, 0.05 / 60.0) } +function pn_fly(wildlife_st: mut WildlifeState, a: WildAnimal) -> void { wildlife_tick_bird(wildlife_st, a, 0.05) } + +function pn_setup(wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) -> void { + fk_setup(wildlife_st, wildlife_tests_fake_st) + pn_species(wildlife_st) +} + +function pn_player(wildlife_tests_fake_st: mut WildlifeTestsFakeState, x: float, z: float) -> void { + wildlife_tests_fake_st.fk_px[0] = x + wildlife_tests_fake_st.fk_pz[0] = z +} diff --git a/packages/ludic.wildlife/tests/posture_pins_test.ludic b/packages/ludic.wildlife/tests/posture_pins_test.ludic new file mode 100644 index 00000000..2a6ec7b6 --- /dev/null +++ b/packages/ludic.wildlife/tests/posture_pins_test.ludic @@ -0,0 +1,77 @@ +# posture_pins_test.ludic - phase 27.2a: the alert's postures as they stand. Over 25 and above what +# it has already reacted to (`seen`) it looks up (ALERT, 1.5 + 2.5 r s); over 60, WARY (2 + 2 r s); +# ALERT gives way to WARY, never WARY to ALERT; a level at or under `seen` does nothing +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program PosturePins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function hare(wildlife_st: mut WildlifeState, state: int, alert: float, seen: float) -> WildAnimal { + let a = pn_at(wildlife_st, 2, 10.0, 0.0, state, 5.0) + a.alert = alert + a.seen = seen + wildlife_seed(wildlife_st, 3) + pn_step(wildlife_st, a) + return a + } + + test "IDLE, alert over 25 and fresh: ALERT for 1.5 + 2.5 r0 s, one roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = hare(wildlife_st, WILD_IDLE, 40.0, 0.0) + expect_eq(a.state, WILD_ALERT) + expect_near(a.timer, 1.5 + 2.5 * pn_roll(3, 0), 0.0001) + expect_near(a.speed, 0.0, 0.00001) + expect_eq(pn_drew(wildlife_st, 3), 1) + } + + test "IDLE, alert over 60 and fresh: WARY for 2 + 2 r0 s, one roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = hare(wildlife_st, WILD_IDLE, 70.0, 0.0) + expect_eq(a.state, WILD_WARY) + expect_near(a.timer, 2.0 + 2.0 * pn_roll(3, 0), 0.0001) + expect_eq(pn_drew(wildlife_st, 3), 1) + } + + test "ALERT, alert rising over 60: WARY, one roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = hare(wildlife_st, WILD_ALERT, 70.0, 0.0) + expect_eq(a.state, WILD_WARY) + expect_near(a.timer, 2.0 + 2.0 * pn_roll(3, 0), 0.0001) + expect_eq(pn_drew(wildlife_st, 3), 1) + } + + test "WARY, a fresh alert between 25 and 60: still WARY, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = hare(wildlife_st, WILD_WARY, 40.0, 0.0) + expect_eq(a.state, WILD_WARY) + expect_near(a.timer, 4.95, 0.0001) + expect_eq(pn_drew(wildlife_st, 3), 0) + } + + test "an alert no higher than what it has seen: nothing, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = hare(wildlife_st, WILD_IDLE, 70.0, 80.0) + expect_eq(a.state, WILD_IDLE) + let b = hare(wildlife_st, WILD_IDLE, 40.0, 50.0) + expect_eq(b.state, WILD_IDLE) + expect_near(b.alert, 40.0 - 2.5 * 0.05, 0.0001) + expect_eq(pn_drew(wildlife_st, 3), 0) + } +} diff --git a/packages/ludic.wildlife/tests/react_pins_test.ludic b/packages/ludic.wildlife/tests/react_pins_test.ludic new file mode 100644 index 00000000..7dde4441 --- /dev/null +++ b/packages/ludic.wildlife/tests/react_pins_test.ludic @@ -0,0 +1,71 @@ +# react_pins_test.ludic - phase 27.2a: the ends of ALERT and WARY as they stand. Either one, its time +# up, wanders for 3 + 4 r0 s toward (r1, r2) about the home it had: three rolls, the time first +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program ReactPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + test "ALERT, its time up: WANDER 3 + 4 r0 s toward (r1, r2) about home, three rolls" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 10.0, 0.0, WILD_ALERT, 0.01) + wildlife_seed(wildlife_st, 9) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 3.0 + 4.0 * pn_roll(9, 0), 0.0001) + expect_near(a.home_x, 10.0, 0.0001) + expect_near(a.tx, pn_tx(a, 9, 1, 60.0), 0.001) + expect_near(a.tz, pn_tz(a, 9, 1, 60.0), 0.001) + expect_eq(pn_drew(wildlife_st, 9), 3) + } + + test "ALERT before its time: stands still, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 10.0, 0.0, WILD_ALERT, 5.0) + a.speed = 3.0 + wildlife_seed(wildlife_st, 9) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_ALERT) + expect_near(a.speed, 0.0, 0.00001) + expect_eq(pn_drew(wildlife_st, 9), 0) + } + + test "WARY, its time up: the step away first, then WANDER 3 + 4 r0 s about the old home, three rolls" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 10.0, 0.0, WILD_WARY, 0.01) + wildlife_seed(wildlife_st, 9) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 3.0 + 4.0 * pn_roll(9, 0), 0.0001) + expect_near(a.home_x, 10.0, 0.0001) + expect(Math.abs(a.x - 10.0) + Math.abs(a.z) > 0.001) + expect_near(a.tx, pn_tx(a, 9, 1, 60.0), 0.001) + expect_eq(pn_drew(wildlife_st, 9), 3) + } + + test "WARY before its time: backs off at 0.8 of its walk, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 10.0, 0.0, WILD_WARY, 5.0) + wildlife_seed(wildlife_st, 9) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_WARY) + expect_near(a.speed, 0.8, 0.0001) + expect_eq(pn_drew(wildlife_st, 9), 0) + } +} diff --git a/packages/ludic.wildlife/tests/seed_pins_test.ludic b/packages/ludic.wildlife/tests/seed_pins_test.ludic new file mode 100644 index 00000000..175cb494 --- /dev/null +++ b/packages/ludic.wildlife/tests/seed_pins_test.ludic @@ -0,0 +1,74 @@ +# seed_pins_test.ludic - phase 27.2a: seed set out, as it stands (the world is scanned for any once a +# second, in wildlife_tick). A bird aloft with seed within 120 m and nobody within 0.7 of its flee +# comes down 1 + 2 r0 m off it at the angle r1; perched at it, it rolls r < 0.12 dt to be FED +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program SeedPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + test "FLY, seed within 120 m: LAND (30 s) at 1 + 2 r0 m from the seed, angle r1: two rolls" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 77, WILD_FEED, WILD_SEED, 0.0, 0.0) + pn_player(wildlife_tests_fake_st, 500.0, 0.0) + let a = pn_at(wildlife_st, 3, 0.0, 0.0, WILD_FLY, 5.0) + wildlife_seed(wildlife_st, 18) + wildlife_tick(wildlife_st, 0.05, 0.0) + expect_eq(a.state, WILD_LAND) + expect_near(a.timer, 30.0, 0.0001) + let off = 1.0 + 2.0 * pn_roll(18, 0) + let oa = pn_roll(18, 1) * 6.2831853 + expect_near(a.tx, Math.sin(oa) * off, 0.001) + expect_near(a.tz, Math.cos(oa) * off, 0.001) + expect_eq(pn_drew(wildlife_st, 18), 2) + } + + test "FLY, seed down but somebody within 0.7 of its flee: no coming down, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 77, WILD_FEED, WILD_SEED, 0.0, 0.0) + pn_player(wildlife_tests_fake_st, 3.0, 0.0) + let a = pn_at(wildlife_st, 3, 0.0, 0.0, WILD_FLY, 5.0) + wildlife_seed(wildlife_st, 18) + wildlife_tick(wildlife_st, 0.05, 0.0) + expect_eq(a.state, WILD_FLY) + expect_eq(pn_drew(wildlife_st, 18), 0) + } + + test "PERCH within 3 m of seed: one roll a tick, under 0.12 dt it is FED (calm 180); it stays" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 77, WILD_FEED, WILD_SEED, 1.0, 0.0) + pn_player(wildlife_tests_fake_st, 500.0, 0.0) + let seeds = [25, 11] + for i in 0 .. 2 { + let a = pn_at(wildlife_st, 3, 0.0, 0.0, WILD_PERCH, 5.0) + a.hunger = 50.0 + wildlife_seed(wildlife_st, seeds[i]) + wildlife_tick(wildlife_st, 0.05, 0.0) + expect_eq(a.state, WILD_PERCH) + expect_near(a.hunger, 50.0 - 30.0 * 0.05, 0.0001) + var fed = 0 + if pn_roll(seeds[i], 0) < 0.12 * 0.05 { fed = 1 } + let fs = pn_drain(wildlife_st) + expect_eq(pn_count(fs, WILD_FED), fed) + if fed == 1 { expect_near(a.calm, 180.0, 0.0001) } + expect_eq(pn_drew(wildlife_st, seeds[i]), 1) + wildlife_remove(a) + } + } +} diff --git a/packages/ludic.wildlife/tests/seek_pins_test.ludic b/packages/ludic.wildlife/tests/seek_pins_test.ludic new file mode 100644 index 00000000..7524c576 --- /dev/null +++ b/packages/ludic.wildlife/tests/seek_pins_test.ludic @@ -0,0 +1,81 @@ +# seek_pins_test.ludic - phase 27.2a: IDLE and WANDER going to water or forage as they stand. Thirst +# or hunger over 60 asks the world for a spot - water when thirst is the greater, forage on a tie - +# and sets off (GO_DRINK / GO_FEED, 120 s) whatever its timer; no spot, no change; no roll either way +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program SeekPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + function needy(wildlife_st: mut WildlifeState, state: int, thirst: float, hunger: float) -> WildAnimal { + let a = pn_at(wildlife_st, 2, 0.0, 0.0, state, 5.0) + a.thirst = thirst + a.hunger = hunger + wildlife_seed(wildlife_st, 6) + pn_step(wildlife_st, a) + return a + } + + test "IDLE, thirst over 60 and over hunger: GO_DRINK to the world's water, 120 s, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = needy(wildlife_st, WILD_IDLE, 70.0, 10.0) + expect_eq(a.state, WILD_GO_DRINK) + expect_eq(a.spot, 900) + expect(a.spot_water) + expect_near(a.spot_x, 30.0, 0.0001) + expect_near(a.timer, 120.0, 0.0001) + expect_near(a.way_t, 0.0, 0.00001) + expect_eq(pn_drew(wildlife_st, 6), 0) + } + + test "IDLE, hunger over 60 and over thirst - or the two level: GO_FEED to forage" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = needy(wildlife_st, WILD_IDLE, 10.0, 70.0) + expect_eq(a.state, WILD_GO_FEED) + expect(not a.spot_water) + let b = needy(wildlife_st, WILD_IDLE, 70.0, 70.0) + expect_eq(b.state, WILD_GO_FEED) + expect_eq(pn_drew(wildlife_st, 6), 0) + } + + test "WANDER sets off the same way" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = needy(wildlife_st, WILD_WANDER, 70.0, 10.0) + expect_eq(a.state, WILD_GO_DRINK) + expect_near(a.timer, 120.0, 0.0001) + } + + test "no spot answered: IDLE stays, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState, wild_pin_st: mut WildPinState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + wild_pin_st.pn_no_spot = true + let a = needy(wildlife_st, WILD_IDLE, 70.0, 10.0) + expect_eq(a.state, WILD_IDLE) + expect_eq(a.spot, 0) + expect_near(a.timer, 4.95, 0.0001) + expect_eq(pn_drew(wildlife_st, 6), 0) + } + + test "thirst beats food set out: water first, the lure not taken" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + pn_lure(wildlife_tests_fake_st, 50, WILD_FEED, WILD_PLANTS, 5.0, 0.0) + let a = needy(wildlife_st, WILD_IDLE, 70.0, 10.0) + expect_eq(a.state, WILD_GO_DRINK) + expect_eq(a.lure, 0) + } +} diff --git a/packages/ludic.wildlife/tests/wander_pins_test.ludic b/packages/ludic.wildlife/tests/wander_pins_test.ludic new file mode 100644 index 00000000..faa1ec6d --- /dev/null +++ b/packages/ludic.wildlife/tests/wander_pins_test.ludic @@ -0,0 +1,77 @@ +# wander_pins_test.ludic - phase 27.2a: WANDER's way out as it stands. At its target (1.5 m) or its +# time up it goes IDLE for 3 + 8 r s; a refused step (water, a climb, a push) rolls nothing, turns +# it 75 degrees and zeroes its timer (the next step that goes ends the wander) +import "ludic.wildlife" +import "ludic.base" +import "fake" +import "pin" +program WanderPins { + numbers float + bind WildlifeWorld { + ground: fn fk_ground + water: fn fk_water + players: fn fk_players + player_on: fn fk_player_on + player_x: fn fk_x + player_y: fn fk_y + player_z: fn fk_z + about: fn fk_about_of + lures: fn fk_count + lure: fn fk_lure + present: fn fk_present + spot: fn pn_spot + taught: fn pn_taught + } + + test "WANDER at its target: IDLE for 3 + 8 r0 s, one roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_WANDER, 10.0) + a.tx = 0.5 + a.tz = 0.0 + wildlife_seed(wildlife_st, 7) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_IDLE) + expect_near(a.timer, 3.0 + 8.0 * pn_roll(7, 0), 0.0001) + expect_eq(pn_drew(wildlife_st, 7), 1) + } + + test "WANDER, its time up short of the target: IDLE for 3 + 8 r0 s, one roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_WANDER, 0.01) + a.tx = 50.0 + a.tz = 0.0 + wildlife_seed(wildlife_st, 7) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_IDLE) + expect_near(a.timer, 3.0 + 8.0 * pn_roll(7, 0), 0.0001) + expect_eq(pn_drew(wildlife_st, 7), 1) + } + + test "WANDER on its way stays, and rolls nothing" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, 0.0, 0.0, WILD_WANDER, 5.0) + a.tx = 50.0 + a.tz = 0.0 + wildlife_seed(wildlife_st, 7) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 4.95, 0.0001) + expect_eq(pn_drew(wildlife_st, 7), 0) + } + + test "a step into the water is refused: turned 75 degrees, timer 0, still WANDER, no roll" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) { + pn_setup(wildlife_st, wildlife_tests_fake_st) + let a = pn_at(wildlife_st, 2, -299.99, 0.0, WILD_WANDER, 5.0) + a.yaw = 1.5707964 + a.tx = -400.0 + a.tz = 0.0 + let wet = wildlife_turned_wet(wildlife_st) + wildlife_seed(wildlife_st, 7) + pn_step(wildlife_st, a) + expect_eq(a.state, WILD_WANDER) + expect_near(a.timer, 0.0, 0.00001) + expect_eq(wildlife_turned_wet(wildlife_st), wet + 1) + expect_near(a.x, -299.99, 0.0001) + expect_eq(pn_drew(wildlife_st, 7), 0) + } +}