ABA Games Variations — KNIGHT VS. PAWNS 7
Fable 5 high
Summary
The ship session: KNIGHT VS. PAWNS was fully integrated with a327ex.com and PUBLISHED as "Reincarnated As The Only Piece That Can L Jump, Mighty And Powerful And Dashingly Handsome Horse Effortlessly Tramples One Thousand Pawns For All To Witness" — fullscreen game page, homepage message, Windows single-exe download, scoreboard/feedback/email/analytics/error-reporting, admin GAMES panel — followed by launch-night live ops (bug fixes from real player feedback) and the start of a fire-particles explainer post with inline interactive demos.
Server: KVP3 sanity-tier replay validation (games.lua rewrite):
- The KVP1 full-mirror philosophy died on contact with the item era: score re-derives through the seeded grng stream + all 30 item behaviors (chain relocations, Boom splash, ally trades, Coin ×3, Coffee wall-clock, transforms changing legality), so a server mirror would drift and REJECT HONEST RUNS. Replaced with strict-format + structural invariants: march-interval RANGE checks, spawn gap + aggregate floor (≥40% of the time-ramp integral), commit-rate ceiling, score ≤ 3×spawns (Coin's triple is the only multiplier), duration consistency.
- True validation moved offline: tools/audit_scores.py pulls board replays and re-simulates through the real game (--verify=check); verify.lua now skips ABSENT expectation fields so score/dur-only audit files work.
- Caps for 100KB+ item-era replays: replay 400k, submit body 512k (api.lua), nginx location 640k. Validated on the VPS with real Lua 5.4 against 26 real replays + negative forgeries.
Publish pipeline: game page, pixel style, packaging:
Fullscreen: yesfrontmatter → doc.game_page → renderer view 'game' (admin-view pattern): integer viewport fit, full-bleed strip, ← a327ex crumb + title drawn POST-composite, LanaPixel title band. Every existing link to the post's path lands on the fullscreen view.<span class="pixel">inline style (LanaPixel 22 from the game package; pick_cjk_font honors style.pixel so FULLWIDTH headers stay pixel — sync subset extended with U+FF00–FFEF + U+3000). Homepage message: [GAME PROTOTYPE][date] header + pixel title link + DOWNLOAD (Windows) below the game. Converter gained one-level nested-bracket links[[TAG] text](url).- ⚠ REUSABLE: a hard-break in a message's first element reached the feed preview; %q serialized it MULTI-LINE in index.lua and build-web.sh's line-oriented message grep missed the slug → body silently unbundled → the whole message rendered EMPTY on web only. convert.lua now flattens previews to one line.
- Windows package: tools/package_win.py → ONE 13.2MB exe (game zip APPENDED to the engine; zip_init(argv[0]) VFS). Engine gained
build.bat novideo(/DANCHOR_NO_VIDEO stubs the video subsystem — no 116MB FFmpeg DLLs; /DELAYLOAD is impossible: BtbN import libs are GNU-format, invisible to MSVC delay-load). Desktop window controls: scale cycle + FULL toggle (runtime engine_set_fullscreen, engine_get_fullscreen, engine_set_window_scale). Full-title filename; hosted at /u/ (access_log off — downloads unmeasurable).
Engine fixes (Anchor3, committed):
- ⭐ global_rng was COMPILE-TIME CONSTANTS — every boot replayed the identical default sequence (the owner's "always Snow first" report). Entropy-seeded at engine_init above the headless branch. Replays unaffected (explicit seeds).
- web_http_post keepalive dropped ≥60KB bodies (fetch spec caps keepalive at 64KiB — large POSTs threw synchronously and vanished); then web_http_post_poll(id,url,body,token) added — pollable POST through the __httpRes drain for submits that must not be lost.
Game features: gate, angel hold, feedback, email, analytics, errors:
- Intro gate: CLICK TO START scrim, first click consumed (the website focus-grab click could commit a move). ⚠ The scrim must not draw into an outline=true emoji-pipeline layer — the outline derivation binarizes a translucent full-screen rect into an OPAQUE slab.
- Guardian Angel input hold (angel_hold): no aim/commits through the 3s revive.
- Feedback box: game-over modal (multi-line, ui_typed_text machinery) → POST /api/feedback with run context; net_post shim = hosted web fetch w/ device token, desktop WinHTTP tokenless; shared kvp_json encoder.
- Auto error reports: game_host xpcall+traceback → game_report_error → kind='error' feedback carrying the run's replay-so-far (its own repro). Deduped, ≤3/session.
- Email capture: death-screen bottom frame ("Liked the game? Add your e-mail to get told about updates."), POST /api/updates → emails table (UNIQUE NOCASE, plaintext-by-necessity, addresses never logged). Migrations 13 (feedback) + 14 (emails).
- Analytics: game_host injects site_game_event → knightvspawns_run_start {seed} / item_pick {item,score,t} / run_end {reason,score,dur,hp,items[]}; admin GAMES panel (runs/players/time/retention/per-item with-without deltas) + feedback reader + include-my-runs toggle (owner devices excluded by default).
Balance: the endless-survival fix (score-driven difficulty):
- March beat −0.08s per 100 SCORE, floor tuned 0.35→0.25→0.15→0.05→0.10 across playtests; spawn interval ÷ (1 + max(0,score−50)/285) hard floor 0.12 (offset added so nothing changes before 50, converging to the old curve at 1000); drop retier to the owner's 4/2/1 lists; Crown/Castle 10 moves; Egg/Chick every 12; Snow every 12; Snail 1-in-8; Comet every 8 ("Comet is the problem"); coins expire after 10 beats (accelerating blink-out, draw-side u² phase) and TRADE with enemy pawns instead of damming columns (item-carriers still bump). Target: full build dies ~1000 — achieved in the owner's hands pre-ship.
- ⚠ Every balance/weight change SHIFTS THE GRNG STREAM: old replays/fixtures desync. Regen verify fixtures per tuning pass; scoreboard wiped at ship.
- DEBUG_FIRST_SEED dev pin (seed 55 = Fire-first opener, found by exact grng replication of the first drop's two draws) — nilled at ship; packager asserts.
Launch night live ops (real players within the hour):
- First 10h: 124 sessions / 90 visitors (40 via the X post; referrers incl. blobfish.dev and a r/northernlion thread), 44 players, 144 runs started / 127 finished / 90 min playtime. The 1000-wall FELL day one: progress 1291 (AU), repetitive strain injury 1056+1004 (LV, 34 runs). Zero errors. 4 high-quality feedback notes; consensus from both maxed players: endgame = button mashing, items make the game play itself, fire dominant, water gun a trap.
- Bugs fixed from player reports: H key played the A/B hit-sound preview (tuner bind cluster wasn't hosted-gated); backspace repeat 100cps→22cps; trailing spaces invisible in the feedback box (wrap swallowed them).
- Scoreboard evolution driven by the owner's live use: slur filter (SHORT hard-slur list, leet-folded, deliberately not a profanity list — "retard should not" be blocked; spic/coon/dyke absent for false positives); owner-bypass for reserved names (device.owner flag, you.owner in the scores fetch); ARCADE semantics (migration 15: every run its own row — "Is that not how it works in arcades?"); re-signing renames the identical-replay newest row; pagination (< p/N > beside HIGH SCORES, limit+offset+total); pollable submit with 'save failed - press SAVE to retry' after two rounds of silent-loss debugging (the 64KiB keepalive cap — a 133-capture run under the new dense-beat balance exceeded it).
- ⚠ Process failure owned: a cleanup
DELETE FROM game_scores WHERE game='knightvspawns'ate the owner's real 93 run along with test rows. Target deletions by name/device only.
Endgame design + docs (for next sessions):
- endgame_design.md: A score-scaled commit cooldown (move timer as a resource; hit vs empty moves), B unequal pawns/elites (pawn HP), C item power routed through player actions, D the King finale (a run that can be WON), E player-owned time — "the march advances on your empty moves, captures don't" (the turn-based question's synthesis; full turn-based rejected as identity-breaking).
- infrastructure.md: the complete systems handbook (pipeline map, deploy runbook, gotchas, the KVP4 balance-change discipline).
Fire-particles explainer post (WIP, stashed to drafts/):
- Answering @StealthOrc's tweet; byline "Written by Claude Fable 5". The effect: no textures/shaders — circles at ~28Hz, accelerating fake-z rise, hold-then-shrink, yellow→red cooling, tongues + base core layers, outline + height-scaled shadows.
- Built the ::demo inline-embed infrastructure (COMMITTED, inert): game_host
demosregistry (small = true defs, MANY co-visible instances beside the untouched single-inst full-game path; repeat/until fix — theif not inst then return endearly-out was silently killing demos on gameless pages), elements small layout (fixed 2x box centered in column), ::demo in convert + server no-op renderer, three framework-free demo packages (fire-demo-1 anatomy w/ RISE/SHRINK/COOL toggles, fire-demo-2 emitter w/ sliders, fire-demo-3 palette/outline/shadow). - Debug chain: rgba() integer-checks args (tweened floats crashed all demos); demos must layer_render before layer_draw (queued shapes never executed into the FBO — "compositing 1 layer" logged while the box stayed gray; probe lesson: verify PIXELS, not logs — PrintWindow captures background windows).
- ⛔ Owner review: demos are drawn SIDE-VIEW but the game is TOP-DOWN — redo in board perspective; the outline must match the emoji pipeline's DERIVED silhouette pass, not per-circle double-draw. Post stashed to drafts/how-the-fire-effect-works.md with publish steps in its header; NOT published.
🔒 Only the summary of this log is public. Private because it contains release internal details.