From 658ff0f10a393c971bfa9c88db5e725e5566b12b Mon Sep 17 00:00:00 2001 From: Jonas <77726472+kobolol@users.noreply.github.com> Date: Fri, 13 Mar 2026 08:56:54 +0100 Subject: [PATCH] docs: add comprehensive technical specification --- TECHNISCHE_SPEZIFIKATION.md | 1352 +++++++++++++++++++++++++++++++++++ 1 file changed, 1352 insertions(+) create mode 100644 TECHNISCHE_SPEZIFIKATION.md diff --git a/TECHNISCHE_SPEZIFIKATION.md b/TECHNISCHE_SPEZIFIKATION.md new file mode 100644 index 0000000..adbf0f0 --- /dev/null +++ b/TECHNISCHE_SPEZIFIKATION.md @@ -0,0 +1,1352 @@ +# Technische Spezifikation – 4Gewinnt + +## 1. Projektübersicht + +### Name der Anwendung +**4Gewinnt** (Vier-Gewinnt als lokaler und online Multiplayer). + +### Ziel der Anwendung +Bereitstellung eines browserbasierten Vier-Gewinnt-Spiels mit zwei Betriebsarten: +- **Lokaler Modus** (zwei lokale Spieler, optional Bot-Spieler) +- **Online-Modus** (zwei entfernte Spieler über Spielcode und SignalR) + +### Problem, das gelöst wird +Die Anwendung löst die technische Herausforderung, ein rundenbasiertes Rasterspiel gleichzeitig: +- mit **reaktiver Benutzeroberfläche**, +- mit **serverautoritärer Validierung der Spielzüge**, +- mit **Echtzeit-Event-Übertragung**, +- und mit **einfacher Lobby-Logik über 6-stellige Codes** +zu betreiben. + +### Zielgruppe +- Spieler:innen, die im Browser schnell ein Vier-Gewinnt-Spiel starten wollen +- Entwickler:innen/Lernende für ein Referenzprojekt zu Vue 3 + SignalR + ASP.NET Core + +### Hauptfunktionen +1. Spiel erstellen mit variabler Feldgröße +2. Spiel beitreten über sechsstelligen Code +3. Echtzeit-Synchronisation von Spielzustand und Spielende +4. Gewinn-/Unentschieden-Erkennung serverseitig +5. Lokales Spiel (ohne externen Gegenspieler) +6. Wiederholungsrunde (Replay-Code) nach Spielende + +--- + +## 2. Systemarchitektur + +### Beschreibung der Gesamtarchitektur +Das Projekt ist als **zweiteilige Client-Server-Architektur** umgesetzt: +- **Frontend (GUI)**: Vue 3 SPA, Rendert UI und delegiert Spiellogik-Kommandos an SignalR-Verbindung +- **Backend (API)**: ASP.NET Core Web API + SignalR Hub, verwaltet Spielinstanzen in Memory + +### Frontend-/Backend-Struktur +- **Frontend** enthält: + - Routing und Seitenzustände + - UI-Komponenten für Menüs, Spielfeld und Spielende + - Client-seitige Orchestrierung (`OnlineGame`, `LocalGame`) + - Transport-Layer (`GameConnection`) für SignalR +- **Backend** enthält: + - Hub-Endpunkte als Eintrittspunkt für Spieloperationen + - Game-Manager als fachliche Orchestrierung + - Repository als In-Memory-Persistenz + - Domain-Modelle (Spiel, Feld, Spieler, DTOs) + +### Verwendete Technologien +- Vue 3 + TypeScript + Vuetify + Vite +- ASP.NET Core 9 + SignalR + Swagger/OpenAPI + +### Datenfluss zwischen Frontend und Backend +1. Frontend öffnet SignalR-Verbindung zu `/api/gamehub`. +2. Frontend ruft Hub-Methoden (`CreateGame`, `JoinGame`, `Drop`, ...). +3. Hub delegiert an `IGameManager`. +4. `GameManager` liest/schreibt via `IGameRepository`. +5. `GameManager` sendet Events an einzelne Clients oder Gruppen. +6. Frontend aktualisiert lokalen State über Event-Callbacks. + +### Wichtige Designentscheidungen +- **Serverautorität**: Züge werden im Backend validiert. +- **In-Memory-Repository**: schnell, aber nicht persistent. +- **Gruppenkommunikation**: SignalR-Gruppen nach `game.Id`. +- **DTO-Transformation**: 2D-Array im Backend wird in jagged array (`int[][]`) für Frontend gewandelt. +- **Zeitgesteuerte Aufräumlogik**: Spiele werden nach Endbedingungen entfernt. + +--- + +## 3. Technologie-Stack + +| Kategorie | Technologie | Rolle | +| --- | --- | --- | +| Frontend Framework | Vue 3 | Komponenten, Reaktivität, SPA | +| Frontend Sprache | TypeScript | Typsicherheit in UI- und Spiel-Logik | +| UI-Bibliothek | Vuetify 3 | Material Design Komponenten | +| Frontend Routing | Vue Router | Seitenwechsel (`/`, `/localMode`, `/onlineMode`) | +| Echtzeitkommunikation (Client) | `@microsoft/signalr` | Verbindungsaufbau, Invoke, Event-Handling | +| Build Tool Frontend | Vite | Dev-Server, Build, Alias-Auflösung | +| Backend Framework | ASP.NET Core 9 | Hosting, DI, HTTP-Pipeline, SignalR | +| Echtzeitkommunikation (Server) | ASP.NET SignalR | Hub-Methoden, Gruppen- und Client-Events | +| API-Dokumentation | OpenAPI + Swagger | Test/Inspektion im Development | +| Persistenz | In-Memory (`GameRepository`) | Laufzeitverwaltung von Spielen | +| Lösung/Build | .NET SDK + npm | Backend/Frontend Build und Start | + +--- + +## 4. Projektstruktur + +```text +/ +├── API/ # Backend (.NET 9) +│ ├── Controllers/ # Status-Controller + SignalR Hub +│ ├── Models/ +│ │ ├── DataClasses/ # Value Objects (z. B. SixDigitInt) +│ │ └── Game/ # Domänenmodelle, DTOs, Spielfeldlogik +│ ├── Repository/GameRepo/ # Spielspeicher (Interface + In-Memory-Impl.) +│ ├── Services/GameManager/ # Use-Case- und Ablaufsteuerung +│ ├── Program.cs # App-Konfiguration und DI +│ └── appsettings*.json # Konfiguration +├── GUI/ # Frontend (Vue 3 + TS) +│ ├── public/ # Statische Assets (Sprites, Hintergrund, Icons) +│ ├── src/ +│ │ ├── components/ # Wiederverwendbare UI-Komponenten +│ │ │ └── game/ # Spielfeld-/Info-Komponenten +│ │ ├── routes/ # View-Komponenten pro Route +│ │ ├── router/ # Router-Konfiguration +│ │ ├── scripts/ +│ │ │ ├── interfaces/ # TS-Typdefinitionen +│ │ │ ├── logic/ # Anwendungslogik (lokal/online/signalR) +│ │ │ └── utils/ # Hilfsfunktionen +│ │ ├── Layout.vue # App-Shell +│ │ ├── Home.vue # Startseite +│ │ ├── NotFound.vue # 404-Fallback +│ │ └── main.ts # Bootstrapping +│ ├── vite.config.ts # Build-/Alias-Konfiguration +│ └── package.json # npm Scripts + Abhängigkeiten +├── 4Gewinnt.sln # .NET Lösung +└── TECHNISCHE_SPEZIFIKATION.md # Diese Spezifikation +``` + +--- + +## 5. Backend-Architektur + +### Überblick +Das Backend besteht aus vier Schichten: +1. **Controller/Hub-Schicht**: externer Zugriff +2. **Service-Schicht**: Geschäftsabläufe +3. **Repository-Schicht**: Speicherung und Lookup +4. **Model-Schicht**: Domänenobjekte und Algorithmen + +### Klassen- und Typdokumentation (Backend) + +## Program (Program.cs) + +### Zweck +Konfiguriert Dependency Injection, Middleware und Endpunkte der Anwendung. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `builder` | `WebApplicationBuilder` | Aufbau des DI-Containers und Hostings | +| `app` | `WebApplication` | Laufende ASP.NET Anwendung | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| Top-Level Setup | - | - | Registriert Controller, SignalR, Swagger, Repositories und Services; mappt Endpunkte | + +### Beziehungen +- Verwendet `IGameManager`/`GameManager` und `IGameRepository`/`GameRepository` via DI. +- Mappt `GameHubSocket` als SignalR-Hub unter `/api/gamehub`. + +--- + +## StatusController + +### Zweck +Einfacher Healthcheck-Endpunkt (`GET /api/status`) zur Verfügbarkeitsprüfung. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| - | - | Keine eigenen Felder | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `Get` | - | `IActionResult` | Gibt `200 OK` mit `"Running"` zurück | + +### Beziehungen +- Keine Abhängigkeiten zu Domain-Services. + +--- + +## GameHubSocket + +### Zweck +SignalR-Hub als Eintrittspunkt für Spiel-Kommandos aus Clients. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `_gameManager` | `IGameManager` | Service für Spielfunktionen | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `CreateGame` | `playerName: string`, `gFs: Coordinates` | `Task` | Validiert Feldgröße, erstellt Spiel, fügt Client zur Spielgruppe hinzu, sendet `GameCreated` | +| `JoinGame` | `playerName: string`, `gameCode: int` | `Task` | Lässt Client einem Spiel beitreten, sendet `GameJoined` oder `Error` | +| `RequestGameInformation` | `gameId: string` | `Task` | Delegiert an Manager für aktuellen Spielzustand | +| `Drop` | `gameId: string`, `column: int` | `Task` | Delegiert Zugausführung an Manager | +| `OnDisconnectedAsync` | `exception?: Exception` | `Task` | Meldet Disconnection und ruft Basisverhalten auf | + +### Beziehungen +- Ruft `IGameManager`-Methoden auf. +- Sendet Events über `Clients` und verwaltet Gruppenmitgliedschaft. + +--- + +## IGameManager (Interface) + +### Zweck +Definiert die fachlichen Operationen für Spiele. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| - | - | Interface ohne Felder | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `CreateGame` | `gFs: Coordinates`, `player: Player` | `(string, int)` | Erstellt neues Spiel und gibt `(gameId, gameCode)` | +| `JoinGame` | `player: Player`, `gameCode: int` | `Task` | Versucht Beitritt; liefert `gameId` oder `null` | +| `RequestGameInformation` | `gameId: string`, `playerConnectionId: string` | `Task` | Liefert Spielzustand an anfragenden Client | +| `Drop` | `gameCode: string`, `column: int`, `playerConnectionId: string` | `Task` | Führt Spielzug aus | +| `DisconnectedPlayer` | `playerConnectionId: string` | `Task` | Beendet Spiel bei Verbindungsabbruch | + +### Beziehungen +- Implementiert durch `GameManager`. + +--- + +## GameManager + +### Zweck +Orchestriert Kern-Use-Cases: Spielstart, Beitritt, Zuglogik, Endbedingungen und Aufräumen. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `gameRepository` | `IGameRepository` | Zugriff auf Spielinstanzen | +| `hubContext` | `IHubContext` | Serverseitiges Senden von SignalR-Events | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `CreateGame` | `gFs: Coordinates`, `player: Player` | `(string, int)` | Legt Spiel an, fügt Ersteller hinzu, liefert Kennungen | +| `JoinGame` | `player: Player`, `gameCode: int` | `Task` | Prüft Lobbyzustand/Platz, fügt Gruppe hinzu, sendet bei 2 Spielern `GameStarted` | +| `RequestGameInformation` | `gameId: string`, `playerConnectionId: string` | `Task` | Baut DTO und sendet `GameInformation` an den Client | +| `Drop` | `gameCode: string`, `column: int`, `playerConnectionId: string` | `Task` | Prüft Zugrecht/Zustand, lässt Stein fallen, sendet `FieldUpdated`, prüft Win/Draw, sendet `GameEnded` | +| `DisconnectedPlayer` | `playerConnectionId: string` | `Task` | Entfernt Spieler aus Spiel, beendet Spiel, sendet `GameEnded` mit `PlayerDisconnected` | +| `ConvertField` *(privat, statisch)* | `field: int[,]?` | `int[][]` | Transformiert 2D-Array in jagged array für DTO | +| `ScheduleGameDeletion` *(privat)* | `gameId: string`, `delay: TimeSpan` | `void` | Verzögerte Löschung in Hintergrund-Task und Versand `GameDestroyed` | + +### Beziehungen +- Verwendet `IGameRepository`, `Game`, `GameField`, `GameInformationDto`. +- Ruft SignalR-Clients/Gruppen auf (`GameStarted`, `FieldUpdated`, `GameEnded`, `GameInformation`, `GameDestroyed`). + +--- + +## IGameRepository (Interface) + +### Zweck +Abstraktion der Spielpersistenz. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| - | - | Interface ohne Felder | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `GetAll` | - | `List` | Liefert alle Spiele | +| `GetOne` | `id: string` | `Game?` | Sucht Spiel per ID | +| `GetOne` | `gameCode: SixDigitInt` | `Game?` | Sucht Spiel per Spielcode | +| `GetOneByConnectionId` | `connectionId: string` | `Game?` | Sucht Spiel, in dem ein Spieler verbunden ist | +| `Create` | `gameFieldSize: Coordinates` | `Game` | Erstellt neues Spiel | +| `Destroy` | `id: string` | `void` | Entfernt Spiel aus Speicher | + +### Beziehungen +- Implementiert durch `GameRepository`. + +--- + +## GameRepository + +### Zweck +In-Memory-Implementierung für Spielverwaltung inklusive Codegenerierung. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `_games` | `List` | Aktive Spiele im Arbeitsspeicher | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `GetAll` | - | `List` | Gibt interne Liste zurück | +| `GetOne` | `id: string` | `Game?` | Lookup über `Game.Id` | +| `GetOne` | `gameCode: SixDigitInt` | `Game?` | Lookup über `Game.GameCode` | +| `GetOneByConnectionId` | `connectionId: string` | `Game?` | Spielsuche nach enthaltenem Spieler | +| `Create` | `gameFieldSize: Coordinates` | `Game` | Erzeugt `Game` mit einzigartigem Code, fügt zur Liste hinzu | +| `Destroy` | `id: string` | `void` | Entfernt Spiele mit ID | +| `GenerateGameCode` *(privat)* | - | `SixDigitInt` | Erzeugt kryptografisch zufälligen 6-stelligen, einzigartigen Code | + +### Beziehungen +- Instanziiert `Game` und `SixDigitInt`. +- Wird von `GameManager` genutzt. + +--- + +## SixDigitInt (record struct) + +### Zweck +Value Object für sechsstellige Spielcodes mit Wertebereichsvalidierung. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `Value` | `int` | Interner numerischer Wert (0–999999) | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| Konstruktor | `value: int` | - | Validiert Bereich und setzt `Value` | +| `ToString` | - | `string` | Gibt Code links mit Nullen aufgefüllt (`D6`) zurück | +| `implicit operator int` | `v: SixDigitInt` | `int` | Ermöglicht implizite Konvertierung nach `int` | + +### Beziehungen +- Wird in `Game` und Repository als Spielcode verwendet. + +--- + +## Coordinates (readonly struct) + +### Zweck +Datenstruktur für Felddimensionen oder Koordinaten. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `X` | `int` | Breite / X-Koordinate | +| `Y` | `int` | Höhe / Y-Koordinate | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| - | - | - | Keine Methoden | + +### Beziehungen +- Wird von `Game`, `GameField` und Hub-Aufrufen genutzt. + +--- + +## GameState (enum) + +### Zweck +Statusautomat eines Spiels. + +### Werte +| Wert | Beschreibung | +| --- | --- | +| `Lobby` | Spiel erstellt, wartet auf zweiten Spieler | +| `Running` | Spiel aktiv | +| `Ended` | Spiel beendet | + +--- + +## Player + +### Zweck +Spielerobjekt mit Name, Connection-ID und Spielerrolle (1 oder 2). + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `Name` | `string` | Anzeigename | +| `ConnectionId` | `string` | SignalR-Verbindungs-ID | +| `PlayerTag` | `int` | Rolle/Steinfarbe (`1` oder `2`) | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| Konstruktor | `name: string`, `connectionId: string` | - | Initialisiert Spieler | + +### Beziehungen +- Bestandteil von `Game.Players`. + +--- + +## Game + +### Zweck +Aggregate Root für einen kompletten Matchzustand. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `Id` | `string` | Eindeutige Spiel-ID (GUID) | +| `GameCode` | `SixDigitInt` | Human-readable Join-Code | +| `Players` | `List` | Teilnehmer (max. 2) | +| `CurrentTurn` | `int` | Aktuelle Spielerrolle (1/2) | +| `State` | `GameState` | Lobby/Running/Ended | +| `Field` | `GameField` | Spielfeldinstanz | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| Konstruktor | `gFs: Coordinates`, `gameCode: SixDigitInt` | - | Erstellt neues Spiel mit Feld | +| `AddPlayer` | `player: Player` | `bool` | Fügt Spieler hinzu, weist `PlayerTag` zu, startet Spiel bei 2 Spielern | +| `RemovePlayer` | `playerConnectionId: string` | `void` | Entfernt Spieler anhand Connection-ID | +| `GetPlayerByConnectionId` | `playerConnectionId: string` | `Player?` | Spielersuche nach Connection-ID | +| `GetPlayerByTag` | `playerTag: int` | `Player?` | Spielersuche nach Rolle | +| `StartGame` | - | `void` | Setzt Status auf `Running` | +| `EndGame` | - | `void` | Setzt Status auf `Ended` | + +### Beziehungen +- Enthält `GameField` und `Player`. +- Wird über `GameManager` gesteuert. + +--- + +## DropResult (enum) + +### Zweck +Ergebniscode für Zugversuche im Spielfeld. + +### Werte +| Wert | Beschreibung | +| --- | --- | +| `OutOfGameField` | Spalte außerhalb gültigen Bereichs | +| `NotAllowedPlayer` | Ungültiger Spielerwert | +| `ColumnFull` | Spalte ist voll | +| `InvalidFieldValue` | Feld enthält ungültige Werte | +| `Placed` | Stein erfolgreich platziert | + +--- + +## FieldState (enum) + +### Zweck +Status eines einzelnen Feldpunktes. + +### Werte +| Wert | Beschreibung | +| --- | --- | +| `OutOfGameField` | Koordinate außerhalb | +| `Empty` | Feld leer | +| `OccupiedRed` | Belegt durch Spieler 1 | +| `OccupiedYellow` | Belegt durch Spieler 2 | +| `InvalidFieldValue` | Unerwarteter Wert | + +--- + +## GameField + +### Zweck +Kapselt Feldzustand und Algorithmen für Einwurf, Vollprüfung und Gewinnerkennung. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `GFs` | `Coordinates` | Spielfeldgröße | +| `CurrentField` | `int[,]` | Aktuelles Feldraster (0 leer, 1/2 Spieler) | +| `BackupField` | `int[,]` | Sicherung für Rücksetzen bei Fehlern | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| Konstruktor | `gFs: Coordinates` | - | Erzeugt Raster und Backup | +| `Drop` | `column: int`, `player: int` | `DropResult` | Lässt Stein in Spalte fallen; nutzt Backup vor Änderung | +| `CheckField` | `coordinates: Coordinates` | `FieldState` | Liefert Status eines Feldpunktes | +| `IsFull` | - | `bool` | Prüft, ob kein Leerfeld mehr existiert | +| `CountInDirection` *(privat)* | `startX,startY,dx,dy,player` | `int` | Zählt zusammenhängende Steine in Richtung | +| `CheckLine` *(privat)* | `startX,startY,dx,dy,player` | `bool` | Prüft 4er-Linie über beide Richtungen | +| `CheckForWin` | - | `int` | Durchsucht Feld nach Gewinner (0/1/2) | +| `ResetToLastSave` | - | `void` | Stellt Feld aus Backup wieder her | +| `CreateSave` | - | `void` | Kopiert `CurrentField` nach `BackupField` | + +### Beziehungen +- Wird von `Game` gehalten. +- Von `GameManager.Drop` aufgerufen. + +--- + +## GameInformationDto + +### Zweck +Transportobjekt für Frontend-Synchronisation. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `Id` | `string` | Spiel-ID | +| `Players` | `List` | Spielerliste | +| `State` | `GameState?` | Spielstatus | +| `CurrentField` | `int[][]` | Feldzustand in serialisierbarer Struktur | +| `CurrentTurn` | `int` | Nächster Spieler | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| - | - | - | Reines DTO ohne Logik | + +### Beziehungen +- Wird vom `GameManager` erzeugt und über Hub-Ereignisse versendet. + +--- + +## 6. Frontend-Architektur + +### Seiten / Views +- `/` → `Home.vue` (Moduswahl) +- `/localMode` → `LocalMode.vue` (lokales Spiel) +- `/onlineMode` → `OnlineMode.vue` (Lobbyauswahl + Online-Spiel) +- Fallback → `NotFound.vue` + +### Komponenten +- **Layout**: App-Shell mit Header und Router-View +- **CreateOrJoinMenu**: Auswahl „Erstellen“/„Beitreten“ +- **GameCreationMenu**: Spieler-/Feldeinstellungen +- **GameJoinMenu**: Name + 6-stelliger Join-Code +- **GameEndedMenu**: Endstand + Restart +- **Field**: Spielfelddarstellung + Spaltenauswahl +- **InfoField**: Textstatus aktueller Spielerzug +- **Slider**: Wiederverwendbarer numerischer Slider + +### State Management +Kein zentrales Store-Framework; Zustand wird komponentenlokal mit Vue `ref`/`computed` gehalten. +- Spielzustand fließt über Callback-Events aus den Logikklassen (`OnlineGame`, `LocalGame`). + +### API-Kommunikation +- Keine klassische REST-Spiel-API. +- Vollständig ereignisgesteuert via SignalR (`GameConnection`). +- REST nur für Healthcheck (`/api/status`). + +### Routing +Router in `router/index.ts` mit `createWebHistory`. + +### Klassen- und Komponentendokumentation (Frontend) + +## main.ts + +### Zweck +Bootstrapping von Vue-App, Vuetify, Router und globalen Styles. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `vuetify` | Vuetify-Instanz | Dark Theme, Komponenten/Directives | +| `app` | Vue App | Root-App mit `Layout.vue` | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| Top-Level Setup | - | - | Initialisiert App, bindet Plugins, mountet `#app` | + +### Beziehungen +- Nutzt `Layout.vue` und Router. + +--- + +## Router (router/index.ts) + +### Zweck +Definiert Navigationsstruktur der SPA. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `router` | Router | Routentabelle und History-Strategie | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `createRouter(...)` | Konfigurationsobjekt | `Router` | Baut Router mit vier Routen | + +### Beziehungen +- Verknüpft Views `Home`, `LocalMode`, `OnlineMode`, `NotFound`. + +--- + +## Layout.vue (Komponente) + +### Zweck +Stellt globale App-Hülle (AppBar + Inhalt) bereit. + +### Props +Keine. + +### State / reactive Daten +Keine. + +### Methoden +Keine. + +### Events +- Klick auf Titel navigiert zur Startseite. + +### API Calls +Keine. + +--- + +## Home.vue (Komponente) + +### Zweck +Startseite zur Auswahl des Spielmodus. + +### Props +Keine. + +### State / reactive Daten +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `buttons` | Array | Konfiguration der zwei Modus-Buttons | + +### Methoden +Keine expliziten Methoden. + +### Events +- Navigationsereignisse über `:to` auf Button. + +### API Calls +Keine. + +--- + +## OnlineMode.vue (Komponente) + +### Zweck +Steuert gesamten Online-Flow von Auswahl bis laufendem Spiel. + +### Props +Keine. + +### State / reactive Daten +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `settings` | `Ref` | Einstellungen zum Erstellen | +| `joiningModel` | `Ref` | Daten für Join-Flow | +| `currentState` | `Ref` | UI-State-Maschine | +| `game` | `Ref` | Online-Spielorchestrator | +| `gameField` | `Ref` | Feld für Anzeige | +| `currentSelectionIndex` | `Ref` | Hover-/Klickspalte | +| `gameEndedInformation` | `Ref` | Endscreen-Daten | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `createGame` | - | `Promise` | Delegiert an `OnlineGame.createGame` | +| `tryToJoin` | - | `Promise` | Delegiert an `OnlineGame.joinGame` | +| `restartGame` | - | `Promise` | Startet neue Runde via Replay-Code | +| `onUnmounted` Hook | - | - | Trennt SignalR-Verbindung | + +### Events +- Child-Events: `createGame`, `join`, `restart-game`, `click-on-game-field`. +- Interne Callbacks: `onGameStateChanged`, `onGameEnded`, `onGameCreated`, `onGameStarted`, `onGameJoinedFailed`. + +### API Calls +Indirekt über `OnlineGame` → `GameConnection` (`CreateGame`, `JoinGame`, `Drop`). + +--- + +## LocalMode.vue (Komponente) + +### Zweck +Steuert lokalen Spielmodus inkl. optionalem Bot. + +### Props +Keine. + +### State / reactive Daten +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `settings` | `Ref` | Lokale Spieleinstellungen | +| `currentState` | `Ref` | UI-State (Create/Game/End) | +| `game` | `Ref` | Lokaler Spielorchestrator | +| `gameField` | `Ref` | Angezeigter Feldzustand | +| `currentSelectionIndex` | `Ref` | Ausgewählte Spalte | +| `gameEndedInformation` | `Ref` | Endinformationen | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `startGame` | - | `Promise` | Verbindet beide lokale Clients und erstellt Spiel | +| `restart` | - | `Promise` | Trennt beide Clients und startet neu | + +### Events +- Child-Events: `create-game`, `restart-game`, `click-on-game-field`. + +### API Calls +Indirekt über `LocalGame` → 2× `GameConnection`. + +--- + +## NotFound.vue (Komponente) + +### Zweck +Fallback-Anzeige für unbekannte Routen. + +### Props / State / Methoden / Events / API Calls +Keine fachliche Logik. + +--- + +## CreateOrJoinMenu.vue (Komponente) + +### Zweck +Zwischenschritt im Online-Modus zur Auswahl zwischen Erstellen und Beitreten. + +### Props +Keine. + +### State / reactive Daten +Keine. + +### Methoden +Keine. + +### Events +| Event | Beschreibung | +| --- | --- | +| `createGame` | Wechselt in Erstellungsflow | +| `joinGame` | Wechselt in Join-Flow | + +### API Calls +Keine. + +--- + +## GameCreationMenu.vue (Komponente) + +### Zweck +Eingabe von Namen, Feldgröße und optional Bot-Konfiguration. + +### Props +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `settings` (Model) | `GameSettings` | Zweiwegegebundene Einstellungen | + +### State / reactive Daten +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `settingsProp` | `ModelRef` | Reaktiver Zugriff auf Einstellungen | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `gameFieldPreset` | `x: number`, `y: number` | `void` | Setzt vordefinierte Spielfeldgröße | + +### Events +| Event | Beschreibung | +| --- | --- | +| `createGame` | Startet Spiel-/Lobby-Erzeugung | + +### API Calls +Keine direkten. + +--- + +## GameJoinMenu.vue (Komponente) + +### Zweck +Erfasst Spielername und sechsstelligen Beitrittscode. + +### Props +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `joinGameObject` (Model) | `JoinGameObject` | Daten inklusive Fehlerzustand | + +### State / reactive Daten +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `joiningModel` | `ModelRef` | Lokaler Zugriff auf Join-Modell | + +### Methoden +Keine. + +### Events +| Event | Beschreibung | +| --- | --- | +| `join` | Löst Join-Operation aus | + +### API Calls +Keine direkten. + +--- + +## GameEndedMenu.vue (Komponente) + +### Zweck +Anzeige des Endzustands (Sieg/Draw/Disconnect) und Neustartaktion. + +### Props +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `gameEndedInformation` | `GameEnded \| null` | Endereignis-Payload | + +### State / reactive Daten +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `restarted` | `Ref` | Verhindert Mehrfachklick auf Neustart | +| `message` | `ComputedRef` | Aufbereiteter Endtext | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `restartClicked` | - | `void` | Emitet Neustart und setzt Sperrflag | + +### Events +| Event | Beschreibung | +| --- | --- | +| `restartGame` | Triggert neues Spiel | + +### API Calls +Keine direkten. + +--- + +## Slider.vue (Komponente) + +### Zweck +Generischer Slider mit numerischer Anzeige. + +### Props +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `label` | `string` | Beschriftung | +| `min` | `number` | Untergrenze | +| `max` | `number` | Obergrenze | +| `step` | `number` | Schrittweite | +| `width` | `string \| number` | Breite Inputfeld | +| `disabled` | `boolean` | Deaktivierung | +| `readonlyValue` | `boolean` | Schreibschutz Input | + +### State / reactive Daten +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `model` | `ModelRef` | Aktueller Wert | +| `inputWidth` | `ComputedRef` | CSS-Breite des Append-Inputs | + +### Methoden +Keine separaten Methoden. + +### Events +Standard `v-model`-Update. + +### API Calls +Keine. + +--- + +## Field.vue (Komponente) + +### Zweck +Visuelle Darstellung des Spielfeldes inkl. Hover-/Auswahlspalte. + +### Props +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `gameState` | `number[][]` | Feldzustand | +| `currentSelectionIndex` (Model) | `number \| null` | aktuell gewählte Spalte | + +### State / reactive Daten +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `gameFieldSize` | `ComputedRef` | abgeleitete Feldgröße | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| `translateNumberToImage` | `num: number` | `string` | Mapt Feldwert auf Sprite-Pfad | +| `selectionUpdate` | `index: number`, `active: boolean` | `void` | Setzt/entfernt Spaltenauswahl | + +### Events +| Event | Beschreibung | +| --- | --- | +| `clickOnGameField` | Anwender klickt auf Spielfeld, Parent löst `drop` aus | + +### API Calls +Keine direkten. + +--- + +## InfoField.vue (Komponente) + +### Zweck +Zeigt Status- oder Hinweistext zum aktuellen Zug. + +### Props +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `msg` | `string` | Meldung | +| `currenlyWaiting?` | `boolean` | Optionales Ausblend-Flag | + +### State / Methoden / Events / API Calls +Keine. + +--- + +## FieldSize (Interface) + +### Zweck +Typsichere Feldgrößenbeschreibung. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `x` | `number` | Breite | +| `y` | `number` | Höhe | + +--- + +## GameSettings (Interface) + +### Zweck +Konfigurationsmodell für Spielanlage. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `fieldSize` | `FieldSize` | Spielfeldgröße | +| `playerName1` | `string` | Spieler 1 | +| `playerName2?` | `string \| null` | Spieler 2 (lokal/optional) | +| `botPlayer2?` | `boolean \| null` | Bot-Flag für Spieler 2 | +| `message?` | `string \| null` | UI-Nachricht nach Erstellung | + +--- + +## JoinGameObject (Interface) + +### Zweck +Eingabemodell für Beitritt zu bestehendem Spiel. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `failed` | `boolean` | Fehlerstatus (UI) | +| `playerName` | `string` | Anzeigename | +| `gameCode?` | `string` | Eingetippter 6-stelliger Code | + +--- + +## GameConnection (Klasse) + +### Zweck +SignalR-Transportabstraktion für Hub-Aufrufe und Eventbindung. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `connection` | `signalR.HubConnection` | Physische SignalR-Verbindung | +| `playerName` | `string` | Lokaler Name für Hub-Aufrufe | +| Callback-Felder | optionale Funktionen | Reaktion auf Hub-Events (`onGameCreated`, `onError`, ... ) | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| Konstruktor | - | - | Baut HubConnection (`/api/gamehub`) und registriert Event-Listener | +| `connect` | `playerName: string` | `Promise` | Startet Verbindung | +| `disconnect` | - | `Promise` | Stoppt Verbindung | +| `createGame` | `gFs: FieldSize` | `Promise` | Hub-Invoke `CreateGame` | +| `joinGame` | `gameCode: number` | `Promise` | Hub-Invoke `JoinGame` | +| `requestGameInformation` | `gameId: string` | `Promise` | Hub-Invoke `RequestGameInformation` | +| `drop` | `gameId: string`, `column: number` | `Promise` | Hub-Invoke `Drop` | + +### Beziehungen +- Wird von `OnlineGame` und `LocalGame` verwendet. +- Spiegelt Server-Eventmodell 1:1. + +--- + +## OnlineGame (Klasse) + +### Zweck +Use-Case-Orchestrierung für Online-Spiel aus Sicht eines einzelnen Clients. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `player` | `GameConnection` | SignalR-Clientinstanz | +| `currentDescription` | `string` | Text zur aktuellen Runde | +| `gameId` | `string` | Serverseitige Spiel-ID | +| `gameState` | `GameInformationDto \| undefined` | Lokaler Snapshot | +| Callback-Felder | optionale Funktionen | Hooks für UI (`onGameCreated`, `onGameEnded`, ...) | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| Konstruktor | - | - | Verkabelt alle Transport-Events auf UI-Callbacks | +| `createGame` | `settings: GameSettings` | `Promise` | Verbindet Spieler und erstellt Lobby | +| `joinGame` | `joinObject: JoinGameObject` | `Promise` | Validiert Codeformat, verbindet, tritt Spiel bei | +| `updateState` | `newState: GameInformationDto` | `Promise` | Aktualisiert lokalen Zustand und Callback | +| `drop` | `index: number` | `Promise` | Führt Zug im aktuellen Spiel aus | +| `changePlaceDescription` | - | `void` | Setzt zufällige Zugmeldung anhand `currentTurn` | +| `restartGame` | `replayGameCode: any` | `Promise` | Trennt Verbindung und joint Replay-Code | + +### Beziehungen +- Nutzt `GameConnection` und Utility `getRandomMovePhrase`. + +--- + +## LocalGame (Klasse) + +### Zweck +Orchestriert lokalen Modus durch zwei parallele `GameConnection`-Instanzen. + +### Eigenschaften +| Name | Typ | Beschreibung | +| --- | --- | --- | +| `player1` | `GameConnection` | Lokaler Client 1 | +| `player2` | `GameConnection` | Lokaler Client 2/Bot | +| `currentDescription` | `string` | UI-Text aktueller Spieler | +| `gameId` | `string` | Aktuelle Spiel-ID | +| `botGame` | `boolean` | Bot-Modus für Spieler 2 | +| `gameState` | `GameInformationDto \| undefined` | Spielsnapshot | +| Callback-Felder | optionale Funktionen | UI-Hooks für State-Änderung und Spielende | + +### Methoden +| Methode | Parameter | Rückgabewert | Beschreibung | +| --- | --- | --- | --- | +| Konstruktor | - | - | Verdrahtet Events beider Player-Verbindungen | +| `start` | `settings: GameSettings` | `Promise` | Verbindet beide Spieler und erstellt neues Spiel | +| `updateState` | `newState: GameInformationDto` | `Promise` | Aktualisiert lokalen Zustand | +| `drop` | `index: number` | `Promise` | Führt Zug durch aktuellen Spieler aus; Bot setzt random verzögert | +| `changePlaceDescription` | - | `void` | Erzeugt zufällige Zugmeldung | +| `disconnectAll` | - | `Promise` | Trennt beide SignalR-Verbindungen | + +### Beziehungen +- Nutzt 2× `GameConnection`, optional Bot-Zug per Zufall. + +--- + +## buildFieldArray (Funktion) + +### Zweck +Erzeugt initiales `number[][]`-Spielfeld in gewünschter Größe. + +### Signatur +`buildFieldArray(fieldSize: FieldSize): number[][]` + +### Verhalten +- Erstellt `y` Zeilen, jede Zeile enthält `x` Nullen. + +--- + +## getRandomMovePhrase (Funktion) + +### Zweck +Erzeugt abwechslungsreiche Statusmeldung für die UI. + +### Signatur +`getRandomMovePhrase(playerName: string): string` + +### Verhalten +- Wählt zufällig eine Phrase aus `movePhrases`. +- Ersetzt Platzhalter `{playerName}`. + +--- + +## 7. Datenmodelle + +## Backend-Datenmodelle + +### Game +- `id`: string +- `gameCode`: SixDigitInt +- `players`: List +- `currentTurn`: int (1 oder 2) +- `state`: GameState +- `field`: GameField + +Beziehungen: +- 1 Game besitzt 1 GameField +- 1 Game besitzt 0..2 Player + +### Player +- `name`: string +- `connectionId`: string +- `playerTag`: int + +### GameField +- `gFs`: Coordinates +- `currentField`: int[,] +- `backupField`: int[,] + +### GameInformationDto +- `id`: string +- `players`: List +- `state`: GameState? +- `currentField`: int[][] +- `currentTurn`: int + +### Coordinates +- `x`: int +- `y`: int + +### SixDigitInt +- `value`: int + +## Frontend-Datenmodelle + +### GameInformationDto (TS) +- `id: string | null` +- `players: Player[]` +- `state: number` +- `currentField: number[][]` +- `currentTurn: number` + +### Player (TS) +- `name: string` +- `connectionId: string` +- `playerTag: number` + +### GameEnded (TS) +- `method: string` +- `player?: Player | null` +- `replayGameCode?: number | null` + +### GameIdentifier (TS) +- `gameId: string` +- `gameCode: number` + +### GameSettings (TS) +- siehe Abschnitt 6 + +### JoinGameObject (TS) +- siehe Abschnitt 6 + +--- + +## 8. Spiel- / Geschäftslogik + +### Spiel erstellen +1. Client verbindet SignalR (`connect`). +2. Client ruft `CreateGame(playerName, fieldSize)`. +3. Server erstellt `Game`, setzt Spieler 1, vergibt GameCode. +4. Server fügt Client zur Gruppe `game.Id` hinzu. +5. Server sendet `GameCreated` an Ersteller. + +### Spiel beitreten +1. Client validiert 6-stelligen Code lokal. +2. Client ruft `JoinGame(playerName, gameCode)`. +3. Server prüft Spiel vorhanden + `Lobby` + Platz frei. +4. Server fügt Spieler 2 hinzu, setzt ggf. Spiel auf `Running`. +5. Server fügt Client zur Gruppe `game.Id` hinzu. +6. Server sendet `GameStarted` an Gruppe. + +### Spiel starten +- Start erfolgt implizit beim zweiten Spieler (`AddPlayer`/`JoinGame`). + +### Spielzüge +1. Aktiver Spieler sendet `Drop(gameId, column)`. +2. Server prüft Verbindung zu Spiel + Turn-Recht + Zustand `Running`. +3. `GameField.Drop` setzt Stein in unterste freie Zelle. +4. Server toggelt `CurrentTurn`. +5. Server sendet `FieldUpdated` an Spielgruppe. + +### Spielende +- **Gewinn**: `CheckForWin() != 0`, dann `GameEnded(Method=Win)` + Replay-Code. +- **Unentschieden**: `IsFull() == true`, dann `GameEnded(Method=Draw)` + Replay-Code. +- **Disconnect**: `DisconnectedPlayer`, dann `GameEnded(Method=PlayerDisconnected)`. + +--- + +## 9. Datenfluss + +### Ablauf „Spiel erstellen“ +1. Frontend (`OnlineGame.createGame`) sendet `CreateGame`. +2. `GameHubSocket.CreateGame` erstellt `Player` und delegiert. +3. `GameManager.CreateGame` ruft Repository `Create`. +4. Hub fügt Verbindung zur SignalR-Gruppe hinzu. +5. Hub sendet `GameCreated` an Caller. +6. Frontend zeigt Join-Code in `GameCreationMenu`. + +### Ablauf „Spielzug“ +1. Nutzer klickt Spalte in `Field.vue`. +2. Route ruft `game.drop(index)`. +3. `GameConnection.drop` invokt Hub-Methode. +4. `GameManager.Drop` validiert und setzt Stein. +5. `FieldUpdated` wird an Gruppe gesendet. +6. Clients aktualisieren `gameField` und Statusnachricht. + +### Ablauf „Spielende + Replay“ +1. Nach Win/Draw erstellt Server ein neues Spiel mit gleicher Feldgröße. +2. Server sendet `GameEnded` inkl. `ReplayGameCode`. +3. Frontend zeigt `GameEndedMenu`. +4. Beim Neustart joint Client per Replay-Code. + +--- + +## 10. Wichtige Algorithmen + +### Spielzug validieren +- Prüfungen in Reihenfolge: + 1. Spiel existiert + 2. Spieler gehört zum Spiel + 3. Spielzustand ist `Running` + 4. `CurrentTurn == player.PlayerTag` + 5. Spalte in Bounds + 6. Spalte nicht voll + 7. Feldwerte konsistent (nur 0/1/2) + +### Gewinner erkennen +- Für jede Zelle mit Spielerwert 1/2 werden vier Richtungen geprüft: + - Horizontal `(1,0)` + - Vertikal `(0,1)` + - Diagonal rechts-unten `(1,1)` + - Diagonal links-unten `(-1,1)` +- Der Algorithmus zählt zusammenhängende Steine in beide Richtungen und addiert die Startzelle. +- `>= 4` ergibt Sieg. + +### Spielstatus berechnen +- `Lobby` → weniger als 2 Spieler. +- `Running` → 2 Spieler und laufendes Spiel. +- `Ended` → bei Win/Draw/Disconnect. + +--- + +## 11. API-Dokumentation + +### HTTP-Endpunkte + +| Endpoint | Methode | Beschreibung | +| --- | --- | --- | +| `/api/status` | `GET` | Healthcheck (`Running`) | + +### SignalR Hub-Endpunkt + +| Endpoint | Methode | Beschreibung | +| --- | --- | --- | +| `/api/gamehub` | SignalR | Echtzeitkommunikation für Spiellogik | + +### Hub-Methoden + +| Hub-Methode | Parameter | Beschreibung | +| --- | --- | --- | +| `CreateGame` | `playerName: string`, `gFs: Coordinates` | Erstellt neues Spiel | +| `JoinGame` | `playerName: string`, `gameCode: int` | Tritt bestehendem Spiel bei | +| `RequestGameInformation` | `gameId: string` | Liefert aktuellen Zustand | +| `Drop` | `gameId: string`, `column: int` | Führt Zug aus | + +--- + +## 12. Ereignisse / Echtzeitkommunikation + +| Event | Sender | Empfänger | Beschreibung | +| --- | --- | --- | --- | +| `GameCreated` | Server | Caller | Enthält `gameId`, `gameCode` nach Erstellung | +| `GameJoined` | Server | Caller | Bestätigt Beitritt | +| `GameStarted` | Server | Gruppe `game.Id` | Initialer Spielzustand nach 2. Spieler | +| `GameInformation` | Server | Einzelclient | Zustandsabfrage-Antwort | +| `FieldUpdated` | Server | Gruppe `game.Id` | Feld/Turn nach gültigem Zug | +| `GameEnded` | Server | Gruppe `game.Id` | Endgrund: Win/Draw/Disconnect + optionale Daten | +| `GameDestroyed` | Server | Gruppe `game.Id` | Hinweis nach geplanter Löschung | +| `Error` | Server | Caller/Client | Fehlertext (z. B. ungültiger Zug / Spiel nicht gefunden) | + +--- + +## 13. Benutzeroberfläche + +### Screens +1. **Startseite**: Moduswahl lokal/online +2. **Online: Auswahlseite**: Erstellen oder Beitreten +3. **Spielerstellung**: Namen, Feldgröße, Presets +4. **Join-Seite**: Name + OTP-Feld für Spielcode +5. **Spielansicht**: Spielfeld + Zugstatus +6. **Endscreen**: Ergebnis + Neustartoption + +### UI-Ablauf +- Nutzer startet auf Home und navigiert über Buttons. +- In Spielansicht zeigt `Field` den Rasterzustand mit Hover-Pfeil. +- `InfoField` zeigt dynamische Spielzugtexte. +- Endzustand sperrt Spiel und bietet Neustart/Abbruch. + +### Benutzeraktionen +- Namen eingeben +- Feldgröße per Slider/Presets auswählen +- Beitrittscode eingeben +- Spalte durch Hover/Klick auswählen +- Neustart auslösen + +--- + +## 14. Anwendungsabläufe (User-Flows) + +### Flow A: Online-Spiel erstellen +1. Home → Online Multiplayer. +2. Auswahl „Erstellen“. +3. Name/Feldgröße setzen, Spiel starten. +4. Code wird angezeigt. +5. Warten bis zweiter Spieler beitritt. +6. Spiel startet automatisch. + +### Flow B: Online-Spiel beitreten +1. Home → Online Multiplayer. +2. Auswahl „Beitreten“. +3. Namen + 6-stelligen Code eingeben. +4. Bei Erfolg Übergang in Spielansicht. +5. Bei Fehler (`failed=true`) visuelles Feedback. + +### Flow C: Lokal spielen +1. Home → Lokaler Multiplayer. +2. Namen/Feldgröße/Bot-Option setzen. +3. Spiel starten. +4. Spieler wechseln ab (oder Bot setzt random). +5. Endscreen erscheint bei Win/Draw. + +### Flow D: Spiel erneut starten +1. Endscreen „Spiel Neustarten“. +2. Online: Join auf vom Server gelieferten Replay-Code. +3. Lokal: Verbindungen schließen und neu initialisieren. + +--- + +## 15. Annahmen und Einschränkungen + +1. **Keine persistente Speicherung**: Alle Spiele gehen bei Serverneustart verloren. +2. **Maximal 2 Spieler pro Spiel**. +3. **Keine Authentifizierung/Autorisierung** für Spielzugriffe. +4. **Disconnect-Handling simpel**: Ein Disconnect beendet das Spiel. +5. **Fehlertoleranz begrenzt**: Teilweise nur Logging/Events, keine Retry-Strategie. +6. **Lokaler Modus nutzt trotzdem Server/SignalR** (zwei Verbindungen im selben Client-Kontext). +7. **Skalierung**: In-Memory-Liste ist nicht für Multi-Instance-Betrieb ausgelegt. + +--- + +## 16. Erweiterungsmöglichkeiten + +1. Persistentes Repository (SQL/NoSQL) inkl. Match-Historie +2. Benutzerkonten, Auth und Freundeslisten +3. Elo-/Ranking-System +4. Beobachtermodus (Spectator) +5. Chat pro Spielgruppe +6. Zugtimer und automatische Aufgabe +7. Mehrsprachigkeit (i18n) +8. KI mit heuristischer oder minimax-basierter Strategie +9. Reconnect-Mechanismus mit Session-Tokens +10. Horizontale Skalierung mit verteiltem Backplane/Cache für SignalR +11. REST-API für Match-Archiv und Statistiken +12. Erweiterte Telemetrie und strukturiertes Logging + +--- + +## Ableitbarkeit für Folgeartefakte + +Diese Spezifikation enthält alle notwendigen Informationen, um automatisiert folgende Artefakte abzuleiten: +- **Pflichtenheft**: Kapitel 1, 2, 8, 14, 15 +- **Meilensteinplanung**: Kapitel 3, 4, 5, 6, 16 +- **UML-Klassendiagramm**: Kapitel 5, 6, 7 +- **Ablaufplan / Flowchart**: Kapitel 8, 9, 14 +- **Benutzerdokumentation**: Kapitel 13, 14