code wiki / _hdl_build / nx_garden.nx

nx_garden.nx source

↩ module page · 206 lines · 12417 B

1// nx_garden.nx -- LIB: the GROW-YOUR-OWN affordability engine (operator: "garden-to-table ... affordability 2// improvement"). For each food ingredient, models whether you can grow it at home, the season, and the cost to 3// BUY vs the cost to GROW (cents, integer -- no float). The savings = buy - grow is the affordability win: 4// grow-your-own turns a Michelin ingredient into a budget one. Each crop CITES a real permaculture research 5// source (knowledge/fetched/perma_*.raw) -- research, not bro science. Crops live in the food store keyed 6// garden:crop:<food-ingredient-id>. Only growable ingredients have a record (proteins/processed sauces don't). 7// Schema: garden:crop:<id> -> difficulty(1-5) <t> season <t> buy_cents <t> grow_cents <t> yield <t> cite 8// license_tier: ORIGINAL 9import "nx_food_science.nx" 10import "nx_seg_store.nx" 11import "nx_syscalls.nx" 12 13func gd_money(dst: *u8, off: i64, cents: i64) -> i64 { 14 var o: i64 = off 15 dst[o] = 36 as u8; o = o + 1 16 o = fd_apnum(dst, o, cents / 100) 17 dst[o] = 46 as u8; o = o + 1 18 let r: i64 = cents % 100 19 dst[o] = (48 + (r / 10)) as u8; o = o + 1 20 dst[o] = (48 + (r % 10)) as u8; o = o + 1 21 return o 22} 23func gd_crop_key(id: *u8, out: *u8) -> i64 { 24 var o: i64 = 0 25 o = as_append(out, o, "garden:crop:" as *u8) 26 o = as_append(out, o, id) 27 out[o] = 0 as u8 28 return o 29} 30func gd_get(prefix: *u8, id: *u8, ptr: *i64, len: *i64) -> i64 { 31 let key: *u8 = sys_mmap(80) 32 gd_crop_key(id, key) 33 return ss_get(prefix, key, ptr, len) 34} 35func gd_growable(prefix: *u8, id: *u8) -> i64 { 36 let pq: *i64 = sys_mmap(16) as *i64 37 let lq: *i64 = sys_mmap(16) as *i64 38 if gd_get(prefix, id, pq, lq) == 1 { return 1 } 39 return 0 40} 41func gd_field_int(prefix: *u8, id: *u8, f: i64) -> i64 { 42 let pq: *i64 = sys_mmap(16) as *i64 43 let lq: *i64 = sys_mmap(16) as *i64 44 if gd_get(prefix, id, pq, lq) != 1 { return 0 - 1 } 45 let buf: *u8 = sys_mmap(32) 46 let l: i64 = fd_field(pq[0] as *u8, lq[0], f, buf) 47 return fd_atoi(buf, l) 48} 49func gd_field_str(prefix: *u8, id: *u8, f: i64, out: *u8) -> i64 { 50 let pq: *i64 = sys_mmap(16) as *i64 51 let lq: *i64 = sys_mmap(16) as *i64 52 if gd_get(prefix, id, pq, lq) != 1 { out[0] = 0 as u8; return 0 } 53 return fd_field(pq[0] as *u8, lq[0], f, out) 54} 55func gd_buy(prefix: *u8, id: *u8) -> i64 { return gd_field_int(prefix, id, 2) } 56func gd_grow(prefix: *u8, id: *u8) -> i64 { return gd_field_int(prefix, id, 3) } 57// savings (cents) of growing vs buying one unit; 0 if not growable 58func gd_savings(prefix: *u8, id: *u8) -> i64 { 59 if gd_growable(prefix, id) == 0 { return 0 } 60 let b: i64 = gd_buy(prefix, id) 61 let g: i64 = gd_grow(prefix, id) 62 if b < 0 { return 0 } 63 if g < 0 { return 0 } 64 return b - g 65} 66 67// ---- companion planting (closes the loop: the perma SYNTHESIS garden:companion:<a>:<b> feeds affordability) ---- 68// read the companion co-occurrence between two crop ids (tries both orders); 0 if not companions. 69func gd_companion(prefix: *u8, a: *u8, b: *u8) -> i64 { 70 let key: *u8 = sys_mmap(96) 71 let pq: *i64 = sys_mmap(16) as *i64 72 let lq: *i64 = sys_mmap(16) as *i64 73 var o: i64 = 0 74 o = as_append(key, o, "garden:companion:" as *u8); o = as_append(key, o, a); key[o] = 58 as u8; o = o + 1; o = as_append(key, o, b); key[o] = 0 as u8 75 if ss_get(prefix, key, pq, lq) == 1 { return fd_atoi(pq[0] as *u8, lq[0]) } 76 o = 0 77 o = as_append(key, o, "garden:companion:" as *u8); o = as_append(key, o, b); key[o] = 58 as u8; o = o + 1; o = as_append(key, o, a); key[o] = 0 as u8 78 if ss_get(prefix, key, pq, lq) == 1 { return fd_atoi(pq[0] as *u8, lq[0]) } 79 return 0 80} 81// seed the mined companion facts (idempotent; mirrors what nx_perma_mine_gate extracted from the corpus). 82func gd_seed_companions(prefix: *u8) -> i64 { 83 let keys: *i64 = sys_mmap(8 * 8) as *i64 84 let vals: *i64 = sys_mmap(8 * 8) as *i64 85 var n: i64 = 0 86 keys[n] = "garden:companion:onion:carrot" as *u8 as i64; vals[n] = "6" as *u8 as i64; n = n + 1 87 keys[n] = "garden:companion:onion:garlic" as *u8 as i64; vals[n] = "2" as *u8 as i64; n = n + 1 88 keys[n] = "garden:companion:onion:mushroom" as *u8 as i64; vals[n] = "1" as *u8 as i64; n = n + 1 89 keys[n] = "garden:companion:onion:lime" as *u8 as i64; vals[n] = "1" as *u8 as i64; n = n + 1 90 let w: *i64 = ss_begin() 91 var tw: i64 = 0 92 var i: i64 = 0 93 while i < n { 94 let k: *u8 = keys[i] as *u8 95 let v: *u8 = vals[i] as *u8 96 let vl: i64 = as_len(v) 97 if fd_streq_store(prefix, k, v, vl) == 0 { ss_add(w, 1, k, v, vl); tw = tw + 1 } 98 i = i + 1 99 } 100 if tw > 0 { let seg: i64 = fd_seg_next(prefix); ss_commit(prefix, w, seg) } 101 return tw 102} 103// companion savings bonus (cents) for a set of growable crop world-indices: each interplanted companion pair 104// boosts yield, worth 5c per companion-strength point. (The synthesis number drives a real affordability gain.) 105func gd_companion_bonus(prefix: *u8, world: *i64, idxs: *i64, nidx: i64) -> i64 { 106 let ids: *i64 = world[1] as *i64 107 var bonus: i64 = 0 108 var i: i64 = 0 109 while i < nidx { 110 var j: i64 = i + 1 111 while j < nidx { 112 let c: i64 = gd_companion(prefix, ids[idxs[i]] as *u8, ids[idxs[j]] as *u8) 113 if c > 0 { bonus = bonus + c * 5 } 114 j = j + 1 115 } 116 i = i + 1 117 } 118 return bonus 119} 120 121// author the growable-crop catalogue (idempotent). Costs in cents. cite -> a fetched permaculture source. 122func gd_seed(prefix: *u8) -> i64 { 123 let keys: *i64 = sys_mmap(8 * 32) as *i64 124 let vals: *i64 = sys_mmap(8 * 32) as *i64 125 var n: i64 = 0 126 keys[n] = "garden:cropids" as *u8 as i64 127 vals[n] = "garlic\tonion\tscallion\tginger\tchili\tbroccoli\tbellpepper\tcarrot\tsnowpea\tbokchoy\tbeansprout\tmushroom\ttofu\tlime\tpeanut" as *u8 as i64; n = n + 1 128 keys[n] = "garden:crop:garlic" as *u8 as i64; vals[n] = "1\tFALL\t100\t12\t8\tperma_a6_companion_planting" as *u8 as i64; n = n + 1 129 keys[n] = "garden:crop:onion" as *u8 as i64; vals[n] = "1\tSPRING\t100\t15\t8\tperma_b11_crop_rotation" as *u8 as i64; n = n + 1 130 keys[n] = "garden:crop:scallion" as *u8 as i64; vals[n] = "1\tPERENNIAL\t100\t8\t15\tperma_c6_seed_saving" as *u8 as i64; n = n + 1 131 keys[n] = "garden:crop:ginger" as *u8 as i64; vals[n] = "3\tSUMMER\t350\t60\t4\tperma_c3_container_garden" as *u8 as i64; n = n + 1 132 keys[n] = "garden:crop:chili" as *u8 as i64; vals[n] = "2\tSUMMER\t200\t20\t12\tperma_c4_hydroponics" as *u8 as i64; n = n + 1 133 keys[n] = "garden:crop:broccoli" as *u8 as i64; vals[n] = "2\tFALL\t200\t30\t4\tperma_a6_companion_planting" as *u8 as i64; n = n + 1 134 keys[n] = "garden:crop:bellpepper" as *u8 as i64; vals[n] = "3\tSUMMER\t150\t25\t6\tperma_c1_raised_bed" as *u8 as i64; n = n + 1 135 keys[n] = "garden:crop:carrot" as *u8 as i64; vals[n] = "2\tSPRING\t120\t20\t10\tperma_b4_soil_structure" as *u8 as i64; n = n + 1 136 keys[n] = "garden:crop:snowpea" as *u8 as i64; vals[n] = "2\tSPRING\t250\t30\t5\tperma_b12_nitrogen_fixation" as *u8 as i64; n = n + 1 137 keys[n] = "garden:crop:bokchoy" as *u8 as i64; vals[n] = "2\tFALL\t180\t25\t4\tperma_a5_polyculture" as *u8 as i64; n = n + 1 138 keys[n] = "garden:crop:beansprout" as *u8 as i64; vals[n] = "1\tPERENNIAL\t150\t10\t20\tperma_c6_seed_saving" as *u8 as i64; n = n + 1 139 keys[n] = "garden:crop:mushroom" as *u8 as i64; vals[n] = "4\tPERENNIAL\t300\t80\t6\tperma_b6_humus" as *u8 as i64; n = n + 1 140 keys[n] = "garden:crop:tofu" as *u8 as i64; vals[n] = "4\tSUMMER\t200\t50\t6\tperma_b12_nitrogen_fixation" as *u8 as i64; n = n + 1 141 keys[n] = "garden:crop:lime" as *u8 as i64; vals[n] = "4\tPERENNIAL\t50\t30\t50\tperma_a3_agroforestry" as *u8 as i64; n = n + 1 142 keys[n] = "garden:crop:peanut" as *u8 as i64; vals[n] = "3\tSUMMER\t300\t40\t8\tperma_b12_nitrogen_fixation" as *u8 as i64; n = n + 1 143 144 let w: *i64 = ss_begin() 145 var towrite: i64 = 0 146 var i: i64 = 0 147 while i < n { 148 let key: *u8 = keys[i] as *u8 149 let val: *u8 = vals[i] as *u8 150 let vl: i64 = as_len(val) 151 if fd_streq_store(prefix, key, val, vl) == 0 { ss_add(w, 1, key, val, vl); towrite = towrite + 1 } 152 i = i + 1 153 } 154 if towrite > 0 { let seg: i64 = fd_seg_next(prefix); ss_commit(prefix, w, seg) } 155 return towrite 156} 157 158// total per-unit savings of growing the growable ingredients in a build; out_grow = how many are growable 159func gd_meal_savings(prefix: *u8, food_world: *i64, build: *i64, nb: i64, out_grow: *i64) -> i64 { 160 let ids: *i64 = food_world[1] as *i64 161 var sav: i64 = 0 162 var g: i64 = 0 163 var i: i64 = 0 164 while i < nb { 165 let id: *u8 = ids[build[i]] as *u8 166 if gd_growable(prefix, id) == 1 { sav = sav + gd_savings(prefix, id); g = g + 1 } 167 i = i + 1 168 } 169 out_grow[0] = g 170 return sav 171} 172 173// render the sovereign grow-your-own affordability plan for a build (NUL-terminated); returns length 174func gd_render_plan(prefix: *u8, food_world: *i64, build: *i64, nb: i64, meals_per_week: i64, out: *u8) -> i64 { 175 let ids: *i64 = food_world[1] as *i64 176 let names: *i64 = food_world[4] as *i64 177 var o: i64 = 0 178 o = as_append(out, o, "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>Grow it yourself</title><style>body{margin:0;font-family:system-ui,sans-serif;color:#1d2a1f;background:#f2f7ee;line-height:1.5}.card{max-width:620px;margin:0 auto;padding:24px 22px}header{background:#2e6b34;color:#fff;padding:24px;border-radius:0 0 12px 12px}header h1{margin:0 0 4px;font-size:1.4rem}header p{margin:0;color:#d6ebcf}h2{font-size:1.05rem;color:#2e6b34;margin:20px 0 8px}table{border-collapse:collapse;width:100%;background:#fff;border-radius:10px;overflow:hidden}th{text-align:left;background:#e3efdc;padding:8px 10px;color:#33503f;font-size:.9rem}td{padding:8px 10px;border-top:1px solid #eef3ea}td.n{text-align:right;font-variant-numeric:tabular-nums}.buy td{color:#999;text-decoration:line-through}.big{background:#fff;border-left:4px solid #2e6b34;border-radius:8px;padding:14px 16px;margin-top:16px;font-size:1.05rem}.ftr{margin-top:20px;color:#7a8a72;font-size:.82rem;text-align:center}</style></head><body><header><h1>Grow it yourself</h1><p>Tonight's stir-fry, from your own garden &mdash; the cheapest premium ingredient is the one you grew.</p></header><div class='card'>" as *u8) 179 o = as_append(out, o, "<h2>What you can grow</h2><table><tr><th>Ingredient</th><th>Season</th><th>Buy</th><th>Grow</th><th>You save</th></tr>" as *u8) 180 let og: *i64 = sys_mmap(16) as *i64 181 let persav: i64 = gd_meal_savings(prefix, food_world, build, nb, og) 182 var i: i64 = 0 183 var notgrow: i64 = 0 184 while i < nb { 185 let id: *u8 = ids[build[i]] as *u8 186 if gd_growable(prefix, id) == 1 { 187 o = as_append(out, o, "<tr><td>" as *u8) 188 o = as_append_escaped(out, o, names[build[i]] as *u8, as_len(names[build[i]] as *u8)) 189 o = as_append(out, o, "</td><td>" as *u8) 190 let se: *u8 = sys_mmap(24); gd_field_str(prefix, id, 1, se); o = as_append(out, o, se) 191 o = as_append(out, o, "</td><td class='n'>" as *u8); o = gd_money(out, o, gd_buy(prefix, id)) 192 o = as_append(out, o, "</td><td class='n'>" as *u8); o = gd_money(out, o, gd_grow(prefix, id)) 193 o = as_append(out, o, "</td><td class='n'>" as *u8); o = gd_money(out, o, gd_savings(prefix, id)) 194 o = as_append(out, o, "</td></tr>" as *u8) 195 } else { notgrow = notgrow + 1 } 196 i = i + 1 197 } 198 o = as_append(out, o, "</table>" as *u8) 199 o = as_append(out, o, "<div class='big'>Per meal you save <b>" as *u8); o = gd_money(out, o, persav) 200 o = as_append(out, o, "</b> by growing " as *u8); o = fd_apnum(out, o, og[0]); o = as_append(out, o, " of the ingredients. Cooked " as *u8) 201 o = fd_apnum(out, o, meals_per_week); o = as_append(out, o, "x a week, that is <b>" as *u8) 202 o = gd_money(out, o, persav * meals_per_week * 52); o = as_append(out, o, "</b> a year &mdash; before counting the quality you cannot buy.</div>" as *u8) 203 o = as_append(out, o, "<p class='ftr'>Grounded in the Nishi permaculture corpus &mdash; sovereign, no tracking. Costs are planning estimates.</p></div></body></html>" as *u8) 204 out[o] = 0 as u8 205 return o 206}