Skip to content

Commit 032bdf0

Browse files
authored
chore: finalize world cup bracket teams (#615)
1 parent 00c9ed0 commit 032bdf0

5 files changed

Lines changed: 134 additions & 28 deletions

File tree

games/world-cup/pinocchio/clients/typescript/src/fifa-2026.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,15 @@ export const MATCH_KICKOFFS: readonly string[] = [
9191
];
9292

9393
/**
94-
* Team slot `0..31` → seeded label. Already-qualified teams (Germany, USA,
95-
* Mexico) are concrete; the rest are group-position placeholders (`1C` = winner
96-
* of Group C, `2F` = runner-up of Group F, `3ABCDF` = third place from one of
97-
* those groups) until the draw fills them. Slot order is bracket order: this is
98-
* what makes the internal tree reproduce FIFA's matchups, so do not reorder.
94+
* Team slot `0..31` → group-position seed label: `1C` = winner of Group C,
95+
* `2F` = runner-up of Group F, `3ABCDF` = best third place from one of those
96+
* groups. Every entry is a positional seed and stays fixed across draws — the
97+
* real nation that lands in each slot lives in `TEAM_NAMES` (teams.ts). Slot
98+
* order is bracket order: this is what makes the internal tree reproduce FIFA's
99+
* matchups, so do not reorder.
99100
*/
100101
export const SLOT_LABELS: readonly string[] = [
101-
'Germany',
102+
'1E',
102103
'3ABCDF',
103104
'1I',
104105
'3CDFGH',
@@ -110,19 +111,19 @@ export const SLOT_LABELS: readonly string[] = [
110111
'2L',
111112
'1H',
112113
'2J',
113-
'USA',
114+
'1D',
114115
'3BEFIJ',
115116
'1G',
116117
'3AEHIJ', // slots 8..15
117118
'1C',
118119
'2F',
119120
'2E',
120121
'2I',
121-
'Mexico',
122+
'1A',
122123
'3CEFHI',
123124
'1L',
124125
'3EHIJK', // slots 16..23
125-
'Argentina',
126+
'1J',
126127
'2H',
127128
'2D',
128129
'2G',
@@ -223,6 +224,9 @@ export function assertFifaScheduleConsistent(): void {
223224
if (!unique.has(m)) throw new Error(`missing match M${m}`);
224225
}
225226
if (new Set(SLOT_LABELS).size !== TEAM_COUNT) throw new Error('slot labels are not unique');
227+
for (const label of SLOT_LABELS) {
228+
if (!/^[123][A-L]+$/.test(label)) throw new Error(`slot label ${label} is not a group-position seed`);
229+
}
226230

227231
for (const [label, feeders] of FIFA_FEEDERS) {
228232
const game = gameIndexOf(label);

games/world-cup/pinocchio/clients/typescript/src/teams.ts

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,52 @@
11
import { TEAM_COUNT } from './bracket.js';
2-
import { SLOT_LABELS } from './fifa-2026.js';
32

43
/**
5-
* Display names for the 32 positional team slots (`0..31`), defaulting to the
6-
* FIFA 2026 seeding: already-qualified teams as country names and the rest as
7-
* group-position placeholders (`1C`, `2F`, `3ABCDF`, …) — see {@link SLOT_LABELS}.
4+
* Display names for the 32 positional team slots (`0..31`), resolved to the real
5+
* FIFA 2026 knockout-stage qualifiers now that the bracket is final. Each entry's
6+
* trailing comment keeps the slot's original seeding id (`1C`, `2F`, `3ABCDF`, …
7+
* from `SLOT_LABELS` in `fifa-2026.ts`) so the mapping back to group positions
8+
* stays auditable.
89
*
9-
* LAUNCH-DAY REPLACEMENT POINT: as the draw fills group positions, this array is
10-
* the single source the webapp swaps in to flow real names through every display
11-
* helper, and nothing else needs to change. Callers that want to override without
12-
* editing this file can pass their own `names` array to the display helpers. The
13-
* slot order is bracket order and must not change.
10+
* LAUNCH-DAY REPLACEMENT POINT: this array is the single source the webapp swaps
11+
* in to flow real names through every display helper, and nothing else needs to
12+
* change. Callers that want to override without editing this file can pass their
13+
* own `names` array to the display helpers. The slot order is bracket order and
14+
* must not change.
1415
*/
15-
export const TEAM_NAMES: readonly string[] = SLOT_LABELS;
16+
export const TEAM_NAMES: readonly string[] = [
17+
'Germany', // 1E
18+
'Paraguay', // 3ABCDF
19+
'France', // 1I
20+
'Sweden', // 3CDFGH
21+
'South Africa', // 2A
22+
'Canada', // 2B
23+
'Netherlands', // 1F
24+
'Morocco', // 2C — slots 0..7
25+
'Portugal', // 2K
26+
'Croatia', // 2L
27+
'Spain', // 1H
28+
'Austria', // 2J
29+
'USA', // 1D
30+
'Bosnia & Herzegovina', // 3BEFIJ
31+
'Belgium', // 1G
32+
'Senegal', // 3AEHIJ — slots 8..15
33+
'Brazil', // 1C
34+
'Japan', // 2F
35+
"Côte d'Ivoire", // 2E
36+
'Norway', // 2I
37+
'Mexico', // 1A
38+
'Ecuador', // 3CEFHI
39+
'England', // 1L
40+
'DR Congo', // 3EHIJK — slots 16..23
41+
'Argentina', // 1J
42+
'Cape Verde', // 2H
43+
'Australia', // 2D
44+
'Egypt', // 2G
45+
'Switzerland', // 1B
46+
'Algeria', // 3EFGIJ
47+
'Colombia', // 1K
48+
'Ghana', // 3DEIJL — slots 24..31
49+
];
1650

1751
/**
1852
* The display name for a positional team slot. Defaults to {@link TEAM_NAMES};

games/world-cup/pinocchio/clients/typescript/test/bracket-display.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import { bracketRows, champion } from '../src/bracket-display.ts';
66
import { teamName, TEAM_NAMES } from '../src/teams.ts';
77

88
describe('teams', () => {
9-
test('TEAM_NAMES has 32 entries defaulting to the FIFA seeding', () => {
9+
test('TEAM_NAMES has 32 entries resolved to the final bracket teams', () => {
1010
assert.equal(TEAM_NAMES.length, 32);
1111
assert.equal(TEAM_NAMES[0], 'Germany');
12-
assert.equal(TEAM_NAMES[31], '3DEIJL');
12+
assert.equal(TEAM_NAMES[31], 'Ghana');
1313
});
1414

1515
test('teamName resolves a slot and honors a custom names override', () => {

games/world-cup/pinocchio/clients/typescript/test/fifa-2026.test.ts

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,80 @@ describe('slot labels', () => {
6161
assert.equal(new Set(SLOT_LABELS).size, TEAM_COUNT);
6262
});
6363

64-
test('place the already-qualified teams', () => {
65-
assert.equal(SLOT_LABELS[0], 'Germany');
66-
assert.equal(SLOT_LABELS[12], 'USA');
67-
assert.equal(SLOT_LABELS[20], 'Mexico');
64+
test('are all group-position seeds (1=winner, 2=runner-up, 3=best third)', () => {
65+
for (const label of SLOT_LABELS) {
66+
assert.match(label, /^[123][A-L]+$/, `${label} is not a group-position seed`);
67+
}
68+
// group winners the example bracket fills with host/seed nations
69+
assert.equal(SLOT_LABELS[0], '1E'); // Germany
70+
assert.equal(SLOT_LABELS[12], '1D'); // USA (FIFA host position D1)
71+
assert.equal(SLOT_LABELS[20], '1A'); // Mexico (FIFA host position A1)
72+
assert.equal(SLOT_LABELS[24], '1J'); // Argentina
73+
});
74+
75+
test('TEAM_NAMES resolves every slot to a distinct real team', () => {
76+
assert.equal(TEAM_NAMES.length, TEAM_COUNT);
77+
assert.equal(new Set(TEAM_NAMES).size, TEAM_COUNT);
6878
});
79+
});
6980

70-
test('TEAM_NAMES defaults to the FIFA seeding', () => {
71-
assert.deepEqual(TEAM_NAMES, SLOT_LABELS);
81+
describe('final-bracket seeding', () => {
82+
// Each slot's nation and the FIFA group-position seed it occupies, transcribed
83+
// from the final bracket / FIFA regulations Annex (e.g. USA won Group D, so it
84+
// is the `1D` seed). Kept independent of TEAM_NAMES and SLOT_LABELS so a
85+
// mis-edit of either array is caught here.
86+
const EXPECTED: ReadonlyArray<readonly [team: string, seed: string]> = [
87+
['Germany', '1E'],
88+
['Paraguay', '3ABCDF'],
89+
['France', '1I'],
90+
['Sweden', '3CDFGH'],
91+
['South Africa', '2A'],
92+
['Canada', '2B'],
93+
['Netherlands', '1F'],
94+
['Morocco', '2C'],
95+
['Portugal', '2K'],
96+
['Croatia', '2L'],
97+
['Spain', '1H'],
98+
['Austria', '2J'],
99+
['USA', '1D'],
100+
['Bosnia & Herzegovina', '3BEFIJ'],
101+
['Belgium', '1G'],
102+
['Senegal', '3AEHIJ'],
103+
['Brazil', '1C'],
104+
['Japan', '2F'],
105+
["Côte d'Ivoire", '2E'],
106+
['Norway', '2I'],
107+
['Mexico', '1A'],
108+
['Ecuador', '3CEFHI'],
109+
['England', '1L'],
110+
['DR Congo', '3EHIJK'],
111+
['Argentina', '1J'],
112+
['Cape Verde', '2H'],
113+
['Australia', '2D'],
114+
['Egypt', '2G'],
115+
['Switzerland', '1B'],
116+
['Algeria', '3EFGIJ'],
117+
['Colombia', '1K'],
118+
['Ghana', '3DEIJL'],
119+
];
120+
121+
test('the expected table covers all 32 slots', () => {
122+
assert.equal(EXPECTED.length, TEAM_COUNT);
123+
});
124+
125+
test('each slot places the right nation in its FIFA seed', () => {
126+
EXPECTED.forEach(([team, seed], slot) => {
127+
assert.equal(TEAM_NAMES[slot], team, `nation at slot ${slot}`);
128+
assert.equal(SLOT_LABELS[slot], seed, `seed at slot ${slot}`);
129+
});
72130
});
73131
});
74132

75133
describe('contestantsOf', () => {
76134
test('Round-of-32 games show their two team slots', () => {
77135
const [a, b] = r32Slots(0);
78136
assert.deepEqual(contestantsOf(0), [SLOT_LABELS[a], SLOT_LABELS[b]]);
79-
assert.deepEqual(contestantsOf(0), ['Germany', '3ABCDF']);
137+
assert.deepEqual(contestantsOf(0), ['1E', '3ABCDF']);
80138
});
81139

82140
test('later rounds show feeder-match winners', () => {

games/world-cup/pinocchio/webapp/src/components/bracket/teams.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,21 @@ export interface DisplayTeam {
1717
* `SLOT_LABELS`, add the matching code entry here.
1818
*/
1919
const CODES: Readonly<Record<string, string>> = {
20+
Algeria: 'DZ',
2021
Argentina: 'AR',
2122
Australia: 'AU',
23+
Austria: 'AT',
2224
Belgium: 'BE',
25+
'Bosnia & Herzegovina': 'BA',
2326
Brazil: 'BR',
2427
Cameroon: 'CM',
2528
Canada: 'CA',
29+
'Cape Verde': 'CV',
2630
Colombia: 'CO',
2731
'Costa Rica': 'CR',
2832
Croatia: 'HR',
33+
"Côte d'Ivoire": 'CI',
34+
'DR Congo': 'CD',
2935
Denmark: 'DK',
3036
Ecuador: 'EC',
3137
Egypt: 'EG',
@@ -40,13 +46,17 @@ const CODES: Readonly<Record<string, string>> = {
4046
Morocco: 'MA',
4147
Netherlands: 'NL',
4248
Nigeria: 'NG',
49+
Norway: 'NO',
50+
Paraguay: 'PY',
4351
Portugal: 'PT',
4452
Qatar: 'QA',
4553
'Saudi Arabia': 'SA',
4654
Senegal: 'SN',
4755
'South Africa': 'ZA',
4856
'South Korea': 'KR',
4957
Spain: 'ES',
58+
Sweden: 'SE',
59+
Switzerland: 'CH',
5060
USA: 'US',
5161
Uruguay: 'UY',
5262
};

0 commit comments

Comments
 (0)