package game import ( "fmt" "chess/internal/server/board" "chess/internal/server/core" ) type Snapshot struct { FEN string `json:"fen"` PreviousMove string `json:"previousMove"` NextTurnColor core.Color `json:"nextTurnColor"` PlayerType core.PlayerType `json:"playerType"` PlayerID string `json:"playerId"` // ID of the player whose turn it is } // MoveResult tracks the outcome of a move type MoveResult struct { Move string `json:"move"` PlayerColor core.Color `json:"playerColor"` GameState core.State `json:"gameState"` Score int `json:"score"` Depth int `json:"depth"` } type Game struct { snapshots []Snapshot `json:"snapshots"` players map[core.Color]*core.Player `json:"players"` state core.State `json:"state"` lastResult *MoveResult `json:"lastResult,omitempty"` } func New(initialFEN string, whitePlayer, blackPlayer *core.Player, startingTurnColor core.Color) *Game { // Determine which player's turn it is initially var initialPlayerID string if startingTurnColor == core.ColorWhite { initialPlayerID = whitePlayer.ID } else { initialPlayerID = blackPlayer.ID } return &Game{ snapshots: []Snapshot{ { FEN: initialFEN, PreviousMove: "", NextTurnColor: startingTurnColor, PlayerID: initialPlayerID, }, }, players: map[core.Color]*core.Player{ core.ColorWhite: whitePlayer, core.ColorBlack: blackPlayer, }, state: core.StateOngoing, } } func (g *Game) SetLastResult(result *MoveResult) { g.lastResult = result } func (g *Game) LastResult() *MoveResult { return g.lastResult } // CurrentSnapshot returns the latest game snapshot func (g *Game) CurrentSnapshot() Snapshot { return g.snapshots[len(g.snapshots)-1] } // CurrentFEN returns the current position in FEN notation func (g *Game) CurrentFEN() string { return g.CurrentSnapshot().FEN } func (g *Game) NextTurnColor() core.Color { return g.CurrentSnapshot().NextTurnColor } func (g *Game) NextPlayer() *core.Player { return g.players[g.NextTurnColor()] } func (g *Game) GetPlayer(color core.Color) *core.Player { return g.players[color] } func (g *Game) AddSnapshot(fen string, move string, nextTurnColor core.Color) { // Get the player ID for the next turn nextPlayer := g.players[nextTurnColor] g.snapshots = append(g.snapshots, Snapshot{ FEN: fen, PreviousMove: move, NextTurnColor: nextTurnColor, PlayerID: nextPlayer.ID, }) } func (g *Game) UpdatePlayers(whitePlayer, blackPlayer *core.Player) { g.players[core.ColorWhite] = whitePlayer g.players[core.ColorBlack] = blackPlayer // Update current snapshot's PlayerID to reflect new player if len(g.snapshots) > 0 { currentSnap := &g.snapshots[len(g.snapshots)-1] currentSnap.PlayerID = g.players[currentSnap.NextTurnColor].ID } } func (g *Game) UndoMoves(count int) error { if count < 1 { return fmt.Errorf("invalid undo count: %d", count) } availableMoves := len(g.snapshots) - 1 if availableMoves < count { return fmt.Errorf("cannot undo %d moves: only %d moves available", count, availableMoves) } g.snapshots = g.snapshots[:len(g.snapshots)-count] g.state = core.StateOngoing // Reset game state when undoing g.lastResult = nil // Clear last result return nil } func (g *Game) Moves() []string { moves := []string{} for i := 1; i < len(g.snapshots); i++ { if g.snapshots[i].PreviousMove != "" { moves = append(moves, g.snapshots[i].PreviousMove) } } return moves } func (g *Game) State() core.State { return g.state } func (g *Game) SetState(s core.State) { g.state = s } func (g *Game) InitialFEN() string { if len(g.snapshots) > 0 { return g.snapshots[0].FEN } return board.StartingFEN } // ClaimSlot claims a player slot for a user // Caller must hold the lock func (g *Game) ClaimSlot(color core.Color, userID string) error { player := g.players[color] if player == nil { return fmt.Errorf("invalid color") } if player.Type != core.PlayerHuman { return fmt.Errorf("cannot claim computer slot") } if player.ClaimedBy != "" && player.ClaimedBy != userID { return fmt.Errorf("slot already claimed by another user") } player.ClaimedBy = userID return nil } // GetSlotOwner returns the userID that claimed the slot, empty if unclaimed // Caller must hold the lock func (g *Game) GetSlotOwner(color core.Color) string { player := g.players[color] if player == nil { return "" } return player.ClaimedBy } // IsSlotClaimedBy checks if a specific user owns the slot func (g *Game) IsSlotClaimedBy(color core.Color, userID string) bool { return g.GetSlotOwner(color) == userID } // HasComputerPlayer returns true if at least one player is computer func (g *Game) HasComputerPlayer() bool { white := g.players[core.ColorWhite] black := g.players[core.ColorBlack] return (white != nil && white.Type == core.PlayerComputer) || (black != nil && black.Type == core.PlayerComputer) }