code wiki / (root) / nx_moment_resolve_test.nx

nx_moment_resolve_test.nx source

↩ module page · 229 lines · 9664 B

1// nx_moment_resolve_test.nx -- smoke for arc -> DirectorsNote bridge. 2 3import "nx_syscalls.nx" 4import "nx_tier.nx" 5import "nx_prng.nx" 6import "nx_arc.nx" 7import "nx_directors_note.nx" 8import "nx_moment_resolve.nx" 9 10func main() -> nx_int { 11 // Build a 1-stage arc with concrete pools so resolve has data 12 // to pick from. ContentDomain MUNDANE. 13 // 14 // Pools layout in arc.pools[]: 15 // [0..1] location IDs: 100, 101 16 // [2..3] outfit IDs: 200, 201 17 // [4..5] pose IDs: 300, 301 18 // [6] expression: NX_AF_FOCUSED (=8) 19 // [7] camera: 500 20 let arc: *Arc = nx_arc_alloc(1, 1, NX_AS_AUTHORED, NX_CD_MUNDANE, 8) 21 nx_arc_pool_set(arc, 0, 100) 22 nx_arc_pool_set(arc, 1, 101) 23 nx_arc_pool_set(arc, 2, 200) 24 nx_arc_pool_set(arc, 3, 201) 25 nx_arc_pool_set(arc, 4, 300) 26 nx_arc_pool_set(arc, 5, 301) 27 nx_arc_pool_set(arc, 6, NX_AF_FOCUSED) 28 nx_arc_pool_set(arc, 7, 500) 29 30 nx_arc_stage_set_basic(arc, 0, 31 1, NX_AW_UNAWARE, NX_ET_DOMESTIC, 32 300, 512, 5) 33 nx_arc_stage_set_pool(arc, 0, NX_ARC_POOL_LOCATION, 0, 2) 34 nx_arc_stage_set_pool(arc, 0, NX_ARC_POOL_OUTFIT, 2, 2) 35 nx_arc_stage_set_pool(arc, 0, NX_ARC_POOL_POSE, 4, 2) 36 nx_arc_stage_set_pool(arc, 0, NX_ARC_POOL_EXPRESSION, 6, 1) 37 nx_arc_stage_set_pool(arc, 0, NX_ARC_POOL_CAMERA, 7, 1) 38 39 let prng: *i64 = (sys_mmap(8)) as *i64 40 nx_prng_init(prng, 1234) 41 42 // === Test 1: canonical resolver fills DirectorsNote ============ 43 let dn: *DirectorsNote = nx_dn_alloc() 44 // Caller pre-fills character + archetype (resolve doesn't invent them) 45 dn.character_id = 7 46 dn.body_archetype_id = 9 47 let usc: nx_int = nx_moment_resolve(arc, 0, 0 - 1, prng, dn) 48 49 // Arc identity propagated 50 if dn.arc_id != 1 { return 1 } 51 if dn.arc_stage != 1 { return 2 } 52 if dn.moment_seq != 0 { return 3 } // prev_seq = -1 -> seq = 0 53 if dn.prev_moment_seq != 0 - 1 { return 4 } 54 55 // Provenance: external-system arc-driven 56 if dn.director_role != NX_DR_EXTERNAL_SYSTEM { return 5 } 57 if dn.source_tag != NX_ST_STAGE_POOL { return 6 } 58 if dn.content_domain != NX_CD_MUNDANE { return 7 } 59 60 // Location picked from {100, 101} 61 if dn.location_id < 100 { return 10 } 62 if dn.location_id > 101 { return 11 } 63 64 // Outfit picked from {200, 201} 65 if dn.outfit_key_id < 200 { return 12 } 66 if dn.outfit_key_id > 201 { return 13 } 67 68 // Pose picked from {300, 301} 69 if dn.pose_id < 300 { return 14 } 70 if dn.pose_id > 301 { return 15 } 71 72 // Camera picked from {500} 73 if dn.camera_motivation_id != 500 { return 16 } 74 75 // Affect: expression pool had NX_AF_FOCUSED -> dn.affect_axis = FOCUSED 76 if dn.affect_axis != NX_AF_FOCUSED { return 17 } 77 78 // Arousal in [300, 512] 79 if dn.arousal_q10 < 300 { return 18 } 80 if dn.arousal_q10 > 512 { return 19 } 81 82 // Awareness UNAWARE -> Perspective VOYEUR 83 if dn.perspective != NX_PERSP_VOYEUR { return 20 } 84 if dn.shot_size != NX_SHOT_MS { return 21 } 85 86 // Quality starts UNVERIFIED + GOOD-tier-by-default 87 if dn.award_class != NX_DN_AWARD_UNVERIFIED { return 22 } 88 if dn.preflight_tier != NX_PFT_GOOD { return 23 } 89 if dn.refine_iteration != 0 { return 24 } 90 91 // Validation: character + body_archetype pre-set; all pools filled 92 // -> UNDERSPEC_NONE. 93 if usc != NX_DN_UNDERSPEC_NONE { return 25 } 94 if dn.underspec_code != NX_DN_UNDERSPEC_NONE { return 26 } 95 96 // === Test 2: determinism — same seed -> same picks ============= 97 let prng2: *i64 = (sys_mmap(8)) as *i64 98 nx_prng_init(prng2, 1234) 99 let dn2: *DirectorsNote = nx_dn_alloc() 100 dn2.character_id = 7 101 dn2.body_archetype_id = 9 102 nx_moment_resolve(arc, 0, 0 - 1, prng2, dn2) 103 if dn2.location_id != dn.location_id { return 30 } 104 if dn2.outfit_key_id != dn.outfit_key_id { return 31 } 105 if dn2.pose_id != dn.pose_id { return 32 } 106 if dn2.arousal_q10 != dn.arousal_q10 { return 33 } 107 108 // === Test 3: different seed -> at least one different pick ===== 109 let prng3: *i64 = (sys_mmap(8)) as *i64 110 nx_prng_init(prng3, 5678) 111 let dn3: *DirectorsNote = nx_dn_alloc() 112 dn3.character_id = 7 113 dn3.body_archetype_id = 9 114 nx_moment_resolve(arc, 0, 0 - 1, prng3, dn3) 115 // PRNG-driven picks should diverge — at least one of these differs. 116 var any_diff: nx_int = 0 117 if dn3.location_id != dn.location_id { any_diff = 1 } 118 if dn3.outfit_key_id != dn.outfit_key_id { any_diff = 1 } 119 if dn3.pose_id != dn.pose_id { any_diff = 1 } 120 if dn3.arousal_q10 != dn.arousal_q10 { any_diff = 1 } 121 if any_diff != 1 { return 40 } 122 123 // === Test 4: companion-authored variant ======================== 124 // 125 // Elara picks EROTIC domain + PLAYFUL_BRATTY affect; resolver 126 // pulls the rest from the arc's pools. 127 let prng4: *i64 = (sys_mmap(8)) as *i64 128 nx_prng_init(prng4, 9999) 129 let dn4: *DirectorsNote = nx_dn_alloc() 130 dn4.character_id = 42 131 dn4.body_archetype_id = 9 132 let usc4: nx_int = nx_moment_resolve_companion(arc, 0, 42, 133 NX_CD_EROTIC, 134 NX_AF_PLAYFUL_BRATTY, 135 prng4, dn4) 136 137 if dn4.director_role != NX_DR_COMPANION { return 50 } 138 if dn4.source_tag != NX_ST_COMPANION_DECISION { return 51 } 139 if dn4.character_id != 42 { return 52 } 140 if dn4.content_domain != NX_CD_EROTIC { return 53 } 141 if dn4.affect_axis != NX_AF_PLAYFUL_BRATTY { return 54 } 142 // Pool picks still came from arc 143 if dn4.location_id < 100 { return 55 } 144 if dn4.location_id > 101 { return 56 } 145 if usc4 != NX_DN_UNDERSPEC_NONE { return 57 } 146 147 // === Test 5: arousal pinned when min == max ==================== 148 // 149 // Build a degenerate stage with min == max; arousal should return 150 // exactly that value regardless of prng. 151 let arc2: *Arc = nx_arc_alloc(2, 1, NX_AS_AUTHORED, NX_CD_INTIMATE, 8) 152 nx_arc_pool_set(arc2, 0, 100) 153 nx_arc_pool_set(arc2, 1, 200) 154 nx_arc_pool_set(arc2, 2, 300) 155 nx_arc_pool_set(arc2, 3, NX_AF_INTIMATE) 156 nx_arc_pool_set(arc2, 4, 500) 157 nx_arc_stage_set_basic(arc2, 0, 158 1, NX_AW_AWARE, NX_ET_TENDER, 159 614, 614, 5) 160 nx_arc_stage_set_pool(arc2, 0, NX_ARC_POOL_LOCATION, 0, 1) 161 nx_arc_stage_set_pool(arc2, 0, NX_ARC_POOL_OUTFIT, 1, 1) 162 nx_arc_stage_set_pool(arc2, 0, NX_ARC_POOL_POSE, 2, 1) 163 nx_arc_stage_set_pool(arc2, 0, NX_ARC_POOL_EXPRESSION, 3, 1) 164 nx_arc_stage_set_pool(arc2, 0, NX_ARC_POOL_CAMERA, 4, 1) 165 166 let prng5: *i64 = (sys_mmap(8)) as *i64 167 nx_prng_init(prng5, 1) 168 let dn5: *DirectorsNote = nx_dn_alloc() 169 dn5.character_id = 1 170 dn5.body_archetype_id = 1 171 nx_moment_resolve(arc2, 0, 5, prng5, dn5) 172 if dn5.arousal_q10 != 614 { return 60 } 173 if dn5.moment_seq != 6 { return 61 } 174 if dn5.prev_moment_seq != 5 { return 62 } 175 // Awareness AWARE -> CANDID 176 if dn5.perspective != NX_PERSP_CANDID { return 63 } 177 // Tone TENDER (no expression pool -> falls through to tone) -- but 178 // here we DID set expression pool to NX_AF_INTIMATE, so affect 179 // becomes INTIMATE. 180 if dn5.affect_axis != NX_AF_INTIMATE { return 64 } 181 182 // === Test 6: tone fallback when expression pool absent ========= 183 // 184 // Build a stage with NO expression pool; affect should derive 185 // from emotional_tone via _tone_to_affect. TONE=TRIUMPHANT 186 // -> AFFECT=JOYFUL. 187 let arc3: *Arc = nx_arc_alloc(3, 1, NX_AS_AUTHORED, NX_CD_NARRATIVE, 4) 188 nx_arc_pool_set(arc3, 0, 100) 189 nx_arc_pool_set(arc3, 1, 200) 190 nx_arc_pool_set(arc3, 2, 300) 191 nx_arc_pool_set(arc3, 3, 500) 192 nx_arc_stage_set_basic(arc3, 0, 193 1, NX_AW_DIRECT_ENGAGEMENT, NX_ET_TRIUMPHANT, 194 700, 900, 1) 195 nx_arc_stage_set_pool(arc3, 0, NX_ARC_POOL_LOCATION, 0, 1) 196 nx_arc_stage_set_pool(arc3, 0, NX_ARC_POOL_OUTFIT, 1, 1) 197 nx_arc_stage_set_pool(arc3, 0, NX_ARC_POOL_POSE, 2, 1) 198 nx_arc_stage_set_pool(arc3, 0, NX_ARC_POOL_CAMERA, 3, 1) 199 // expression pool intentionally NOT set (offset/len stay 0/0) 200 201 let prng6: *i64 = (sys_mmap(8)) as *i64 202 nx_prng_init(prng6, 7) 203 let dn6: *DirectorsNote = nx_dn_alloc() 204 dn6.character_id = 1 205 dn6.body_archetype_id = 1 206 nx_moment_resolve(arc3, 0, 0, prng6, dn6) 207 208 // Awareness DIRECT_ENGAGEMENT -> POV_BOYFRIEND 209 if dn6.perspective != NX_PERSP_POV_BOYFRIEND { return 70 } 210 // Tone TRIUMPHANT -> Affect JOYFUL (via _tone_to_affect) 211 if dn6.affect_axis != NX_AF_JOYFUL { return 71 } 212 if dn6.content_domain != NX_CD_NARRATIVE { return 72 } 213 214 // === Test 7: underspec when character + archetype missing ===== 215 // 216 // Don't pre-fill character_id or body_archetype_id -- resolve 217 // should still run but the validator should flag missing required 218 // fields. 219 let prng7: *i64 = (sys_mmap(8)) as *i64 220 nx_prng_init(prng7, 11) 221 let dn7: *DirectorsNote = nx_dn_alloc() 222 let usc7: nx_int = nx_moment_resolve(arc, 0, 0, prng7, dn7) 223 // character_id and body_archetype_id are still 0 224 if usc7 == NX_DN_UNDERSPEC_NONE { return 80 } // must report something 225 // dn7.underspec_code must equal what nx_moment_resolve returned 226 if dn7.underspec_code != usc7 { return 81 } 227 228 return 0 229}