code wiki / _hdl_build / nx_game_critic.nx

nx_game_critic.nx source

↩ module page · 150 lines · 6991 B

1// nx_game_critic.nx -- the REAL experiential QUALITY critic (not a floor check). nx_frame_sanity only 2// proves a frame isn't BROKEN (not blown/void, has some edges); it green-lit toy games (flat discs on an 3// empty starfield) as if they were good. That is the self-flattery the output-reality doctrine forbids. 4// This critic measures whether a rendered game frame reads as RICH vs TOY, on dimensions that actually 5// separate them, and returns an honest verdict TOY/BASIC/RICH/SOTA. You CLIMB against this, never the 6// eyeball's flattery. Calibrated so a flat-primitive frame scores TOY and a detailed/shaded/textured 7// frame scores higher -- the gate mutation-proves it distinguishes them. 8// Dimensions (all integer, on a packed-pixel i64 fb = swgpu/game_raster format): 9// [0] palette -- distinct quantized colours (5-bit/chan). Flat fills ~ a dozen; rich art >> 40. 10// [1] detail1 -- fine 1px luma-step density permil (texture/edges up close). 11// [2] detail4 -- 4px luma-step density permil (structure at a coarse scale -- real composition). 12// [3] gradient -- pixels inside a SMOOTH shade ramp permil (lighting/depth, not flat colour blocks). 13// [4] coverage -- non-background pixels permil (a filled scene vs mostly-empty void). 14// LIB, no main. license_tier: ORIGINAL 15import "nx_syscalls.nx" 16const GC_MAGIC_4096: i64 = 4096 17 18const GC_N: i64 = 6 19const GC_M_PAL: i64 = 0 20const GC_M_DET1: i64 = 1 21const GC_M_DET4: i64 = 2 22const GC_M_GRAD: i64 = 3 23const GC_M_COV: i64 = 4 24const GC_M_COHERE: i64 = 5 // spatial coherence: fraction of adjacent pairs that are locally smooth. 25 // real art is coherent (fills, gradients, shapes); NOISE is not -- this is 26 // what stops random churn from scoring RICH (the red-team fooled the old critic). 27// verdict tiers (composite score 0..1000) 28const GC_TOY: i64 = 300 29const GC_BASIC: i64 = 550 30const GC_RICH: i64 = 780 31 32func gc_lum(v: i64) -> i64 { return ((v & 0xff)*2 + ((v>>8)&0xff)*5 + ((v>>16)&0xff)) / 8 } 33 34// score fb (w*h packed i64) into out[GC_N] 35func gc_score(fb: *i64, w: i64, h: i64, out: *i64) -> i64 { 36 let n: i64 = w*h 37 // background = the corner pixel (renderers clear to it); coverage = pixels != bg 38 let bg: i64 = fb[0] & 0xFFFFFF 39 // palette: distinct 15-bit quantized colours via a 32768-bit set (4096 bytes) 40 let seen: *u8 = sys_mmap(GC_MAGIC_4096) 41 var z: i64=0 42 while z<GC_MAGIC_4096 { seen[z]=0 as u8; z=z+1 } 43 var pal: i64=0 44 var cov: i64=0 45 var i: i64=0 46 while i<n { 47 let v: i64 = fb[i] & 0xFFFFFF 48 if v != bg { cov = cov + 1 } 49 let q: i64 = ((v>>3)&0x1f) | (((v>>11)&0x1f)<<5) | (((v>>19)&0x1f)<<10) 50 let byte: i64 = q>>3 51 let bit: i64 = q & 7 52 let cur: i64 = seen[byte] & 0xff 53 if ((cur>>bit)&1)==0 { seen[byte]=(cur | (1<<bit)) as u8; pal=pal+1 } 54 i=i+1 55 } 56 // detail1 (horiz 1px), detail4 (horiz 4px), gradient (smooth mid steps) 57 var det1: i64=0 58 var det4: i64=0 59 var grad: i64=0 60 var cohere: i64=0 61 var hcnt: i64=0 62 var y: i64=0 63 while y<h { 64 var x: i64=0 65 while x<w { 66 let o: i64 = y*w+x 67 if x+1<w { 68 hcnt=hcnt+1 69 let l0: i64 = gc_lum(fb[o]) 70 let l1: i64 = gc_lum(fb[o+1]) 71 var d: i64 = l0-l1 72 if d<0 { d=0-d } 73 if d>=16 { det1=det1+1 } 74 if d>=3 { if d<=18 { grad=grad+1 } } 75 if d<=6 { cohere=cohere+1 } // locally smooth pair (coherent structure) 76 } 77 if x+4<w { 78 let la: i64 = gc_lum(fb[o]) 79 let lb: i64 = gc_lum(fb[o+4]) 80 var d4: i64 = la-lb 81 if d4<0 { d4=0-d4 } 82 if d4>=20 { det4=det4+1 } 83 } 84 x=x+1 85 } 86 y=y+1 87 } 88 if hcnt<1 { hcnt=1 } 89 out[GC_M_PAL] = pal 90 out[GC_M_DET1] = det1*1000/hcnt 91 out[GC_M_DET4] = det4*1000/hcnt 92 out[GC_M_GRAD] = grad*1000/hcnt 93 out[GC_M_COV] = cov*1000/n 94 out[GC_M_COHERE] = cohere*1000/hcnt 95 return 0 96} 97 98// composite quality score 0..1000 from the 5 dimensions (each capped to its SOTA target then weighted). 99func gc_capw(v: i64, target: i64, weight: i64) -> i64 { 100 var c: i64 = v 101 if c>target { c=target } 102 return c*weight/target 103} 104func gc_quality(out: *i64) -> i64 { 105 // targets = what a rich frame reaches; weights sum to 1000 106 let p: i64 = gc_capw(out[GC_M_PAL], 120, 240) // palette richness 107 let d1: i64 = gc_capw(out[GC_M_DET1], 120, 200) // fine detail 108 let d4: i64 = gc_capw(out[GC_M_DET4], 90, 200) // coarse structure 109 let g: i64 = gc_capw(out[GC_M_GRAD], 260, 220) // smooth shading 110 let c: i64 = gc_capw(out[GC_M_COV], 520, 140) // coverage 111 var total: i64 = p+d1+d4+g+c 112 // ★RED-TEAM HARDENING: an INCOHERENT frame (random churn) is not art no matter how busy. Real fills 113 // and gradients score coherence >> 450; random noise scores ~50. Cap incoherent frames below TOY so 114 // busyness can never masquerade as quality (the attack that scored SOTA noise on the old critic). 115 if out[GC_M_COHERE] < 450 { if total > GC_TOY-1 { total = GC_TOY-1 } } 116 return total 117} 118func gc_verdict(score: i64) -> *u8 { 119 if score < GC_TOY { return "TOY" as *u8 } 120 if score < GC_BASIC { return "BASIC" as *u8 } 121 if score < GC_RICH { return "RICH" as *u8 } 122 return "SOTA" as *u8 123} 124 125// ---- graphics-ladder instrument (2026-08-01): the UNSATURATED headline -------------------------- 126// gc_quality clamps every dimension at its target, so every frame past the targets pins at 1000 -- 127// craft 983 vs Veloren 1000 became unmeasurable and the ruler could no longer register work. 128// gc_quality2 keeps the old score as its BASE (the historical 684->865->983 series stays comparable, 129// rule 19: never silently redefine a published number) and adds a DIMINISHING headroom bonus per 130// dimension: bonus = weight*excess/(excess+target). The hyperbola is asymptotic, so the 2000 ceiling 131// is unreachable BY CONSTRUCTION -- the top of the scale can always spread, and "beat the reference" 132// is measurable again. The coherence red-team cap is inherited ABSOLUTELY: an incoherent frame gets 133// NO bonus whatsoever (noise has a huge palette; without this guard busyness would outscore art on 134// the extended scale exactly the way it once fooled the old critic). 135func gc_bonus(v: i64, target: i64, weight: i64) -> i64 { 136 if v <= target { return 0 } 137 let ex: i64 = v - target 138 return weight*ex/(ex + target) 139} 140func gc_quality2(out: *i64) -> i64 { 141 let base: i64 = gc_quality(out) 142 if out[GC_M_COHERE] < 450 { return base } 143 var b: i64 = 0 144 b = b + gc_bonus(out[GC_M_PAL], 120, 240) 145 b = b + gc_bonus(out[GC_M_DET1], 120, 200) 146 b = b + gc_bonus(out[GC_M_DET4], 90, 200) 147 b = b + gc_bonus(out[GC_M_GRAD], 260, 220) 148 b = b + gc_bonus(out[GC_M_COV], 520, 140) 149 return base + b 150}