# weather_test.ludic - spells change on their clock and by the kinds' odds, the edges ease, the # story leans when asked, only the owner runs the spells, and the save reads back import "ludic.weather" import "ludic.base" program WeatherTest { numbers float state WeatherTestState { owns: bool = true hours: float = 12.0 bias: int = 0 lean: []float = null } function fake_owns(weather_test_st: WeatherTestState) -> bool { return weather_test_st.owns } function fake_hours(weather_test_st: WeatherTestState) -> float { return weather_test_st.hours } function fake_night(weather_test_st: WeatherTestState) -> bool { return weather_test_st.hours < 6.0 or weather_test_st.hours > 20.0 } function fake_bias(weather_test_st: WeatherTestState) -> int { return weather_test_st.bias } function fake_seed() -> int { return 7 } function fake_front_hours() -> float { return 6.0 } function fake_odds(weather_st: WeatherState, weather_test_st: WeatherTestState, from: int) -> []float { if weather_test_st.lean != null { return weather_test_st.lean } return weather_kind(weather_st, from).odds } function odds3(a: float, b: float, c: float) -> []float { let w = new []float push(w, a) push(w, b) push(w, c) return w } function kind(name: string, wet: bool, thunder: bool, odds: []float) -> WeatherKind { let k = new WeatherKind k.name = name k.clear = not wet k.wet = wet k.thunder = thunder k.odds = odds if wet { k.soak = 1.0 k.overcast = 0.8 k.cold = 5.0 k.wind = 1.0 } k.spell_lo = 1.0 k.spell_hi = 3.0 return k } bind WeatherWorld { owns_world: fn fake_owns hours: fn fake_hours is_night: fn fake_night bias: fn fake_bias seed: fn fake_seed odds: fn fake_odds front_hours: fn fake_front_hours } # clear, rain and storm: clear always goes to rain, rain to storm, storm to clear function fresh(weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) -> void { weather_test_st.owns = true weather_test_st.hours = 12.0 weather_test_st.bias = WEATHER_NO_BIAS weather_test_st.lean = null let ks = new []WeatherKind push(ks, kind("clear", false, false, odds3(0.0, 1.0, 0.0))) push(ks, kind("rain", true, false, odds3(0.0, 0.0, 1.0))) push(ks, kind("storm", true, true, odds3(1.0, 0.0, 0.0))) weather_kinds(weather_st, ks) weather_config_wind(weather_st, 0.5, 2.5, 10.0, 19.0) weather_reset(weather_st) } function facts(weather_st: mut WeatherState) -> []WeatherFact { return q_drain(weather_facts(weather_st)) } function step(weather_st: mut WeatherState, dt: float, gh: float, n: int) -> void { for i in 0 .. n { weather_run(weather_st, dt, gh) } } function count(fs: []WeatherFact, what: int) -> int { var n = 0 for i in 0 .. len(fs) { if fs[i].what == what { n += 1 } } return n } test "a new trip is clear, dry and forecast clear" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) expect_eq(weather_state(weather_st), 0) expect_eq(weather_forecast(weather_st), 0) expect(not weather_wet(weather_st)) expect(weather_cold(weather_st) == 0.0) } test "a spell that runs out gives way to the next, as a fact" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_set(weather_st, 0, 1, 0.1) step(weather_st, 0.1, 0.2, 1) expect_eq(weather_state(weather_st), 1) expect_eq(weather_next(weather_st), 2) let fs = facts(weather_st) expect_eq(count(fs, WEATHER_BEGAN), 1) expect(weather_left(weather_st) >= 1.0 and weather_left(weather_st) < 3.0) } test "the amount eases in, and wet follows it" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_set(weather_st, 0, 1, 0.01) step(weather_st, 0.1, 0.02, 1) expect(not weather_wet(weather_st)) step(weather_st, 0.1, 0.0, 200) expect(weather_wet(weather_st)) expect(weather_cold(weather_st) > 4.0) expect(weather_ground_wet(weather_st) > 0.5) } test "a storm throws lightning, and a clearing in daylight is a fact" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_set(weather_st, 2, 0, 2.0) step(weather_st, 1.0, 0.0, 60) expect(count(facts(weather_st), WEATHER_THUNDER) > 0) weather_set(weather_st, 2, 0, 0.05) step(weather_st, 0.1, 0.1, 1) expect_eq(weather_state(weather_st), 0) expect(weather_after_front(weather_st)) expect_eq(count(facts(weather_st), WEATHER_CLEARED), 1) } test "the world's odds overrule the kind's" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_test_st.lean = odds3(0.0, 0.0, 1.0) weather_set(weather_st, 0, 0, 0.01) step(weather_st, 0.1, 0.1, 1) expect_eq(weather_next(weather_st), 2) } test "wanting rain forecasts a front and holds it long enough" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_test_st.bias = WEATHER_WANT_WET step(weather_st, 0.1, 0.01, 1) expect(weather_forced(weather_st)) expect_eq(weather_forecast(weather_st), 1) expect_eq(count(facts(weather_st), WEATHER_FRONT), 1) step(weather_st, 0.1, 2.0, 1) expect_eq(weather_state(weather_st), 1) expect(not weather_forced(weather_st)) expect(weather_left(weather_st) >= 6.0) } test "wanting clear sky clears it and keeps it" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_set(weather_st, 1, 1, 5.0) weather_test_st.bias = WEATHER_WANT_CLEAR step(weather_st, 0.1, 0.6, 1) expect_eq(weather_state(weather_st), 0) step(weather_st, 0.1, 0.9, 5) expect_eq(weather_state(weather_st), 0) } test "a machine that does not own the world runs no spells, but is told them" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_test_st.owns = false weather_set(weather_st, 0, 1, 0.1) step(weather_st, 0.1, 5.0, 3) expect_eq(weather_state(weather_st), 0) weather_told(weather_st, 2, 0, 3.0, 1.0, 0.9, 1.0, 2) expect_eq(weather_state(weather_st), 2) expect_eq(count(facts(weather_st), WEATHER_BEGAN), 1) step(weather_st, 1.0, 0.0, 60) expect(count(facts(weather_st), WEATHER_THUNDER) > 0) } test "the wind turns with the hours" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_test_st.hours = 3.0 step(weather_st, 1.0, 0.0, 100) expect(Math.abs(weather_dir(weather_st) - 0.5) < 0.01) weather_test_st.hours = 12.0 step(weather_st, 1.0, 0.0, 100) expect(Math.abs(weather_dir(weather_st) - 2.5) < 0.01) } test "a morning after rain remembers it was wet" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_set(weather_st, 1, 1, 5.0) step(weather_st, 0.1, 0.0, 1) weather_morning(weather_st) expect(weather_wet_yesterday(weather_st)) } test "a save section reads back the same sky" (weather_st: mut WeatherState, weather_test_st: mut WeatherTestState) { fresh(weather_st, weather_test_st) weather_set(weather_st, 1, 2, 4.25) weather_morning(weather_st) let fc = weather_forecast(weather_st) let root = save_tree() save_section(root, "weather", 1, weather_save(weather_st)) let text = save_encode(root) weather_reset(weather_st) let n = load_section(save_decode(text), "weather") weather_load(weather_st, n.data, n.version) expect_eq(weather_state(weather_st), 1) expect(weather_left(weather_st) == 4.25) expect_eq(weather_forecast(weather_st), fc) expect(weather_wet_yesterday(weather_st)) } }