games-development-ai/tier2/harness/business-serve.driver.js
lili 329b82cad4 feat(polaris): 《山海巡异录》M1——host集成修根因+单局通关闭环+card driver v1(harness双席合笔)
host修根因(mini-desktop CDP验出双Phaser实例致可见区纯黑+真指针死):main.js重写为宿主契约形态——
工厂零Phaser import、永不自建引擎实例,场景经ctx.game.scene.add挂bootPhaserHost唯一实例(与夜市款同构);
真人pointerdown与injectInput('tap')同走route(x,y)→readState().tapTargets命中序(ui/layout.js唯一坐标事实)=同因果,
M0手牌盖板吃点击缺陷随重构消失;run-smoke S0六条静态自查防回潮;battle-scene.js退役git留档。
M1单局闭环:S5链轴完整化(链轨延链指引/graceUsed/周天金闪)+S6 map-core三幕DAG(战/精英/憩/宝匣/Boss,
静态可达自检)+S7敌8种(多段/甲攻复合/烛阴双相位/饕餮增势,意图回合开始钉住「展示=执行」逐字段成立)+
S8 card driver v1+play-spec;30卡五行各6/3遗物全规则级/奖励三选一可跳过/HP携带/run级三latch。
数值首轮回填全标〔草案〕(贪心v1胜率5%=下界非标定对象,A0带45-60%校准归M2 S12三档driver)。
完整run单局墙钟首实测(§6.4 M1出口):40 seed中位1.6ms/p95 3ms级(headless开发机口径)→外推M2全矩阵
96k局≈分钟级单线程,数值CI可行性坐实。harness共享面双席合笔:card-battle.driver+business-serve.driver新增、
play-phaser.cdp.cjs runDriver type分发additive(卡牌/夜市两席同文件双分发共存复核OK,未声明时行为逐字不变)。
证据:battle-core 40断言(T1a/T1b随定容更新)+run-core 12组+run-smoke全绿(地图150张/首局门5 seed全win/
通关2/40全自然终局零invalid/幕缩放1070采样全中);主会话真跑复验。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:13:51 -07:00

77 lines
4.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* business-serve.driver.js —— 经营·服务循环形状(business-serve)确定性 harness driver(tier2 富游戏线)
* owner:北极星顶级线①(W-POLARIS)《夜市一条街》M1 九门接线 被 play-phaser.cdp.cjs runDriver 分发调用
* (与 business-sim/card-battle 并列,按 runDriver 注释官方配方接入)
*
* ════════════════════════════════════════════════════════════════════════════
* 【这份是什么】「点顾客即成单」服务循环的确定性驱动:读语义 state 自适应出招(不盲打坐标),
* 把经营日循环真输入驱动到 day_end 终态,供九门判定(E_live/G_input/H_progress 的 driven 供给端)。
* 与 business-sim.driver(mini-肥鹅 merge/orders 形状)是同一经营品类的两个 state 形状;
* 富三门的经营-serve 语义归 M2 定义,本 driver 只管九门 driven——spec 用独立 driver.type
* 'business-serve',富三门按 harness 品类占位「门待建」走,不套 merge 语义防假绿。
*
* 【读什么 state】(game-core readState 契约)
* { phase:'title'|'play'|'day_end', coins, served,
* tapTargets:[{customerId,x,y,patienceLeft,servable}], // 等待中顾客可点逻辑坐标
* stoves:[{idx,busy,...}] }
*
* 【出招策略】(与 scripts/logic-smoke.mjs 勤快席同族,浏览器侧无白盒进货,只按 servable 优先)
* 有空灶时:优先「可做(servable)且耐心最紧」的顾客,真触摸点其逻辑坐标;
* 无可点(灶全忙/无客)则小步等待;phase 进入 terminalPhases 即停,交 latch 校验。
* ════════════════════════════════════════════════════════════════════════════
*/
'use strict';
/** 逻辑坐标钳制(避免点出取证视口外;竖屏 390×844)。 */
function clamp(v, max) {
const hi = max != null ? max : 386;
if (typeof v !== 'number' || Number.isNaN(v)) return Math.floor(hi / 2);
return Math.max(4, Math.min(hi - 4, v));
}
/** 从 state 选出招目标:可做优先、耐心紧迫优先;无灶/无客返 null。(纯函数,单测直测) */
function pickTarget(state) {
if (!state) return null;
const targets = Array.isArray(state.tapTargets) ? state.tapTargets : [];
const stoves = Array.isArray(state.stoves) ? state.stoves : [];
if (!targets.length) return null;
if (!stoves.some((s) => s && !s.busy)) return null; // 无空灶不空点(点了也 stoves_busy)
const byUrgency = [...targets].sort((a, b) => a.patienceLeft - b.patienceLeft);
return byUrgency.find((t) => t.servable !== false) || byUrgency[0];
}
/**
* 主路径:驱动服务循环到 day_end(或步数上限)。
* @param cdp CDP 会话
* @param driver play-spec.driver({type:'business-serve', steps?, stepMs?, terminalPhases?})
* @param hashes 帧哈希数组(供 E 门活性采样)
* @param deps {tap,key,drag,readGameState,sampleHash,getPath,delay}
* @returns {drove, steps}
*/
async function run(cdp, driver, hashes, deps) {
const { tap, readGameState, sampleHash, delay } = deps;
const steps = driver.steps || 110;
const stepMs = driver.stepMs != null ? driver.stepMs : 400;
const terminals = Array.isArray(driver.terminalPhases) && driver.terminalPhases.length
? driver.terminalPhases : ['day_end'];
let drove = 0;
for (let i = 0; i < steps; i++) {
const s = await readGameState(cdp);
if (s && terminals.indexOf(s.phase) >= 0) break; // 已收钟 → 停,交 latch 校验
const target = pickTarget(s);
if (target) {
await tap(cdp, clamp(target.x), clamp(target.y, 840)); // 真触摸(经浏览器→Phaser input→core.tapAt)
drove++;
} else {
await delay(Math.min(stepMs, 200)); // 无可点(灶忙/无客/缺料无可做单):小步等出餐腾灶
}
try { hashes.push(await sampleHash(cdp)); } catch (_) {}
await delay(stepMs);
}
return { drove, steps };
}
module.exports = { run, pickTarget, clamp };