2023-05-31 21:55:40 +00:00
|
|
|
package chess
|
2022-12-14 21:19:47 +00:00
|
|
|
|
|
|
|
import (
|
2023-06-25 22:51:20 +00:00
|
|
|
"log"
|
2023-11-26 20:29:14 +00:00
|
|
|
"math/rand"
|
2023-06-25 14:11:29 +00:00
|
|
|
"mchess_server/api"
|
|
|
|
"mchess_server/types"
|
2023-04-22 17:23:46 +00:00
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2023-11-26 23:12:42 +00:00
|
|
|
"github.com/samber/lo"
|
2022-12-14 21:19:47 +00:00
|
|
|
)
|
|
|
|
|
2023-04-22 17:23:46 +00:00
|
|
|
type Game struct {
|
|
|
|
id uuid.UUID
|
2023-06-12 20:32:31 +00:00
|
|
|
board Board
|
2023-06-02 19:14:02 +00:00
|
|
|
players []*Player
|
|
|
|
currentTurnPlayer *Player
|
2023-10-12 19:03:12 +00:00
|
|
|
gameState int
|
2023-11-26 23:12:42 +00:00
|
|
|
isBeingHandled bool
|
2022-12-14 21:19:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2023-11-20 00:25:27 +00:00
|
|
|
Init = iota
|
|
|
|
Prepare
|
|
|
|
PlayerToMove
|
2023-06-12 20:32:31 +00:00
|
|
|
CheckMove
|
|
|
|
CheckPlayerChange
|
2022-12-14 21:19:47 +00:00
|
|
|
)
|
|
|
|
|
2023-04-22 17:23:46 +00:00
|
|
|
func NewGame() *Game {
|
2023-08-12 09:24:40 +00:00
|
|
|
var game = Game{
|
2023-10-12 19:03:12 +00:00
|
|
|
id: uuid.New(),
|
|
|
|
board: newBoard(),
|
2023-11-26 20:29:14 +00:00
|
|
|
gameState: Init,
|
2022-12-14 21:19:47 +00:00
|
|
|
}
|
2023-06-12 20:32:31 +00:00
|
|
|
game.board.Init()
|
2022-12-14 21:19:47 +00:00
|
|
|
|
|
|
|
return &game
|
|
|
|
}
|
|
|
|
|
2023-06-06 20:58:33 +00:00
|
|
|
func (game Game) GetPlayers() []*Player {
|
|
|
|
return game.players
|
|
|
|
}
|
|
|
|
|
|
|
|
func (game Game) GetPlayer1() *Player {
|
2023-06-02 19:14:02 +00:00
|
|
|
return game.players[0]
|
2023-05-30 20:01:20 +00:00
|
|
|
}
|
|
|
|
|
2023-11-26 20:29:14 +00:00
|
|
|
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 {
|
2023-06-02 19:14:02 +00:00
|
|
|
return game.players[1]
|
2023-05-30 20:01:20 +00:00
|
|
|
}
|
|
|
|
|
2023-11-20 00:58:36 +00:00
|
|
|
func (game *Game) prepare() {
|
2023-11-26 20:29:14 +00:00
|
|
|
game.players[0].color = types.White
|
|
|
|
game.players[1].color = types.Black
|
2023-05-30 20:01:20 +00:00
|
|
|
|
2023-11-26 23:12:42 +00:00
|
|
|
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
|
|
|
|
2023-11-26 23:12:42 +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()
|
2023-11-20 00:58:36 +00:00
|
|
|
|
2023-11-26 23:12:42 +00:00
|
|
|
game.prepare()
|
2023-11-26 20:29:14 +00:00
|
|
|
|
2023-06-08 18:20:37 +00:00
|
|
|
var receivedMove types.Move
|
2023-11-20 00:25:27 +00:00
|
|
|
var err error
|
2022-12-14 21:19:47 +00:00
|
|
|
|
|
|
|
for {
|
2023-10-12 19:03:12 +00:00
|
|
|
switch game.gameState {
|
2023-11-20 00:25:27 +00:00
|
|
|
case Init:
|
|
|
|
game.currentTurnPlayer = game.GetPlayer1()
|
2023-11-26 20:29:14 +00:00
|
|
|
game.gameState = Prepare
|
|
|
|
|
2023-11-20 00:25:27 +00:00
|
|
|
case Prepare:
|
|
|
|
game.prepare()
|
2023-11-26 20:29:14 +00:00
|
|
|
game.gameState = PlayerToMove
|
|
|
|
|
2022-12-21 23:02:07 +00:00
|
|
|
case PlayerToMove:
|
2023-11-26 20:29:14 +00:00
|
|
|
log.Println("with ", game.currentTurnPlayer.GetPlayerColor(), " to move")
|
2023-11-20 00:58:36 +00:00
|
|
|
receivedMove, err = game.currentTurnPlayer.ReadMove()
|
2022-12-14 21:19:47 +00:00
|
|
|
if err != nil {
|
2022-12-21 23:02:07 +00:00
|
|
|
log.Println("Error while reading message:", err)
|
2022-12-14 21:19:47 +00:00
|
|
|
return
|
|
|
|
}
|
2023-06-08 18:20:37 +00:00
|
|
|
log.Println("Player ", game.currentTurnPlayer, " moved:\n", receivedMove)
|
2022-12-14 22:16:51 +00:00
|
|
|
|
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
|
2023-06-20 21:53:54 +00:00
|
|
|
} else {
|
2023-07-11 20:28:07 +00:00
|
|
|
invalidMoveMessage, err := api.GetInvalidMoveMessage(receivedMove, ruleViolation.String())
|
2023-06-20 21:53:54 +00:00
|
|
|
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
|
|
|
}
|
2022-12-18 00:05:23 +00:00
|
|
|
case CheckPlayerChange:
|
2023-04-22 19:41:24 +00:00
|
|
|
if game.currentTurnPlayer.Uuid == game.players[0].Uuid {
|
|
|
|
game.currentTurnPlayer = game.players[1]
|
2022-12-14 21:19:47 +00:00
|
|
|
} else {
|
2023-04-22 19:41:24 +00:00
|
|
|
game.currentTurnPlayer = game.players[0]
|
2022-12-14 21:19:47 +00:00
|
|
|
}
|
|
|
|
|
2023-10-12 19:03:12 +00:00
|
|
|
err := game.broadcastMove(receivedMove)
|
2023-04-22 19:41:24 +00:00
|
|
|
if err != nil {
|
2023-06-08 18:20:37 +00:00
|
|
|
log.Println("Error broadcasting move ", err)
|
|
|
|
return
|
2023-04-22 19:41:24 +00:00
|
|
|
}
|
|
|
|
|
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)
|
2022-12-14 21:19:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-02 19:14:02 +00:00
|
|
|
func (game *Game) AddPlayersToGame(player *Player) {
|
|
|
|
game.players = append(game.players, player)
|
2022-12-14 21:19:47 +00:00
|
|
|
}
|
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
|
|
|
|
}
|
2023-11-26 23:12:42 +00:00
|
|
|
|
|
|
|
func (game *Game) playerDisconnected(p *Player) {
|
|
|
|
log.Println(string(p.color), " disconnected")
|
|
|
|
playerLeft := lo.Filter(game.players, func(player *Player, _ int) bool {
|
|
|
|
return player.color != p.color
|
|
|
|
})
|
|
|
|
game.players = playerLeft
|
|
|
|
}
|