From 7918103b8b94708422d09c41e8a3ff934ebef990 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Wed, 30 Sep 2026 15:36:08 +0300 Subject: [PATCH] render3d: ground_fill_golden.json is valid JSON (the cover and solid blocks each closed one brace too many) gg_cover_json and gg_solid_json ended in plain strings with "}}", which only a template literal reads as one brace: the cover's closed the top-level object before "solid", and the solid's left a stray "}" at the end. The committed golden is fixed by hand to what the generator now writes. ground_fill_test also checks the golden as JSON: one value with nothing after it (Json.parse reads the first value and ignores the rest, so it alone would not have caught this), an object, its eleven sections in order. The generator notes what the studio asked: past the density patch an empty tile reads 0 for R and G (only past the map's edge does a texel clamp), and several clearings multiply. Co-Authored-By: Claude Opus 5.5 --- .../tests/fakes/ground_golden_check.ludic | 44 +++++++++++++++++++ .../tests/fakes/ground_golden_things.ludic | 4 +- .../tests/gen/ground_fill_golden.ludic | 6 +++ .../tests/ground_fill_golden.json | 4 +- .../tests/ground_fill_test.ludic | 4 ++ 5 files changed, 58 insertions(+), 4 deletions(-) diff --git a/packages/ludic.render3d/tests/fakes/ground_golden_check.ludic b/packages/ludic.render3d/tests/fakes/ground_golden_check.ludic index 289e95bb..a8c5cab5 100644 --- a/packages/ludic.render3d/tests/fakes/ground_golden_check.ludic +++ b/packages/ludic.render3d/tests/fakes/ground_golden_check.ludic @@ -19,3 +19,47 @@ function gft_subset(render3d_st: Render3dState, n: int, l: GroundSolids) -> int } return bad } + +# whether s is one JSON value with only whitespace after it: every bracket closed by its own kind, strings +# skipped (Json.parse reads the first value and ignores the rest, which is how a stray brace went unseen) +function gft_json_whole(s: string) -> bool { + let want = new []int + var in_str = false + var done = false + var k = 0 + while k < Text.length(s) { + let c = Text.char_at(s, k) + k += 1 + if in_str { + if c == 92 { k += 1 } else if c == 34 { in_str = false } + continue + } + if c == 32 or c == 9 or c == 10 or c == 13 { continue } + if done { return false } + if c == 34 { in_str = true } + else if c == 123 { push(want, 125) } + else if c == 91 { push(want, 93) } + else if c == 125 or c == 93 { + if len(want) == 0 or List.pop(want) != c { return false } + done = len(want) == 0 + } + } + return done and not in_str +} + +# what is wrong with the golden's text as JSON, "" when nothing: whole, an object, its sections in order +function gft_golden_fault(text: string) -> string { + if text == null { return "the golden could not be read" } + if not gft_json_whole(text) { return "not one JSON value with nothing after it" } + let v = Json.parse(text) + if v == null or Value.kind(v) != 6 { return "not a JSON object" } + let keys = ["about", "numbers", "lerp_order", "lerp", "yaw", "hash", "density", "clearings_rule", "clearings", "cover", "solid"] + var fault = "" + if Value.count(v) != len(keys) { fault = `{Value.count(v)} sections, not {len(keys)}` } + for k in 0 .. min(Value.count(v), len(keys)) { + if fault == "" and Value.key_at(v, k) != keys[k] { fault = `section {k} is {Value.key_at(v, k)}, not {keys[k]}` } + } + if fault == "" and Value.count(Value.get(Value.get(v, "solid"), "things")) < 8 { fault = "the solid layer lost its things" } + Json.free_all(v) + return fault +} diff --git a/packages/ludic.render3d/tests/fakes/ground_golden_things.ludic b/packages/ludic.render3d/tests/fakes/ground_golden_things.ludic index 6e31d0a3..c7d3e3a5 100644 --- a/packages/ludic.render3d/tests/fakes/ground_golden_things.ludic +++ b/packages/ludic.render3d/tests/fakes/ground_golden_things.ludic @@ -37,7 +37,7 @@ function gg_cover_json(render3d_st: mut Render3dState) -> string { s = s + ` [{c.cell}, {gg_thing(c)}]` } } - return s + "\n ]}},\n" + return s + "\n ]},\n" } # the solid layer's things: [id, cell, x, z, scale, yaw, seed, wind, drawn at band 2] @@ -53,7 +53,7 @@ function gg_solid_json(render3d_st: mut Render3dState) -> string { if k > 0 { s = s + ",\n" } s = s + ` [{c.id}, {c.cell}, {gg_thing(c)}, {far}]` } - return s + "\n ]}}\n" + return s + "\n ]}\n" } # the whole golden, as ground_fill_golden.json holds it diff --git a/packages/ludic.render3d/tests/gen/ground_fill_golden.ludic b/packages/ludic.render3d/tests/gen/ground_fill_golden.ludic index 4a6941f0..10631f22 100644 --- a/packages/ludic.render3d/tests/gen/ground_fill_golden.ludic +++ b/packages/ludic.render3d/tests/gen/ground_fill_golden.ludic @@ -1,6 +1,12 @@ # gen/ground_fill_golden.ludic - writes tests/ground_fill_golden.json from what ground_fill grows today. Run it # from the repository root after a deliberate change to the fill, and commit the file with the change. import "ludic.render3d/r3d.ludic" + +# Past the patch (tx0 .. tx0 + w - 1, all the chunk reads) an empty tile reads 0 for R and G, never its neighbour; +# only a texel past the map's edge clamps to the edge texel (gd_texel, gd_fill), and a missing channel reads 128. + +# Several clearings MULTIPLY (ground_clear_at), in the order handed over: each floor..1, 1 past r_out, floor +# clamped to 0..1, at most GD_CLEAR_MAX (32); a layer with trample multiplies its chance by the product. program GroundFillGolden { numbers float import "../fakes/ground_golden.ludic" diff --git a/packages/ludic.render3d/tests/ground_fill_golden.json b/packages/ludic.render3d/tests/ground_fill_golden.json index daf37581..836a2f3c 100644 --- a/packages/ludic.render3d/tests/ground_fill_golden.json +++ b/packages/ludic.render3d/tests/ground_fill_golden.json @@ -99,7 +99,7 @@ [196620, 1121118525, -1040010303, 1061322280, 1077929596, 1052826214, 1064042924], [589836, 1123092738, -1039727871, 1068558543, 1084332302, 1049315840, 1057920874], [655372, 1123240528, -1039705474, 1068208532, 1086445325, 1062070265, 1061650604] - ]}}, + ]}, "solid": {"fill": {"layer": 0, "steps": [1082130432, 1084227584, 1090519040, 1094713344], "jitter": 1061997773, "scale_lo": 1061997773, "scale_hi": 1070386381, "wind_lo": 1056964608, "wind_hi": 1065353216, "scale_var": 1045220557, "band_grow": 1050253722, "sink": 1022739087, "trample": 0, "solid": 1}, "ox": 0, "oz": 0, "size": 1107296256, "cx": 3, "cz": -2, "far_band": 2, "things": [ [16582144, 0, 1120293028, -1032252411, 1068709886, 1085262221, 1065248692, 1058250104, 0], [16582176, 262144, 1122044225, -1032619009, 1070248319, 1073719313, 1064173498, 1063466348, 1], @@ -131,5 +131,5 @@ [16582175, 196615, 1121761085, -1039599090, 1067374368, 1067261885, 1058627350, 1062771573, 0], [16582191, 327687, 1122751322, -1039341503, 1069678485, 1068499230, 1053671156, 1059624745, 0], [16582199, 393223, 1123213029, -1039687681, 1071554254, 1053024656, 1029041920, 1063208826, 1] - ]}} + ]} } diff --git a/packages/ludic.render3d/tests/ground_fill_test.ludic b/packages/ludic.render3d/tests/ground_fill_test.ludic index ee749368..70b2fe25 100644 --- a/packages/ludic.render3d/tests/ground_fill_test.ludic +++ b/packages/ludic.render3d/tests/ground_fill_test.ludic @@ -25,6 +25,10 @@ program GroundFillTest { } } + test "the golden is one JSON object, every section in it and nothing after it" { + expect_eq(gft_golden_fault(Fs.read_text("packages/ludic.render3d/tests/ground_fill_golden.json")), "") + } + test "Math.lerp is a + (b - a) * t" { let a = [1.0, 2.0, 6.04, -2.19] let b = [1.7, 7.0, 1.72, 3.62]