code wiki / _hdl_build / nx_rewards.nx
nx_rewards.nx source
↩ module page · 186 lines · 10752 B
1// nx_rewards.nx -- LIB: REAL-REWARD gamification (operator: "we want gamification for lots of these popular
2// activities but not dumb rewards but real rewards and real positives and real capabilities"). A GENERAL
3// gamification substrate (works for ANY activity -- swap, garden, compost, cook, ...) where every reward is REAL,
4// never a meaningless point or cosmetic badge.
5//
6// A reward is REAL iff it pays out in at least one of:
7// VALUE -- measured money saved / value gained (cents, grounded in real data e.g. store prices).
8// CAPABILITY -- a real unlocked ability the ecosystem HONORS (rw_can gates real features on it).
9// PROVISION -- a real good earned (seeds, a tool), with its real worth.
10// ANTI-DUMB BY CONSTRUCTION: rw_def_achievement REFUSES any achievement with no real backing (value<=0 AND no
11// capability). So a "dumb reward" cannot exist in the system -- proven mechanically, not promised. Sovereign
12// seg-store (knowledge/store/reward-*, NO TSV), integer cents. license_tier: ORIGINAL
13import "nx_food_science.nx"
14import "nx_seg_store.nx"
15import "nx_syscalls.nx"
16const K_MAGIC_2048: i64 = 2048
17
18func rw_put(prefix: *u8, key: *u8, val: *u8) -> i64 {
19 let vl: i64 = as_len(val)
20 if fd_streq_store(prefix, key, val, vl) == 1 { return 0 }
21 let w: *i64 = ss_begin()
22 ss_add(w, 1, key, val, vl)
23 let seg: i64 = fd_seg_next(prefix)
24 ss_commit(prefix, w, seg)
25 return 1
26}
27func rw_money(dst: *u8, off: i64, cents: i64) -> i64 {
28 var o: i64 = off
29 dst[o] = 36 as u8; o = o + 1
30 o = fd_apnum(dst, o, cents / 100)
31 dst[o] = 46 as u8; o = o + 1
32 let r: i64 = cents % 100
33 dst[o] = (48 + (r / 10)) as u8; o = o + 1
34 dst[o] = (48 + (r % 10)) as u8; o = o + 1
35 return o
36}
37
38// achievement record: reward:ach:<aid> -> name <t> activity <t> kind <t> value_cents <t> capability <t> cite
39func rw_ach_key(aid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "reward:ach:" as *u8); o = as_append(out, o, aid); out[o] = 0 as u8; return o }
40
41// is this proposed reward REAL? (value > 0, OR a non-empty capability/provision). This is the anti-dumb test.
42func rw_backing_real(value_cents: i64, capability: *u8) -> i64 {
43 if value_cents > 0 { return 1 }
44 if capability[0] == (0 as u8) { return 0 }
45 if fd_streq(capability, "-" as *u8) == 1 { return 0 }
46 return 1
47}
48
49// define an achievement. REFUSES (returns 0) any reward with no real backing -- "no dumb rewards" by construction.
50func rw_def_achievement(prefix: *u8, aid: *u8, name: *u8, activity: *u8, kind: *u8, value_cents: i64, capability: *u8, cite: *u8) -> i64 {
51 if rw_backing_real(value_cents, capability) == 0 { return 0 }
52 let key: *u8 = sys_mmap(96); rw_ach_key(aid, key)
53 let val: *u8 = sys_mmap(384); var v: i64 = 0
54 v = as_append(val, v, name); val[v] = 9 as u8; v = v + 1
55 v = as_append(val, v, activity); val[v] = 9 as u8; v = v + 1
56 v = as_append(val, v, kind); val[v] = 9 as u8; v = v + 1
57 v = fd_apnum(val, v, value_cents); val[v] = 9 as u8; v = v + 1
58 v = as_append(val, v, capability); val[v] = 9 as u8; v = v + 1
59 v = as_append(val, v, cite)
60 val[v] = 0 as u8
61 return rw_put(prefix, key, val)
62}
63func rw_ach_defined(prefix: *u8, aid: *u8) -> i64 {
64 let key: *u8 = sys_mmap(96); rw_ach_key(aid, key)
65 let pq: *i64 = sys_mmap(16) as *i64
66 let lq: *i64 = sys_mmap(16) as *i64
67 if ss_get(prefix, key, pq, lq) != 1 { return 0 }
68 return 1
69}
70func rw_ach_str(prefix: *u8, aid: *u8, f: i64, out: *u8) -> i64 {
71 let key: *u8 = sys_mmap(96); rw_ach_key(aid, key)
72 let pq: *i64 = sys_mmap(16) as *i64
73 let lq: *i64 = sys_mmap(16) as *i64
74 if ss_get(prefix, key, pq, lq) != 1 { out[0] = 0 as u8; return 0 }
75 return fd_field(pq[0] as *u8, lq[0], f, out)
76}
77func rw_ach_int(prefix: *u8, aid: *u8, f: i64) -> i64 {
78 let fb: *u8 = sys_mmap(24)
79 let fl: i64 = rw_ach_str(prefix, aid, f, fb)
80 if fl == 0 { return 0 }
81 return fd_atoi(fb, fl)
82}
83
84// ---- earning a reward applies the REAL payout ----
85func rw_earned_key(uid: *u8, aid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "reward:earned:" as *u8); o = as_append(out, o, uid); out[o] = 58 as u8; o = o + 1; o = as_append(out, o, aid); out[o] = 0 as u8; return o }
86func rw_cap_key(uid: *u8, cap: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "reward:cap:" as *u8); o = as_append(out, o, uid); out[o] = 58 as u8; o = o + 1; o = as_append(out, o, cap); out[o] = 0 as u8; return o }
87func rw_value_key(uid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "reward:value:" as *u8); o = as_append(out, o, uid); out[o] = 0 as u8; return o }
88func rw_earnedids_key(uid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "reward:earnedids:" as *u8); o = as_append(out, o, uid); out[o] = 0 as u8; return o }
89
90func rw_has(prefix: *u8, uid: *u8, aid: *u8) -> i64 {
91 let key: *u8 = sys_mmap(128); rw_earned_key(uid, aid, key)
92 let pq: *i64 = sys_mmap(16) as *i64
93 let lq: *i64 = sys_mmap(16) as *i64
94 if ss_get(prefix, key, pq, lq) != 1 { return 0 }
95 return 1
96}
97func rw_value_total(prefix: *u8, uid: *u8) -> i64 {
98 let key: *u8 = sys_mmap(96); rw_value_key(uid, key)
99 let pq: *i64 = sys_mmap(16) as *i64
100 let lq: *i64 = sys_mmap(16) as *i64
101 if ss_get(prefix, key, pq, lq) != 1 { return 0 }
102 return fd_atoi(pq[0] as *u8, lq[0])
103}
104// 1 if the user has unlocked this REAL capability. THE gate other organs check before a real action.
105func rw_can(prefix: *u8, uid: *u8, cap: *u8) -> i64 {
106 let key: *u8 = sys_mmap(128); rw_cap_key(uid, cap, key)
107 let pq: *i64 = sys_mmap(16) as *i64
108 let lq: *i64 = sys_mmap(16) as *i64
109 if ss_get(prefix, key, pq, lq) != 1 { return 0 }
110 return 1
111}
112
113// grant an earned achievement: records it (additive) + applies the REAL payout (adds value, unlocks capability).
114// Idempotent: a second grant of the same achievement is a no-op (no double-pay). Returns 1 if newly granted.
115func rw_grant(prefix: *u8, uid: *u8, aid: *u8, day: i64) -> i64 {
116 if rw_ach_defined(prefix, aid) == 0 { return 0 }
117 if rw_has(prefix, uid, aid) == 1 { return 0 }
118 let ek: *u8 = sys_mmap(128); rw_earned_key(uid, aid, ek)
119 let dv: *u8 = sys_mmap(16); var do: i64 = fd_apnum(dv, 0, day); dv[do] = 0 as u8
120 rw_put(prefix, ek, dv)
121 // real value payout
122 let v: i64 = rw_ach_int(prefix, aid, 3)
123 if v > 0 {
124 let cur: i64 = rw_value_total(prefix, uid)
125 let vk: *u8 = sys_mmap(96); rw_value_key(uid, vk)
126 let nv: *u8 = sys_mmap(24); var no: i64 = fd_apnum(nv, 0, cur + v); nv[no] = 0 as u8
127 rw_put(prefix, vk, nv)
128 }
129 // real capability unlock
130 let cap: *u8 = sys_mmap(96); rw_ach_str(prefix, aid, 4, cap)
131 if cap[0] != (0 as u8) { if fd_streq(cap, "-" as *u8) == 0 {
132 let ck: *u8 = sys_mmap(128); rw_cap_key(uid, cap, ck)
133 rw_put(prefix, ck, "1" as *u8)
134 } }
135 // earned index (for the render)
136 let ik: *u8 = sys_mmap(96); rw_earnedids_key(uid, ik)
137 let pq: *i64 = sys_mmap(16) as *i64
138 let lq: *i64 = sys_mmap(16) as *i64
139 let cur: *u8 = sys_mmap(K_MAGIC_2048)
140 var co: i64 = 0
141 if ss_get(prefix, ik, pq, lq) == 1 { let b: *u8 = pq[0] as *u8; let n: i64 = lq[0]; var u: i64 = 0; while u < n { cur[u] = b[u]; u = u + 1 } co = n; cur[co] = 9 as u8; co = co + 1 }
142 co = as_append(cur, co, aid); cur[co] = 0 as u8
143 rw_put(prefix, ik, cur)
144 return 1
145}
146
147// render the user's REAL rewards (money saved + capabilities unlocked); sovereign, honest framing.
148func rw_render_rewards(prefix: *u8, uid: *u8, out: *u8) -> i64 {
149 var o: i64 = 0
150 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>Your real rewards</title><style>body{margin:0;font-family:system-ui,sans-serif;color:#1a2410;background:#f3f7ea;line-height:1.5}header{background:#5a6b1f;color:#fff;padding:22px}header h1{margin:0;font-size:1.4rem}header p{margin:4px 0 0;color:#e7efce}main{max-width:680px;margin:0 auto;padding:16px}section{background:#fff;border-radius:14px;padding:16px 18px;margin-top:16px;box-shadow:0 1px 0 #e3e9d3}h2{font-size:1.1rem;margin:0 0 8px;color:#5a6b1f}.tot{font-size:1.6rem;font-weight:800;color:#3f7a1f}ul{list-style:none;padding:0;margin:0}li{padding:8px 0;border-bottom:1px solid #eef3e2}.val{color:#3f7a1f;font-weight:700}.cap{color:#7a5a1f;font-weight:700}.muted{color:#5b6a52;font-size:.88rem}</style></head><body><header><h1>Your real rewards</h1><p>Real money saved and real abilities unlocked — not points.</p></header><main>" as *u8)
151 o = as_append(out, o, "<section><h2>Real value earned</h2><p class='tot'>" as *u8)
152 o = rw_money(out, o, rw_value_total(prefix, uid))
153 o = as_append(out, o, "</p><p class='muted'>Measured money you kept by growing/swapping instead of buying.</p></section>" as *u8)
154 o = as_append(out, o, "<section><h2>What you earned</h2><ul>" as *u8)
155 let ik: *u8 = sys_mmap(96); rw_earnedids_key(uid, ik)
156 let pq: *i64 = sys_mmap(16) as *i64
157 let lq: *i64 = sys_mmap(16) as *i64
158 if ss_get(prefix, ik, pq, lq) == 1 {
159 let b: *u8 = pq[0] as *u8
160 let n: i64 = lq[0]
161 var i: i64 = 0
162 var ls: i64 = 0
163 while i <= n {
164 var sep: i64 = 0
165 if i == n { sep = 1 } else { if b[i] == (9 as u8) { sep = 1 } }
166 if sep == 1 {
167 let tl: i64 = i - ls
168 if tl > 0 {
169 let aid: *u8 = sys_mmap(64); var t: i64 = 0; while t < tl { aid[t] = b[ls + t]; t = t + 1 } aid[tl] = 0 as u8
170 let nm: *u8 = sys_mmap(96); rw_ach_str(prefix, aid, 0, nm)
171 let cap: *u8 = sys_mmap(96); rw_ach_str(prefix, aid, 4, cap)
172 let v: i64 = rw_ach_int(prefix, aid, 3)
173 o = as_append(out, o, "<li><b>" as *u8); o = as_append_escaped(out, o, nm, as_len(nm)); o = as_append(out, o, "</b> — " as *u8)
174 if v > 0 { o = as_append(out, o, "<span class='val'>" as *u8); o = rw_money(out, o, v); o = as_append(out, o, " value</span> " as *u8) }
175 if cap[0] != (0 as u8) { if fd_streq(cap, "-" as *u8) == 0 { o = as_append(out, o, "<span class='cap'>unlocked: " as *u8); o = as_append_escaped(out, o, cap, as_len(cap)); o = as_append(out, o, "</span>" as *u8) } }
176 o = as_append(out, o, "</li>" as *u8)
177 }
178 ls = i + 1
179 }
180 i = i + 1
181 }
182 }
183 o = as_append(out, o, "</ul></section><p class='muted'>Every reward here is real: measured value or a capability the ecosystem honors. No cosmetic points.</p></main></body></html>" as *u8)
184 out[o] = 0 as u8
185 return o
186}