mchess-server/chess/game.go

192 lines
4.2 KiB
Go
Raw Normal View History

package chess
import (
"context"
2023-06-25 22:51:20 +00:00
"log"
"math/rand"
2023-06-25 14:11:29 +00:00
"mchess_server/api"
"mchess_server/types"
"github.com/google/uuid"
"github.com/samber/lo"
"nhooyr.io/websocket"
)
type Game struct {
id uuid.UUID
2023-06-12 20:32:31 +00:00
board Board
players []*Player
currentTurnPlayer *Player
2023-10-12 19:03:12 +00:00
gameState int
isBeingHandled bool
}
const (
Init = iota
Prepare
PlayerToMove
2023-06-12 20:32:31 +00:00
CheckMove
CheckPlayerChange
)
func NewGame() *Game {
var game = Game{
2023-10-12 19:03:12 +00:00
id: uuid.New(),
board: newBoard(),
gameState: Init,
}
2023-06-12 20:32:31 +00:00
game.board.Init()
return &game
}
2023-06-06 20:58:33 +00:00
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]
}
2023-06-06 20:58:33 +00:00
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.players[0].SetDisconnectCallback(game.playerDisconnected)
game.players[1].SetDisconnectCallback(game.playerDisconnected)
2023-06-08 18:20:37 +00:00
err := game.notifyPlayersAboutGameStart()
if err != nil {
return
}
2023-10-12 19:03:12 +00:00
}
2023-06-08 18:20:37 +00:00
func (game *Game) StartHandling() {
if game.isBeingHandled {
return
}
game.isBeingHandled = true
go game.Handle()
}
2023-10-12 19:03:12 +00:00
func (game *Game) Handle() {
defer game.killGame()
game.prepare()
2023-06-08 18:20:37 +00:00
var receivedMove types.Move
var err error
for {
2023-10-12 19:03:12 +00:00
switch game.gameState {
case Init:
game.currentTurnPlayer = game.GetPlayer1()
game.gameState = Prepare
case Prepare:
game.prepare()
game.gameState = PlayerToMove
2022-12-21 23:02:07 +00:00
case PlayerToMove:
log.Println("with ", game.currentTurnPlayer.GetPlayerColor(), " to move")
receivedMove, err = game.currentTurnPlayer.ReadMove()
if err != nil {
2022-12-21 23:02:07 +00:00
log.Println("Error while reading message:", err)
return
}
2023-06-08 18:20:37 +00:00
log.Println("Player ", game.currentTurnPlayer, " moved:\n", receivedMove)
2023-10-12 19:03:12 +00:00
game.gameState = CheckMove
2023-06-12 20:32:31 +00:00
case CheckMove:
2023-07-11 20:28:07 +00:00
valid, ruleViolation := game.board.CheckAndPlay(receivedMove)
2023-06-12 20:32:31 +00:00
if valid {
2023-10-12 19:03:12 +00:00
game.gameState = CheckPlayerChange
} else {
2023-07-11 20:28:07 +00:00
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)
2023-10-12 19:03:12 +00:00
game.gameState = PlayerToMove
2023-06-12 20:32:31 +00:00
}
case CheckPlayerChange:
if game.currentTurnPlayer.Uuid == game.players[0].Uuid {
game.currentTurnPlayer = game.players[1]
} else {
game.currentTurnPlayer = game.players[0]
}
2023-10-12 19:03:12 +00:00
err := game.broadcastMove(receivedMove)
if err != nil {
2023-06-08 18:20:37 +00:00
log.Println("Error broadcasting move ", err)
return
}
2023-10-12 19:03:12 +00:00
game.gameState = PlayerToMove
2022-12-21 23:02:07 +00:00
}
2023-10-12 19:03:12 +00:00
log.Println("GameState = ", game.gameState)
}
}
func (game *Game) AddPlayersToGame(player *Player) {
game.players = append(game.players, player)
}
2023-06-06 20:58:33 +00:00
func (game *Game) killGame() {
log.Println("Game should be killed")
}
2023-06-08 18:20:37 +00:00
func (game Game) notifyPlayersAboutGameStart() error {
2023-06-12 20:32:31 +00:00
colorDeterminedPlayer1, err := api.GetColorDeterminedMessage(types.White)
2023-06-08 18:20:37 +00:00
if err != nil {
log.Println("Error marshalling 'colorDetermined' message for player 1", err)
return err
}
2023-06-12 20:32:31 +00:00
colorDeterminedPlayer2, err := api.GetColorDeterminedMessage(types.Black)
2023-06-08 18:20:37 +00:00
if err != nil {
log.Println("Error marshalling 'colorDetermined' message for player 2", err)
return err
}
game.GetPlayer1().writeMessage(colorDeterminedPlayer1)
game.GetPlayer2().writeMessage(colorDeterminedPlayer2)
return nil
}
func (game Game) broadcastMove(move types.Move) error {
2023-08-13 22:05:47 +00:00
err := game.GetPlayer1().SendMoveAndPosition(move, game.board.PGN())
2023-06-08 18:20:37 +00:00
if err != nil {
return err
}
2023-08-13 22:05:47 +00:00
err = game.GetPlayer2().SendMoveAndPosition(move, game.board.PGN())
2023-06-08 18:20:37 +00:00
if err != nil {
return err
}
return nil
}
func (game *Game) playerDisconnected(p *Player) {
log.Println(string(p.color), " disconnected")
playerStillInGame := lo.Filter(game.players, func(player *Player, _ int) bool {
return player.color != p.color
})
game.players = playerStillInGame
}
func (game *Game) SetWebsocketConnectionFor(ctx context.Context, p *Player, ws *websocket.Conn) {
p.SetWebsocketConnectionAndSendBoardState(ctx, ws, game.board.PGN(), game.board.colorToMove)
}