# fishing_test.ludic - a line over a fake lake: casting only into water, bait halving the wait and # said as a fact, the bite, the strike, the reel's window, a legend on a lure that runs and turns, # the gentle ring, losing a fish every way there is, species and weight from data, and the save import "ludic.fishing" import "ludic.base" program FishingTest { numbers float def FishSpecies cutthroat { weight: 0.45 } def FishSpecies rainbow { weight: 0.30 } def FishSpecies brook { weight: 0.25, cm_min: 20, cm_spread: 10 } def FishSpecies old_maroon { legend: true, cm_min: 70, cm_spread: 25, skill_cm: 0.0, chance: 1.0, chance_deep: 1.0, need: 5, runs: 2, fight: 30.0, speed: 1.3, speed_per_hit: 0.22, closes: 0.12 } state FishingTestState { bait: bool = false lure: bool = false boat: bool = false level: int = 0 skill: float = 0.0 } # water past x = 10, deepening a metre every ten function depth(x: float, z: float) -> float { return Math.max((x - 10.0) / 10.0, 0.0) } function afloat(fishing_test_st: FishingTestState) -> bool { return fishing_test_st.boat } function has_bait(fishing_test_st: FishingTestState) -> bool { return fishing_test_st.bait } function has_lure(fishing_test_st: FishingTestState) -> bool { return fishing_test_st.lure } function fake_skill(fishing_test_st: FishingTestState) -> float { return fishing_test_st.skill } function fake_level(fishing_test_st: FishingTestState) -> int { return fishing_test_st.level } bind FishingWorld { depth: fn depth, afloat: fn afloat, has_bait: fn has_bait, has_lure: fn has_lure, skill: fn fake_skill, level: fn fake_level } function fresh(fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) -> void { fishing_config(fishing_st, 0.15, 2.5, 99) fishing_reset(fishing_st) fishing_test_st.bait = false fishing_test_st.lure = false fishing_test_st.boat = false fishing_test_st.level = FISHING_RELAXED fishing_test_st.skill = 0.0 } function facts(fishing_st: mut FishingState) -> []FishingFact { return q_drain(fishing_facts(fishing_st)) } function saw(fs: []FishingFact, what: int) -> bool { for i in 0 .. len(fs) { if fs[i].what == what { return true } } return false } # run until it bites, then strike function hook(fishing_st: mut FishingState) -> void { fishing_set_wait(fishing_st, 0.0) fishing_run(fishing_st, 0.02) fishing_press(fishing_st) } # hold the marker in the green and press, until it is landed or `n` presses are spent function play(fishing_st: mut FishingState, n: int) -> void { for i in 0 .. n { if fishing_state(fishing_st) != FISHING_REEL { return } var guard = 0 while (fishing_running(fishing_st) or fishing_marker(fishing_st) <= fishing_window_lo(fishing_st) or fishing_marker(fishing_st) >= fishing_window_hi(fishing_st)) and guard < 4000 { fishing_run(fishing_st, 0.005) guard += 1 } if fishing_state(fishing_st) == FISHING_REEL { fishing_press(fishing_st) } } } test "a cast lands only in water, and never twice" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) expect_eq(fishing_cast(fishing_st, 5.0, 0.0), FISHING_NOT_WATER) expect(not fishing_active(fishing_st)) expect_eq(fishing_cast(fishing_st, 20.0, 0.0), FISHING_CAST_OK) expect_eq(fishing_state(fishing_st), FISHING_WAIT) expect_eq(fishing_cast(fishing_st, 20.0, 0.0), FISHING_BUSY) fishing_test_st.boat = true fishing_stop(fishing_st) expect_eq(fishing_cast(fishing_st, 0.0, 0.0), FISHING_CAST_OK) } test "bait is used and said, and halves the wait" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_cast(fishing_st, 20.0, 0.0) expect(not saw(facts(fishing_st), FISHING_BAIT_USED)) var plain = 0 while fishing_state(fishing_st) == FISHING_WAIT { fishing_run(fishing_st, 0.1) plain += 1 } expect(plain <= 201) fresh(fishing_st, fishing_test_st) fishing_test_st.bait = true fishing_cast(fishing_st, 20.0, 0.0) let fs = facts(fishing_st) expect(saw(fs, FISHING_BAIT_USED)) expect(fs[0].bait) var baited = 0 while fishing_state(fishing_st) == FISHING_WAIT { fishing_run(fishing_st, 0.1) baited += 1 } expect(baited <= 101) } test "a bite missed is gone, a bite struck is hooked" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_cast(fishing_st, 20.0, 0.0) fishing_set_wait(fishing_st, 0.0) fishing_run(fishing_st, 0.02) expect_eq(fishing_state(fishing_st), FISHING_BITE_ON) expect(fishing_species(fishing_st) >= 0) fishing_run(fishing_st, 2.0) expect(not fishing_active(fishing_st)) expect(saw(facts(fishing_st), FISHING_MISSED)) fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) expect_eq(fishing_state(fishing_st), FISHING_REEL) expect(saw(facts(fishing_st), FISHING_HOOKED)) } test "everyday fish only without a lure, sized and weighed from the data" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) for i in 0 .. 40 { fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) let s = fishing_species(fishing_st) expect(s != FISH_OLD_MAROON) let k = FishSpecies[s] expect(fishing_cm(fishing_st) >= k.cm_min and fishing_cm(fishing_st) <= k.cm_min + k.cm_spread) fishing_stop(fishing_st) } expect(fishing_weight_of(FISH_CUTTHROAT, 50) > fishing_weight_of(FISH_CUTTHROAT, 30)) } test "reeled in the green three times, it is landed and counted" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) facts(fishing_st) play(fishing_st, 3) let fs = facts(fishing_st) expect(saw(fs, FISHING_CAUGHT)) expect_eq(fishing_caught(fishing_st), 1) expect(fishing_biggest(fishing_st) > 0) expect(not fishing_active(fishing_st)) let last = fs[len(fs) - 1] expect(last.kg > 0.0) expect(last.x == 20.0) } test "a press outside the green throws the hook" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) while fishing_marker(fishing_st) < fishing_window_hi(fishing_st) + 0.01 { fishing_run(fishing_st, 0.005) } fishing_press(fishing_st) expect(saw(facts(fishing_st), FISHING_LINE_SNAPPED)) expect(not fishing_active(fishing_st)) } test "a fight left alone goes slack" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) for i in 0 .. 700 { fishing_run(fishing_st, 0.02) } expect(saw(facts(fishing_st), FISHING_SLACK)) expect_eq(fishing_caught(fishing_st), 0) } test "a legend takes the lure, runs, turns, and is landed at five" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_test_st.lure = true fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) expect_eq(fishing_species(fishing_st), FISH_OLD_MAROON) expect_eq(fishing_need(fishing_st), 5) let w0 = fishing_window(fishing_st) play(fishing_st, 2) expect(fishing_window(fishing_st) < w0) for i in 0 .. 400 { if not fishing_running(fishing_st) { fishing_run(fishing_st, 0.02) } } let fs = facts(fishing_st) expect(saw(fs, FISHING_RUNS)) if fishing_running(fishing_st) { let before = fishing_hits(fishing_st) fishing_press(fishing_st) expect_eq(fishing_hits(fishing_st), before - 1) } play(fishing_st, 40) expect(fishing_legend_caught(fishing_st)) let last = facts(fishing_st) expect(last[len(last) - 1].lure) } test "gentle: one press inside the ring, and the ring closes" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_test_st.level = FISHING_GENTLE fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) expect(fishing_gentle(fishing_st)) expect_eq(fishing_need(fishing_st), 1) fishing_press(fishing_st) expect_eq(fishing_caught(fishing_st), 1) fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) fishing_run(fishing_st, 2.1) expect(saw(facts(fishing_st), FISHING_LINE_SNAPPED)) } test "a legend fights on gentle too" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_test_st.level = FISHING_GENTLE fishing_test_st.lure = true fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) expect(not fishing_gentle(fishing_st)) expect_eq(fishing_need(fishing_st), 5) } test "hard closes the window for everyday fish, relaxed keeps it centred" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) expect(fishing_window_lo(fishing_st) + fishing_window_hi(fishing_st) > 0.999 and fishing_window_lo(fishing_st) + fishing_window_hi(fishing_st) < 1.001) fishing_stop(fishing_st) fishing_test_st.level = FISHING_HARD fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) let w0 = fishing_window(fishing_st) play(fishing_st, 1) expect(fishing_window(fishing_st) < w0) } test "skill makes a fish bigger" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_test_st.skill = 1.0 fishing_cast(fishing_st, 20.0, 0.0) hook(fishing_st) let k = FishSpecies[fishing_species(fishing_st)] expect(fishing_cm(fishing_st) >= k.cm_min + 60) } test "the record is saved, and a line in the water is not" (fishing_st: mut FishingState, fishing_test_st: mut FishingTestState) { fresh(fishing_st, fishing_test_st) fishing_record_set(fishing_st, 7, 52, true) fishing_cast(fishing_st, 20.0, 0.0) let root = save_tree() save_section(root, "fishing", 1, fishing_save(fishing_st)) let text = save_encode(root) fishing_reset(fishing_st) let n = load_section(save_decode(text), "fishing") fishing_load(fishing_st, n.data, n.version) expect_eq(fishing_caught(fishing_st), 7) expect_eq(fishing_biggest(fishing_st), 52) expect(fishing_legend_caught(fishing_st)) expect(not fishing_active(fishing_st)) } }