test(wg1): gamedef 路 lili-mac 快走查驱动 + U3 cutover 门早读(pong 9/9 真过)
plan 2026-06-18-001 U3 验证:run.py 的 gamedef 版——generate(GAMEDEF_SYSTEM,从 SaaPrompts.java 抽取 drift-free) → build-from-source.mjs(校验 schema/引用/静态扫描+装配)→ scaffold/build/play 九门(harness 复用零改)。 早读(deepseek-v4-flash 便宜档,lili-mac 小样): - gd-pong 9/9 真过门(声明式 gameDefinition→可玩→满九门,范式端到端证实)。 - gd-runner/whack 8/9、gd-flappy 7/9,共性缺口 G_input——本快走查跳了 design 步、多数 brief 的 play_spec 无 controlCheck/driver(gatespec),故 G_input 无控制驱动可施=快走查 harness 工件,非 gamedef 生成缺陷 (游戏真渲染/真构建/输入真接线:runner justTapped→jump 在;pong 满过)。 - authoritative ≥60% cutover 门(全图含 design 产 gatespec) = mini-desktop SaaFullGraphE2eTest -Dsaa.e2e=1。 密钥经 _client 读 .env(gitignored)、绝不入码;throwaway 生成游戏(gd-*)已清,不入仓。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
fd5b245baa
commit
d8df5f27ec
123
wg1/gen-worker/gamedef_quickcheck.py
Normal file
123
wg1/gen-worker/gamedef_quickcheck.py
Normal file
@ -0,0 +1,123 @@
|
||||
"""gamedef_quickcheck.py —— plan 2026-06-18-001 U3 gamedef 路 lili-mac 快走查(cutover 门早读)。
|
||||
|
||||
run.py 的 gamedef 版:把 generate(iife) → validate(静态) 换成
|
||||
generate(GAMEDEF_SYSTEM) → build-from-source.mjs(校验 schema/引用/静态扫描 + 装配出工厂),
|
||||
其余(scaffold/build/play 九门)复用 run.py(harness 零改)。
|
||||
|
||||
GAMEDEF_SYSTEM **从后端 SaaPrompts.java 抽取**(drift-free:跑的就是后端真用的 prompt)。
|
||||
模型/网关/代理旁路复用 _client;play-spec/brief 复用现有 briefs/*.json(gatespec driver 同口径)。
|
||||
|
||||
用法:.venv/bin/python gamedef_quickcheck.py runner flappy whack pong # 默认这 4
|
||||
仅 lili-mac 快走查(小样早读);authoritative ≥60% cutover 门在 mini-desktop(SaaFullGraphE2eTest)。
|
||||
密钥经 _client 读 .env,绝不打印。
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from worker import _client, run # noqa: E402 复用 chat + scaffold/build/play
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
SAA_PROMPTS = (REPO_ROOT / "game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/"
|
||||
"com/wanxiang/huijing/game/module/aigc/saa/SaaPrompts.java")
|
||||
GAME_RUNTIME = REPO_ROOT / "game-runtime"
|
||||
GEN_DIR = GAME_RUNTIME / "games" / "_wg1-gen"
|
||||
BRIEFS = Path(__file__).resolve().parent / "briefs"
|
||||
|
||||
|
||||
def extract_gamedef_system():
|
||||
"""从 SaaPrompts.java 抽 GAMEDEF_SYSTEM 文本块(去 Java 文本块 12 空格缩进)。"""
|
||||
java = SAA_PROMPTS.read_text(encoding="utf-8")
|
||||
m = re.search(r'GAMEDEF_SYSTEM = """\n(.*?)\n\s*""";', java, re.DOTALL)
|
||||
if not m:
|
||||
raise RuntimeError("未能从 SaaPrompts.java 抽取 GAMEDEF_SYSTEM")
|
||||
lines = [(ln[12:] if ln.startswith(" " * 12) else ln) for ln in m.group(1).split("\n")]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def extract_json_obj(content):
|
||||
"""从模型输出抠最外层 {...}(去 ```json 围栏/前后说明)。"""
|
||||
s = content.strip()
|
||||
s = re.sub(r"^```(?:json)?\s*", "", s)
|
||||
s = re.sub(r"\s*```$", "", s)
|
||||
lo, hi = s.find("{"), s.rfind("}")
|
||||
if lo < 0 or hi <= lo:
|
||||
return None
|
||||
try:
|
||||
return json.loads(s[lo:hi + 1])
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def assemble_via_cli(game_id, gamedef_obj):
|
||||
"""写 source.json → 跑 build-from-source.mjs 校验+装配 → generated-factory.js。返回 (ok, err)。"""
|
||||
gdir = GEN_DIR / game_id
|
||||
gdir.mkdir(parents=True, exist_ok=True)
|
||||
src = gdir / "source.json"
|
||||
src.write_text(json.dumps(gamedef_obj, ensure_ascii=False), encoding="utf-8")
|
||||
r = subprocess.run(
|
||||
["node", "src/host/build-from-source.mjs", str(src), str(gdir / "generated-factory.js")],
|
||||
cwd=str(GAME_RUNTIME), capture_output=True, text=True, timeout=60,
|
||||
)
|
||||
return r.returncode == 0, (r.stdout + r.stderr).strip()
|
||||
|
||||
|
||||
def run_one(game_id, gamedef_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": ""}
|
||||
try:
|
||||
resp = _client.chat(model, gamedef_system, user, max_tokens=16000)
|
||||
except Exception as e:
|
||||
out["stage"] = "generate"; out["err"] = str(e)[:200]; return out
|
||||
out["tokens"] = (resp.get("prompt_tokens"), resp.get("completion_tokens"))
|
||||
gd = extract_json_obj(resp["content"])
|
||||
if gd is None:
|
||||
out["stage"] = "parse"; out["err"] = "未解析出 gameDefinition JSON"; return out
|
||||
ok, err = assemble_via_cli(gid, gd)
|
||||
if not ok:
|
||||
out["stage"] = "validate/assemble"; out["err"] = err[:400]; return out
|
||||
# 复用 run.py 的 scaffold 余下(拷模板 + play-spec)+ build + play(factory 已由 CLI 写好)。
|
||||
gdir = GEN_DIR / gid
|
||||
shutil.copyfile(GEN_DIR / "_shared" / "entry-bundle.template.js", gdir / "entry-bundle.js")
|
||||
shutil.copyfile(GEN_DIR / "_shared" / "index.template.html", gdir / "index.html")
|
||||
(gdir / "play-spec.json").write_text(json.dumps(play_spec, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
bok, blog = run.build(gid)
|
||||
if not bok:
|
||||
out["stage"] = "build"; out["err"] = blog[:400]; return out
|
||||
rc, _, verdict = run.play(gid)
|
||||
out["stage"] = "play"
|
||||
out["guards"] = {k: g.get("pass") for k, g in ((verdict or {}).get("guards") or {}).items()}
|
||||
out["pass"] = bool(rc == 0 and verdict and verdict.get("pass"))
|
||||
if not out["pass"]:
|
||||
out["err"] = run._verdict_feedback(verdict)[:300]
|
||||
return out
|
||||
|
||||
|
||||
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")
|
||||
results = []
|
||||
for g in games:
|
||||
r = run_one(g, gds, 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])}")
|
||||
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(
|
||||
json.dumps(results, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
x
Reference in New Issue
Block a user