diff --git a/app/components/Simulation.tsx b/app/components/Simulation.tsx
index a41809b..1682998 100644
--- a/app/components/Simulation.tsx
+++ b/app/components/Simulation.tsx
@@ -4,10 +4,10 @@ import { useState, useMemo, useCallback, useEffect } from "react";
import { Match, Team } from "@/lib/types";
import { computeGroupTables, computeThirdPlaceTable } from "@/lib/standings";
import { qualifiedThirdGroups, resolveAnnexC } from "@/lib/bracket";
-import { resolveBracket, ResolvedTie } from "@/lib/resolve-bracket";
+import { resolveBracket } from "@/lib/resolve-bracket";
import {
loadOverrides, saveOverrides, buildSimMatches,
- propagateSimWinners, defaultScore, SimOverrides,
+ defaultScore, SimOverrides,
} from "@/lib/simulation";
import Bracket from "./Bracket";
import Flag from "./Flag";
@@ -76,11 +76,11 @@ export default function Simulation({ teams, matches }: { teams: Team[]; matches:
);
const simAnnexResolved = simAnnex != null;
- const { r32, later: laterRaw } = useMemo(
- () => resolveBracket(simMatches, teams, simTables, simThirds, simAnnex, simAnnexResolved),
+ const simBracket = useMemo(
+ () => resolveBracket(simMatches, teams, simTables, simThirds, simAnnex, simAnnexResolved, true),
[simMatches, teams, simTables, simThirds, simAnnex, simAnnexResolved],
);
- const later = useMemo(() => propagateSimWinners(r32, laterRaw), [r32, laterRaw]);
+ const { r32, later } = simBracket;
const preResolved = useMemo(() => ({ r32, later }), [r32, later]);
// ---- Eingabeliste: offene Spiele aus dem echten Feed ----
@@ -95,6 +95,11 @@ export default function Simulation({ teams, matches }: { teams: Team[]; matches:
const groupPhase = openMatches.filter((m) => m.stage === "GROUP");
const koPhase = openMatches.filter((m) => m.stage !== "GROUP");
+ // KO-Eingaben erst freigeben, wenn alle Gruppenspiele beendet sind.
+ const groupPhaseComplete = matches
+ .filter((m) => m.group != null)
+ .every((m) => m.status === "FINISHED");
+
const hasChanges = Object.keys(overrides).length > 0;
return (
@@ -144,11 +149,17 @@ export default function Simulation({ teams, matches }: { teams: Team[]; matches:
K.o.-Phase
-
- {koPhase.map((m) => (
-
- ))}
-
+ {groupPhaseComplete ? (
+
+ {koPhase.map((m) => (
+
+ ))}
+
+ ) : (
+
+ K.o.-Spiele werden zur Eingabe freigegeben, sobald die Gruppenphase abgeschlossen ist.
+
+ )}
)}
diff --git a/lib/resolve-bracket.ts b/lib/resolve-bracket.ts
index 8d02dcc..cd4edf6 100644
--- a/lib/resolve-bracket.ts
+++ b/lib/resolve-bracket.ts
@@ -130,10 +130,15 @@ function sideFrom(
}
// Baut die komplette aufgelöste Bracket-Struktur.
+// resolveWinners: wenn true, werden die Gewinner-TeamIDs aus den aufgelösten
+// R32-Slots abgeleitet (basierend auf den Spiel-Scores), statt aus den Team-IDs
+// des Feeds. Nötig für die Simulation, wo KO-Matches Scores aber keine Team-IDs
+// im Feed haben.
export function resolveBracket(
matches: Match[], teams: Team[], tables: GroupTable[],
thirds: ThirdPlaceRow[], assignment: ThirdAssignment | null,
annexResolved: boolean,
+ resolveWinners = false,
): { r32: ResolvedTie[]; later: Record } {
// Map: Match-Nummer -> Sieger-Team-ID (für Propagation in Folgerunden)
const winners = new Map();
@@ -153,8 +158,25 @@ export function resolveBracket(
const home = sideFrom(h.teamId, slotLabel(rm.home, assignment, winnerGroup), teams, feed, "home", h.provisional, h.tooltip);
const away = sideFrom(a.teamId, slotLabel(rm.away, assignment, winnerGroup), teams, feed, "away", a.provisional, a.tooltip);
- winners.set(rm.matchNumber, winnerOf(feed));
- losers.set(rm.matchNumber, loserOf(feed));
+ const feedWinner = winnerOf(feed);
+ if (resolveWinners && !feedWinner && feed && h.teamId && a.teamId
+ && feed.homeScore != null && feed.awayScore != null) {
+ // Simulation: Gewinner aus Scores und aufgelösten Team-IDs ableiten
+ if (feed.homeScore > feed.awayScore) {
+ winners.set(rm.matchNumber, h.teamId);
+ losers.set(rm.matchNumber, loserOf(feed) ?? a.teamId);
+ } else if (feed.awayScore > feed.homeScore) {
+ winners.set(rm.matchNumber, a.teamId);
+ losers.set(rm.matchNumber, loserOf(feed) ?? h.teamId);
+ } else {
+ // Unentschieden → Heim gewinnt
+ winners.set(rm.matchNumber, h.teamId);
+ losers.set(rm.matchNumber, loserOf(feed) ?? a.teamId);
+ }
+ } else {
+ winners.set(rm.matchNumber, feedWinner);
+ losers.set(rm.matchNumber, loserOf(feed));
+ }
decided.set(rm.matchNumber, feed?.status === "FINISHED");
return {
matchNumber: rm.matchNumber, stage: "R32",
@@ -177,8 +199,24 @@ export function resolveBracket(
const awayTtip = `${km.losers ? "Verlierer" : "Sieger"} aus Spiel ${km.fromAway}`;
const home = sideFrom(homeId, homeLabel, teams, feed, "home", homeProv, homeTtip);
const away = sideFrom(awayId, awayLabel, teams, feed, "away", awayProv, awayTtip);
- winners.set(km.matchNumber, winnerOf(feed));
- losers.set(km.matchNumber, loserOf(feed));
+
+ const feedWinner = winnerOf(feed);
+ if (resolveWinners && !feedWinner && feed && homeId && awayId
+ && feed.homeScore != null && feed.awayScore != null) {
+ if (feed.homeScore > feed.awayScore) {
+ winners.set(km.matchNumber, homeId);
+ losers.set(km.matchNumber, loserOf(feed) ?? awayId);
+ } else if (feed.awayScore > feed.homeScore) {
+ winners.set(km.matchNumber, awayId);
+ losers.set(km.matchNumber, loserOf(feed) ?? homeId);
+ } else {
+ winners.set(km.matchNumber, homeId);
+ losers.set(km.matchNumber, loserOf(feed) ?? awayId);
+ }
+ } else {
+ winners.set(km.matchNumber, feedWinner);
+ losers.set(km.matchNumber, loserOf(feed));
+ }
decided.set(km.matchNumber, feed?.status === "FINISHED");
later[km.matchNumber] = {
matchNumber: km.matchNumber, stage: km.stage,