nx_colonize_gate.nx source
↩ module page · 139 lines · 6697 B
1// nx_colonize_gate.nx -- PG21: DO TREES GROW BY COMPETITION FOR SPACE, AND DOES THE CANOPY MATCH THE LAYOUT?
2//
3// SUBJECT: nx_treegen.tg_colonize (in-process) fed by nx_forest_layout's OWN species params (in-process), so the
4// envelope the grower is asked for is the envelope the LIVE layout will place it in. The rung's done-rule in its own
5// words: distinct seeds yield distinct branching, and the canopy envelope matches the layout's species params.
6// Every value is emitted with gv_kv; the per-species numbers name any failing species without a second run.
7import "nx_syscalls.nx"
8import "nx_gate_verdict.nx"
9import "nx_treegen.nx"
10import "nx_forest_layout.nx"
11const CG_UNITS_NUM: i64 = 640 // tree units per CG_UNITS_DEN forest Q: the tallest species (pine, 25000 Q) spans 640 units
12const CG_UNITS_DEN: i64 = 25000
13const CG_SEED: i64 = 2026
14const CG_SEEDS: i64 = 8
15const CG_R_LO: i64 = 550 // measured crown reach as permil of the requested radius: lower and upper bands
16const CG_R_HI: i64 = 1100
17const CG_H_LO: i64 = 800 // measured height as permil of the requested height
18const CG_H_HI: i64 = 1100
19const CG_LEAF_R: i64 = 22
20const CG_TINY_H: i64 = 30 // the neg-control envelope: a crown narrower than one step
21const CG_TINY_R: i64 = 1
22const CG_TINY_MAXP: i64 = 20
23const CG_PARAMS: i64 = 3
24// trunk fraction per species, permil of height: the crown base the layout's silhouettes imply
25func cg_trunk_permil(species: i64) -> i64 {
26 if species == NX_TREE_OAK { return 350 }
27 if species == NX_TREE_PINE { return 200 }
28 if species == NX_TREE_BIRCH { return 400 }
29 if species == NX_TREE_SPRUCE { return 150 }
30 if species == NX_TREE_WILLOW { return 300 }
31 if species == NX_TREE_MAPLE { return 350 }
32 if species == NX_TREE_DEAD { return 450 }
33 if species == NX_TREE_PALM { return 850 }
34 return 350
35}
36// grow one species from the LIVE layout's params; prm receives height, crown radius, trunk radius in tree units
37func cg_grow(P: *i64, seed: i64, species: i64, prm: *i64) -> i64 {
38 let q: *i64 = sys_mmap(64) as *i64
39 _forest_species_params(species, q)
40 prm[0] = q[0]*CG_UNITS_NUM/CG_UNITS_DEN
41 prm[1] = q[1]*CG_UNITS_NUM/CG_UNITS_DEN
42 prm[2] = q[2]*CG_UNITS_NUM/CG_UNITS_DEN
43 if prm[2] < TG_COL_RMIN { prm[2] = TG_COL_RMIN }
44 var lr: i64 = CG_LEAF_R
45 if q[1] == 0 { lr = 0 }
46 return tg_colonize(P, seed, prm[0], prm[1], cg_trunk_permil(species), prm[2], lr)
47}
48func cg_same(P: *i64, np: i64, Q: *i64, nq: i64) -> i64 {
49 if np != nq { return 0 }
50 var k: i64 = 0
51 while k < np*TG_STR {
52 if P[k] != Q[k] { return 0 }
53 k = k + 1
54 }
55 return 1
56}
57
58func main() -> i64 {
59 let ctr: *i64 = gv_ctr()
60 gv_head("=== NX-COLONIZE gate (PG21): space colonization grows distinct trees that fill the layout's envelope ===" as *u8)
61 let P: *i64 = sys_mmap(TG_CAP*TG_STR*8) as *i64
62 let Q: *i64 = sys_mmap(TG_CAP*TG_STR*8) as *i64
63 let prm: *i64 = sys_mmap(32) as *i64
64 let env: *i64 = sys_mmap(64) as *i64
65 // 1. every layout species grows inside the band the layout asked for
66 var env_ok: i64 = 1
67 var bounded: i64 = 1
68 var pipe_ok: i64 = 1
69 var dead_leafless: i64 = 1
70 var grown: i64 = 0
71 var s: i64 = 0
72 let base_y: i64 = 0 - TG_COL_BASE_Y
73 let rperm: *i64 = sys_mmap(64) as *i64
74 let hperm: *i64 = sys_mmap(64) as *i64
75 let npk: *i64 = sys_mmap(64) as *i64
76 while s < NX_TREE_SPECIES_COUNT {
77 let np: i64 = cg_grow(P, CG_SEED, s, prm)
78 npk[s] = np
79 if np > 0 { grown = grown + 1 }
80 if np > TG_CAP { bounded = 0 }
81 tg_col_envelope(P, np, env)
82 hperm[s] = (env[0] - base_y)*1000/prm[0]
83 if hperm[s] < CG_H_LO { env_ok = 0 }
84 if hperm[s] > CG_H_HI { env_ok = 0 }
85 if prm[1] > 0 {
86 rperm[s] = env[1]*1000/prm[1]
87 if rperm[s] < CG_R_LO { env_ok = 0 }
88 if rperm[s] > CG_R_HI { env_ok = 0 }
89 } else {
90 rperm[s] = 0
91 if env[3] != 0 { dead_leafless = 0 }
92 }
93 if env[4] != prm[2] { pipe_ok = 0 }
94 if env[5] < TG_COL_RMIN { pipe_ok = 0 }
95 s = s + 1
96 }
97 gv_check("every-layout-species-grows (parts > 0 for all 8)" as *u8, (grown == NX_TREE_SPECIES_COUNT) as i64, ctr)
98 gv_check("envelope-matches-the-layout: height and crown reach inside the bands for every species with a canopy" as *u8, env_ok, ctr)
99 gv_check("a-canopy-of-zero-grows-no-leaves (the dead species)" as *u8, dead_leafless, ctr)
100 gv_check("parts-bounded-by-TG_CAP for every species" as *u8, bounded, ctr)
101 gv_check("pipe-model: the thickest capsule IS the trunk radius and no capsule is thinner than TG_COL_RMIN" as *u8, pipe_ok, ctr)
102 // 2. determinism and distinct seeds on the oak
103 let np1: i64 = cg_grow(P, 1, NX_TREE_OAK, prm)
104 let np2: i64 = cg_grow(Q, 1, NX_TREE_OAK, prm)
105 gv_check("determinism: the same seed grows the same tree, every part byte-equal" as *u8, cg_same(P, np1, Q, np2), ctr)
106 var distinct_pairs: i64 = 0
107 var pairs: i64 = 0
108 var i: i64 = 1
109 while i <= CG_SEEDS {
110 let na: i64 = cg_grow(P, i, NX_TREE_OAK, prm)
111 var j: i64 = i + 1
112 while j <= CG_SEEDS {
113 let nb: i64 = cg_grow(Q, j, NX_TREE_OAK, prm)
114 pairs = pairs + 1
115 if cg_same(P, na, Q, nb) == 0 { distinct_pairs = distinct_pairs + 1 }
116 j = j + 1
117 }
118 i = i + 1
119 }
120 gv_check_eq("distinct-seeds-distinct-branching: every seed pair differs (denominator bound: pairs)" as *u8, distinct_pairs, pairs, ctr)
121 gv_check("pairs-denominator-is-the-declared-8-choose-2" as *u8, (pairs == CG_SEEDS*(CG_SEEDS-1)/2) as i64, ctr)
122 // 3. neg-control: an envelope narrower than one step still grows a trunk and nothing wild
123 let npt: i64 = tg_colonize(P, CG_SEED, CG_TINY_H, CG_TINY_R, 500, TG_COL_RMIN, CG_LEAF_R)
124 gv_check("neg-control-tiny-envelope: grows at least the trunk and fewer than CG_TINY_MAXP parts" as *u8, ((npt >= 1) as i64) * ((npt < CG_TINY_MAXP) as i64), ctr)
125 gv_values_head()
126 var s2: i64 = 0
127 while s2 < NX_TREE_SPECIES_COUNT {
128 gv_kv("species" as *u8, s2)
129 gv_kv("parts" as *u8, npk[s2])
130 gv_kv("height_permil_of_requested" as *u8, hperm[s2])
131 gv_kv("crown_reach_permil_of_requested" as *u8, rperm[s2])
132 s2 = s2 + 1
133 }
134 gv_kv("oak_seed1_parts" as *u8, np1)
135 gv_kv("distinct_pairs" as *u8, distinct_pairs)
136 gv_kv("pairs" as *u8, pairs)
137 gv_kv("tiny_envelope_parts" as *u8, npt)
138 return gv_verdict("NX-COLONIZE" as *u8, ctr, "trees are grown by competition for space inside the envelope the layout asks for, then placed" as *u8)
139}