ludic/packages/ludic.needs/tests/needs_test.ludic
Orkuncakilkaya cf42214974 ludic.needs: a body's four needs, their rates, meals and the countdown to a collapse as a fact
Warmth, food, water and energy as bars of 0..100; what an hour of doing something
costs them (the package's rates: a full bar is a day at a trip's median); the
countdown when one runs out, reported as NEEDS_CRITICAL / NEEDS_RESTORED /
NEEDS_COLLAPSED facts. The body's work, the air, clothing, the sun, the
difficulty and the effects come through the NeedsWorld port. Its own save
section. Uses ludic_base only; 13 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 06:03:57 +03:00

210 lines
5.5 KiB
Text

# needs_test.ludic - a full bar is a day, work drains faster, the port's multipliers and bonuses
# count, meals fill, a need at zero counts down to a collapse as a fact, and the save reads back
import "ludic.needs"
import "ludic.base"
program NeedsTest {
numbers float
var act: int = 0
var air: float = -2.0
var keep: float = 0.0
var hot: bool = false
var thirst: bool = true
var gentle: bool = false
var held: bool = false
var food_mult: float = 1.0
var warm_bonus: float = 0.0
function fake_act() -> int { return act }
function fake_air() -> float { return air }
function fake_keep() -> float { return keep }
function fake_hot() -> bool { return hot }
function fake_thirst() -> bool { return thirst }
function fake_gentle() -> bool { return gentle }
function fake_held() -> bool { return held }
function fake_drain(k: int) -> float {
if k == NEED_FOOD { return food_mult }
return 1.0
}
function fake_bonus(k: int) -> float {
if k == NEED_WARMTH { return warm_bonus }
return 0.0
}
bind NeedsWorld {
activity: fn fake_act
warmth: fn fake_air
keep: fn fake_keep
hot: fn fake_hot
thirst: fn fake_thirst
gentle: fn fake_gentle
held: fn fake_held
drain: fn fake_drain
bonus: fn fake_bonus
}
function fresh() -> void { needs_reset() }
function hours(h: float) -> void { for i in 0 .. int(h * 10.0) { needs_run(0.1) } }
function facts() -> []NeedFact { return q_drain(needs_facts()) }
test "a new body is warm, rested, fed and watered" {
fresh()
expect(needs_warmth() == 100.0)
expect(needs_energy() == 100.0)
expect(needs_food() == 70.0)
expect(needs_water() == 85.0)
expect(needs_pace() == 1.0)
expect(needs_can_run())
}
test "a walking day empties a full bar of food in about a day, a run much sooner" {
fresh()
needs_set(NEED_FOOD, 100.0)
act = NEEDS_WALK
hours(12.0)
expect_near(needs_food(), 52.0, 0.5)
needs_set(NEED_FOOD, 100.0)
act = NEEDS_RUN
hours(12.0)
expect(needs_food() < 20.0)
}
test "the sun and the lake make thirst faster, but never past the cap" {
fresh()
needs_set(NEED_WATER, 100.0)
act = NEEDS_SWIM_HARD
hot = true
hours(1.0)
expect_near(needs_water(), 92.5, 0.01)
}
test "thirst off holds water full" {
fresh()
thirst = false
needs_set(NEED_WATER, 10.0)
hours(1.0)
expect(needs_water() == 100.0)
}
test "the port's drain and bonus are what the bars get" {
fresh()
food_mult = 2.0
hours(1.0)
expect_near(needs_food(), 62.0, 0.01)
warm_bonus = 10.0
air = 0.0
needs_set(NEED_WARMTH, 50.0)
hours(1.0)
expect_near(needs_warmth(), 60.0, 0.01)
}
test "clothing keeps what is being lost, and a dry body loses more" {
fresh()
air = -20.0
keep = 0.5
hours(1.0)
expect_near(needs_warmth(), 90.0, 0.01)
needs_set(NEED_WARMTH, 100.0)
needs_set(NEED_WATER, 10.0)
keep = 0.0
hours(1.0)
expect_near(needs_warmth(), 75.0, 0.01)
}
test "sitting gives energy back, swimming takes it" {
fresh()
needs_set(NEED_ENERGY, 40.0)
act = NEEDS_SIT
hours(1.0)
expect_near(needs_energy(), 80.0, 0.01)
act = NEEDS_SWIM
hours(1.0)
expect_near(needs_energy(), 52.0, 0.01)
}
test "meals fill, and every bar is held to 0..100" {
fresh()
needs_meal(50.0, 50.0, 10.0)
expect(needs_food() == 100.0)
expect(needs_water() == 100.0)
needs_feed(NEED_ENERGY, -150.0)
expect(needs_energy() == 0.0)
needs_floor(NEED_ENERGY, 30.0)
expect(needs_energy() == 30.0)
}
test "a need at zero is a countdown, then a collapse, as facts" {
fresh()
needs_set(NEED_WATER, 0.0)
needs_watch(0.1)
let a = facts()
expect_eq(len(a), 1)
expect_eq(a[0].what, NEEDS_CRITICAL)
expect_eq(a[0].need, NEED_WATER)
expect(needs_last_legs() > 0.0)
expect(needs_pace() < 0.6)
var i = 0
while q_len(needs_facts()) == 0 and i < 200 {
needs_watch(0.1)
i += 1
}
let b = facts()
expect_eq(len(b), 1)
expect_eq(b[0].what, NEEDS_COLLAPSED)
expect_eq(b[0].need, NEED_WATER)
expect(i >= 99 and i <= 101)
expect_eq(needs_collapses(), 0)
needs_went_down()
expect_eq(needs_collapses(), 1)
}
test "a drink in time calls the countdown off" {
fresh()
needs_set(NEED_FOOD, 0.0)
needs_watch(0.1)
needs_feed(NEED_FOOD, 30.0)
needs_watch(0.1)
let f = facts()
expect_eq(len(f), 2)
expect_eq(f[1].what, NEEDS_RESTORED)
expect(needs_last_legs() == 0.0)
}
test "gentle never counts down, and a body already down waits" {
fresh()
gentle = true
needs_set(NEED_WARMTH, 0.0)
needs_watch(0.1)
expect(needs_warmth() == 22.0)
expect_eq(q_len(needs_facts()), 0)
gentle = false
held = true
needs_set(NEED_WARMTH, 0.0)
needs_watch(0.1)
expect_eq(q_len(needs_facts()), 0)
}
test "a hand up costs the body but leaves nothing below twenty" {
fresh()
needs_set(NEED_FOOD, 25.0)
needs_helped_up()
expect(needs_food() == 20.0)
expect(needs_warmth() == 85.0)
expect(needs_energy() == 75.0)
}
test "a save section reads back the same body" {
fresh()
needs_set(NEED_FOOD, 33.5)
needs_set(NEED_WARMTH, 12.25)
needs_went_down()
let root = save_tree()
save_section(root, "needs", 1, needs_save())
let text = save_encode(root)
needs_reset()
let n = load_section(save_decode(text), "needs")
needs_load(n.data, n.version)
expect(needs_food() == 33.5)
expect(needs_warmth() == 12.25)
expect_eq(needs_collapses(), 1)
}
}