41 KiB
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
- Spiel erstellen mit variabler Feldgröße
- Spiel beitreten über sechsstelligen Code
- Echtzeit-Synchronisation von Spielzustand und Spielende
- Gewinn-/Unentschieden-Erkennung serverseitig
- Lokales Spiel (ohne externen Gegenspieler)
- 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
- Frontend öffnet SignalR-Verbindung zu
/api/gamehub. - Frontend ruft Hub-Methoden (
CreateGame,JoinGame,Drop, ...). - Hub delegiert an
IGameManager. GameManagerliest/schreibt viaIGameRepository.GameManagersendet Events an einzelne Clients oder Gruppen.- 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
/
├── 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:
- Controller/Hub-Schicht: externer Zugriff
- Service-Schicht: Geschäftsabläufe
- Repository-Schicht: Speicherung und Lookup
- 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/GameManagerundIGameRepository/GameRepositoryvia DI. - Mappt
GameHubSocketals 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
Clientsund 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<string?> |
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<GameHubSocket> |
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<string?> |
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<Game> |
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<Game> |
Aktive Spiele im Arbeitsspeicher |
Methoden
| Methode | Parameter | Rückgabewert | Beschreibung |
|---|---|---|---|
GetAll |
- | List<Game> |
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
GameundSixDigitInt. - Wird von
GameManagergenutzt.
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
Gameund 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,GameFieldund 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<Player> |
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
GameFieldundPlayer. - Wird über
GameManagergesteuert.
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
Gamegehalten. - Von
GameManager.Dropaufgerufen.
GameInformationDto
Zweck
Transportobjekt für Frontend-Synchronisation.
Eigenschaften
| Name | Typ | Beschreibung |
|---|---|---|
Id |
string |
Spiel-ID |
Players |
List<Player> |
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
GameManagererzeugt 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.vueund 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
:toauf 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<GameSettings> |
Einstellungen zum Erstellen |
joiningModel |
Ref<JoinGameObject> |
Daten für Join-Flow |
currentState |
Ref<CurrentState> |
UI-State-Maschine |
game |
Ref<OnlineGame> |
Online-Spielorchestrator |
gameField |
Ref<number[][]> |
Feld für Anzeige |
currentSelectionIndex |
Ref<number | null> |
Hover-/Klickspalte |
gameEndedInformation |
Ref<GameEnded | null> |
Endscreen-Daten |
Methoden
| Methode | Parameter | Rückgabewert | Beschreibung |
|---|---|---|---|
createGame |
- | Promise<void> |
Delegiert an OnlineGame.createGame |
tryToJoin |
- | Promise<void> |
Delegiert an OnlineGame.joinGame |
restartGame |
- | Promise<void> |
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<GameSettings> |
Lokale Spieleinstellungen |
currentState |
Ref<CurrentState> |
UI-State (Create/Game/End) |
game |
Ref<LocalGame> |
Lokaler Spielorchestrator |
gameField |
Ref<number[][]> |
Angezeigter Feldzustand |
currentSelectionIndex |
Ref<number | null> |
Ausgewählte Spalte |
gameEndedInformation |
Ref<GameEnded | null> |
Endinformationen |
Methoden
| Methode | Parameter | Rückgabewert | Beschreibung |
|---|---|---|---|
startGame |
- | Promise<void> |
Verbindet beide lokale Clients und erstellt Spiel |
restart |
- | Promise<void> |
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<GameSettings> |
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<JoinGameObject> |
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<boolean> |
Verhindert Mehrfachklick auf Neustart |
message |
ComputedRef<string> |
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<number> |
Aktueller Wert |
inputWidth |
ComputedRef<string> |
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<FieldSize> |
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<void> |
Startet Verbindung |
disconnect |
- | Promise<void> |
Stoppt Verbindung |
createGame |
gFs: FieldSize |
Promise<void> |
Hub-Invoke CreateGame |
joinGame |
gameCode: number |
Promise<void> |
Hub-Invoke JoinGame |
requestGameInformation |
gameId: string |
Promise<void> |
Hub-Invoke RequestGameInformation |
drop |
gameId: string, column: number |
Promise<void> |
Hub-Invoke Drop |
Beziehungen
- Wird von
OnlineGameundLocalGameverwendet. - 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<void> |
Verbindet Spieler und erstellt Lobby |
joinGame |
joinObject: JoinGameObject |
Promise<void> |
Validiert Codeformat, verbindet, tritt Spiel bei |
updateState |
newState: GameInformationDto |
Promise<void> |
Aktualisiert lokalen Zustand und Callback |
drop |
index: number |
Promise<void> |
Führt Zug im aktuellen Spiel aus |
changePlaceDescription |
- | void |
Setzt zufällige Zugmeldung anhand currentTurn |
restartGame |
replayGameCode: any |
Promise<void> |
Trennt Verbindung und joint Replay-Code |
Beziehungen
- Nutzt
GameConnectionund UtilitygetRandomMovePhrase.
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<void> |
Verbindet beide Spieler und erstellt neues Spiel |
updateState |
newState: GameInformationDto |
Promise<void> |
Aktualisiert lokalen Zustand |
drop |
index: number |
Promise<void> |
Führt Zug durch aktuellen Spieler aus; Bot setzt random verzögert |
changePlaceDescription |
- | void |
Erzeugt zufällige Zugmeldung |
disconnectAll |
- | Promise<void> |
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
yZeilen, jede Zeile enthältxNullen.
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: stringgameCode: SixDigitIntplayers: ListcurrentTurn: int (1 oder 2)state: GameStatefield: GameField
Beziehungen:
- 1 Game besitzt 1 GameField
- 1 Game besitzt 0..2 Player
Player
name: stringconnectionId: stringplayerTag: int
GameField
gFs: CoordinatescurrentField: int[,]backupField: int[,]
GameInformationDto
id: stringplayers: Liststate: GameState?currentField: int[][]currentTurn: int
Coordinates
x: inty: int
SixDigitInt
value: int
Frontend-Datenmodelle
GameInformationDto (TS)
id: string | nullplayers: Player[]state: numbercurrentField: number[][]currentTurn: number
Player (TS)
name: stringconnectionId: stringplayerTag: number
GameEnded (TS)
method: stringplayer?: Player | nullreplayGameCode?: number | null
GameIdentifier (TS)
gameId: stringgameCode: number
GameSettings (TS)
- siehe Abschnitt 6
JoinGameObject (TS)
- siehe Abschnitt 6
8. Spiel- / Geschäftslogik
Spiel erstellen
- Client verbindet SignalR (
connect). - Client ruft
CreateGame(playerName, fieldSize). - Server erstellt
Game, setzt Spieler 1, vergibt GameCode. - Server fügt Client zur Gruppe
game.Idhinzu. - Server sendet
GameCreatedan Ersteller.
Spiel beitreten
- Client validiert 6-stelligen Code lokal.
- Client ruft
JoinGame(playerName, gameCode). - Server prüft Spiel vorhanden +
Lobby+ Platz frei. - Server fügt Spieler 2 hinzu, setzt ggf. Spiel auf
Running. - Server fügt Client zur Gruppe
game.Idhinzu. - Server sendet
GameStartedan Gruppe.
Spiel starten
- Start erfolgt implizit beim zweiten Spieler (
AddPlayer/JoinGame).
Spielzüge
- Aktiver Spieler sendet
Drop(gameId, column). - Server prüft Verbindung zu Spiel + Turn-Recht + Zustand
Running. GameField.Dropsetzt Stein in unterste freie Zelle.- Server toggelt
CurrentTurn. - Server sendet
FieldUpdatedan Spielgruppe.
Spielende
- Gewinn:
CheckForWin() != 0, dannGameEnded(Method=Win)+ Replay-Code. - Unentschieden:
IsFull() == true, dannGameEnded(Method=Draw)+ Replay-Code. - Disconnect:
DisconnectedPlayer, dannGameEnded(Method=PlayerDisconnected).
9. Datenfluss
Ablauf „Spiel erstellen“
- Frontend (
OnlineGame.createGame) sendetCreateGame. GameHubSocket.CreateGameerstelltPlayerund delegiert.GameManager.CreateGameruft RepositoryCreate.- Hub fügt Verbindung zur SignalR-Gruppe hinzu.
- Hub sendet
GameCreatedan Caller. - Frontend zeigt Join-Code in
GameCreationMenu.
Ablauf „Spielzug“
- Nutzer klickt Spalte in
Field.vue. - Route ruft
game.drop(index). GameConnection.dropinvokt Hub-Methode.GameManager.Dropvalidiert und setzt Stein.FieldUpdatedwird an Gruppe gesendet.- Clients aktualisieren
gameFieldund Statusnachricht.
Ablauf „Spielende + Replay“
- Nach Win/Draw erstellt Server ein neues Spiel mit gleicher Feldgröße.
- Server sendet
GameEndedinkl.ReplayGameCode. - Frontend zeigt
GameEndedMenu. - Beim Neustart joint Client per Replay-Code.
10. Wichtige Algorithmen
Spielzug validieren
- Prüfungen in Reihenfolge:
- Spiel existiert
- Spieler gehört zum Spiel
- Spielzustand ist
Running CurrentTurn == player.PlayerTag- Spalte in Bounds
- Spalte nicht voll
- 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)
- Horizontal
- Der Algorithmus zählt zusammenhängende Steine in beide Richtungen und addiert die Startzelle.
>= 4ergibt 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
- Startseite: Moduswahl lokal/online
- Online: Auswahlseite: Erstellen oder Beitreten
- Spielerstellung: Namen, Feldgröße, Presets
- Join-Seite: Name + OTP-Feld für Spielcode
- Spielansicht: Spielfeld + Zugstatus
- Endscreen: Ergebnis + Neustartoption
UI-Ablauf
- Nutzer startet auf Home und navigiert über Buttons.
- In Spielansicht zeigt
Fieldden Rasterzustand mit Hover-Pfeil. InfoFieldzeigt 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
- Home → Online Multiplayer.
- Auswahl „Erstellen“.
- Name/Feldgröße setzen, Spiel starten.
- Code wird angezeigt.
- Warten bis zweiter Spieler beitritt.
- Spiel startet automatisch.
Flow B: Online-Spiel beitreten
- Home → Online Multiplayer.
- Auswahl „Beitreten“.
- Namen + 6-stelligen Code eingeben.
- Bei Erfolg Übergang in Spielansicht.
- Bei Fehler (
failed=true) visuelles Feedback.
Flow C: Lokal spielen
- Home → Lokaler Multiplayer.
- Namen/Feldgröße/Bot-Option setzen.
- Spiel starten.
- Spieler wechseln ab (oder Bot setzt random).
- Endscreen erscheint bei Win/Draw.
Flow D: Spiel erneut starten
- Endscreen „Spiel Neustarten“.
- Online: Join auf vom Server gelieferten Replay-Code.
- Lokal: Verbindungen schließen und neu initialisieren.
15. Annahmen und Einschränkungen
- Keine persistente Speicherung: Alle Spiele gehen bei Serverneustart verloren.
- Maximal 2 Spieler pro Spiel.
- Keine Authentifizierung/Autorisierung für Spielzugriffe.
- Disconnect-Handling simpel: Ein Disconnect beendet das Spiel.
- Fehlertoleranz begrenzt: Teilweise nur Logging/Events, keine Retry-Strategie.
- Lokaler Modus nutzt trotzdem Server/SignalR (zwei Verbindungen im selben Client-Kontext).
- Skalierung: In-Memory-Liste ist nicht für Multi-Instance-Betrieb ausgelegt.
16. Erweiterungsmöglichkeiten
- Persistentes Repository (SQL/NoSQL) inkl. Match-Historie
- Benutzerkonten, Auth und Freundeslisten
- Elo-/Ranking-System
- Beobachtermodus (Spectator)
- Chat pro Spielgruppe
- Zugtimer und automatische Aufgabe
- Mehrsprachigkeit (i18n)
- KI mit heuristischer oder minimax-basierter Strategie
- Reconnect-Mechanismus mit Session-Tokens
- Horizontale Skalierung mit verteiltem Backplane/Cache für SignalR
- REST-API für Match-Archiv und Statistiken
- 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