wip(0.S2): migrate - a var nothing writes becomes a let; a state is keyed by its module, directory or program, so every program's plan agrees; paths normalized; program states named SceneDemoState / scene_demo_st; the LSP reads state, mut and entry/handler parameters

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 15:31:34 +03:00
parent 3eda72e8c2
commit 5ffe50ed02
463 changed files with 72045 additions and 67421 deletions

View file

@ -1,15 +1,15 @@
# bearing.ludic - the frame's marks as the strip reads them: how many, each one, where it bears
# from the viewer's facing and how far it is. A heading is the world's (0 toward -z, a quarter
# turn toward -x), a bearing is the heading less the viewer's yaw, in -pi .. pi.
export function compass_count(compass_st: CompassState) -> int {
if compass_st.cmp_list == null { return 0 }
return len(compass_st.cmp_list)
export function compass_count() -> int {
if cmp_list == null { return 0 }
return len(cmp_list)
}
# the i-th mark, or an empty one past the end
export function compass_at(compass_st: CompassState, i: int) -> CompassMark {
if i < 0 or i >= compass_count(compass_st) { return new CompassMark }
return compass_st.cmp_list[i]
export function compass_at(i: int) -> CompassMark {
if i < 0 or i >= compass_count() { return new CompassMark }
return cmp_list[i]
}
export function compass_wrap(a: float) -> float {
@ -33,13 +33,13 @@ export function compass_dist_to(x: float, z: float) -> float {
return Math.sqrt(dx * dx + dz * dz)
}
export function compass_bearing(compass_st: CompassState, i: int) -> float {
let m = compass_at(compass_st, i)
export function compass_bearing(i: int) -> float {
let m = compass_at(i)
return compass_bearing_to(m.x, m.z)
}
export function compass_dist(compass_st: CompassState, i: int) -> float {
let m = compass_at(compass_st, i)
export function compass_dist(i: int) -> float {
let m = compass_at(i)
return compass_dist_to(m.x, m.z)
}
@ -47,8 +47,8 @@ export function compass_dist(compass_st: CompassState, i: int) -> float {
export function compass_strip_t(bearing: float, half: float) -> float { return bearing / Math.max(half, 0.001) }
# the objective: the first tracked mark of the frame, or -1
export function compass_tracked(compass_st: CompassState) -> int {
for i in 0 .. compass_count(compass_st) { if compass_st.cmp_list[i].prio >= COMPASS_TRACKED { return i } }
export function compass_tracked() -> int {
for i in 0 .. compass_count() { if cmp_list[i].prio >= COMPASS_TRACKED { return i } }
return -1
}

View file

@ -1,56 +1,56 @@
# gather.ludic - a frame's marks: begin, every provider marks, end sorts and culls. While a
# capture is open the marks go to whoever is listening instead, whatever the tier.
export function compass_begin(compass_st: mut CompassState) -> void { compass_st.cmp_list = new []CompassMark }
export function compass_begin() -> void { cmp_list = new []CompassMark }
# a provider's verb: a thing at (x, z), shown from compass `tier` up
export function compass_mark(compass_st: mut CompassState, x: float, z: float, icon: int, colour: int, tier: int, prio: int, label: string) -> void {
if compass_st.cmp_list == null { compass_begin(compass_st) }
export function compass_mark(x: float, z: float, icon: int, colour: int, tier: int, prio: int, label: string) -> void {
if cmp_list == null { compass_begin() }
let m = cmp_new(x, z, icon, colour, tier, prio, label)
if compass_st.cmp_capturing {
push(compass_st.cmp_caught, m)
if cmp_capturing {
push(cmp_caught, m)
return
}
if CompassWorld.tier() < tier { return }
push(compass_st.cmp_list, m)
push(cmp_list, m)
}
# highest priority first, the providers' order kept within one; then the cap
export function compass_end(compass_st: mut CompassState) -> void {
if compass_st.cmp_list == null { compass_begin(compass_st) }
export function compass_end() -> void {
if cmp_list == null { compass_begin() }
let out = new []CompassMark
for p in 0 .. 3 {
let want = COMPASS_TRACKED - p
for i in 0 .. len(compass_st.cmp_list) { if cmp_band(compass_st.cmp_list[i].prio) == want and len(out) < compass_st.cmp_cap { push(out, compass_st.cmp_list[i]) } }
for i in 0 .. len(cmp_list) { if cmp_band(cmp_list[i].prio) == want and len(out) < cmp_cap { push(out, cmp_list[i]) } }
}
compass_st.cmp_list = out
cmp_list = out
}
function cmp_band(p: int) -> int { return Math.clamp(p, COMPASS_PLAIN, COMPASS_TRACKED) }
# once a frame: ask every registered provider, in the registry's order
export function compass_gather(compass_st: mut CompassState) -> void {
compass_begin(compass_st)
export function compass_gather() -> void {
compass_begin()
if CompassWorld.tier() <= 0 { return }
for i in 0 .. COMPASS_FROM_COUNT {
let f = CompassProviders[i].gather
if f != null { f() }
}
compass_end(compass_st)
compass_end()
}
# someone else wants to know where the providers point (a guide's arrow): until the end every
# mark is theirs, whatever the tier, and the frame's list is left alone
export function compass_capture_begin(compass_st: mut CompassState) -> void {
compass_st.cmp_capturing = true
compass_st.cmp_caught = new []CompassMark
export function compass_capture_begin() -> void {
cmp_capturing = true
cmp_caught = new []CompassMark
}
export function compass_capture_end(compass_st: mut CompassState) -> []CompassMark {
compass_st.cmp_capturing = false
var got = compass_st.cmp_caught
compass_st.cmp_caught = new []CompassMark
export function compass_capture_end() -> []CompassMark {
cmp_capturing = false
var got = cmp_caught
cmp_caught = new []CompassMark
if got == null { got = new []CompassMark }
return got
}
export function compass_capturing(compass_st: CompassState) -> bool { return compass_st.cmp_capturing }
export function compass_capturing() -> bool { return cmp_capturing }

View file

@ -22,19 +22,15 @@ export property CompassProvider {
export open registry CompassProviders of CompassProvider as COMPASS_FROM
export state CompassState {
cmp_cap: int = 64
cmp_list: []CompassMark = null # this frame's, sorted and culled
cmp_capturing: bool = false
cmp_caught: []CompassMark = null # what providers said while someone was listening
cmp_ranges: []float = null
cmp_radar_from: int = 2 # marks of at least this tier are blips; the tracked one always is
}
var cmp_cap: int = 64
var cmp_list: []CompassMark = null # this frame's, sorted and culled
var cmp_capturing: bool = false
var cmp_caught: []CompassMark = null # what providers said while someone was listening
# how many marks a frame keeps (the lowest priorities go first past it)
export function compass_config(compass_st: mut CompassState, cap: int) -> void {
compass_st.cmp_cap = Math.max(cap, 1)
compass_begin(compass_st)
export function compass_config(cap: int) -> void {
cmp_cap = Math.max(cap, 1)
compass_begin()
}
function cmp_new(x: float, z: float, icon: int, colour: int, tier: int, prio: int, label: string) -> CompassMark {

View file

@ -1,38 +1,40 @@
# radar.ludic - the radar's tiers: a range for each compass tier (0 is no radar), which marks it
# shows, and where each falls on a unit disc with ahead at the top
var cmp_ranges: []float = null
var cmp_radar_from: int = 2 # marks of at least this tier are blips; the tracked one always is
export function compass_config_radar(compass_st: mut CompassState, ranges: []float, from_tier: int) -> void {
compass_st.cmp_ranges = new []float
for i in 0 .. len(ranges) { push(compass_st.cmp_ranges, ranges[i]) }
compass_st.cmp_radar_from = from_tier
export function compass_config_radar(ranges: []float, from_tier: int) -> void {
cmp_ranges = new []float
for i in 0 .. len(ranges) { push(cmp_ranges, ranges[i]) }
cmp_radar_from = from_tier
}
# how far the radar reaches with the viewer's compass, 0 without one; a tier past the table has
# the last range
export function compass_radar_range(compass_st: CompassState) -> float {
if compass_st.cmp_ranges == null or len(compass_st.cmp_ranges) == 0 { return 0.0 }
export function compass_radar_range() -> float {
if cmp_ranges == null or len(cmp_ranges) == 0 { return 0.0 }
let t = CompassWorld.tier()
if t < 0 { return 0.0 }
if t >= len(compass_st.cmp_ranges) { return compass_st.cmp_ranges[len(compass_st.cmp_ranges) - 1] }
return compass_st.cmp_ranges[t]
if t >= len(cmp_ranges) { return cmp_ranges[len(cmp_ranges) - 1] }
return cmp_ranges[t]
}
export function compass_radar_on(compass_st: CompassState) -> bool { return compass_radar_range(compass_st) > 0.0 }
export function compass_radar_on() -> bool { return compass_radar_range() > 0.0 }
# the i-th mark is a blip: a radar is carried, it is shown there and it is in range
export function compass_radar_has(compass_st: CompassState, i: int) -> bool {
if i < 0 or i >= compass_count(compass_st) or not compass_radar_on(compass_st) { return false }
let m = compass_st.cmp_list[i]
if m.tier < compass_st.cmp_radar_from and m.prio < COMPASS_TRACKED { return false }
return compass_dist(compass_st, i) <= compass_radar_range(compass_st)
export function compass_radar_has(i: int) -> bool {
if i < 0 or i >= compass_count() or not compass_radar_on() { return false }
let m = cmp_list[i]
if m.tier < cmp_radar_from and m.prio < COMPASS_TRACKED { return false }
return compass_dist(i) <= compass_radar_range()
}
# where on a unit disc: x right, y down, so ahead is (0, -1) and the edge is the range
export function compass_radar_x(compass_st: CompassState, i: int) -> float { return -Math.sin(compass_bearing(compass_st, i)) * cmp_radar_frac(compass_st, i) }
export function compass_radar_y(compass_st: CompassState, i: int) -> float { return -Math.cos(compass_bearing(compass_st, i)) * cmp_radar_frac(compass_st, i) }
export function compass_radar_x(i: int) -> float { return -Math.sin(compass_bearing(i)) * cmp_radar_frac(i) }
export function compass_radar_y(i: int) -> float { return -Math.cos(compass_bearing(i)) * cmp_radar_frac(i) }
function cmp_radar_frac(compass_st: CompassState, i: int) -> float {
let r = compass_radar_range(compass_st)
function cmp_radar_frac(i: int) -> float {
let r = compass_radar_range()
if r <= 0.0 { return 0.0 }
return Math.min(compass_dist(compass_st, i) / r, 1.0)
return Math.min(compass_dist(i) / r, 1.0)
}

View file

@ -6,122 +6,120 @@ import "ludic.base"
program CompassTest {
numbers float
state CompasstestState {
vx: float = 0.0
vz: float = 0.0
yaw: float = 0.0
tier: int = 4
objective: bool = true
}
var vx: float = 0.0
var vz: float = 0.0
var yaw: float = 0.0
var tier: int = 4
var objective: bool = true
function fk_x(compasstest_st: CompasstestState) -> float { return compasstest_st.vx }
function fk_z(compasstest_st: CompasstestState) -> float { return compasstest_st.vz }
function fk_yaw(compasstest_st: CompasstestState) -> float { return compasstest_st.yaw }
function fk_tier(compasstest_st: CompasstestState) -> int { return compasstest_st.tier }
function fk_x() -> float { return vx }
function fk_z() -> float { return vz }
function fk_yaw() -> float { return yaw }
function fk_tier() -> int { return tier }
bind CompassWorld { x: fn fk_x, z: fn fk_z, yaw: fn fk_yaw, tier: fn fk_tier }
function places(compass_st: mut CompassState) -> void {
compass_mark(compass_st, 0.0, -100.0, 1, 10, 1, COMPASS_PLAIN, "camp")
compass_mark(compass_st, -50.0, 0.0, 2, 11, 2, COMPASS_PLAIN, "sign")
compass_mark(compass_st, 0.0, 300.0, 3, 12, 4, COMPASS_PLAIN, "legend")
compass_mark(compass_st, 10.0, 10.0, 4, 13, 1, COMPASS_NOTE, "my mark")
function places() -> void {
compass_mark(0.0, -100.0, 1, 10, 1, COMPASS_PLAIN, "camp")
compass_mark(-50.0, 0.0, 2, 11, 2, COMPASS_PLAIN, "sign")
compass_mark(0.0, 300.0, 3, 12, 4, COMPASS_PLAIN, "legend")
compass_mark(10.0, 10.0, 4, 13, 1, COMPASS_NOTE, "my mark")
}
function goal(compass_st: mut CompassState, compasstest_st: CompasstestState) -> void { if compasstest_st.objective { compass_mark(compass_st, 80.0, 0.0, 5, 14, 2, COMPASS_TRACKED, "the dock") } }
function goal() -> void { if objective { compass_mark(80.0, 0.0, 5, 14, 2, COMPASS_TRACKED, "the dock") } }
def CompassProviders places { gather: fn places }
def CompassProviders goal { gather: fn goal }
function fresh(compass_st: mut CompassState, compasstest_st: mut CompasstestState) -> void {
compass_config(compass_st, 64)
compass_config_radar(compass_st, [0.0, 0.0, 0.0, 120.0, 260.0], 2)
compasstest_st.vx = 0.0
compasstest_st.vz = 0.0
compasstest_st.yaw = 0.0
compasstest_st.tier = 4
compasstest_st.objective = true
function fresh() -> void {
compass_config(64)
compass_config_radar([0.0, 0.0, 0.0, 120.0, 260.0], 2)
vx = 0.0
vz = 0.0
yaw = 0.0
tier = 4
objective = true
}
function labels(compass_st: CompassState) -> string {
function labels() -> string {
var s = ""
for i in 0 .. compass_count(compass_st) { s = s + compass_at(compass_st, i).label + ";" }
for i in 0 .. compass_count() { s = s + compass_at(i).label + ";" }
return s
}
test "every provider is asked, the tracked mark first, then the player's, then the rest in order" (compass_st: mut CompassState, compasstest_st: mut CompasstestState) {
fresh(compass_st, compasstest_st)
compass_gather(compass_st)
expect_eq(compass_count(compass_st), 5)
expect(labels(compass_st) == "the dock;my mark;camp;sign;legend;")
expect_eq(compass_tracked(compass_st), 0)
test "every provider is asked, the tracked mark first, then the player's, then the rest in order" {
fresh()
compass_gather()
expect_eq(compass_count(), 5)
expect(labels() == "the dock;my mark;camp;sign;legend;")
expect_eq(compass_tracked(), 0)
}
test "a mark shows from its tier up, and without a compass nothing is gathered" (compass_st: mut CompassState, compasstest_st: mut CompasstestState) {
fresh(compass_st, compasstest_st)
compasstest_st.tier = 1
compass_gather(compass_st)
expect(labels(compass_st) == "my mark;camp;")
expect_eq(compass_tracked(compass_st), -1)
compasstest_st.tier = 0
compass_gather(compass_st)
expect_eq(compass_count(compass_st), 0)
test "a mark shows from its tier up, and without a compass nothing is gathered" {
fresh()
tier = 1
compass_gather()
expect(labels() == "my mark;camp;")
expect_eq(compass_tracked(), -1)
tier = 0
compass_gather()
expect_eq(compass_count(), 0)
}
test "past the cap the lowest priorities go" (compass_st: mut CompassState, compasstest_st: mut CompasstestState) {
fresh(compass_st, compasstest_st)
compass_config(compass_st, 2)
compass_gather(compass_st)
expect(labels(compass_st) == "the dock;my mark;")
test "past the cap the lowest priorities go" {
fresh()
compass_config(2)
compass_gather()
expect(labels() == "the dock;my mark;")
}
test "a bearing is from the viewer's facing, a distance from where it stands" (compass_st: mut CompassState, compasstest_st: mut CompasstestState) {
fresh(compass_st, compasstest_st)
compass_gather(compass_st)
expect_near(compass_bearing(compass_st, 2), 0.0, 0.001)
expect_near(compass_dist(compass_st, 2), 100.0, 0.01)
expect_near(compass_bearing(compass_st, 3), 1.5708, 0.001)
expect_near(compass_bearing(compass_st, 0), -1.5708, 0.001)
compasstest_st.yaw = 1.5708
expect_near(compass_bearing(compass_st, 3), 0.0, 0.001)
expect_near(compass_bearing(compass_st, 2), -1.5708, 0.001)
compasstest_st.vx = -50.0
compasstest_st.vz = 100.0
expect_near(compass_dist(compass_st, 3), 100.0, 0.01)
test "a bearing is from the viewer's facing, a distance from where it stands" {
fresh()
compass_gather()
expect_near(compass_bearing(2), 0.0, 0.001)
expect_near(compass_dist(2), 100.0, 0.01)
expect_near(compass_bearing(3), 1.5708, 0.001)
expect_near(compass_bearing(0), -1.5708, 0.001)
yaw = 1.5708
expect_near(compass_bearing(3), 0.0, 0.001)
expect_near(compass_bearing(2), -1.5708, 0.001)
vx = -50.0
vz = 100.0
expect_near(compass_dist(3), 100.0, 0.01)
expect_near(compass_wrap(7.0), 7.0 - 6.2832, 0.001)
expect_near(compass_strip_t(0.5, 1.0), 0.5, 0.001)
}
test "a capture hears every mark whatever the tier and leaves the frame's list alone" (compass_st: mut CompassState, compasstest_st: mut CompasstestState) {
fresh(compass_st, compasstest_st)
compass_gather(compass_st)
compasstest_st.tier = 1
compass_capture_begin(compass_st)
expect(compass_capturing(compass_st))
goal(compass_st, compasstest_st)
places(compass_st)
let got = compass_capture_end(compass_st)
expect(not compass_capturing(compass_st))
test "a capture hears every mark whatever the tier and leaves the frame's list alone" {
fresh()
compass_gather()
tier = 1
compass_capture_begin()
expect(compass_capturing())
goal()
places()
let got = compass_capture_end()
expect(not compass_capturing())
expect_eq(len(got), 5)
expect(got[0].label == "the dock")
expect_eq(compass_count(compass_st), 5)
expect_eq(compass_count(), 5)
}
test "the radar: its range by tier, the blips in it, and where each falls on the disc" (compass_st: mut CompassState, compasstest_st: mut CompasstestState) {
fresh(compass_st, compasstest_st)
compasstest_st.tier = 2
compass_gather(compass_st)
expect(not compass_radar_on(compass_st))
expect(not compass_radar_has(compass_st, 0))
compasstest_st.tier = 3
compass_gather(compass_st)
expect(compass_radar_on(compass_st))
expect_near(compass_radar_range(compass_st), 120.0, 0.01)
expect(compass_radar_has(compass_st, 0))
expect(not compass_radar_has(compass_st, 1))
expect(not compass_radar_has(compass_st, 2))
expect(compass_radar_has(compass_st, 3))
expect_near(compass_radar_x(compass_st, 3), -50.0 / 120.0, 0.001)
expect_near(compass_radar_y(compass_st, 3), 0.0, 0.001)
expect_near(compass_radar_x(compass_st, 0), 80.0 / 120.0, 0.001)
compasstest_st.tier = 9
expect_near(compass_radar_range(compass_st), 260.0, 0.01)
test "the radar: its range by tier, the blips in it, and where each falls on the disc" {
fresh()
tier = 2
compass_gather()
expect(not compass_radar_on())
expect(not compass_radar_has(0))
tier = 3
compass_gather()
expect(compass_radar_on())
expect_near(compass_radar_range(), 120.0, 0.01)
expect(compass_radar_has(0))
expect(not compass_radar_has(1))
expect(not compass_radar_has(2))
expect(compass_radar_has(3))
expect_near(compass_radar_x(3), -50.0 / 120.0, 0.001)
expect_near(compass_radar_y(3), 0.0, 0.001)
expect_near(compass_radar_x(0), 80.0 / 120.0, 0.001)
tier = 9
expect_near(compass_radar_range(), 260.0, 0.01)
}
test "the eight points go round from the heading 0 toward -x" {