algorithmus improvements

This commit is contained in:
Marc Wieland 2026-01-23 11:30:15 +01:00
parent 36d7821629
commit 7d68026955
2 changed files with 166 additions and 6 deletions

View File

@ -1,6 +1,6 @@
import React, { createContext, useContext, useState, ReactNode, useMemo } from 'react'; import React, { createContext, useContext, useState, ReactNode, useMemo } from 'react';
import { Team, Round, TeamScore, Match, MatchResult } from '@/types/tournament'; import { Team, Round, TeamScore, Match, MatchResult } from '@/types/tournament';
import { generateMatches } from '@/utils/tournamentUtils'; import { generateMatches, createNextRoundMatches } from '@/utils/tournamentUtils';
interface TournamentContextType { interface TournamentContextType {
teams: Team[]; teams: Team[];
@ -53,7 +53,17 @@ export const TournamentProvider = ({ children }: { children: ReactNode }) => {
const startNewRound = () => { const startNewRound = () => {
if (currentRound && !currentRound.completed) return; if (currentRound && !currentRound.completed) return;
const state = generateMatches(teams, fieldCount); let state;
if (rounds.length === 0) {
// First round: initial random generation
state = generateMatches(teams, fieldCount);
} else {
// Subsequent rounds: rotation system
const lastRound = rounds[rounds.length - 1];
state = createNextRoundMatches(lastRound, fieldCount);
}
const allMatches = [...state.bundesligaMatches, ...state.championsMatches]; const allMatches = [...state.bundesligaMatches, ...state.championsMatches];
const newRound: Round = { const newRound: Round = {
@ -69,12 +79,26 @@ export const TournamentProvider = ({ children }: { children: ReactNode }) => {
}; };
const updateMatchResult = (matchId: string, scoreA: number, scoreB: number) => { const updateMatchResult = (matchId: string, scoreA: number, scoreB: number) => {
const diff = scoreB - scoreA; const diff = Math.abs(scoreB - scoreA);
let pointsA: number;
let pointsB: number;
if (scoreA > scoreB) {
// Team A gewinnt
pointsA = diff + 2;
pointsB = -diff;
} else {
// Team B gewinnt
pointsA = -diff;
pointsB = diff + 2;
}
const result: MatchResult = { const result: MatchResult = {
scoreA, scoreA,
scoreB, scoreB,
pointsA: -diff, pointsA,
pointsB: diff, pointsB,
}; };
setRounds((prev) => setRounds((prev) =>

View File

@ -1,4 +1,4 @@
import { Team, Match, TournamentState, League } from '@/types/tournament'; import { Team, Match, TournamentState, League, Round } from '@/types/tournament';
export const generateMatches = (teams: Team[], fieldCount: number): TournamentState => { export const generateMatches = (teams: Team[], fieldCount: number): TournamentState => {
const bundesligaTeams = teams.filter((t) => t.league === 'bundesliga'); const bundesligaTeams = teams.filter((t) => t.league === 'bundesliga');
@ -71,3 +71,139 @@ export const generateMatches = (teams: Team[], fieldCount: number): TournamentSt
championsFields, championsFields,
}; };
}; };
/**
* Rotation system for subsequent rounds
* Implements 3 cases:
* - Case 1: Even teams (no waiting) - all rotate using Circle Rotation algorithm
* - Case 2: Odd teams (1 waiting) - waiting joins, last playing goes to waiting
* - Case 3: Multiple waiting (3+) - all waiting play, last N playing go to waiting
*/
export const createNextRoundMatches = (
previousRound: Round,
fieldCount: number
): TournamentState => {
const createRotatedMatches = (
currentMatches: Match[],
waitingTeams: Team[],
availableFields: number,
league: League,
fieldOffset: number
): { matches: Match[]; waiting: Team[] } => {
if (currentMatches.length === 0) {
return { matches: [], waiting: waitingTeams };
}
// Collect all teams from current matches and waiting
const playingTeams = currentMatches.flatMap((m) => [m.teamA, m.teamB]);
const allTeamsForLeague = [...playingTeams, ...waitingTeams];
if (allTeamsForLeague.length < 2) {
return { matches: [], waiting: allTeamsForLeague };
}
const numWaiting = waitingTeams.length;
let rotatedTeams: Team[] = [];
if (numWaiting === 0) {
// Case 1: No waiting teams - use Circle Rotation algorithm
rotatedTeams = rotateTeamsInCircle(allTeamsForLeague);
} else if (numWaiting === 1) {
// Case 2: 1 waiting team - waiting joins, last playing goes to waiting
rotatedTeams = [waitingTeams[0], ...playingTeams.slice(0, -1), playingTeams[playingTeams.length - 1]];
} else {
// Case 3: Multiple waiting - all waiting play, last N playing go to waiting
rotatedTeams = [
...waitingTeams,
...playingTeams.slice(0, playingTeams.length - numWaiting),
...playingTeams.slice(-numWaiting),
];
}
// Now create matches based on available fields
const maxTeamsPlaying = availableFields * 2;
const teamsToPlay = rotatedTeams.slice(0, maxTeamsPlaying);
const newWaiting = rotatedTeams.slice(maxTeamsPlaying);
// Create matches from playing teams
const matches: Match[] = [];
let teamIndex = 0;
let fieldNumber = fieldOffset + 1;
while (teamIndex < teamsToPlay.length - 1 && matches.length < availableFields) {
matches.push({
id: crypto.randomUUID(),
teamA: teamsToPlay[teamIndex],
teamB: teamsToPlay[teamIndex + 1],
fieldNumber,
league,
});
teamIndex += 2;
fieldNumber++;
}
return { matches, waiting: newWaiting };
};
// Determine field allocation
const bundesligaMatches = previousRound.matches.filter((m) => m.league === 'bundesliga');
const championsMatches = previousRound.matches.filter((m) => m.league === 'champions');
const bundesligaHasTeams = bundesligaMatches.length > 0 || previousRound.bundesligaWaiting.length > 0;
const championsHasTeams = championsMatches.length > 0 || previousRound.championsWaiting.length > 0;
let bundesligaFields = 0;
let championsFields = 0;
if (bundesligaHasTeams && championsHasTeams) {
bundesligaFields = Math.floor(fieldCount / 2);
championsFields = fieldCount - bundesligaFields;
} else if (bundesligaHasTeams) {
bundesligaFields = fieldCount;
} else if (championsHasTeams) {
championsFields = fieldCount;
}
// Apply rotation for each league
const bundesliga = createRotatedMatches(
bundesligaMatches,
previousRound.bundesligaWaiting,
bundesligaFields,
'bundesliga',
0
);
const champions = createRotatedMatches(
championsMatches,
previousRound.championsWaiting,
championsFields,
'champions',
bundesligaFields
);
return {
bundesligaMatches: bundesliga.matches,
championsMatches: champions.matches,
bundesligaWaiting: bundesliga.waiting,
championsWaiting: champions.waiting,
bundesligaFields,
championsFields,
};
};
/**
* Circle Rotation Algorithm (Roundrobin)
* Keeps first team fixed, rotates all others by 1 position
* Example: [A, B, C, D] [A, D, B, C] [A, C, D, B] [A, B, C, D]
*/
function rotateTeamsInCircle(teams: Team[]): Team[] {
if (teams.length <= 2) return teams;
const fixed = teams[0];
const rotating = teams.slice(1);
// Rotate: move last to first
const rotated = [rotating[rotating.length - 1], ...rotating.slice(0, -1)];
return [fixed, ...rotated];
}