package chess import ( "context" "log" "math/rand" "mchess_server/api" "mchess_server/types" "github.com/google/uuid" "nhooyr.io/websocket" ) type Game struct { id uuid.UUID board Board players []*Player currentTurnPlayer *Player gameState int isBeingHandled bool } const ( Init = iota Prepare PlayerToMove CheckMove CheckPlayerChange GameEnded ) func NewGame() *Game { var game = Game{ id: uuid.New(), board: newBoard(), gameState: Init, } game.board.Init() return &game } func (game Game) GetPlayers() []*Player { return game.players } func (game Game) GetPlayer1() *Player { return game.players[0] } func (game Game) GetRandomPlayer() *Player { index := rand.Int() % 2 return game.players[index] } func (game Game) GetPlayer2() *Player { return game.players[1] } func (game *Game) prepare() { game.players[0].color = types.White game.players[1].color = types.Black game.currentTurnPlayer = game.GetPlayer1() game.players[0].SetDisconnectCallback(game.playerDisconnected) game.players[1].SetDisconnectCallback(game.playerDisconnected) err := game.notifyPlayersAboutGameStart() if err != nil { return } } func (game *Game) StartHandling() { if game.isBeingHandled { return } game.isBeingHandled = true go game.Handle() } func (game *Game) Handle() { defer game.killGame() var receivedMove types.Move var gameEndReason GameEndedReason var err error for { switch game.gameState { case Init: // Todo: Randomize which player will play white game.gameState = Prepare case Prepare: game.prepare() game.gameState = PlayerToMove case PlayerToMove: log.Println("with ", game.currentTurnPlayer.GetPlayerColor(), " to move") receivedMove, err = game.currentTurnPlayer.ReadMove() if err != nil { log.Println("Error while reading message:", err) return } log.Println("Player ", game.currentTurnPlayer, " moved:\n", receivedMove) game.gameState = CheckMove case CheckMove: valid, ruleViolation := game.board.CheckAndPlay(&receivedMove) if !valid { invalidMoveMessage, err := api.GetInvalidMoveMessage(receivedMove, ruleViolation.String()) if err != nil { log.Println("Error marshalling 'colorDetermined' message for player 1", err) return } game.currentTurnPlayer.writeMessage(invalidMoveMessage) game.gameState = PlayerToMove continue } game.gameState = CheckPlayerChange case CheckPlayerChange: if game.currentTurnPlayer.Uuid == game.players[0].Uuid { game.currentTurnPlayer = game.players[1] } else { game.currentTurnPlayer = game.players[0] } err := game.broadcastMove(receivedMove) if err != nil { log.Println("Error broadcasting move ", err) return } if gameEnded, reason := game.board.HasGameEnded(receivedMove); gameEnded { gameEndReason = reason game.gameState = GameEnded continue } game.gameState = PlayerToMove case GameEnded: game.broadcastGameEnd(gameEndReason) return } log.Println("GameState = ", game.gameState) } } func (game *Game) AddPlayersToGame(player *Player) { game.players = append(game.players, player) } func (game *Game) killGame() { log.Println("Game should be killed") } func (game Game) notifyPlayersAboutGameStart() error { colorDeterminedPlayer1, err := api.GetColorDeterminedMessage(types.White) if err != nil { log.Println("Error marshalling 'colorDetermined' message for player 1", err) return err } colorDeterminedPlayer2, err := api.GetColorDeterminedMessage(types.Black) if err != nil { log.Println("Error marshalling 'colorDetermined' message for player 2", err) return err } game.GetPlayer1().writeMessage(colorDeterminedPlayer1) game.GetPlayer1().SendBoardState(types.Move{}, game.board.PGN(), types.White) game.GetPlayer2().writeMessage(colorDeterminedPlayer2) game.GetPlayer2().SendBoardState(types.Move{}, game.board.PGN(), types.White) return nil } func (game Game) broadcastMove(move types.Move) error { err := game.GetPlayer1().SendBoardState(move, game.board.PGN(), game.currentTurnPlayer.color) if err != nil { return err } err = game.GetPlayer2().SendBoardState(move, game.board.PGN(), game.currentTurnPlayer.color) if err != nil { return err } return nil } func (game Game) broadcastGameEnd(reason GameEndedReason) error { err := game.GetPlayer1().SendGameEnded(reason) if err != nil { return err } err = game.GetPlayer2().SendGameEnded(reason) if err != nil { return err } return nil } func (game *Game) playerDisconnected(p *Player) { } func (game *Game) SetWebsocketConnectionFor(ctx context.Context, p *Player, ws *websocket.Conn) { p.SetWebsocketConnectionAndSendBoardState(ctx, ws, game.board.PGN(), game.board.colorToMove) }