fix(runtime): Random.* in any program; value_* called directly brings the value tree
The seeded random numbers lived in the ECS runtime, so a tool or a program of test blocks got "unknown function rng_range". They are runtime/native/rng.ludic now, spliced on Random.* or a bare rng_*/seed call nobody defines, and imported by core.ludic for a game. A bare value_*/json_* call nothing in the program defines splices the value tree the way Value.* does. Reseed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
bf73f7edaf
commit
f5e7fe4245
13 changed files with 52262 additions and 51500 deletions
|
|
@ -1739,7 +1739,7 @@ literal; test any pointer/record/slice with `x == null` / `x != null` (an unset
|
||||||
|
|
||||||
```
|
```
|
||||||
# math min max abs clamp (int)
|
# math min max abs clamp (int)
|
||||||
# rng seed(i) rng_range(lo,hi)->int rng_chance(pct)->bool (deterministic)
|
# rng seed(i) rng_range(lo,hi)->int rng_chance(pct)->bool (deterministic; any program, ECS or not)
|
||||||
# fixed fixed(i)->fixed floor(f)->int
|
# fixed fixed(i)->fixed floor(f)->int
|
||||||
# tilemap map_size(w,h) map_row(y,str) tile(x,y)->int
|
# tilemap map_size(w,h) map_row(y,str) tile(x,y)->int
|
||||||
# 2D draw clear(color) fill_rect(x,y,w,h,color) frame_rect(...) put_px(x,y,color)
|
# 2D draw clear(color) fill_rect(x,y,w,h,color) frame_rect(...) put_px(x,y,color)
|
||||||
|
|
|
||||||
7
changes/runtime-on-call.md
Normal file
7
changes/runtime-on-call.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
bump: patch
|
||||||
|
type: fix
|
||||||
|
**Random.* works in any program, and the value tree comes in when `value_*` is called directly.**
|
||||||
|
The seeded random numbers lived in the ECS runtime, so a tool or a program made only of `test`
|
||||||
|
blocks got "unknown function rng_range"; they are their own runtime file now, spliced on demand
|
||||||
|
(and imported by the ECS runtime for a game). A package calling `value_get` or `json_encode`
|
||||||
|
itself, without a `Value.*` / `Json.*` anywhere, now gets the value tree too.
|
||||||
|
|
@ -4,4 +4,4 @@ title: Random
|
||||||
order: 7
|
order: 7
|
||||||
---
|
---
|
||||||
|
|
||||||
A seeded, deterministic RNG — same seed, same sequence, every run and every platform.
|
A seeded, deterministic RNG — same seed, same sequence, every run and every platform. It is there in any program - a tool or a test program with no properties or handlers as well as a game.
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@ title: Value
|
||||||
order: 35
|
order: 35
|
||||||
---
|
---
|
||||||
|
|
||||||
A generic, self-describing value tree — the node type reflection serializes into and JSON round-trips through. A node is one of null, <code>int</code>, <code>fixed</code>, <code>bool</code>, <code>str</code>, <code>list</code>, or <code>object</code> (see <a href="value-kind"><code>Value.kind</code></a>). Build one with the constructors (<a href="value-int"><code>Value.int</code></a>, <a href="value-object"><code>Value.object</code></a>, …) and the builders <a href="value-add"><code>Value.add</code></a>/<a href="value-put"><code>Value.put</code></a>; read it with <a href="value-get"><code>Value.get</code></a>/<a href="value-at"><code>Value.at</code></a>/<a href="value-count"><code>Value.count</code></a> and the <code>as_*</code> accessors. Related: <a href="json"><code>Json</code></a>, <a href="reflect"><code>Reflect</code></a>.
|
A generic, self-describing value tree — the node type reflection serializes into and JSON round-trips through. A node is one of null, <code>int</code>, <code>fixed</code>, <code>bool</code>, <code>str</code>, <code>list</code>, or <code>object</code> (see <a href="value-kind"><code>Value.kind</code></a>). Build one with the constructors (<a href="value-int"><code>Value.int</code></a>, <a href="value-object"><code>Value.object</code></a>, …) and the builders <a href="value-add"><code>Value.add</code></a>/<a href="value-put"><code>Value.put</code></a>; read it with <a href="value-get"><code>Value.get</code></a>/<a href="value-at"><code>Value.at</code></a>/<a href="value-count"><code>Value.count</code></a> and the <code>as_*</code> accessors. Related: <a href="json"><code>Json</code></a>, <a href="reflect"><code>Reflect</code></a>. The tree comes into a program when it writes `Value.*` or `Json.*`, or calls `value_*` / `json_*` directly, as a package does.
|
||||||
|
|
|
||||||
19
examples/library/random_plain.ludic
Normal file
19
examples/library/random_plain.ludic
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# random_plain.ludic — Random.* in a program with no properties or handlers: the seeded random
|
||||||
|
# numbers are spliced on their own, and a helper may call rng_range directly. The same seed gives
|
||||||
|
# the same draws.
|
||||||
|
#
|
||||||
|
# Running it prints: 1 1 1
|
||||||
|
program RandomPlain {
|
||||||
|
function roll() -> int {
|
||||||
|
return rng_range(1, 6)
|
||||||
|
}
|
||||||
|
entry {
|
||||||
|
Random.seed(7)
|
||||||
|
let a = Random.range(1, 6)
|
||||||
|
let b = roll()
|
||||||
|
Random.seed(7)
|
||||||
|
let c = Random.range(1, 6)
|
||||||
|
let d = roll()
|
||||||
|
print(`{a == c} {b == d} {a >= 1 and a <= 6}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
12
examples/library/testing_random.ludic
Normal file
12
examples/library/testing_random.ludic
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# testing_random.ludic — a program made only of test blocks has the runtime's namespaces too:
|
||||||
|
# Random.range through a helper. Its runner prints:
|
||||||
|
# == 1 passed, 0 failed ==
|
||||||
|
program TestingRandom {
|
||||||
|
function roll() -> int {
|
||||||
|
return Random.range(1, 6)
|
||||||
|
}
|
||||||
|
test "a roll is a die's" {
|
||||||
|
let r = roll()
|
||||||
|
expect(r >= 1 and r <= 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
examples/library/value_direct.ludic
Normal file
11
examples/library/value_direct.ludic
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# value_direct.ludic — the value tree is spliced when a program calls value_* / json_* itself,
|
||||||
|
# as a package does, not only when it writes Value.* or Json.*.
|
||||||
|
#
|
||||||
|
# Running it prints: {"fish":3} 3
|
||||||
|
program ValueDirect {
|
||||||
|
entry {
|
||||||
|
let o = value_object()
|
||||||
|
value_put(o, "fish", value_int(3))
|
||||||
|
print(`{json_encode(o)} {value_as_int(value_get(o, "fish"))}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,6 @@ var rt_fb: words = null # framebuffer, one i32 (0x00RRGGBB) per pixel
|
||||||
var rt_fbw: int = 320
|
var rt_fbw: int = 320
|
||||||
var rt_fbh: int = 240
|
var rt_fbh: int = 240
|
||||||
var rt_regs: words = null # the 64 general-purpose game registers
|
var rt_regs: words = null # the 64 general-purpose game registers
|
||||||
var rt_rng: int = 305419896 # xorshift32 state
|
|
||||||
var rt_alive: int = 1 # platform still running?
|
var rt_alive: int = 1 # platform still running?
|
||||||
|
|
||||||
# ---- renderer state (camera / clip / blend) -------------------------------
|
# ---- renderer state (camera / clip / blend) -------------------------------
|
||||||
|
|
@ -455,53 +454,6 @@ function rt_set_reg(i: int, v: int) -> void {
|
||||||
rt_regs[i] = v
|
rt_regs[i] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- rng (xorshift32) -----------------------------------------------------
|
|
||||||
function rt_seed(s: int) -> void {
|
|
||||||
if s == 0 {
|
|
||||||
rt_rng = 305419896
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rt_rng = s
|
|
||||||
}
|
|
||||||
|
|
||||||
# xorshift32 (Marsaglia). Runs on the raw 32-bit pattern, so the sign bit is
|
|
||||||
# masked off only when a caller asks for a number.
|
|
||||||
function rt_next_rand() -> int {
|
|
||||||
var x = rt_rng
|
|
||||||
x = (x ^ (x << 13))
|
|
||||||
x = (x ^ (x >> 17))
|
|
||||||
x = (x ^ (x << 5))
|
|
||||||
rt_rng = x
|
|
||||||
return (x & 2147483647)
|
|
||||||
}
|
|
||||||
|
|
||||||
function rt_rng_range(lo: int, hi: int) -> int {
|
|
||||||
if hi <= lo { return lo }
|
|
||||||
return lo + rt_next_rand() % (hi - lo + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
function rt_rng_chance(pct: int) -> bool {
|
|
||||||
return rt_next_rand() % 100 < pct
|
|
||||||
}
|
|
||||||
|
|
||||||
# a deterministic fixed-point value in [0, 1) — the raw 0..65535 is exactly the
|
|
||||||
# Q16.16 fraction (fixed and int share the i32 representation).
|
|
||||||
function rt_rng_value() -> fixed {
|
|
||||||
return rt_rng_range(0, 65535)
|
|
||||||
}
|
|
||||||
|
|
||||||
# a deterministic integer in [0, max) — 0 when max <= 0
|
|
||||||
function rt_rng_int(max: int) -> int {
|
|
||||||
if max <= 0 { return 0 }
|
|
||||||
return rt_rng_range(0, max - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
# a deterministic +1 or -1
|
|
||||||
function rt_rng_sign() -> int {
|
|
||||||
if rt_rng_chance(50) { return 1 }
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- platform: input ------------------------------------------------------
|
# ---- platform: input ------------------------------------------------------
|
||||||
function rt_poll() -> int {
|
function rt_poll() -> int {
|
||||||
if is_windowed() {
|
if is_windowed() {
|
||||||
|
|
@ -590,6 +542,7 @@ function rt_dump_ppm(path: string) -> void {
|
||||||
free(hdr)
|
free(hdr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import "rng.ludic"
|
||||||
import "inflate.ludic"
|
import "inflate.ludic"
|
||||||
import "image.ludic"
|
import "image.ludic"
|
||||||
import "truetype.ludic"
|
import "truetype.ludic"
|
||||||
|
|
@ -712,25 +665,6 @@ function rt_bar(x: int, y: int, w: int, h: int, value: int, max: int, color: int
|
||||||
if filled > 0 { rt_fill_rect(x, y, filled, h, color) }
|
if filled > 0 { rt_fill_rect(x, y, filled, h, color) }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Random.weighted(weights): an index drawn in proportion to its weight (0 = never);
|
|
||||||
# -1 when every weight is 0. Deterministic, from the seeded RNG.
|
|
||||||
function rt_rng_weighted(weights: []int) -> int {
|
|
||||||
var total = 0
|
|
||||||
var i = 0
|
|
||||||
while i < len(weights) { if weights[i] > 0 { total += weights[i] }; i += 1 }
|
|
||||||
if total <= 0 { return -1 }
|
|
||||||
var roll = rt_rng_range(0, total - 1)
|
|
||||||
i = 0
|
|
||||||
while i < len(weights) {
|
|
||||||
if weights[i] > 0 {
|
|
||||||
if roll < weights[i] { return i }
|
|
||||||
roll -= weights[i]
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
# List.sample(pool, count): `count` picks from an int slice, distinct while the
|
# List.sample(pool, count): `count` picks from an int slice, distinct while the
|
||||||
# pool has enough, repeating a valid pick when it does not; empty in -> zeros
|
# pool has enough, repeating a valid pick when it does not; empty in -> zeros
|
||||||
function rt_list_sample(pool: []int, count: int) -> []int {
|
function rt_list_sample(pool: []int, count: int) -> []int {
|
||||||
|
|
|
||||||
70
runtime/native/rng.ludic
Normal file
70
runtime/native/rng.ludic
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# rng.ludic — the engine's seeded random numbers (Random.*, seed, rng_*): xorshift32, the same
|
||||||
|
# stream on every machine. Its own file so a program with no ECS - a tool, a test program - gets
|
||||||
|
# Random.* too: the compiler splices it on demand, and core.ludic imports it for a game.
|
||||||
|
var rt_rng: int = 305419896 # xorshift32 state
|
||||||
|
|
||||||
|
# ---- rng (xorshift32) -----------------------------------------------------
|
||||||
|
function rt_seed(s: int) -> void {
|
||||||
|
if s == 0 {
|
||||||
|
rt_rng = 305419896
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rt_rng = s
|
||||||
|
}
|
||||||
|
|
||||||
|
# xorshift32 (Marsaglia). Runs on the raw 32-bit pattern, so the sign bit is
|
||||||
|
# masked off only when a caller asks for a number.
|
||||||
|
function rt_next_rand() -> int {
|
||||||
|
var x = rt_rng
|
||||||
|
x = (x ^ (x << 13))
|
||||||
|
x = (x ^ (x >> 17))
|
||||||
|
x = (x ^ (x << 5))
|
||||||
|
rt_rng = x
|
||||||
|
return (x & 2147483647)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rt_rng_range(lo: int, hi: int) -> int {
|
||||||
|
if hi <= lo { return lo }
|
||||||
|
return lo + rt_next_rand() % (hi - lo + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rt_rng_chance(pct: int) -> bool {
|
||||||
|
return rt_next_rand() % 100 < pct
|
||||||
|
}
|
||||||
|
|
||||||
|
# a deterministic fixed-point value in [0, 1) — the raw 0..65535 is exactly the
|
||||||
|
# Q16.16 fraction (fixed and int share the i32 representation).
|
||||||
|
function rt_rng_value() -> fixed {
|
||||||
|
return rt_rng_range(0, 65535)
|
||||||
|
}
|
||||||
|
|
||||||
|
# a deterministic integer in [0, max) — 0 when max <= 0
|
||||||
|
function rt_rng_int(max: int) -> int {
|
||||||
|
if max <= 0 { return 0 }
|
||||||
|
return rt_rng_range(0, max - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
# a deterministic +1 or -1
|
||||||
|
function rt_rng_sign() -> int {
|
||||||
|
if rt_rng_chance(50) { return 1 }
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Random.weighted(weights): an index drawn in proportion to its weight (0 = never);
|
||||||
|
# -1 when every weight is 0. Deterministic, from the seeded RNG.
|
||||||
|
function rt_rng_weighted(weights: []int) -> int {
|
||||||
|
var total = 0
|
||||||
|
var i = 0
|
||||||
|
while i < len(weights) { if weights[i] > 0 { total += weights[i] }; i += 1 }
|
||||||
|
if total <= 0 { return -1 }
|
||||||
|
var roll = rt_rng_range(0, total - 1)
|
||||||
|
i = 0
|
||||||
|
while i < len(weights) {
|
||||||
|
if weights[i] > 0 {
|
||||||
|
if roll < weights[i] { return i }
|
||||||
|
roll -= weights[i]
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
@ -298,6 +298,7 @@ function p_postfix() -> Node {
|
||||||
while true {
|
while true {
|
||||||
if is_op(".") { pi += 1; let m = node(E_MEMBER); m.a = e; m.s = eat_id(); e = m
|
if is_op(".") { pi += 1; let m = node(E_MEMBER); m.a = e; m.s = eat_id(); e = m
|
||||||
if e.a.kind == E_ID and e.a.s == "Regex" { g_uses_regex = true } # splice the regex runtime on demand
|
if e.a.kind == E_ID and e.a.s == "Regex" { g_uses_regex = true } # splice the regex runtime on demand
|
||||||
|
if e.a.kind == E_ID and e.a.s == "Random" { g_uses_rng = true } # splice the seeded random numbers on demand
|
||||||
if e.a.kind == E_ID and e.a.s == "Fs" and (e.s == "read_bytes" or e.s == "write_bytes") { g_uses_bytes = true }
|
if e.a.kind == E_ID and e.a.s == "Fs" and (e.s == "read_bytes" or e.s == "write_bytes") { g_uses_bytes = true }
|
||||||
if e.a.kind == E_ID and (e.a.s == "BigInt" or e.a.s == "Decimal") { g_uses_bignum = true } # splice the bignum runtime on demand
|
if e.a.kind == E_ID and (e.a.s == "BigInt" or e.a.s == "Decimal") { g_uses_bignum = true } # splice the bignum runtime on demand
|
||||||
if e.a.kind == E_ID and (e.a.s == "Dict" or e.a.s == "Set") { g_uses_dict = true } # splice the hash-table runtime on demand
|
if e.a.kind == E_ID and (e.a.s == "Dict" or e.a.s == "Set") { g_uses_dict = true } # splice the hash-table runtime on demand
|
||||||
|
|
@ -349,6 +350,7 @@ function p_postfix() -> Node {
|
||||||
else { let ix = node(E_INDEX); ix.a = e; ix.b = lo; eat_op("]"); e = ix }
|
else { let ix = node(E_INDEX); ix.a = e; ix.b = lo; eat_op("]"); e = ix }
|
||||||
}
|
}
|
||||||
else if is_op("(") { let c = node(E_CALL); c.a = e; c.line = toks[pi].line; args_call(c); e = c
|
else if is_op("(") { let c = node(E_CALL); c.a = e; c.line = toks[pi].line; args_call(c); e = c
|
||||||
|
if (c.a.kind == E_ID) and rt_wanted_name(c.a.s) { push(g_rt_wants, c.a.s) } # a runtime called by its own name
|
||||||
if (c.a.kind == E_ID) and ((c.a.s == "draw_sprite") or (c.a.s == "draw_sprite_scaled")) { warn_draw_sprite(c.a.s) } # #85 deprecate the bare aliases
|
if (c.a.kind == E_ID) and ((c.a.s == "draw_sprite") or (c.a.s == "draw_sprite_scaled")) { warn_draw_sprite(c.a.s) } # #85 deprecate the bare aliases
|
||||||
}
|
}
|
||||||
else { break }
|
else { break }
|
||||||
|
|
@ -751,6 +753,8 @@ var g_uses_query: bool = false # a program mentioned Query.* -> splice the qu
|
||||||
var g_uses_reflect: bool = false # a program mentioned Reflect.* -> force-emit the reflection ABI
|
var g_uses_reflect: bool = false # a program mentioned Reflect.* -> force-emit the reflection ABI
|
||||||
var g_uses_light: bool = false # a program mentioned Light.* -> splice the 2D light pass
|
var g_uses_light: bool = false # a program mentioned Light.* -> splice the 2D light pass
|
||||||
var g_uses_value: bool = false # Value.*/Json.*/Reflect.serialize -> splice the value tree + JSON (#44)
|
var g_uses_value: bool = false # Value.*/Json.*/Reflect.serialize -> splice the value tree + JSON (#44)
|
||||||
|
var g_uses_rng: bool = false # Random.* -> splice the seeded random numbers (runtime/native/rng.ludic)
|
||||||
|
var g_rt_wants: []pointer = new []pointer # bare names a spliced runtime answers (rng_*, seed, value_*, json_*)
|
||||||
var g_uses_xml: bool = false # Xml.* -> splice the XML reader (Tiled #67)
|
var g_uses_xml: bool = false # Xml.* -> splice the XML reader (Tiled #67)
|
||||||
var g_uses_base64: bool = false # Base64.* -> splice the base64 codec + inflate (Tiled #67)
|
var g_uses_base64: bool = false # Base64.* -> splice the base64 codec + inflate (Tiled #67)
|
||||||
var g_uses_tiled: bool = false # Tiled.* -> splice the Tiled map runtime (#69)
|
var g_uses_tiled: bool = false # Tiled.* -> splice the Tiled map runtime (#69)
|
||||||
|
|
@ -1177,6 +1181,22 @@ function do_import(rel: pointer) -> void {
|
||||||
|
|
||||||
# a game (has handlers/components) links the Ludic runtime; auto-splice it.
|
# a game (has handlers/components) links the Ludic runtime; auto-splice it.
|
||||||
# Tools (an `entry` block, no ECS) get nothing.
|
# Tools (an `entry` block, no ECS) get nothing.
|
||||||
|
# a bare name that only a spliced runtime answers: the runtime comes in when a program calls it
|
||||||
|
# directly (a package calling value_get, a helper calling rng_range), not only through Value.*
|
||||||
|
function rt_wanted_name(n: pointer) -> bool {
|
||||||
|
if (n == "seed") { return true }
|
||||||
|
return str_starts(n, "rng_") or str_starts(n, "value_") or str_starts(n, "json_")
|
||||||
|
}
|
||||||
|
# a wanted name with this prefix that nothing in the program defines
|
||||||
|
function rt_wants_unmet(prefix: pointer) -> bool {
|
||||||
|
var i = 0
|
||||||
|
while i < len(g_rt_wants) {
|
||||||
|
let n = g_rt_wants[i]
|
||||||
|
if str_starts(n, prefix) and find_fn(n) == null and find_global(n) == null { return true }
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
function maybe_splice_runtime() -> void {
|
function maybe_splice_runtime() -> void {
|
||||||
let saved = cur_dir
|
let saved = cur_dir
|
||||||
# L6: the engine's namespaces that are aliases of runtime functions, declared in Ludic
|
# L6: the engine's namespaces that are aliases of runtime functions, declared in Ludic
|
||||||
|
|
@ -1186,6 +1206,13 @@ function maybe_splice_runtime() -> void {
|
||||||
# the asset pack, and a program that does not should not carry the pack's machinery)
|
# the asset pack, and a program that does not should not carry the pack's machinery)
|
||||||
if g_uses_bytes { do_import("runtime/native/bytes.ludic") }
|
if g_uses_bytes { do_import("runtime/native/bytes.ludic") }
|
||||||
cur_dir = saved
|
cur_dir = saved
|
||||||
|
# Random.* (or a bare rng_* / seed nobody else defines) in a program with no ECS: the seeded
|
||||||
|
# random numbers on their own; a game has them through core.ludic
|
||||||
|
if not has_ecs() and (g_uses_rng or rt_wants_unmet("rng_") or rt_wants_unmet("seed")) {
|
||||||
|
cur_dir = ""
|
||||||
|
do_import("runtime/native/rng.ludic")
|
||||||
|
cur_dir = saved
|
||||||
|
}
|
||||||
# a game (has systems/components) links the Ludic runtime.
|
# a game (has systems/components) links the Ludic runtime.
|
||||||
if has_ecs() {
|
if has_ecs() {
|
||||||
cur_dir = ""
|
cur_dir = ""
|
||||||
|
|
@ -1246,6 +1273,7 @@ function maybe_splice_runtime() -> void {
|
||||||
}
|
}
|
||||||
# Value.*/Json.* (#44) get the generic value tree + JSON bridge spliced in; it
|
# Value.*/Json.* (#44) get the generic value tree + JSON bridge spliced in; it
|
||||||
# is self-contained (only string/slice ops), so it works in a plain tool too.
|
# is self-contained (only string/slice ops), so it works in a plain tool too.
|
||||||
|
if rt_wants_unmet("value_") or rt_wants_unmet("json_") { g_uses_value = true } # value_get called directly
|
||||||
if g_uses_value {
|
if g_uses_value {
|
||||||
cur_dir = ""
|
cur_dir = ""
|
||||||
do_import("runtime/native/value.ludic")
|
do_import("runtime/native/value.ludic")
|
||||||
|
|
@ -1467,6 +1495,8 @@ function parse_program() -> void {
|
||||||
g_uses_world_despawn = false
|
g_uses_world_despawn = false
|
||||||
g_uses_light = false
|
g_uses_light = false
|
||||||
g_uses_value = false
|
g_uses_value = false
|
||||||
|
g_uses_rng = false
|
||||||
|
g_rt_wants = new []pointer
|
||||||
g_uses_xml = false
|
g_uses_xml = false
|
||||||
g_uses_base64 = false
|
g_uses_base64 = false
|
||||||
g_uses_tiled = false
|
g_uses_tiled = false
|
||||||
|
|
|
||||||
51768
selfhost/ludicc.seed.ll
51768
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -714,6 +714,9 @@ function cmd_dev_test() -> int {
|
||||||
controller_case("games/rpg_demo", "", "1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1", "rpg_demo.ludic (ludic.rpg #59 all 7 modules: grid move+veto+interact, inventory+equip, crafting, quests, dialog graph, sokoban+signal gate, poison status)")
|
controller_case("games/rpg_demo", "", "1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1", "rpg_demo.ludic (ludic.rpg #59 all 7 modules: grid move+veto+interact, inventory+equip, crafting, quests, dialog graph, sokoban+signal gate, poison status)")
|
||||||
spec_case("library/testing", "== 6 passed, 0 failed ==")
|
spec_case("library/testing", "== 6 passed, 0 failed ==")
|
||||||
spec_case("library/testing_generic", "== 2 passed, 0 failed ==")
|
spec_case("library/testing_generic", "== 2 passed, 0 failed ==")
|
||||||
|
spec_case("library/testing_random", "== 1 passed, 0 failed ==")
|
||||||
|
feat_case("library/random_plain", "", "1 1 1", "random_plain.ludic (Random.* and a bare rng_range in a program with no ECS)")
|
||||||
|
feat_case("library/value_direct", "", "{\"fish\":3} 3", "value_direct.ludic (value_* / json_* called directly splice the value tree)")
|
||||||
spec_case("library/coverage", "== 3 passed, 0 failed ==")
|
spec_case("library/coverage", "== 3 passed, 0 failed ==")
|
||||||
feat_case("library/errors", "", "5 10 0 7 1", "errors.ludic (assert guards an invariant, holds -> runs to the end; issue #8 success path)")
|
feat_case("library/errors", "", "5 10 0 7 1", "errors.ludic (assert guards an invariant, holds -> runs to the end; issue #8 success path)")
|
||||||
feat_case("library/recover", "", "2600 100 1 1 42 7", "recover.ludic (try/else recovers a result value + fallback, error binding, is_ok/is_err; issue #46)")
|
feat_case("library/recover", "", "2600 100 1 1 42 7", "recover.ludic (try/else recovers a result value + fallback, error binding, is_ok/is_err; issue #46)")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue