mchess-server/chess/game.go

182 lines
4.1 KiB
Go
Raw Normal View History

package chess
import (
2023-06-25 22:51:20 +00:00
"log"
2023-06-25 14:11:29 +00:00
"mchess_server/api"
"mchess_server/types"
2023-06-06 20:58:33 +00:00
"time"
"github.com/google/uuid"
)
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
}
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: PlayerToMove,
}
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]
}
2023-06-06 20:58:33 +00:00
func (game Game) GetPlayer2() *Player {
return game.players[1]
}
func (game *Game) prepare() {
2023-06-06 20:58:33 +00:00
ok := game.waitForWebsocketConnections()
if !ok {
return
}
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
//CHANGES:
/*
Do not wait for the players websocket connection before the game.
Let Connection do it.
Then here in game handler, just read from Connection (aka messagebuffer) and when data arrives, we read it
Question: how do we handle connection losses, should game handler observe state of connection and send a notification to the other player when one player is disconnected
Question: How would reconnect work in this scenario?
*/
2023-10-12 19:03:12 +00:00
func (game *Game) Handle() {
defer game.killGame()
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()
case Prepare:
game.prepare()
2022-12-21 23:02:07 +00:00
case PlayerToMove:
2023-10-12 19:03:12 +00:00
log.Println("with player ", game.currentTurnPlayer, " 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")
}
func (game *Game) waitForWebsocketConnections() bool {
timer := time.NewTimer(5 * time.Second)
numberOfConnections := 0
waitingForPlayers := make(chan bool)
go game.GetPlayer1().WaitForWebsocketConnection(waitingForPlayers)
go game.GetPlayer2().WaitForWebsocketConnection(waitingForPlayers)
for numberOfConnections < 2 {
select {
case <-waitingForPlayers:
numberOfConnections++
case <-timer.C:
return false
}
}
return true
}
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
}