Files
ark-stronghold-protocol/test/sim/perf.test.js
T
sganggs 01a44cd288 卫戍协议:盟约 web remake — initial import
Online co-op browser remake of Arknights' Stronghold Protocol: Alliance
(client-simulated combat, official waves/bonds/items/strategies, 3D board,
facing wheel, operator loadout, official emotes, deployment scripts).
2026-09-29 16:21:31 +08:00

59 lines
3.0 KiB
JavaScript

// Performance: a normal battle with ~70 enemies + 10 operators must average < 0.5 ms per tick (DESIGN §11).
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Battle } from '../../server/sim/Battle.js';
import { getDefaultSource, spawnsFromTemplate } from '../../server/sim/simdata.js';
const ds = getDefaultSource();
const quiet = { error() {}, warn() {} };
const LINEUP = [
['chess_char_1_02_a', 9, 7], ['chess_char_2_09_a', 9, 4], ['chess_char_4_09_a', 12, 5], ['chess_char_3_08_a', 12, 7],
['chess_char_1_01_a', 10, 4], ['chess_char_1_03_a', 11, 4], ['chess_char_2_02_a', 12, 4], ['chess_char_2_14_a', 10, 5],
['chess_char_5_12_a', 11, 5], ['chess_char_6_13_a', 9, 8],
];
function build(spawns, routes, timeLimit, seed = 7) {
return new Battle({
seed, kind: 'normal', stageId: 'act2autochess_m01', routes, spawns, timeLimit, logger: quiet, quiet: true,
players: [{ playerId: 'p', units: LINEUP.map(([chessId, row, col], i) => ({ uid: i + 1, kind: 'chess', chessId, row, col, abs: true })) }],
});
}
test('benchmark: 70-enemy wave (h05) + 10 operators < 0.5 ms/tick average', () => {
const tpl = ds.getWave('act1autochess_h05');
const { routes, spawns, maxPlayTime } = spawnsFromTemplate(tpl, { mods: { hpMul: 3 } });
// warm-up (JIT)
for (let i = 0; i < 2; i++) { const w = build(spawns, routes, maxPlayTime, i + 100); while (!w.finished) w.step(); }
const b = build(spawns, routes, maxPlayTime);
let n = 0, maxAlive = 0;
const t0 = performance.now();
while (!b.finished) {
b.step();
n++;
if (n % 3 === 0) { b.snapshot(); b.drainEvents(); }
if (b.enemies.length > maxAlive) maxAlive = b.enemies.length;
}
const avg = (performance.now() - t0) / n;
// eslint-disable-next-line no-console
console.log(`bench wave: ${n} ticks, total ${b.total} enemies, peak ${maxAlive} alive, avg ${(avg * 1000).toFixed(1)} µs/tick, reason ${b.reason}`);
assert.ok(b.total >= 60);
assert.ok(avg < 0.5, `avg ${avg} ms/tick`);
});
test('benchmark: worst case — 70 enemies alive at once + 10 operators < 0.5 ms/tick', () => {
const tpl = ds.getWave('act1autochess_h05');
const { routes } = spawnsFromTemplate(tpl);
const keys = ['enemy_1422_lrsldr', 'enemy_1427_lrnazg', 'enemy_1005_yokai', 'enemy_1042_frostd', 'enemy_1425_lrcmra', 'enemy_1040_bombd'];
const spawns = [];
for (let i = 0; i < 70; i++) spawns.push({ time: (i % 10) * 0.2, enemyKey: keys[i % keys.length], routeIndex: i % routes.length, mods: { hpMul: 10 } });
for (let i = 0; i < 2; i++) { const w = build(spawns, routes, 20, 50 + i); while (!w.finished) w.step(); }
const b = build(spawns, routes, 60);
let n = 0;
const t0 = performance.now();
while (!b.finished && b.time < 40) { b.step(); n++; if (n % 3 === 0) { b.snapshot(); b.drainEvents(); } }
const avg = (performance.now() - t0) / n;
// eslint-disable-next-line no-console
console.log(`bench worst: ${n} ticks, ${b.enemies.length} alive at end, avg ${(avg * 1000).toFixed(1)} µs/tick`);
assert.ok(avg < 0.5, `avg ${avg} ms/tick`);
});