algorithmus improvements
This commit is contained in:
parent
36d7821629
commit
7d68026955
@ -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) =>
|
||||||
|
|||||||
@ -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];
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user