Browse Source

fix(chase): enforce distinct start positions in round generation

Made-with: Cursor
master
npmrun 2 weeks ago
parent
commit
3692b59ccc
  1. 9
      src/game/chase/generator.ts
  2. 23
      tests/chase/chaseGenerator.test.ts

9
src/game/chase/generator.ts

@ -174,7 +174,13 @@ function generateGraphAttempt(seed: number, difficulty: Difficulty): ChaseRound
const exitNodeId = farthest.id; const exitNodeId = farthest.id;
const pathLength = shortestPathLength(graph, thiefStartNodeId, exitNodeId); const pathLength = shortestPathLength(graph, thiefStartNodeId, exitNodeId);
const farthestFromExit = getFarthestNode(graph, exitNodeId); const farthestFromExit = getFarthestNode(graph, exitNodeId);
const guardStartNodeId = farthestFromExit.id; let guardStartNodeId = farthestFromExit.id;
if (guardStartNodeId === thiefStartNodeId) {
const fallbackCandidates = existingNodeIds.filter(
(nodeId) => nodeId !== thiefStartNodeId,
);
guardStartNodeId = fallbackCandidates[0] ?? thiefStartNodeId;
}
return { return {
snapshot: { snapshot: {
@ -206,6 +212,7 @@ export function generateChaseRound(input: GenerateChaseRoundInput): ChaseRound {
const isPlayable = const isPlayable =
isConnected(round.snapshot.graph) && isConnected(round.snapshot.graph) &&
round.meta.hasEscapePath && round.meta.hasEscapePath &&
round.snapshot.guardStartNodeId !== round.snapshot.thiefStartNodeId &&
round.meta.pathLength >= minPathLength; round.meta.pathLength >= minPathLength;
if (isPlayable) { if (isPlayable) {

23
tests/chase/chaseGenerator.test.ts

@ -40,8 +40,8 @@ describe("chase round generator", () => {
"2,-1", "2,-1",
"1,-2", "1,-2",
], ],
"guardNodeId": "4,-5", "guardNodeId": "0,0",
"guardStartNodeId": "4,-5", "guardStartNodeId": "0,0",
"hasEscapePath": true, "hasEscapePath": true,
"nodeCount": 20, "nodeCount": 20,
"seed": 424242, "seed": 424242,
@ -71,4 +71,23 @@ describe("chase round generator", () => {
expect(pathLength).toBeGreaterThan(0); expect(pathLength).toBeGreaterThan(0);
expect(pathLength).toBeLessThan(Infinity); expect(pathLength).toBeLessThan(Infinity);
}); });
it("ensures thief and guard do not start on the same node", () => {
const round = generateChaseRound({ seed: 123456, difficulty: "normal" });
expect(round.snapshot.thiefStartNodeId).not.toBe(round.snapshot.guardStartNodeId);
});
it("stays playable and stable across many seeds", () => {
for (let seed = 1; seed <= 60; seed += 1) {
const round = generateChaseRound({ seed, difficulty: "normal" });
const nodeCount = Object.keys(round.snapshot.graph.nodes).length;
expect(nodeCount).toBeGreaterThanOrEqual(20);
expect(nodeCount).toBeLessThanOrEqual(30);
expect(round.meta.hasEscapePath).toBe(true);
expect(round.snapshot.thiefStartNodeId).not.toBe(
round.snapshot.guardStartNodeId,
);
}
});
}); });

Loading…
Cancel
Save