118 lines
4.1 KiB
TypeScript
118 lines
4.1 KiB
TypeScript
|
|
/**
|
||
|
|
* The AFL Wildcard winners are re-seeded into the Elimination Finals by ladder position
|
||
|
|
* (5th draws the lower-ranked winner, 6th the higher-ranked one) rather than crossing
|
||
|
|
* over from a fixed Wildcard match. These tests pin that mapping for every combination
|
||
|
|
* of results, and for either order of entry.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { describe, it, expect } from "vitest";
|
||
|
|
import {
|
||
|
|
resolveAflWildcardPlacements,
|
||
|
|
AFL_WILDCARD_DRAW,
|
||
|
|
AFL_ELIMINATION_HOSTS,
|
||
|
|
type AflWildcardResult,
|
||
|
|
} from "../afl-wildcard-reseed";
|
||
|
|
|
||
|
|
/** Both Wildcard games decided, addressed by the seed that won each. */
|
||
|
|
function bothDecided(match1Winner: 7 | 10, match2Winner: 8 | 9): AflWildcardResult[] {
|
||
|
|
return [
|
||
|
|
{ matchNumber: 1, winnerSlot: match1Winner === 7 ? 1 : 2 },
|
||
|
|
{ matchNumber: 2, winnerSlot: match2Winner === 8 ? 1 : 2 },
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Elimination Finals match number each winning seed was sent to. */
|
||
|
|
function slotsBySeed(results: AflWildcardResult[]): Record<number, number> {
|
||
|
|
return Object.fromEntries(
|
||
|
|
resolveAflWildcardPlacements(results).map((p) => [p.seed, p.eliminationMatchNumber])
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
describe("AFL Wildcard draw constants", () => {
|
||
|
|
it("draws 7v10 and 8v9", () => {
|
||
|
|
expect(AFL_WILDCARD_DRAW[1]).toEqual([7, 10]);
|
||
|
|
expect(AFL_WILDCARD_DRAW[2]).toEqual([8, 9]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("hosts the Elimination Finals with seeds 5 and 6", () => {
|
||
|
|
expect(AFL_ELIMINATION_HOSTS[1]).toBe(5);
|
||
|
|
expect(AFL_ELIMINATION_HOSTS[2]).toBe(6);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("resolveAflWildcardPlacements", () => {
|
||
|
|
it("sends the higher-ranked winner to 6th and the lower to 5th (7 and 8 win)", () => {
|
||
|
|
expect(slotsBySeed(bothDecided(7, 8))).toEqual({ 7: 2, 8: 1 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it("re-seeds when the lower seed wins the 7v10 game (10 and 8 win)", () => {
|
||
|
|
// The bug this replaces sent the 7v10 winner to 6th regardless, pairing 5th with
|
||
|
|
// 8th and handing 6th the weakest survivor.
|
||
|
|
expect(slotsBySeed(bothDecided(10, 8))).toEqual({ 8: 2, 10: 1 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it("re-seeds when the lower seed wins the 8v9 game (7 and 9 win)", () => {
|
||
|
|
expect(slotsBySeed(bothDecided(7, 9))).toEqual({ 7: 2, 9: 1 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it("re-seeds when both lower seeds win (10 and 9 win)", () => {
|
||
|
|
expect(slotsBySeed(bothDecided(10, 9))).toEqual({ 9: 2, 10: 1 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it("places the 7v10 winner alone, since its rank is settled either way", () => {
|
||
|
|
// 7th outranks both possible 8v9 winners; 10th is outranked by both.
|
||
|
|
expect(slotsBySeed([
|
||
|
|
{ matchNumber: 1, winnerSlot: 1 },
|
||
|
|
{ matchNumber: 2, winnerSlot: null },
|
||
|
|
])).toEqual({ 7: 2 });
|
||
|
|
|
||
|
|
expect(slotsBySeed([
|
||
|
|
{ matchNumber: 1, winnerSlot: 2 },
|
||
|
|
{ matchNumber: 2, winnerSlot: null },
|
||
|
|
])).toEqual({ 10: 1 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it("holds an 8v9 winner back until the 7v10 game is decided", () => {
|
||
|
|
// 8th and 9th both sit between 7th and 10th, so either slot is still possible.
|
||
|
|
expect(slotsBySeed([
|
||
|
|
{ matchNumber: 1, winnerSlot: null },
|
||
|
|
{ matchNumber: 2, winnerSlot: 1 },
|
||
|
|
])).toEqual({});
|
||
|
|
|
||
|
|
expect(slotsBySeed([
|
||
|
|
{ matchNumber: 1, winnerSlot: null },
|
||
|
|
{ matchNumber: 2, winnerSlot: 2 },
|
||
|
|
])).toEqual({});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("places nothing while both games are undecided", () => {
|
||
|
|
expect(resolveAflWildcardPlacements([
|
||
|
|
{ matchNumber: 1, winnerSlot: null },
|
||
|
|
{ matchNumber: 2, winnerSlot: null },
|
||
|
|
])).toEqual([]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("gives the same answer whichever result is entered first", () => {
|
||
|
|
for (const m1 of [7, 10] as const) {
|
||
|
|
for (const m2 of [8, 9] as const) {
|
||
|
|
const final = slotsBySeed(bothDecided(m1, m2));
|
||
|
|
|
||
|
|
// Whatever a single result places must survive the second result unchanged.
|
||
|
|
const m1First = slotsBySeed([
|
||
|
|
{ matchNumber: 1, winnerSlot: m1 === 7 ? 1 : 2 },
|
||
|
|
{ matchNumber: 2, winnerSlot: null },
|
||
|
|
]);
|
||
|
|
for (const [seed, slot] of Object.entries(m1First)) {
|
||
|
|
expect(final[Number(seed)]).toBe(slot);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it("rejects a match number outside the Wildcard draw", () => {
|
||
|
|
expect(() => resolveAflWildcardPlacements([{ matchNumber: 3, winnerSlot: 1 }])).toThrow(
|
||
|
|
/Unknown AFL Wildcard Round match number 3/
|
||
|
|
);
|
||
|
|
});
|
||
|
|
});
|