Files
wm-projekt/lib/venues.ts
2026-06-22 22:11:04 -05:00

207 lines
7.8 KiB
TypeScript

// Stadion-Zuordnung — die alleinige Quelle für Spielorte (Feed-venue wird ignoriert).
//
// Gruppenphase: über die Teampaarung (Ländercodes). Eindeutig, da jede Paarung
// im Turnier genau einmal vorkommt — unabhängig von Heim/Auswärts-Reihenfolge
// und von gleichzeitigen Anstößen.
// K.o.-Phase: über die FIFA-Spielnummer (dort sind die Anstöße eindeutig).
import { GroupId, Match, Team } from "./types";
// --- GRUPPENPHASE: "CODE1-CODE2" -> Stadion ---
export const FIXTURES_GROUP_STAGE: Record<string, string> = {
// Gruppe A
"MEX-RSA": "Estadio Azteca, Mexiko-Stadt",
"KOR-CZE": "Estadio Akron, Guadalajara",
"CZE-RSA": "Mercedes-Benz Stadium, Atlanta",
"MEX-KOR": "Estadio Akron, Guadalajara",
"CZE-MEX": "Estadio Azteca, Mexiko-Stadt",
"RSA-KOR": "Estadio BBVA, Monterrey",
// Gruppe B
"CAN-BIH": "BMO Field, Toronto",
"QAT-SUI": "Levi's Stadium, San Francisco Bay Area",
"SUI-BIH": "SoFi Stadium, Los Angeles",
"CAN-QAT": "BC Place, Vancouver",
"SUI-CAN": "BC Place, Vancouver",
"BIH-QAT": "Lumen Field, Seattle",
// Gruppe C
"BRA-MAR": "Gillette Stadium, Boston",
"HAI-SCO": "MetLife Stadium, New York/New Jersey",
"BRA-HAI": "Lincoln Financial Field, Philadelphia",
"SCO-MAR": "Gillette Stadium, Boston",
"SCO-BRA": "Hard Rock Stadium, Miami",
"MAR-HAI": "Mercedes-Benz Stadium, Atlanta",
// Gruppe D
"USA-PAR": "SoFi Stadium, Los Angeles",
"AUS-TUR": "BC Place, Vancouver",
"TUR-PAR": "Levi's Stadium, San Francisco Bay Area",
"USA-AUS": "Lumen Field, Seattle",
"TUR-USA": "SoFi Stadium, Los Angeles",
"PAR-AUS": "Levi's Stadium, San Francisco Bay Area",
// Gruppe E
"CIV-ECU": "Lincoln Financial Field, Philadelphia",
"GER-CUR": "NRG Stadium, Houston",
"GER-CIV": "BMO Field, Toronto",
"ECU-CUR": "Arrowhead Stadium, Kansas City",
"CUR-CIV": "Lincoln Financial Field, Philadelphia",
"ECU-GER": "MetLife Stadium, New York/New Jersey",
// Gruppe F
"NED-JPN": "AT&T Stadium, Dallas",
"SWE-TUN": "Estadio BBVA, Monterrey",
"NED-SWE": "NRG Stadium, Houston",
"TUN-JPN": "Estadio BBVA, Monterrey",
"JPN-SWE": "AT&T Stadium, Dallas",
"TUN-NED": "Arrowhead Stadium, Kansas City",
// Gruppe G
"IRN-NZL": "SoFi Stadium, Los Angeles",
"BEL-EGY": "Lumen Field, Seattle",
"BEL-IRN": "SoFi Stadium, Los Angeles",
"NZL-EGY": "BC Place, Vancouver",
"NZL-BEL": "Lumen Field, Seattle",
"EGY-IRN": "BC Place, Vancouver",
// Gruppe H
"KSA-URU": "Hard Rock Stadium, Miami",
"ESP-CPV": "Mercedes-Benz Stadium, Atlanta",
"URU-CPV": "Mercedes-Benz Stadium, Atlanta",
"ESP-KSA": "Hard Rock Stadium, Miami",
"URU-ESP": "Estadio Akron, Guadalajara",
"CPV-KSA": "NRG Stadium, Houston",
// Gruppe I
"FRA-SEN": "MetLife Stadium, New York/New Jersey",
"IRQ-NOR": "Gillette Stadium, Boston",
"NOR-SEN": "Lincoln Financial Field, Philadelphia",
"FRA-IRQ": "MetLife Stadium, New York/New Jersey",
"NOR-FRA": "Gillette Stadium, Boston",
"SEN-IRQ": "BMO Field, Toronto",
// Gruppe J
"ARG-ALG": "Arrowhead Stadium, Kansas City",
"AUT-JOR": "Levi's Stadium, San Francisco Bay Area",
"ARG-AUT": "AT&T Stadium, Dallas",
"JOR-ALG": "Levi's Stadium, San Francisco Bay Area",
"JOR-ARG": "Arrowhead Stadium, Kansas City",
"ALG-AUT": "NRG Stadium, Houston",
// Gruppe K
"UZB-COL": "Estadio Azteca, Mexiko-Stadt",
"POR-COD": "NRG Stadium, Houston",
"POR-UZB": "NRG Stadium, Houston",
"COL-COD": "Estadio Akron, Guadalajara",
"COL-POR": "Hard Rock Stadium, Miami",
"COD-UZB": "Estadio BBVA, Monterrey",
// Gruppe L
"GHA-PAN": "BMO Field, Toronto",
"ENG-CRO": "AT&T Stadium, Dallas",
"ENG-GHA": "Gillette Stadium, Boston",
"PAN-CRO": "BMO Field, Toronto",
"PAN-ENG": "MetLife Stadium, New York/New Jersey",
"CRO-GHA": "Arrowhead Stadium, Kansas City",
};
// Reihenfolge-unabhängiger Lookup: baut beide Schlüsselrichtungen.
const PAIR_VENUE = new Map<string, string>();
for (const [key, venue] of Object.entries(FIXTURES_GROUP_STAGE)) {
const [a, b] = key.split("-");
PAIR_VENUE.set(`${a}-${b}`, venue);
PAIR_VENUE.set(`${b}-${a}`, venue);
}
// --- K.O.-PHASE: FIFA-Spielnummer -> Stadion ---
export const VENUE_BY_KO_MATCH: Record<number, string> = {
// Sechzehntelfinale (R32)
73: "SoFi Stadium, Los Angeles",
74: "Gillette Stadium, Boston",
75: "Estadio Azteca, Mexiko-Stadt",
76: "Hard Rock Stadium, Miami",
77: "Mercedes-Benz Stadium, Atlanta",
78: "AT&T Stadium, Dallas",
79: "Levi's Stadium, San Francisco Bay Area",
80: "Lumen Field, Seattle",
81: "MetLife Stadium, New York/New Jersey",
82: "Lincoln Financial Field, Philadelphia",
83: "BMO Field, Toronto",
84: "NRG Stadium, Houston",
85: "BC Place, Vancouver",
86: "Estadio BBVA, Monterrey",
87: "Arrowhead Stadium, Kansas City",
88: "AT&T Stadium, Dallas",
// Achtelfinale (R16)
89: "MetLife Stadium, New York/New Jersey",
90: "Hard Rock Stadium, Miami",
91: "NRG Stadium, Houston",
92: "Mercedes-Benz Stadium, Atlanta",
93: "AT&T Stadium, Dallas",
94: "Lumen Field, Seattle",
95: "BC Place, Vancouver",
96: "Estadio Azteca, Mexiko-Stadt",
// Viertelfinale
97: "Gillette Stadium, Boston",
98: "SoFi Stadium, Los Angeles",
99: "Hard Rock Stadium, Miami",
100: "Arrowhead Stadium, Kansas City",
// Halbfinale
101: "AT&T Stadium, Dallas",
102: "Mercedes-Benz Stadium, Atlanta",
// Spiel um Platz 3
103: "Hard Rock Stadium, Miami",
// Finale
104: "MetLife Stadium, New York/New Jersey",
};
// Manche Feeds nutzen abweichende 3-Buchstaben-Codes als die Map oben.
// Diese Tabelle übersetzt bekannte Feed-Codes auf den Map-Code.
// (Beispiel: FIFA/ISO nutzt CUW für Curaçao, die Map verwendet CUR.)
const CODE_ALIAS: Record<string, string> = {
CUW: "CUR", // Curaçao
CRC: "CRC", // (Platzhalter, falls weitere auftauchen)
};
// Fallback über den Teamnamen, falls ein Code unbekannt/abweichend ist.
// Schlüssel: normalisierter Name -> Map-Code.
const NAME_TO_CODE: Record<string, string> = {
"curacao": "CUR", "curaçao": "CUR",
"uruguay": "URU",
"cape verde": "CPV", "cape verde islands": "CPV", "cabo verde": "CPV",
"saudi arabia": "KSA",
"ivory coast": "CIV", "côte d'ivoire": "CIV", "cote d'ivoire": "CIV",
"south korea": "KOR", "korea republic": "KOR",
"south africa": "RSA",
"czechia": "CZE", "czech republic": "CZE",
"bosnia and herzegovina": "BIH", "bosnia-herzegovina": "BIH",
"dr congo": "COD", "congo dr": "COD", "democratic republic of the congo": "COD",
};
// Normalisiert den Code eines Teams auf den in der Map verwendeten Code.
function canonicalCode(team: Team | undefined): string | null {
if (!team) return null;
if (team.code) {
const c = team.code.toUpperCase();
if (CODE_ALIAS[c]) return CODE_ALIAS[c];
// Wenn der Code direkt in der Map vorkommt, nimm ihn.
return c;
}
// kein Code -> über den Namen versuchen
const n = team.name?.toLowerCase().trim();
return n ? (NAME_TO_CODE[n] ?? null) : null;
}
// Liefert das Stadion eines Spiels.
// Gruppenspiel -> über Teampaarung (Codes, mit Alias/Namen-Fallback),
// K.o.-Spiel -> über Spielnummer.
export function venueFor(m: Match, teams: Team[]): string | null {
if (m.group != null) {
const home = teams.find((t) => t.id === m.homeTeamId);
const away = teams.find((t) => t.id === m.awayTeamId);
let hc = canonicalCode(home);
let ac = canonicalCode(away);
// Erstversuch mit Codes
let venue = hc && ac ? PAIR_VENUE.get(`${hc}-${ac}`) : undefined;
// Falls kein Treffer: über die Namen normalisieren und erneut probieren.
if (!venue) {
const hn = home?.name?.toLowerCase().trim();
const an = away?.name?.toLowerCase().trim();
hc = (hn && NAME_TO_CODE[hn]) || hc;
ac = (an && NAME_TO_CODE[an]) || ac;
venue = hc && ac ? PAIR_VENUE.get(`${hc}-${ac}`) : undefined;
}
return venue ?? null;
}
return VENUE_BY_KO_MATCH[m.matchNumber] ?? null;
}