{ "title": "小行星 Asteroids", "brief": "做一款经典小行星 Asteroids。竖屏逻辑视口 390×844。\n- 玩家飞船在屏幕中央,有惯性(推进后保持速度、缓慢摩擦)。按键 ArrowLeft/ArrowRight 旋转朝向;ArrowUp 朝当前朝向推进;Space 朝当前朝向发射子弹。\n- 屏幕四周漂浮大陨石(用受控 random 初始化方向);子弹击中大陨石→裂成两块小陨石、得分 +1;击中小陨石→击碎、得分 +1。所有元素屏幕环绕(飞出一边从另一边回来)。\n- 飞船被陨石撞到 = 失败;清光所有陨石 = 胜利。\n- 【CCD 注意】子弹与小陨石都可能高速移动,请用【连续碰撞/沿速度向量扫掠检测】判命中,避免高速穿透(漏判)。\n- 顶部文本 HUD:分数 / 剩余陨石数 / 状态。\n- 【终态契约·务必遵守】失败/胜利即把 phase 置 'gameover' 并【驻留】(显示结果 + 「点击重玩」);点击重玩前 phase 恒 'gameover',绝不自动重开 / 不下一帧复位(宿主每 500ms 轮询)。\n- 【可测性·务必导出状态】_forensicsView().state() 纯只读实时真值:phase;result(null|'win'|'lose');score(整数,开局0);asteroidsRemaining(剩余陨石数);ship:{x,y,angle}(angle=朝向弧度,与 atan2 同向:指上≈-π/2);asteroids:[{x,y}...](所有活跃陨石中心逻辑像素,供 harness 旋向瞄准开火)。harness 据 ship.angle/asteroids 旋转对齐开火、据 score/phase 验进展与终局。\n- 反馈(juice):开火 / 击碎 / 撞毁触发 getEngine().particles.spawnEmitter + getEngine().audio.synth.synthSfx。\n- 画面:深色背景铺满;飞船 / 陨石 / 子弹 / 文字亮色实心。", "play_spec": { "driver": { "type": "aim-fire", "shipPath": "ship", "targetsPath": "asteroids", "fireKey": "Space", "steps": 90, "stepMs": 120, "downMs": 80 }, "expectedEngineCallPrefixes": ["particles.", "audio.synth"], "assertAfterPlay": [ { "path": "score", "op": "increased", "why": "aim-fire 读陨石坐标旋向对齐开火→击碎得分上升;若游戏不暴露陨石坐标致盲射则不升=可测性边界" } ], "expectLatch": true } }