Marco
ff2ec599fe
In order to simplify special moves like en passant or castling for the client, we want to deliver the board state after every move (and not only start square and end square). With PGN we can encode a chess position into a string. This commit implies changes to logic of the pieces' shortnames. This will break the client/server connection (at least for promotions).
171 lines
3.5 KiB
Go
171 lines
3.5 KiB
Go
package chess
|
|
|
|
import (
|
|
"log"
|
|
"mchess_server/api"
|
|
"mchess_server/types"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type Game struct {
|
|
id uuid.UUID
|
|
board Board
|
|
players []*Player
|
|
currentTurnPlayer *Player
|
|
}
|
|
|
|
const (
|
|
PlayerToMove = iota
|
|
CheckMove
|
|
CheckPlayerChange
|
|
)
|
|
|
|
func NewGame() *Game {
|
|
var game = Game{
|
|
id: uuid.New(),
|
|
board: newBoard(),
|
|
}
|
|
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) GetPlayer2() *Player {
|
|
return game.players[1]
|
|
}
|
|
|
|
func (game *Game) Handle() {
|
|
defer game.killGame()
|
|
|
|
ok := game.waitForWebsocketConnections()
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
err := game.notifyPlayersAboutGameStart()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
gameState := PlayerToMove
|
|
game.currentTurnPlayer = game.GetPlayer1()
|
|
var receivedMove types.Move
|
|
|
|
for {
|
|
|
|
switch gameState {
|
|
case PlayerToMove:
|
|
receivedMove, err = game.currentTurnPlayer.ReadMove()
|
|
if err != nil {
|
|
log.Println("Error while reading message:", err)
|
|
return
|
|
}
|
|
log.Println("Player ", game.currentTurnPlayer, " moved:\n", receivedMove)
|
|
|
|
gameState = CheckMove
|
|
|
|
case CheckMove:
|
|
valid, ruleViolation := game.board.CheckAndPlay(receivedMove)
|
|
|
|
if valid {
|
|
gameState = CheckPlayerChange
|
|
} else {
|
|
log.Println(err)
|
|
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)
|
|
gameState = PlayerToMove
|
|
}
|
|
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
|
|
}
|
|
|
|
gameState = PlayerToMove
|
|
}
|
|
|
|
log.Println("GameState = ", gameState)
|
|
if gameState == PlayerToMove {
|
|
log.Println("with player ", game.currentTurnPlayer, " to move")
|
|
}
|
|
}
|
|
}
|
|
|
|
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) 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
|
|
}
|
|
|
|
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.GetPlayer2().writeMessage(colorDeterminedPlayer2)
|
|
return nil
|
|
}
|
|
|
|
func (game Game) broadcastMove(move types.Move) error {
|
|
err := game.GetPlayer1().SendMove(move)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = game.GetPlayer2().SendMove(move)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|