From 253b8cfbcd18c7d759900141b87dda9efa61d155 Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Wed, 24 Jun 2026 10:27:15 -0500 Subject: [PATCH] fix --- app/components/Simulation.tsx | 31 +++++++++++++++-------- lib/resolve-bracket.ts | 46 ++++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 14 deletions(-) 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,