From 25893a51877480554473a79c3e8286ce0b679b44 Mon Sep 17 00:00:00 2001 From: Marco Date: Tue, 16 Apr 2024 18:17:28 +0200 Subject: [PATCH] Prevent castling in case there is no rook on the castling side --- chess/board_castling_test.go | 140 +++++++++++++++++++++++++++++++++++ chess/king.go | 20 ++++- 2 files changed, 156 insertions(+), 4 deletions(-) diff --git a/chess/board_castling_test.go b/chess/board_castling_test.go index efe4ad1..a2ad144 100644 --- a/chess/board_castling_test.go +++ b/chess/board_castling_test.go @@ -7,6 +7,76 @@ import ( "github.com/stretchr/testify/assert" ) +func Test_Board_WhiteCastlesLeft(t *testing.T) { + board := newBoard() + + board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} + board.position[types.Coordinate{Col: 1, Row: 1}] = Rook{Color: types.White} + + castling := types.Move{ + StartSquare: types.Coordinate{Col: 5, Row: 1}, + EndSquare: types.Coordinate{Col: 3, Row: 1}, + ColorMoved: types.White, + } + + valid, _ := board.CheckAndPlay(&castling) + + assert.True(t, valid) +} + +func Test_Board_WhiteCastlesRight(t *testing.T) { + board := newBoard() + + board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} + board.position[types.Coordinate{Col: 8, Row: 1}] = Rook{Color: types.White} + + castling := types.Move{ + StartSquare: types.Coordinate{Col: 5, Row: 1}, + EndSquare: types.Coordinate{Col: 7, Row: 1}, + ColorMoved: types.White, + } + + valid, _ := board.CheckAndPlay(&castling) + + assert.True(t, valid) +} + +func Test_Board_BlackCastlesLeft(t *testing.T) { + board := newBoard() + board.colorToMove = types.Black + + board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} + board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black} + + castling := types.Move{ + StartSquare: types.Coordinate{Col: 5, Row: 8}, + EndSquare: types.Coordinate{Col: 3, Row: 8}, + ColorMoved: types.Black, + } + + valid, _ := board.CheckAndPlay(&castling) + + assert.True(t, valid) +} + +func Test_Board_BlackCastlesRight(t *testing.T) { + board := newBoard() + board.colorToMove = types.Black + + board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} + board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black} + + castling := types.Move{ + StartSquare: types.Coordinate{Col: 5, Row: 8}, + EndSquare: types.Coordinate{Col: 7, Row: 8}, + ColorMoved: types.Black, + } + + valid, _ := board.CheckAndPlay(&castling) + + assert.True(t, valid) +} + func Test_Board_WhiteCastlesLeft_QueenCoversSquareInBetween(t *testing.T) { board := newBoard() @@ -156,3 +226,73 @@ func Test_Board_BlackCastlesLeft_QueenCoversKingsSquare(t *testing.T) { assert.False(t, valid) } + +func Test_Board_WhiteCastlesLeft_NoRook(t *testing.T) { + board := newBoard() + + board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} + + castling := types.Move{ + StartSquare: types.Coordinate{Col: 5, Row: 1}, + EndSquare: types.Coordinate{Col: 3, Row: 1}, + ColorMoved: types.White, + } + + valid, _ := board.CheckAndPlay(&castling) + + assert.False(t, valid) +} + +func Test_Board_WhiteCastlesRight_NoRook(t *testing.T) { + board := newBoard() + + board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} + + castling := types.Move{ + StartSquare: types.Coordinate{Col: 5, Row: 1}, + EndSquare: types.Coordinate{Col: 7, Row: 1}, + ColorMoved: types.White, + } + + valid, _ := board.CheckAndPlay(&castling) + + assert.False(t, valid) +} + +func Test_Board_BlackCastlesLeft_NoRook(t *testing.T) { + board := newBoard() + board.colorToMove = types.Black + + kingSquare := types.Coordinate{Col: 5, Row: 8} + kingDestinationSquare := types.Coordinate{Col: 3, Row: 8} + + board.position[kingSquare] = King{Color: types.Black} + + castling := types.Move{ + StartSquare: kingSquare, + EndSquare: kingDestinationSquare, + ColorMoved: types.Black, + } + + valid, _ := board.CheckAndPlay(&castling) + + assert.False(t, valid) + assert.Equal(t, nil, board.getPieceAt(kingDestinationSquare)) +} + +func Test_Board_BlackCastlesRight_NoRook(t *testing.T) { + board := newBoard() + board.colorToMove = types.Black + + board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} + + castling := types.Move{ + StartSquare: types.Coordinate{Col: 5, Row: 8}, + EndSquare: types.Coordinate{Col: 7, Row: 8}, + ColorMoved: types.Black, + } + + valid, _ := board.CheckAndPlay(&castling) + + assert.False(t, valid) +} diff --git a/chess/king.go b/chess/king.go index 865b67c..b59bc4d 100644 --- a/chess/king.go +++ b/chess/king.go @@ -80,7 +80,10 @@ func (k King) isCastlingAllowed(b *Board, move types.Move) (bool, CastlingDirect !b.isSquareAttacked(destinationSquareForKingRight, types.Black) && !b.isSquareAttacked(*destinationSquareForKingRight.Left(1), types.Black) && !b.state.WhiteHRookMoved { - return true, CastlingRight + _, ok := b.getPieceAt(types.Coordinate{Col: 8, Row: 1}).(Rook) + if ok { + return true, CastlingRight + } } if move.EndSquare == destinationSquareForKingLeft && @@ -90,7 +93,10 @@ func (k King) isCastlingAllowed(b *Board, move types.Move) (bool, CastlingDirect !b.isSquareAttacked(destinationSquareForKingLeft, types.Black) && !b.isSquareAttacked(*destinationSquareForKingLeft.Right(1), types.Black) && !b.state.WhiteARookMoved { - return true, CastlingLeft + _, ok := b.getPieceAt(types.Coordinate{Col: 1, Row: 1}).(Rook) + if ok { + return true, CastlingLeft + } } case types.Black: destinationSquareForKingRight := types.Coordinate{Col: 7, Row: 8} @@ -102,7 +108,10 @@ func (k King) isCastlingAllowed(b *Board, move types.Move) (bool, CastlingDirect !b.isSquareAttacked(destinationSquareForKingRight, types.White) && !b.isSquareAttacked(*destinationSquareForKingRight.Left(1), types.White) && !b.state.BlackHRookMoved { - return true, CastlingRight + _, ok := b.getPieceAt(types.Coordinate{Col: 8, Row: 8}).(Rook) + if ok { + return true, CastlingRight + } } if move.EndSquare == destinationSquareForKingLeft && @@ -112,7 +121,10 @@ func (k King) isCastlingAllowed(b *Board, move types.Move) (bool, CastlingDirect !b.isSquareAttacked(destinationSquareForKingLeft, types.White) && !b.isSquareAttacked(*destinationSquareForKingLeft.Right(1), types.White) && !b.state.BlackARookMoved { - return true, CastlingLeft + _, ok := b.getPieceAt(types.Coordinate{Col: 1, Row: 8}).(Rook) + if ok { + return true, CastlingLeft + } } } return valid, NoDirection