fix(hunt): v2 九修 S1-S9 落地——10 人并发模拟迭代裁定(同阵容复测:去留 6/10→10/10)
研究闭环:v1 三轮 28 局四死因(失衡/社交真空/交互摩擦/观光羞辱)→ 四视角分析 → synthesis S1-S9 裁定 → 实施 → 同 10 人配置复测两轮 20 局。依据 research/hunt-sim/ (synthesis.md / metrics-v2.md / report.md),设计登记 23-hunt.md §16。 服务端 hunt-room.js: - S5 协作人声包:真人座位角色接线(杀熊/三连杀/旋风斩≥3 播角色句、greet 全座位); 满编真人房呼喊 2.5s 无 bot 应答 → 邻座角色代答 help_ack;喊话预算 10s 窗 2→3 - S7 难度重构:waveCounts 4 人房 ×4/3;alignPincers 第 3-5 波齐压拍(跨座位同种类 同秒进内圈,种子确定性保持);mobYielded bot 对人类防区怪让位 2s(只约束 bot, 不拦真人避免挥空真话说谎);gradeForEx 双轴评级(S=耐久≥9 且 ≥2 人各 6 杀, 单人带飞局上限 A) 客户端 game.js: - S1 WAVE 下滑恒出击(无怪=空挥真话),菜单收窄到波间休整窗(E3 修订)—— v1 误触菜单单人单局最多 14 次清零 - S2 冷却首拦才震 + 就绪 beat 轻叮(v1 全场 666 次乱滑连震) - S3 0 杀不播 kill_band0(「你还没开张」下架;复测 P08 0杀→12杀) - S4 看门狗重定义:本波出过手一级语音不触发、语音闲置每局 ≤1、一级降纯震 - S5③ help_call/help_ack 永不超时丢弃 - S6 后半平面 loop 低通 750Hz 第二线索(N/S 判读 63/69%→75/81%) - S8 教学作答步(T1-T3)下滑不再误跳 - S9 状态链按变化播(「固若金汤」复读归零) 测试同步:hunt-sim A1/A2/A12 断言更新(真人 greet/波次缩放/预算 3)、 playtest Block 3 按 S1 新语义改写(战斗中下滑=空挥 + 波间菜单) 回归:hunt-sim 16/16 + playtest 213 + guide 132 + engine-voice 19 全绿; balance-scan:挂机局 5/8 落 C/失守带(v1 恒满血)、optimal 单人带飞封顶 A 遗留:营地零损耗未破(18/20 局余10)——v3 头号,参数方案 report.md §5
This commit is contained in:
@@ -29,7 +29,7 @@ import { fileURLToPath } from 'node:url';
|
||||
import hunt from '../hunt-room.js';
|
||||
|
||||
const {
|
||||
createManager, buildWaveScript, gradeFor, DIRS, LAYOUTS, WAVE_TABLE,
|
||||
createManager, buildWaveScript, waveCounts, gradeFor, gradeForEx, DIRS, LAYOUTS, WAVE_TABLE,
|
||||
TICK_MS, GRACE_MS, REST_MS, LOBBY_MS, END_KEEP_MS,
|
||||
} = hunt;
|
||||
|
||||
@@ -189,7 +189,7 @@ test('A1 全流程:匹配→开局 bot 补位→5 波→胜利结算,事件
|
||||
const end = lastEv(conn, 'end');
|
||||
assert.equal(end.win, true, '1 真人+2 bot 应能守过 5 波');
|
||||
assert.ok(end.dur >= 1 && end.dur <= 10, `余耐久 ${end.dur} 在 1-10`);
|
||||
assert.equal(end.grade, gradeFor(end.dur), '评级与耐久一致(§3.6,归因真话)');
|
||||
assert.equal(end.grade, gradeForEx(end.dur, end.kills), '评级双轴一致(v2 S7④:耐久档 × 参与度,归因真话)');
|
||||
let killSum = 0;
|
||||
for (const [k, v] of Object.entries(end.kills)) {
|
||||
assert.ok(['0', '1', '2'].includes(k));
|
||||
@@ -211,7 +211,7 @@ test('A1 全流程:匹配→开局 bot 补位→5 波→胜利结算,事件
|
||||
assert.ok(snaps[snaps.length - 1].ts <= endTs, '终局后 snap 停发');
|
||||
// ---- botline greet:逐只间隔 ≥1.5s(§7.4) ----
|
||||
const greets = evs(conn, 'botline').filter((e) => e.line === 'greet');
|
||||
assert.equal(greets.length, 2);
|
||||
assert.equal(greets.length, 3, 'greet:2 bot + 真人座位角色(v2 S5① 真人接线)');
|
||||
assert.ok(greets[1].ts - greets[0].ts >= 1500, 'greet 间隔 ≥1.5s');
|
||||
|
||||
// ---- 失守不写榜(服务器侧口径):本局为胜局,反向用 A9 覆盖 ----
|
||||
@@ -228,11 +228,15 @@ test('A2 刷怪剧本 seed 复现:同 seed 同剧本,方位 ∈ 座位防区
|
||||
assert.deepEqual(a, b, `seat=${seatCount} wave=${w} 同 seed 同剧本`);
|
||||
const c = buildWaveScript(43, w, seatCount);
|
||||
assert.notDeepEqual(a, c, '异 seed 异剧本');
|
||||
const spec = WAVE_TABLE[w];
|
||||
const counts = waveCounts(spec, seatCount); // v2 S7①:波次量按座位缩放
|
||||
assert.equal(a.length, counts.wolf + counts.bear, `seat=${seatCount} w=${w} 波次量 = 缩放后狼+熊`);
|
||||
assert.equal(a.filter((e) => e.mkind === 'bear').length, counts.bear, `seat=${seatCount} w=${w} 熊数`);
|
||||
const layout = LAYOUTS[seatCount];
|
||||
a.forEach((e, i) => {
|
||||
assert.equal(e.seat, i % seatCount, '按座位数轮转发牌');
|
||||
assert.ok(layout[e.seat].sector.includes(e.dir), '方位落在该座位防区内');
|
||||
assert.equal(e.at, (i + 1) * WAVE_TABLE[w].gap * 1000, '刷怪间隔 §3.4');
|
||||
assert.equal(e.at, (i + 1) * spec.gap * 1000, '刷怪间隔 §3.4(齐压拍在 startWave 层,纯函数不动)');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -628,17 +632,17 @@ test('A12 botline 全局节流:10s 窗氛围 ≤2 条;help_call/help_ack 优
|
||||
|
||||
const before = evs(conn, 'botline').length;
|
||||
for (let i = 0; i < 5; i++) room.emitBotline(bot, 'greet'); // 连发 5 条氛围句
|
||||
assert.equal(evs(conn, 'botline').length - before, 2, '10s 窗氛围句 ≤2,超出丢弃');
|
||||
assert.equal(evs(conn, 'botline').length - before, 3, '10s 窗氛围句 ≤3(v2 S5① 预算 2→3),超出丢弃');
|
||||
|
||||
// 预算耗尽时呼救语义仍必达
|
||||
room.emitBotline(bot, 'help_ack', { priority: true });
|
||||
assert.equal(evs(conn, 'botline').length - before, 3, 'help_ack 优先不被氛围顶掉');
|
||||
assert.equal(evs(conn, 'botline').length - before, 4, 'help_ack 优先不被氛围顶掉(3 氛围 + 1 优先)');
|
||||
assert.equal(evs(conn, 'botline').filter((e) => e.line === 'help_ack').length, 1);
|
||||
|
||||
// 窗口滑过后恢复
|
||||
fastForward(mgr, 10.5);
|
||||
room.emitBotline(bot, 'greet');
|
||||
assert.equal(evs(conn, 'botline').length - before, 4, '10s 窗滑过后氛围恢复投递');
|
||||
assert.equal(evs(conn, 'botline').length - before, 5, '10s 窗滑过后氛围恢复投递');
|
||||
room.destroy();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user