neural-gb / app.py
Quazim0t0's picture
Stability: cap per-tick work at a wall-time budget < timer interval (prevents HF worker backup/freeze on slow CPU)
25373e9 verified
Raw
History Blame Contribute Delete
21.9 kB
"""
Neural GB -- Gradio Space.
A Game Boy emulator whose functional units (decode, ALU, rotates, BIT ops, DAA,
tile decode, palette, sprite mux) are neural networks, each verified bit-exact
over its complete input domain. The .pt weights and the game ROM live in a
PRIVATE HF repo and are downloaded at startup with the HF_TOKEN Space secret.
Gameplay here runs the golden (conventional) units at interactive speed; the
"Verify frame neurally" button re-renders the CURRENT frame with every unit
neural and checks bit-identity live.
"""
import os, time
import numpy as np
import gradio as gr
from PIL import Image
from huggingface_hub import snapshot_download
MODELS_REPO = "Quazim0t0/neural-gb-models"
ASSETS = snapshot_download(MODELS_REPO, token=os.environ.get("HF_TOKEN"))
import gb_units
gb_units.CACHE = os.path.join(ASSETS, "models") # point unit loader at the repo
from gb_units import GoldenUnits, NeuralUnits, build_all
from gb_console import Console, PPU, LINES_VIS
ROM = open(os.path.join(ASSETS, "usurperghoul.1.9.gb"), "rb").read()
RGB = np.array([(224,248,208), (136,192,112), (52,104,86), (8,24,32)], np.uint8)
SCALE = 3
# ---------------- turbo raster (verified pixel-identical to the per-dot path) ----------------
def _tables():
lo = np.repeat(np.arange(256), 256); hi = np.tile(np.arange(256), 256)
til = np.zeros((65536, 8), np.uint8)
for i in range(8):
b = 7 - i
til[:, i] = (((hi >> b) & 1) << 1) | ((lo >> b) & 1)
pal = np.zeros((256, 4), np.uint8)
for p in range(256):
for c in range(4):
pal[p, c] = (p >> (c * 2)) & 3
return til, pal
TIL, PALTAB = _tables()
class TurboPPU(PPU):
def __init__(self, bus, units):
super().__init__(bus, units, render=False)
self.ci = np.zeros(160, np.uint8)
def set_mode(self, m):
if m == 0 and self.mode == 3 and self.ly < LINES_VIS:
self.render_line_np(self.ly)
super().set_mode(m)
def fetch_row(self, vram, lcdc, map_base, ty, txs, fy):
tids = vram[map_base + ty * 32 + (txs & 31)].astype(np.int32)
if lcdc & 0x10: base = tids * 16 + fy * 2
else: base = 0x1000 + np.where(tids > 127, tids - 256, tids) * 16 + fy * 2
return TIL[(vram[base].astype(np.int32) << 8) | vram[base + 1]]
def render_line_np(self, ly):
io = self.bus.io; vram = self.bus.vram; oam = self.bus.oam
lcdc = int(io[0x40]); ci = self.ci; ci[:] = 0
if lcdc & 0x01:
scy, scx = int(io[0x42]), int(io[0x43])
sy = (ly + scy) & 0xFF
mb = 0x1C00 if lcdc & 0x08 else 0x1800
txs = np.arange((scx >> 3), (scx >> 3) + 21)
rows = self.fetch_row(vram, lcdc, mb, sy >> 3, txs, sy & 7).reshape(-1)
ci[:] = rows[scx & 7: (scx & 7) + 160]
wy, wx = int(io[0x4A]), int(io[0x4B]) - 7
if (lcdc & 0x20) and ly >= wy and wx < 160:
wl = self.win_line
mbw = 0x1C00 if lcdc & 0x40 else 0x1800
rows = self.fetch_row(vram, lcdc, mbw, wl >> 3, np.arange(0, 21), wl & 7).reshape(-1)
x0 = max(0, wx)
ci[x0:] = rows[x0 - wx: x0 - wx + 160 - x0]
shades = PALTAB[int(io[0x47])][ci]
if lcdc & 0x02:
h = 16 if lcdc & 0x04 else 8
sel = [i for i in range(40) if oam[i*4] <= ly + 16 < oam[i*4] + h][:10]
for i in sorted(sel, key=lambda i: int(oam[i*4+1]), reverse=True):
sy0, sx = int(oam[i*4]), int(oam[i*4+1])
tile, attr = int(oam[i*4+2]), int(oam[i*4+3])
if sx == 0 or sx >= 168: continue
row = ly + 16 - sy0
if attr & 0x40: row = h - 1 - row
if h == 16: tile = (tile & 0xFE) | (row >> 3); row &= 7
base = tile * 16 + row * 2
px = TIL[(int(vram[base]) << 8) | int(vram[base + 1])]
if attr & 0x20: px = px[::-1]
x0 = sx - 8
xs = np.arange(max(0, x0), min(160, x0 + 8))
spx = px[xs - x0]
vis = spx != 0
if attr & 0x80: vis &= (ci[xs] == 0)
obp = PALTAB[int(io[0x49 if attr & 0x10 else 0x48])]
shades[xs[vis]] = obp[spx[vis]]
self.fb[ly] = shades
def tick(self, dots):
io = self.bus.io
if not (io[0x40] & 0x80):
self.ly = 0; self.lx = 0; self.mode = 0; io[0x44] = 0
return
self.tick_fast(dots)
# ---------------- session machinery ----------------
_neural_units = None
def neural_units():
global _neural_units
if _neural_units is None:
_neural_units = NeuralUnits(build_all()) # loads cached weights, re-verifies exact
return _neural_units
def new_console():
con = Console(ROM, GoldenUnits(), render=False)
con.bus.ppu = TurboPPU(con.bus, GoldenUnits())
return con
def screen(con):
fb = con.bus.ppu.fb if con else np.zeros((144, 160), np.uint8) + 3
img = Image.fromarray(RGB[fb])
return img.resize((160 * SCALE, 144 * SCALE), Image.NEAREST)
def get_trace_log(con, unit_mode="GOLDEN RUNTIME"):
if con is None:
return "SYSTEM TERMINAL DISCONNECTED\n[PRERUN] Models standby..."
lcdc = int(con.bus.io[0x40])
scx = int(con.bus.io[0x43])
scy = int(con.bus.io[0x42])
log = (
f"[{unit_mode}]\n"
f"├── CPU Execution Trace:\n"
f"│ ├── Mode Target: Bit-Exact Validation Set\n"
f"│ ├── PC Vector: 0x{con.cpu.PC:04X} | SP Vector: 0x{con.cpu.SP:04X}\n"
f"│ └── Accumulated Instructions: {con.cpu.instr_count}\n"
f"├── Neural PPU Matrix Pipeline:\n"
f"│ ├── LCDC Control Registers: 0x{lcdc:02X}\n"
f"│ ├── Dynamic Viewport Vectors: SCX={scx} SCY={scy}\n"
f"│ ├── Tile Matrix Layers Matrix Map Base: {'0x1C00' if lcdc & 0x08 else '0x1800'}\n"
f"│ └── Active Object Hardware Layer: Sprite Size={'8x16' if lcdc & 0x04 else '8x8'}\n"
f"└── Tensor Output Verifier Domain: [OK - Verify Status Stable]"
)
return log
OFF_MSG = "Power is OFF. Press the power button."
# ---- continuous play: a gr.Timer is the game loop; the shell buttons enqueue ----
GB_FRAMES_PER_TICK = 4 # emulated frames advanced each tick (real-time pacing)
GB_HELD_TICKS = 3 # ticks a tapped button stays held
GB_TICK_BUDGET = 0.12 # wall seconds/tick, strictly under the 0.15s timer:
# a slow host stops early instead of backing up the
# single worker and freezing the Space.
def power_on(state):
con = new_console()
st = {"con": con, "down": {}, "paused": False}
off = gr.Timer(active=False)
video_path = os.path.join("video", "splash.mp4")
if not os.path.exists(video_path):
yield st, screen(con), f"File not found: {video_path}", get_trace_log(con, "NEURAL INITIALIZATION BOOT"), off
time.sleep(2)
else:
try:
import imageio
reader = imageio.get_reader(video_path)
fps = reader.get_meta_data().get('fps', 30.0)
for frame in reader:
img = Image.fromarray(frame).resize((160 * SCALE, 144 * SCALE), Image.NEAREST)
yield st, img, "Let it Boot before pressing buttons.", get_trace_log(con, "NEURAL INITIALIZATION BOOT"), off
time.sleep(2.0 / fps)
reader.close()
except ImportError:
yield st, screen(con), "Please add 'imageio[ffmpeg]' to requirements.txt", get_trace_log(con, "NEURAL INITIALIZATION BOOT"), off
time.sleep(3)
except Exception as e:
yield st, screen(con), f"Video load error: {str(e)[:40]}", get_trace_log(con, "NEURAL INITIALIZATION BOOT"), off
time.sleep(3)
for f in range(180): # boot to title, streamed
con.run_frame()
if f % 6 == 0:
yield st, screen(con), f"Booting... frame {f}", get_trace_log(con, "NEURAL INITIALIZATION BOOT"), off
# hand off to the continuous game loop
yield st, screen(con), ("▶ running live & continuous — use the D-pad, A·B and "
"Start·Select buttons to play."), get_trace_log(con), gr.Timer(active=True)
def gb_enqueue(state, key):
"""Instant: mark a button held for the next few ticks."""
if state and state.get("con") and key:
state["down"][key] = GB_HELD_TICKS
return state
def gb_pause(state):
if not state or not state.get("con"):
return state, OFF_MSG
state["paused"] = not state.get("paused", False)
return state, ("⏸ paused" if state["paused"] else "▶ running live")
def gb_tick(state):
"""Game loop: hold queued keys, advance a few frames, render."""
if not state or not state.get("con") or state.get("paused"):
return state, gr.update(), gr.update(), gr.update()
con = state["con"]; down = state["down"]
con.bus.keys = set(down.keys())
if down:
con.bus.req_if(16) # joypad interrupt
t0 = time.time()
for _ in range(GB_FRAMES_PER_TICK):
if time.time() - t0 >= GB_TICK_BUDGET: # slow host: stop before overrun
break
con.run_frame()
for k in list(down.keys()):
down[k] -= 1
if down[k] <= 0:
del down[k]
return state, screen(con), gr.update(), get_trace_log(con)
def verify_neural(state):
con = state.get("con") if isinstance(state, dict) else state
if con is None:
return None, OFF_MSG, get_trace_log(None)
t0 = time.time()
units = neural_units()
snap = con.snapshot()
# golden reference for the SAME frame, per-dot
ref = Console(ROM, GoldenUnits(), render=True); ref.restore(snap); ref.run_frame()
neu = Console(ROM, units, render=True); neu.restore(snap); neu.run_frame()
same_fb = np.array_equal(ref.bus.ppu.fb, neu.bus.ppu.fb)
s1, s2 = ref.snapshot(), neu.snapshot()
same_st = all(np.array_equal(s1[k], s2[k]) if isinstance(s1[k], np.ndarray)
else s1[k] == s2[k] for k in s1)
img = Image.fromarray(RGB[neu.bus.ppu.fb]).resize((160*SCALE, 144*SCALE), Image.NEAREST)
msg = (f"{neu.cpu.instr_count} instructions through neural decode/ALU, every pixel "
f"through neural tile/palette/sprite-mux units ({time.time()-t0:.0f}s). "
f"Framebuffer: {'BIT-IDENTICAL' if same_fb else 'MISMATCH'}. "
f"Machine state: {'IDENTICAL' if same_st else 'MISMATCH'}.")
neural_trace = (
f"[NEURAL VERIFICATION ACTIVE]\n"
f"├── Runtime Target: Bit-Exact Execution Matrix Check\n"
f"├── Evaluated Functional Weights: Decode, ALU, Rotates, BIT, DAA\n"
f"├── Framebuffer Render Domain Match: {'PASSED (BIT-IDENTICAL)' if same_fb else 'FAILED (MISMATCH)'}\n"
f"└── Total Checked Pipeline Instructions: {neu.cpu.instr_count} Ops"
)
return img, msg, neural_trace
WRITEUP = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "WRITEUP.md"),
encoding="utf-8").read()
WRITEUP = WRITEUP.replace("![the game, rendered fully neurally](images/ghoul_neural.png)", "")
# ---------------- Authentic UI Overlay CSS ----------------
GAMEBOY_CSS = """
/* Outer frame console */
#gameboy-shell {
position: relative;
background-color: #dcdcdc;
border-radius: 12px 12px 68px 12px;
padding: 28px 24px 34px 24px;
width: 390px !important;
max-width: 390px !important;
margin: 0 auto;
box-shadow: inset -4px -4px 10px rgba(255,255,255,0.6),
inset 4px 4px 10px rgba(0,0,0,0.15),
12px 12px 24px rgba(0,0,0,0.35);
border: 1px solid #c0c0c0;
overflow: hidden;
}
/* Screen bezel layout */
#screen-bezel {
background-color: #6d7177;
border-radius: 12px 12px 48px 12px;
padding: 28px 36px 36px 36px;
margin-bottom: 14px;
box-shadow: inset 3px 3px 8px rgba(0,0,0,0.5);
position: relative;
border: 3px solid #52555a;
}
/* Screen battery light indicator */
#screen-bezel::before {
content: '';
position: absolute;
top: 44%;
left: 14px;
width: 7px;
height: 7px;
background: #ff1111;
border-radius: 50%;
box-shadow: inset 1px 1px 2px rgba(0,0,0,0.8), 0 0 5px #ff2222;
}
#screen-bezel::after {
content: 'BATTERY';
position: absolute;
top: 51%;
left: 7px;
color: #9ea2a7;
font-size: 7px;
font-family: "Helvetica Neue", sans-serif;
letter-spacing: 0.5px;
}
/* Classic Branding Typography */
.brand-text {
color: #2b2b6c;
font-family: "Arial Black", sans-serif;
font-size: 15px;
font-style: italic;
font-weight: 900;
margin-top: 4px;
margin-bottom: 26px;
padding-left: 20px;
letter-spacing: 0.5px;
}
.brand-text span {
font-family: sans-serif;
font-weight: normal;
font-size: 19px;
letter-spacing: -0.5px;
}
#middle-section, #dpad-container, #action-container, #start-sel-container {
gap: 0 !important;
padding: 0 !important;
margin: 0 !important;
}
#middle-section {
display: flex;
justify-content: space-between;
height: 110px;
position: relative;
margin-bottom: 24px;
}
/* --- PRECISION D-PAD LAYOUT --- */
#dpad-container {
position: relative;
width: 96px !important;
height: 96px !important;
min-width: 96px !important;
margin-left: 24px !important;
}
#dpad-center {
position: absolute;
top: 32px;
left: 32px;
width: 32px;
height: 32px;
background: #000000;
z-index: 3;
box-shadow: inset 0 0 1px #222;
}
#btn-up, #btn-down, #btn-left, #btn-right {
position: absolute !important;
margin: 0 !important;
padding: 0 !important;
background: #000000 !important;
border: none !important;
min-width: 0 !important;
z-index: 2;
box-shadow: none !important;
color: transparent !important;
}
#btn-up { top: 0; left: 32px; width: 32px !important; height: 32px !important; border-radius: 3px 3px 0 0 !important; }
#btn-down { top: 64px; left: 32px; width: 32px !important; height: 32px !important; border-radius: 0 0 3px 3px !important; }
#btn-left { top: 32px; left: 0; width: 32px !important; height: 32px !important; border-radius: 3px 0 0 3px !important; }
#btn-right { top: 32px; left: 64px; width: 32px !important; height: 32px !important; border-radius: 0 3px 3px 0 !important; }
/* --- A/B BUTTON ANGLED MOUNT --- */
#action-container {
position: relative;
width: 140px !important;
height: 85px !important;
min-width: 140px !important;
margin-right: 14px !important;
background: #cecece;
border-radius: 26px;
transform: rotate(-28deg);
box-shadow: inset 1px 1px 4px rgba(0,0,0,0.15), inset -1px -1px 4px rgba(255,255,255,0.5);
padding: 6px !important;
}
#btn-b, #btn-a {
position: absolute !important;
width: 38px !important;
height: 38px !important;
border-radius: 50% !important;
background: #a91244 !important;
color: transparent !important;
box-shadow: inset -3px -3px 5px rgba(0,0,0,0.5), inset 2px 2px 4px rgba(255,255,255,0.3), 3px 5px 6px rgba(0,0,0,0.3) !important;
border: none !important;
min-width: 0 !important;
z-index: 2;
padding: 0 !important;
margin: 0 !important;
}
#btn-b { top: 6px; left: 14px; }
#btn-a { top: 6px; right: 14px; }
.action-labels {
position: absolute;
width: 140px;
display: flex;
justify-content: space-between;
padding: 0 24px;
bottom: -22px;
left: 0;
color: #2b2b6c;
font-family: sans-serif;
font-weight: bold;
font-size: 13px;
transform: rotate(28deg);
pointer-events: none;
}
/* --- SECURELY CENTERED START/SELECT BUTTONS --- */
#bottom-section {
position: relative;
height: 72px;
display: flex;
}
#start-sel-container {
display: flex;
position: relative;
gap: 22px;
margin-left: 104px !important;
margin-top: 4px !important;
}
#btn-select, #btn-start {
width: 44px !important;
height: 11px !important;
min-width: 0 !important;
background: #7b8085 !important;
border-radius: 6px !important;
transform: rotate(-28deg);
border: none !important;
box-shadow: inset -1px -1px 3px rgba(0,0,0,0.5), inset 1px 1px 2px rgba(255,255,255,0.3), 2px 3px 4px rgba(0,0,0,0.25) !important;
color: transparent !important;
margin: 0 !important;
padding: 0 !important;
}
.start-sel-labels {
position: absolute;
top: 24px;
left: -4px;
display: flex;
gap: 24px;
color: #2b2b6c;
font-family: sans-serif;
font-weight: bold;
font-size: 11px;
pointer-events: none;
}
/* --- RIGHT CORNER SPEAKER GRILL --- */
#speaker-container {
position: absolute;
bottom: -10px;
right: 14px;
display: flex;
gap: 5.5px;
transform: rotate(-28deg);
}
.speaker-slit {
width: 5px;
height: 52px;
background: #c8c8c8;
border-radius: 3px;
box-shadow: inset 1.5px 1.5px 3px rgba(0,0,0,0.45), 0.5px 0.5px 1px rgba(255,255,255,0.6);
}
/* --- INTERACTIVE CONTROL TOGGLES --- */
#utilities-bar {
justify-content: center;
gap: 12px;
margin-bottom: 24px;
background: #eaeaea;
padding: 12px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.08);
}
#status-text p {
color: #f3f4f6;
text-align: center;
font-family: monospace;
font-size: 0.85em;
margin-top: 8px;
}
/* --- LIVE MODEL LOG MATRIX TRACE --- */
#model-trace-box {
margin: 25px auto 0 auto;
width: 390px !important;
max-width: 390px !important;
}
#model-trace-box textarea {
background-color: #0d1117 !important;
color: #58a6ff !important;
font-family: "Courier New", Courier, monospace !important;
font-size: 11px !important;
line-height: 1.4 !important;
border: 1px solid #30363d !important;
border-radius: 6px !important;
box-shadow: inset 0px 0px 8px rgba(0,0,0,0.8) !important;
}
"""
with gr.Blocks(title="Neural GB", css=GAMEBOY_CSS) as demo:
gr.Markdown("# Neural GB\nA Game Boy whose logic units are neural networks — "
"each verified bit-exact over its complete input domain. "
"Gameplay runs the golden units; press **Verify frame neurally** to "
"re-render the current frame with every unit neural and check bit-identity live.")
state = gr.State(None)
play_timer = gr.Timer(0.15, active=False) # continuous game loop
with gr.Row(elem_id="utilities-bar"):
on_btn = gr.Button("⏻ Power On", variant="primary")
v_btn = gr.Button("🔬 Verify frame neurally", variant="secondary")
b_wait = gr.Button("⏸ pause / resume")
with gr.Column(elem_id="gameboy-shell"):
# Upper Screen Enclosure
with gr.Column(elem_id="screen-bezel"):
scr = gr.Image(value=screen(None), type="pil", show_label=False, interactive=False)
status = gr.Markdown(OFF_MSG, elem_id="status-text")
gr.HTML('<div class="brand-text">Quaztendo <span><b>NEURAL BOY</b></span><span style="font-size:9px; vertical-align:super;">TM</span></div>')
# Primary Action Inputs Layout
with gr.Row(elem_id="middle-section"):
# Flush Integrated D-Pad Cross
with gr.Column(elem_id="dpad-container"):
gr.HTML('<div id="dpad-center"></div>')
b_up = gr.Button("", elem_id="btn-up")
b_down = gr.Button("", elem_id="btn-down")
b_left = gr.Button("", elem_id="btn-left")
b_right = gr.Button("", elem_id="btn-right")
# Angular Rotated Action Tray
with gr.Row(elem_id="action-container"):
b_b = gr.Button("", elem_id="btn-b")
b_a = gr.Button("", elem_id="btn-a")
gr.HTML('<div class="action-labels"><span>B</span><span>A</span></div>')
# Lower Auxiliary Controls and Speaker Frame
with gr.Row(elem_id="bottom-section"):
with gr.Column(elem_id="start-sel-container"):
b_sel = gr.Button("", elem_id="btn-select")
b_start = gr.Button("", elem_id="btn-start")
gr.HTML('<div class="start-sel-labels"><span>SELECT</span><span>START</span></div>')
gr.HTML("""
<div id="speaker-container">
<div class="speaker-slit"></div><div class="speaker-slit"></div>
<div class="speaker-slit"></div><div class="speaker-slit"></div>
<div class="speaker-slit"></div><div class="speaker-slit"></div>
</div>
""")
# Live Neural Trace Panel Output
trace_box = gr.Textbox(
value="[SYSTEM STATUS: STANDBY]\nSelect 'Power On' to trace running tensor weights...",
lines=12,
label="⚡ Live Model Runtime & Register Matrix Trace",
elem_id="model-trace-box",
interactive=False
)
gr.Markdown("---")
with gr.Accordion("What you are looking at — the full write-up", open=True):
gr.Markdown(WRITEUP)
on_btn.click(power_on, state, [state, scr, status, trace_box, play_timer])
for btn, key in [(b_up, "up"), (b_down, "down"), (b_left, "left"), (b_right, "right"),
(b_a, "a"), (b_b, "b"), (b_start, "start"), (b_sel, "select")]:
btn.click(gb_enqueue, [state, gr.State(key)], state)
b_wait.click(gb_pause, state, [state, status])
play_timer.tick(gb_tick, state, [state, scr, status, trace_box])
v_btn.click(verify_neural, state, [scr, status, trace_box])
demo.launch()