Prevent stage early replays in scrim map by map mode
Some checks are pending
E2E Tests / e2e (push) Waiting to run
Tests and checks on push / run-checks-and-tests (push) Waiting to run
Updates translation progress / update-translation-progress-issue (push) Waiting to run

This commit is contained in:
Kalle
2026-05-26 18:49:41 +03:00
parent 52de76491c
commit 79242c13df
2 changed files with 28 additions and 10 deletions

View File

@@ -700,16 +700,15 @@ describe("MapList.resume()", () => {
).toBe("SZ");
});
it("exclusion is keyed on (mode, stage), not stage alone", () => {
it("avoids the just-played stage when alternatives exist, even across modes", () => {
const sharedPool = new MapPool({
TW: [],
SZ: [1, 2],
TC: [1, 2],
SZ: [1, 2, 3, 4, 5],
TC: [1, 2, 3, 4, 5],
RM: [],
CB: [],
});
const seenForTC = new Set<StageId>();
for (let i = 0; i < 50; i++) {
const gen = MapList.resume({
mapPool: sharedPool,
@@ -718,10 +717,10 @@ describe("MapList.resume()", () => {
gen.next();
const next = gen.next({ amount: 1 }).value![0];
expect(next.mode).toBe("TC");
seenForTC.add(next.stageId);
expect(
next.stageId,
"same stage picked back-to-back across modes",
).not.toBe(1);
}
expect(seenForTC.has(1)).toBe(true);
expect(seenForTC.has(2)).toBe(true);
});
});

View File

@@ -54,6 +54,8 @@ export function* generate(args: {
skipEnsureMinimumCandidates?: boolean;
/** Fixed mode order — when set, skips the random `modeOrders` shuffle and uses only this order. Intended for `resume`. */
modeOrder?: ModeShort[];
/** Initial weights for stages (mode-agnostic). Used by `resume` to carry over stage-level penalties from history. */
initialStageWeights?: Map<StageId, number>;
}): Generator<Array<ModeWithStage>, Array<ModeWithStage>, GenerateNext> {
if (args.mapPool.isEmpty()) {
while (true) yield [];
@@ -65,6 +67,7 @@ export function* generate(args: {
modes,
args.mapPool.parsed,
args.initialWeights,
args.initialStageWeights,
);
const orderedModes = args.modeOrder ? [args.modeOrder] : modeOrders(modes);
let currentOrderIndex = 0;
@@ -164,13 +167,28 @@ export function resume(args: {
initialWeights.set(modeStageKey(pair.mode, pair.stageId), 0);
}
for (const { mode, stageId } of args.history) {
initialWeights.set(modeStageKey(mode, stageId), -1000);
initialWeights.set(modeStageKey(mode, stageId), -100);
}
const STAGE_PENALTY = -20;
const initialStageWeights = new Map<StageId, number>();
for (const pair of args.mapPool.stageModePairs) {
if (!initialStageWeights.has(pair.stageId)) {
initialStageWeights.set(pair.stageId, 0);
}
}
for (const { stageId } of args.history) {
for (const [key, value] of initialStageWeights.entries()) {
initialStageWeights.set(key, value + 1);
}
initialStageWeights.set(stageId, STAGE_PENALTY);
}
return generate({
mapPool: args.mapPool,
modeOrder,
initialWeights: initialWeights.size > 0 ? initialWeights : undefined,
initialStageWeights,
skipEnsureMinimumCandidates: true,
});
}
@@ -179,6 +197,7 @@ function initializeWeights(
modes: ModeShort[],
mapPool: ReadonlyMapPoolObject,
initialWeights?: Map<string, number>,
initialStageWeights?: Map<StageId, number>,
) {
const stageWeights = new Map<StageId, number>();
const stageModeWeights = new Map<string, number>();
@@ -188,7 +207,7 @@ function initializeWeights(
for (const mode of modes) {
const stageIds = mapPool[mode];
for (const stageId of stageIds) {
stageWeights.set(stageId, 0);
stageWeights.set(stageId, initialStageWeights?.get(stageId) ?? 0);
const key = modeStageKey(mode, stageId);
const initialWeight =
initialWeights?.get(key) ?? (hasInitialWeights ? -1000 : 0);