feat: add time controls, correspondence games, and tournament mode

**Zeitmodi (Blitz/Rapid):**
- 5, 10, 30 Min Zeitkontrolle bei Spielerstellung
- Server-seitige Uhren: Zeit wird bei jedem Zug abgezogen
- Timeout-Erkennung via claimTimeout Socket-Event
- Schachuhr-Anzeige in GamePage (rot bei < 30s)
- Turnierspiele erhalten automatisch Zeitkontrolle

**Tagespartien (Correspondence):**
- Neue DB-Tabelle correspondence_game
- REST API: erstellen, beitreten, Zug machen, aufgeben
- Seite /correspondence: Liste aktiver Tagespartien
- Seite /correspondence/[code]: Spielen mit interaktivem Brett
- Einladungslink teilen, Gegner tritt via Link bei

**Turniermodus (Round-Robin):**
- Neue DB-Tabellen: tournament, tournament_player, tournament_round
- Round-Robin Paarungen mit Circle-Methode
- Turnier erstellen: /tournament/create
- Turnierliste: /tournament
- Turnierseite /tournament/[code]: Standings, Runden, Spiele
- Automatische Aktivisierung der nächsten Runde
- Turnierspiele werden als normale activeGames erstellt
- Ergebnisse aktualisieren Punkte automatisch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Michess
2026-04-14 09:44:35 +02:00
parent 09e5271dfb
commit aeb5fe313e
25 changed files with 1653 additions and 16 deletions

57
types/index.d.ts vendored
View File

@@ -4,7 +4,7 @@ export interface Game {
white?: User;
black?: User;
winner?: "white" | "black" | "draw";
endReason?: "draw" | "checkmate" | "stalemate" | "repetition" | "insufficient" | "abandoned";
endReason?: "draw" | "checkmate" | "stalemate" | "repetition" | "insufficient" | "abandoned" | "timeout" | "resign";
host?: User;
code?: string;
unlisted?: boolean;
@@ -14,6 +14,10 @@ export interface Game {
endedAt?: number;
vsAi?: boolean;
aiLevel?: number;
timeControl?: number;
whiteTimeMs?: number;
blackTimeMs?: number;
lastMoveAt?: number;
}
export interface User {
@@ -50,3 +54,54 @@ export interface Friendship {
draws?: number;
createdAt?: string;
}
export interface CorrespondenceGame {
id?: number;
code?: string;
white?: { id?: number; name?: string };
black?: { id?: number; name?: string };
pgn?: string;
winner?: "white" | "black" | "draw";
endReason?: "checkmate" | "stalemate" | "repetition" | "insufficient" | "resign" | "timeout" | "draw";
daysPerMove?: number;
lastMoveAt?: number;
startedAt?: number;
endedAt?: number;
}
export interface Tournament {
id?: number;
code?: string;
name?: string;
hostId?: number;
hostName?: string;
status?: "waiting" | "active" | "finished";
timeControl?: number;
maxPlayers?: number;
currentRound?: number;
totalRounds?: number;
players?: TournamentPlayer[];
rounds?: TournamentRound[];
createdAt?: number;
}
export interface TournamentPlayer {
tournamentId?: number;
userId?: number;
userName?: string;
score?: number;
gamesPlayed?: number;
}
export interface TournamentRound {
id?: number;
tournamentId?: number;
round?: number;
gameCode?: string;
gameId?: number;
whiteId?: number;
whiteName?: string;
blackId?: number;
blackName?: string;
result?: "white" | "black" | "draw" | null;
}