From da4801828d6187e9314b3e58eb57fc4db0cd2501 Mon Sep 17 00:00:00 2001 From: lili Date: Thu, 18 Jun 2026 00:58:01 -0700 Subject: [PATCH] =?UTF-8?q?fix(saa):=20GAMEDEF=5FSYSTEM=20driver=20?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=E5=8A=A0=E5=9B=BA=20+=20=E5=BF=AB=E8=B5=B0?= =?UTF-8?q?=E6=9F=A5=E5=8D=87=E7=BA=A7=E5=85=A8=E5=9B=BE=E9=95=9C=E5=83=8F?= =?UTF-8?q?(design+gamedef)=E2=80=94=E2=80=94=E6=8D=AE=20U3=20=E6=97=A9?= =?UTF-8?q?=E8=AF=BB=E6=A0=B9=E5=9B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit plan 2026-06-18-001 U3 迭代1轮:据 lili-mac gamedef 快走查根因诊断,加固 GAMEDEF_SYSTEM 与九门 driver 的对齐。 快走查根因(design+gamedef 全图镜像, deepseek-v4-flash 单发无 repair): - 路径端到端真成立:声明式 gameDefinition→装配→构建→真九门;pong 9/9(无 design 简版)、breakout 8/9。 - 单发首攻失败模式根因:① I_control——挡板命名变体(paddlePlayer≠gatespec 的 paddle)/ 只接键盘不接指针(driver 靠点指针位驱动) / 控制体 clamp 范围窄于 driver 边缘 tapX(340);② H_progress——设计稿 assertAfterPlay 引用取证读不到的自定义字段(totalRound)。 GAMEDEF_SYSTEM 加固(SaaPrompts.java,gamedef 路专属,不动现 SYSTEM): - 命名逐字对齐 gatespec(挡板=paddle、球=ball,别用变体——读到 undefined=挂)。 - 可控体必须响应指针(`if(rt.input.pointer.down) paddle.x=rt.input.pointer.x`——driver 靠点指针位驱动,只接键盘=I_control 挂)。 - 进展落在可取证字段(score/remaining/实体位置;assertAfterPlay 别用读不到的自定义字段)。 验证:再跑 breakout 模型确按指令接指针控制(`if(pointer.down) paddle.x 追 pointer`)+ I_control tapX:50 已过; 残留 tapX:340 未过=首攻 clamp 范围精度,属**repair 回环**(已建:验收失败→verdict 回喂→重生成)territory。 快走查工具升级(gamedef_quickcheck.py):加 design 步(抽 DESIGN_SYSTEM+复刻 extractGatespec)→ 全图镜像 (classify 略/design→gamedef→装配→九门),getter truer 早读;prompt 抽自 SaaPrompts.java drift-free。 定位:单发快走查=首攻质量早读(非 cutover 门)。**≥60% cutover 门=全图含 repair 回环, authoritative=mini-desktop SaaFullGraphE2eTest -Dsaa.e2e=1**(单发 0/6≠失败:repair 回环抬升率,本机不跑 repap 多轮)。密钥经 _client 读 .env 不入码。 Co-Authored-By: Claude Opus 4.8 --- .../game/module/aigc/saa/SaaPrompts.java | 13 +++-- wg1/gen-worker/gamedef_quickcheck.py | 57 +++++++++++++++---- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/com/wanxiang/huijing/game/module/aigc/saa/SaaPrompts.java b/game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/com/wanxiang/huijing/game/module/aigc/saa/SaaPrompts.java index 0a1ef7e0..1cbf19d1 100644 --- a/game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/com/wanxiang/huijing/game/module/aigc/saa/SaaPrompts.java +++ b/game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/com/wanxiang/huijing/game/module/aigc/saa/SaaPrompts.java @@ -176,11 +176,16 @@ final class SaaPrompts { 5. condition 是无副作用布尔表达式: 禁 ; / 赋值 / 调 win/lose/addScore/spawn/destroy。 6. 死循环: 禁 while(true)/for(;;)。 - 【可玩 + 被九门驱动(关键命名约定,务必对齐)】 + 【可玩 + 被九门驱动(关键约定,务必对齐——否则机制门必挂)】 - 实体须有 render 组件才可见; 放一个 shape:"fill" 实体铺满 390x844 作背景(否则判白屏)。 - - 九门据【命名路径】驱动你的游戏: - · 挡板接球类: 球实体 id 命名 "ball"、挡板命名 "paddle"(九门读 ball.x/paddle.x 真玩对齐)。 - · 离散点击类(井字棋/打地鼠/翻牌/扫雷): 每个可点目标实体打 tags:["target"], 设 idx(序号)/occupied(是否已占)(九门据 targets[] 逐个点; 不打 target 标→点不到→机制门必挂)。 + - **命名逐字对齐设计稿 gatespec**: 设计稿末尾 ```gatespec 块的 driver.ballPath / controlCheck.paddlePath / assertAfterPlay.path + 引用的名字,你的实体 id 与进展字段必须**逐字一致**: + · 挡板接球类: 球实体 id **就叫 "ball"**、挡板 **就叫 "paddle"**(别用 paddlePlayer/ball1 等变体——九门读 ball.x/paddle.x,变体=读到 undefined=挂)。 + · 离散点击类(井字棋/打地鼠/翻牌/扫雷): 每个可点目标实体打 tags:["target"], 设 idx(序号)/occupied(是否已占)(九门据 targets[] 逐个点; 不打 target 标=点不到=挂)。 + - **可控体必须响应指针**(九门 driver 靠点击指针位驱动你的控制体): 挡板/玩家等可控实体务必随指针移动,如 + `const p=rt.getEntity('paddle'); if(p && rt.input.pointer.down){ p.x = rt.input.pointer.x; }`(只接键盘=driver 推不动=I_control 挂)。 + - **进展落在可取证字段**: 取证只暴露 score / remaining(=存活实体数,destroy 目标即降)/ 实体位置——进展务必体现在 rt.addScore 或 + rt.destroy(目标/砖)上; 确保设计稿 assertAfterPlay 的 path 落在 score 或 remaining(别用取证读不到的自定义字段)。 - HUD/进展可见: 分数/剩余看得到; 前 30 秒能上手、有正反馈、看得到分数增长。 【参照(严格按此结构产 JSON, 但实现成题目要求的那款游戏; 这是个躲避类示例)】 diff --git a/wg1/gen-worker/gamedef_quickcheck.py b/wg1/gen-worker/gamedef_quickcheck.py index 80e27aa9..f63fa1a2 100644 --- a/wg1/gen-worker/gamedef_quickcheck.py +++ b/wg1/gen-worker/gamedef_quickcheck.py @@ -29,16 +29,35 @@ GEN_DIR = GAME_RUNTIME / "games" / "_wg1-gen" BRIEFS = Path(__file__).resolve().parent / "briefs" -def extract_gamedef_system(): - """从 SaaPrompts.java 抽 GAMEDEF_SYSTEM 文本块(去 Java 文本块 12 空格缩进)。""" +def extract_block(name): + """从 SaaPrompts.java 抽指定 SYSTEM 文本块(去 Java 文本块 12 空格缩进)。drift-free=跑后端真 prompt。""" java = SAA_PROMPTS.read_text(encoding="utf-8") - m = re.search(r'GAMEDEF_SYSTEM = """\n(.*?)\n\s*""";', java, re.DOTALL) + m = re.search(name + r' = """\n(.*?)\n\s*""";', java, re.DOTALL) if not m: - raise RuntimeError("未能从 SaaPrompts.java 抽取 GAMEDEF_SYSTEM") + raise RuntimeError("未能从 SaaPrompts.java 抽取 " + name) lines = [(ln[12:] if ln.startswith(" " * 12) else ln) for ln in m.group(1).split("\n")] return "\n".join(lines) +def extract_gatespec(design_text): + """复刻 SaaPrompts.extractGatespec:抠 ```gatespec JSON 块 → 补 expectLatch=true + ballPath .y→.x。无则 None。""" + m = re.search(r"```gatespec\s*(.+?)```", design_text or "", re.DOTALL) + if not m: + return None + gs = extract_json_obj(m.group(1)) + if not isinstance(gs, dict): + return None + if "assertAfterPlay" in gs and not isinstance(gs["assertAfterPlay"], list): + gs["assertAfterPlay"] = [] + gs.setdefault("expectLatch", True) + drv = gs.get("driver") + if isinstance(drv, dict) and drv.get("type") == "paddle-intercept": + bp = drv.get("ballPath") + if isinstance(bp, str) and bp.endswith(".y"): + drv["ballPath"] = bp[:-2] + ".x" + return gs + + def extract_json_obj(content): """从模型输出抠最外层 {...}(去 ```json 围栏/前后说明)。""" s = content.strip() @@ -66,11 +85,25 @@ def assemble_via_cli(game_id, gamedef_obj): return r.returncode == 0, (r.stdout + r.stderr).strip() -def run_one(game_id, gamedef_system, model): +def run_one(game_id, gamedef_system, design_system, model): brief_text, play_spec = run._load_brief(game_id) - user = "请为下面这款游戏产出一份 gameDefinition(只输出一个 JSON 对象,不要 ```代码块、不要解释):\n\n" + brief_text gid = "gd-" + game_id - out = {"game_id": gid, "stage": None, "pass": False, "guards": {}, "err": ""} + out = {"game_id": gid, "stage": None, "pass": False, "guards": {}, "err": "", "driver": None} + # ① design 步(mirror 真图 design 节点):产 gatespec(driver/controlCheck/assert) 覆写 play_spec + enriched。 + enriched = brief_text + try: + dresp = _client.chat(model, design_system, brief_text, max_tokens=16000) + enriched = brief_text + "\n\n## 设计稿\n" + dresp["content"] + gs = extract_gatespec(dresp["content"]) + if gs: + for k in ("exportState", "driver", "controlCheck", "assertAfterPlay", "expectLatch"): + if k in gs and gs[k] is not None: + play_spec[k] = gs[k] + out["driver"] = (gs.get("driver") or {}).get("type") + except Exception as e: + out["err"] = "design 降级:" + str(e)[:80] # design 失败不致命,凭 brief 继续 generate + # ② generate gamedef(用 enriched=brief+设计稿) + user = "请为下面这款游戏产出一份 gameDefinition(只输出一个 JSON 对象,不要 ```代码块、不要解释):\n\n" + enriched try: resp = _client.chat(model, gamedef_system, user, max_tokens=16000) except Exception as e: @@ -102,16 +135,18 @@ def run_one(game_id, gamedef_system, model): def main(): games = sys.argv[1:] or ["runner", "flappy", "whack", "pong"] model = _client.DEFAULT_MODEL - gds = extract_gamedef_system() - print(f"[gamedef-qc] model={model} games={games} GAMEDEF_SYSTEM={len(gds)}字 (extracted from SaaPrompts.java)\n") + gds = extract_block("GAMEDEF_SYSTEM") + dsys = extract_block("DESIGN_SYSTEM") + print(f"[gamedef-qc] model={model} games={games} (design+gamedef 全图镜像; prompts 抽自 SaaPrompts.java)\n") results = [] for g in games: - r = run_one(g, gds, model) + r = run_one(g, gds, dsys, model) results.append(r) mark = "✅ PASS" if r["pass"] else f"❌ {r['stage']}" gpass = sum(1 for v in r["guards"].values() if v) gtot = len(r["guards"]) - print(f" {r['game_id']:14s} {mark:16s} gates={gpass}/{gtot} {('' if r['pass'] else r['err'][:120])}") + drv = r.get("driver") or "-" + print(f" {r['game_id']:14s} {mark:16s} drv={drv:16s} gates={gpass}/{gtot} {('' if r['pass'] else r['err'][:110])}") npass = sum(1 for r in results if r["pass"]) print(f"\n[gamedef-qc] 过门 {npass}/{len(results)} (cutover 门基线=iife 60%; 本为 lili-mac 小样早读, authoritative=mini-desktop)") Path(__file__).resolve().parent.joinpath("results", "gamedef-quickcheck.json").write_text(