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}