From af993876fd3476163f2ef68e89439b308ba2cf64 Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 17 Jan 2024 17:38:06 +0100 Subject: [PATCH] Introduce check if the game has ended (checkmate/stalemate) Introduce checkmate check and send out 'gameEnded' message --- api/move.go | 1 + chess/bishop.go | 4 +- chess/board.go | 71 ++++++++++++++++++-- chess/board_test.go | 137 ++++++++++++++++++++++++++++++--------- chess/game.go | 36 ++++++++-- chess/king.go | 4 +- chess/knight.go | 4 +- chess/pawn.go | 4 +- chess/piece_interface.go | 2 +- chess/player.go | 18 +++++ chess/queen.go | 4 +- chess/rook.go | 4 +- chess/rook_test.go | 4 +- connection/type.go | 2 +- types/common.go | 5 +- 15 files changed, 243 insertions(+), 57 deletions(-) diff --git a/api/move.go b/api/move.go index e42a743..794cb81 100644 --- a/api/move.go +++ b/api/move.go @@ -21,6 +21,7 @@ const ( MoveMessage MessageType = "move" InvalidMoveMessage MessageType = "invalidMove" ColorDetermined MessageType = "colorDetermined" + GameEnded MessageType = "gameEnded" ) func (m WebsocketMessage) IsValid() bool { diff --git a/chess/bishop.go b/chess/bishop.go index 30d9dde..e990402 100644 --- a/chess/bishop.go +++ b/chess/bishop.go @@ -9,14 +9,14 @@ type Bishop struct { } func (b Bishop) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { - return b.GetAllNonBlockedMoves(board, fromSquare) + return b.GetAllNonBlockedSquares(board, fromSquare) } func (b Bishop) GetColor() types.ChessColor { return b.Color } -func (b Bishop) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { +func (b Bishop) GetAllNonBlockedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { return board.GetNonBlockedDiagonals(fromSquare) } diff --git a/chess/board.go b/chess/board.go index b596134..7d6a730 100644 --- a/chess/board.go +++ b/chess/board.go @@ -57,7 +57,7 @@ func (b *Board) Init() { b.position[types.Coordinate{Row: 8, Col: 8}] = Rook{Color: types.Black} } -func (b *Board) CheckAndPlay(move types.Move) (bool, Violation) { +func (b *Board) CheckAndPlay(move *types.Move) (bool, Violation) { tempBoard := b.getCopyOfBoard() //Check start square of move @@ -80,13 +80,13 @@ func (b *Board) CheckAndPlay(move types.Move) (bool, Violation) { } } - wasSpecialMove, err := tempBoard.handleSpecialMove(move) + wasSpecialMove, err := tempBoard.handleSpecialMove(*move) if err != nil { return false, InvalidMove } if !wasSpecialMove { - allMovesExceptBlocked := pieceAtStartSquare.GetAllNonBlockedMoves(tempBoard, move.StartSquare) + allMovesExceptBlocked := pieceAtStartSquare.GetAllNonBlockedSquares(tempBoard, move.StartSquare) legal := lo.Contains(allMovesExceptBlocked, move.EndSquare) if !legal { return false, InvalidMove @@ -109,7 +109,7 @@ func (b *Board) CheckAndPlay(move types.Move) (bool, Violation) { b.position = tempBoard.position b.history = tempBoard.history b.colorToMove = b.colorToMove.Opposite() - b.appendMoveToHistory(move) + b.appendMoveToHistory(*move) pieceAtStartSquare.AfterMoveAction(b, move.StartSquare) @@ -204,3 +204,66 @@ func (b *Board) handleSpecialMove(move types.Move) (bool, error) { } return was, err } + +type GameEndedReason string + +const ( + NoReason GameEndedReason = "noReason" + WhiteIsCheckmated GameEndedReason = "whiteIsCheckmated" + BlackIsCheckmated GameEndedReason = "blackIsCheckmated" + StalemateReason GameEndedReason = "stalemate" +) + +func (r GameEndedReason) String() string { + return string(r) +} + +func (b *Board) HasGameEnded(lastMove types.Move) (bool, GameEndedReason) { + checkForColor := lastMove.ColorMoved.Opposite() + if checkmate := b.isColorCheckmated(checkForColor); checkmate { + switch checkForColor { + case types.White: + return true, WhiteIsCheckmated + case types.Black: + return true, BlackIsCheckmated + } + } + if b.isStalemate() { + return true, StalemateReason + } + + return false, NoReason +} + +func (b *Board) isColorCheckmated(color types.ChessColor) bool { + inCheck, _ := b.isKingOfMovingColorInCheck(color) + if !inCheck { + return false + } + + copyOfBoard := b.getCopyOfBoard() + + var movesToCheck []types.Move + + for startSquare, piece := range b.position { + if piece.GetColor() == color { + for _, endSquare := range piece.GetAllNonBlockedSquares(*b, startSquare) { + move := types.Move{StartSquare: startSquare, EndSquare: endSquare} + movesToCheck = append(movesToCheck, move) + } + } + } + + for _, move := range movesToCheck { + valid, _ := copyOfBoard.CheckAndPlay(&move) + if valid { + return false + } + } + + return true +} + +func (b *Board) isStalemate() bool { + return false +} diff --git a/chess/board_test.go b/chess/board_test.go index 4c3ce04..be48f64 100644 --- a/chess/board_test.go +++ b/chess/board_test.go @@ -20,7 +20,7 @@ func Test_CheckMove_validPawnMove(t *testing.T) { EndSquare: types.Coordinate{Col: 1, Row: 3}, } - good, _ := board.CheckAndPlay(move) + good, _ := board.CheckAndPlay(&move) assert.True(t, good) //we take the pawn @@ -28,7 +28,7 @@ func Test_CheckMove_validPawnMove(t *testing.T) { StartSquare: types.Coordinate{Col: 2, Row: 4}, EndSquare: types.Coordinate{Col: 1, Row: 3}, } - good, _ = board.CheckAndPlay(secondMove) + good, _ = board.CheckAndPlay(&secondMove) assert.True(t, good) } @@ -45,7 +45,7 @@ func Test_CheckMove_enPassant(t *testing.T) { EndSquare: types.Coordinate{Col: 5, Row: 4}, } - good, reason := board.CheckAndPlay(move) + good, reason := board.CheckAndPlay(&move) assert.True(t, good) assert.Empty(t, reason) assert.Equal(t, Pawn{Color: types.White}, board.position[types.Coordinate{Col: 5, Row: 4}]) @@ -54,7 +54,7 @@ func Test_CheckMove_enPassant(t *testing.T) { StartSquare: types.Coordinate{Col: 6, Row: 4}, EndSquare: types.Coordinate{Col: 5, Row: 3}, } - good, reason = board.CheckAndPlay(newMove) + good, reason = board.CheckAndPlay(&newMove) assert.True(t, good) assert.Empty(t, reason) // the black pawn is on its correct square @@ -77,7 +77,7 @@ func Test_CheckMove_invalidPawnMoves(t *testing.T) { StartSquare: types.Coordinate{Col: 2, Row: 5}, EndSquare: types.Coordinate{Col: 2, Row: 6}, } - legalMove, _ := board.CheckAndPlay(move) + legalMove, _ := board.CheckAndPlay(&move) assert.False(t, legalMove) }) @@ -93,7 +93,7 @@ func Test_CheckMove_invalidPawnMoves(t *testing.T) { StartSquare: types.Coordinate{Col: 2, Row: 5}, EndSquare: types.Coordinate{Col: 3, Row: 5}, } - legal, _ := board.CheckAndPlay(move) + legal, _ := board.CheckAndPlay(&move) assert.False(t, legal) assert.Equal(t, boardBeforeMove, board) @@ -102,7 +102,7 @@ func Test_CheckMove_invalidPawnMoves(t *testing.T) { StartSquare: types.Coordinate{Col: 2, Row: 5}, EndSquare: types.Coordinate{Col: 1, Row: 5}, } - legal, _ = board.CheckAndPlay(move) + legal, _ = board.CheckAndPlay(&move) assert.False(t, legal) assert.Equal(t, boardBeforeMove, board) @@ -111,7 +111,7 @@ func Test_CheckMove_invalidPawnMoves(t *testing.T) { StartSquare: types.Coordinate{Col: 2, Row: 5}, EndSquare: types.Coordinate{Col: 6, Row: 5}, } - legal, _ = board.CheckAndPlay(move) + legal, _ = board.CheckAndPlay(&move) assert.False(t, legal) assert.Equal(t, boardBeforeMove, board) @@ -130,7 +130,7 @@ func Test_CheckMove_invalidPawnMoves(t *testing.T) { StartSquare: types.Coordinate{Col: 2, Row: 6}, EndSquare: types.Coordinate{Col: 2, Row: 7}, } - good, _ := board.CheckAndPlay(move) + good, _ := board.CheckAndPlay(&move) assert.False(t, good) assert.Equal(t, boardBeforeMove, board) @@ -151,7 +151,7 @@ func Test_CheckMove_validPromotion(t *testing.T) { EndSquare: types.Coordinate{Col: 1, Row: 8}, PromotionToPiece: &shortName, } - good, reason := board.CheckAndPlay(move) + good, reason := board.CheckAndPlay(&move) assert.Empty(t, reason) assert.True(t, good) @@ -179,13 +179,13 @@ func Test_CheckMove_HistoryWorks(t *testing.T) { EndSquare: types.Coordinate{Col: 1, Row: 4}, } - good, _ := board.CheckAndPlay(firstMove) + good, _ := board.CheckAndPlay(&firstMove) assert.True(t, good) - good, _ = board.CheckAndPlay(secondMove) + good, _ = board.CheckAndPlay(&secondMove) assert.True(t, good) - good, _ = board.CheckAndPlay(thirdMove) + good, _ = board.CheckAndPlay(&thirdMove) assert.True(t, good) expectedHistory := []types.Move{ @@ -221,13 +221,13 @@ func Test_Promotion_BlackKing(t *testing.T) { board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black} //Make dummy move for white - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 7, Row: 8}, } - good, reason := board.CheckAndPlay(move) + good, reason := board.CheckAndPlay(&move) assert.True(t, good) assert.Empty(t, reason) @@ -246,13 +246,13 @@ func Test_Promotion_BlackKing(t *testing.T) { board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black} //Make dummy move for white - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } - good, reason := board.CheckAndPlay(move) + good, reason := board.CheckAndPlay(&move) assert.True(t, good) assert.Empty(t, reason) @@ -272,25 +272,25 @@ func Test_Promotion_BlackKing(t *testing.T) { board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black} //Make dummy move for white - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) //Move black - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 4, Row: 8}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 4, Row: 8}}) //Make dummy move for white - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 2}, EndSquare: types.Coordinate{Col: 5, Row: 1}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 2}, EndSquare: types.Coordinate{Col: 5, Row: 1}}) //Move black back - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 4, Row: 8}, EndSquare: types.Coordinate{Col: 5, Row: 8}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 4, Row: 8}, EndSquare: types.Coordinate{Col: 5, Row: 8}}) //Make dummy move for white - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } - good, _ := board.CheckAndPlay(move) + good, _ := board.CheckAndPlay(&move) assert.False(t, good) }) @@ -304,13 +304,13 @@ func Test_Promotion_BlackKing(t *testing.T) { board.position[types.Coordinate{Col: 7, Row: 8}] = Bishop{Color: types.Black} //Make dummy move for white - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 7, Row: 8}, } - good, _ := board.CheckAndPlay(move) + good, _ := board.CheckAndPlay(&move) assert.False(t, good) }) @@ -324,13 +324,13 @@ func Test_Promotion_BlackKing(t *testing.T) { board.position[types.Coordinate{Col: 4, Row: 8}] = Bishop{Color: types.Black} //Make dummy move for white - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } - good, _ := board.CheckAndPlay(move) + good, _ := board.CheckAndPlay(&move) assert.False(t, good) }) @@ -344,13 +344,13 @@ func Test_Promotion_BlackKing(t *testing.T) { board.position[types.Coordinate{Col: 3, Row: 8}] = Bishop{Color: types.Black} //Make dummy move for white - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } - good, _ := board.CheckAndPlay(move) + good, _ := board.CheckAndPlay(&move) assert.False(t, good) }) @@ -364,13 +364,88 @@ func Test_Promotion_BlackKing(t *testing.T) { board.position[types.Coordinate{Col: 2, Row: 8}] = Bishop{Color: types.Black} //Make dummy move for white - board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) + board.CheckAndPlay(&types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } - good, _ := board.CheckAndPlay(move) + good, _ := board.CheckAndPlay(&move) assert.False(t, good) }) } + +func Test_Board_HasGameEnded(t *testing.T) { + board := newBoard() + + t.Run("no checkmate yet", func(t *testing.T) { + board.position[types.Coordinate{Col: 1, Row: 1}] = King{Color: types.White} + board.position[types.Coordinate{Col: 7, Row: 6}] = Queen{Color: types.White} + board.position[types.Coordinate{Col: 7, Row: 7}] = Queen{Color: types.White} + + board.position[types.Coordinate{Col: 2, Row: 8}] = King{Color: types.Black} + + whiteMove := types.Move{ + StartSquare: types.Coordinate{Col: 7, Row: 6}, + EndSquare: types.Coordinate{Col: 8, Row: 6}, + ColorMoved: types.White, + } + valid, violation := board.CheckAndPlay(&whiteMove) + assert.True(t, valid) + assert.Empty(t, violation) + + gameEnded, reason := board.HasGameEnded(whiteMove) + + assert.False(t, gameEnded) + assert.Equal(t, NoReason, reason) + }) + + t.Run("checkmate move is made", func(t *testing.T) { + blackMove := types.Move{ + StartSquare: types.Coordinate{Col: 2, Row: 8}, + EndSquare: types.Coordinate{Col: 1, Row: 8}, + ColorMoved: types.Black, + } + valid, violation := board.CheckAndPlay(&blackMove) + assert.True(t, valid) + assert.Empty(t, violation) + + checkmateMove := types.Move{ + StartSquare: types.Coordinate{Col: 8, Row: 6}, + EndSquare: types.Coordinate{Col: 8, Row: 8}, + ColorMoved: types.White, + } + + valid, violation = board.CheckAndPlay(&checkmateMove) + assert.True(t, valid) + assert.Empty(t, violation) + + gameEnded, reason := board.HasGameEnded(checkmateMove) + assert.True(t, gameEnded) + assert.Equal(t, BlackIsCheckmated, reason) + }) +} + +func Test_Board_HasGameEnded_RookBlocks(t *testing.T) { + board := newBoard() + + board.position[types.Coordinate{Col: 1, Row: 1}] = King{Color: types.White} + board.position[types.Coordinate{Col: 8, Row: 7}] = Queen{Color: types.White} + board.position[types.Coordinate{Col: 7, Row: 7}] = Queen{Color: types.White} + + board.position[types.Coordinate{Col: 1, Row: 8}] = King{Color: types.Black} + board.position[types.Coordinate{Col: 2, Row: 2}] = Rook{Color: types.Black} + + whiteMove := types.Move{ + StartSquare: types.Coordinate{Col: 8, Row: 7}, + EndSquare: types.Coordinate{Col: 8, Row: 8}, + ColorMoved: types.White, + } + valid, violation := board.CheckAndPlay(&whiteMove) + assert.True(t, valid) + assert.Empty(t, violation) + + gameEnded, reason := board.HasGameEnded(whiteMove) + assert.False(t, gameEnded) + assert.Equal(t, NoReason, reason) +} diff --git a/chess/game.go b/chess/game.go index ea2c283..09b6a57 100644 --- a/chess/game.go +++ b/chess/game.go @@ -27,6 +27,7 @@ const ( PlayerToMove CheckMove CheckPlayerChange + GameEnded ) func NewGame() *Game { @@ -85,6 +86,7 @@ func (game *Game) Handle() { defer game.killGame() var receivedMove types.Move + var gameEndReason GameEndedReason var err error for { @@ -108,11 +110,9 @@ func (game *Game) Handle() { game.gameState = CheckMove case CheckMove: - valid, ruleViolation := game.board.CheckAndPlay(receivedMove) + valid, ruleViolation := game.board.CheckAndPlay(&receivedMove) - if valid { - game.gameState = CheckPlayerChange - } else { + if !valid { invalidMoveMessage, err := api.GetInvalidMoveMessage(receivedMove, ruleViolation.String()) if err != nil { log.Println("Error marshalling 'colorDetermined' message for player 1", err) @@ -120,7 +120,11 @@ func (game *Game) Handle() { } 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] @@ -134,7 +138,17 @@ func (game *Game) Handle() { 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) } @@ -181,6 +195,20 @@ func (game Game) broadcastMove(move types.Move) error { 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) { log.Println(string(p.color), " disconnected") playerStillInGame := lo.Filter(game.players, func(player *Player, _ int) bool { diff --git a/chess/king.go b/chess/king.go index c7ee6fe..96bd63c 100644 --- a/chess/king.go +++ b/chess/king.go @@ -9,14 +9,14 @@ type King struct { } func (k King) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { - return k.GetAllNonBlockedMoves(board, fromSquare) + return k.GetAllNonBlockedSquares(board, fromSquare) } func (k King) GetColor() types.ChessColor { return k.Color } -func (k King) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { +func (k King) GetAllNonBlockedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { return board.GetNonBlockedKingMoves(fromSquare) } diff --git a/chess/knight.go b/chess/knight.go index 4f31174..9cb801c 100644 --- a/chess/knight.go +++ b/chess/knight.go @@ -9,14 +9,14 @@ type Knight struct { } func (n Knight) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { - return n.GetAllNonBlockedMoves(board, fromSquare) + return n.GetAllNonBlockedSquares(board, fromSquare) } func (n Knight) GetColor() types.ChessColor { return n.Color } -func (n Knight) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { +func (n Knight) GetAllNonBlockedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { return board.GetNonBlockedKnightMoves(fromSquare) } diff --git a/chess/pawn.go b/chess/pawn.go index fa3d250..88e7451 100644 --- a/chess/pawn.go +++ b/chess/pawn.go @@ -12,7 +12,7 @@ type Pawn struct { func (p Pawn) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { attackingMoves := make([]types.Coordinate, 0, 2) - allMoves := p.GetAllNonBlockedMoves(board, fromSquare) + allMoves := p.GetAllNonBlockedSquares(board, fromSquare) for _, move := range allMoves { if move.Col != fromSquare.Col { attackingMoves = append(attackingMoves, move) @@ -21,7 +21,7 @@ func (p Pawn) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) [] return attackingMoves } -func (p Pawn) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { +func (p Pawn) GetAllNonBlockedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { theoreticalSquares := p.getAllMoves(fromSquare) legalSquares := p.filterBlockedSquares(board, fromSquare, theoreticalSquares) diff --git a/chess/piece_interface.go b/chess/piece_interface.go index c690e90..5696317 100644 --- a/chess/piece_interface.go +++ b/chess/piece_interface.go @@ -6,7 +6,7 @@ import ( ) type Piece interface { - GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate + GetAllNonBlockedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate GetColor() types.ChessColor AfterMoveAction(board *Board, fromSquare types.Coordinate) diff --git a/chess/player.go b/chess/player.go index 28d2b57..c9b593e 100644 --- a/chess/player.go +++ b/chess/player.go @@ -107,6 +107,24 @@ func (p *Player) SendMoveAndPosition(move types.Move, boardPosition string) erro return nil } +func (p *Player) SendGameEnded(reason GameEndedReason) error { + reasonToSend := reason.String() + messageToSend, err := json.Marshal(api.WebsocketMessage{ + Type: api.GameEnded, + Reason: &reasonToSend, + }) + if err != nil { + log.Println("Error while marshalling: ", err) + return err + } + err = p.writeMessage(messageToSend) + if err != nil { + log.Println("Error during message writing:", err) + return err + } + return nil +} + func (p *Player) writeMessage(msg []byte) error { return p.Conn.Write(msg) } diff --git a/chess/queen.go b/chess/queen.go index 7dcb556..9f1b0be 100644 --- a/chess/queen.go +++ b/chess/queen.go @@ -9,14 +9,14 @@ type Queen struct { } func (q Queen) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { - return q.GetAllNonBlockedMoves(board, fromSquare) + return q.GetAllNonBlockedSquares(board, fromSquare) } func (q Queen) GetColor() types.ChessColor { return q.Color } -func (q Queen) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { +func (q Queen) GetAllNonBlockedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { squares := board.GetNonBlockedRowAndColumn(fromSquare) squares = append(squares, board.GetNonBlockedDiagonals(fromSquare)...) return squares diff --git a/chess/rook.go b/chess/rook.go index 7ba2361..de572bd 100644 --- a/chess/rook.go +++ b/chess/rook.go @@ -9,14 +9,14 @@ type Rook struct { } func (r Rook) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { - return r.GetAllNonBlockedMoves(board, fromSquare) + return r.GetAllNonBlockedSquares(board, fromSquare) } func (r Rook) GetColor() types.ChessColor { return r.Color } -func (r Rook) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { +func (r Rook) GetAllNonBlockedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { return board.GetNonBlockedRowAndColumn(fromSquare) } diff --git a/chess/rook_test.go b/chess/rook_test.go index 7189a91..66b7598 100644 --- a/chess/rook_test.go +++ b/chess/rook_test.go @@ -14,7 +14,7 @@ func Test_Rook_GetNonBlockedSquares(t *testing.T) { rook := Rook{Color: types.Black} board.position[types.Coordinate{Col: 5, Row: 5}] = rook - squares := rook.GetAllNonBlockedMoves(board, types.Coordinate{Col: 5, Row: 5}) + squares := rook.GetAllNonBlockedSquares(board, types.Coordinate{Col: 5, Row: 5}) assert.Len(t, squares, 14) }) @@ -28,7 +28,7 @@ func Test_Rook_GetNonBlockedSquares(t *testing.T) { board.position[types.Coordinate{Col: 5, Row: 6}] = Pawn{Color: types.White} board.position[types.Coordinate{Col: 6, Row: 5}] = Pawn{Color: types.Black} - squares := rook.GetAllNonBlockedMoves(board, rookCoordinate) + squares := rook.GetAllNonBlockedSquares(board, rookCoordinate) squaresOnLeft := lo.Filter(squares, func(square types.Coordinate, _ int) bool { return square.Row == rookCoordinate.Row && square.Col < rookCoordinate.Col diff --git a/connection/type.go b/connection/type.go index b4c6ebd..4b073ee 100644 --- a/connection/type.go +++ b/connection/type.go @@ -96,7 +96,7 @@ func (conn *Connection) Read() ([]byte, error) { msg, err := conn.buffer.Get() if err != nil { conn.ws = nil - return nil, err // Tell game-handler that connection was lost + return nil, err // TODO: Tell game-handler that connection was lost } return []byte(msg), err diff --git a/types/common.go b/types/common.go index f7c7f2c..a085bd0 100644 --- a/types/common.go +++ b/types/common.go @@ -3,8 +3,9 @@ package types type ChessColor string const ( - White ChessColor = "white" - Black ChessColor = "black" + NoColor ChessColor = "no_color" + White ChessColor = "white" + Black ChessColor = "black" ) func (c ChessColor) Opposite() ChessColor {