package chess import ( "mchess_server/types" "testing" "github.com/stretchr/testify/assert" ) func Test_CheckMove_validPawnMove(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 1, Row: 2}] = Pawn{Color: types.White} board.position[types.Coordinate{Col: 2, Row: 4}] = Pawn{Color: types.Black} board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} move := types.Move{ StartSquare: types.Coordinate{Col: 1, Row: 2}, EndSquare: types.Coordinate{Col: 1, Row: 3}, } good, _ := board.CheckAndPlay(&move) assert.True(t, good) //we take the pawn secondMove := types.Move{ StartSquare: types.Coordinate{Col: 2, Row: 4}, EndSquare: types.Coordinate{Col: 1, Row: 3}, } good, _ = board.CheckAndPlay(&secondMove) assert.True(t, good) } func Test_CheckMove_enPassant(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 6, Row: 4}] = Pawn{Color: types.Black} board.position[types.Coordinate{Col: 5, Row: 2}] = Pawn{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 2}, EndSquare: types.Coordinate{Col: 5, Row: 4}, } 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}]) newMove := types.Move{ StartSquare: types.Coordinate{Col: 6, Row: 4}, EndSquare: types.Coordinate{Col: 5, Row: 3}, } good, reason = board.CheckAndPlay(&newMove) assert.True(t, good) assert.Empty(t, reason) // the black pawn is on its correct square assert.Equal(t, Pawn{Color: types.Black}, board.position[types.Coordinate{Col: 5, Row: 3}]) //the white pawn is gone assert.Nil(t, board.position[types.Coordinate{Col: 5, Row: 4}]) } func Test_CheckMove_invalidPawnMoves(t *testing.T) { t.Run("pawn is blocked", func(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 2, Row: 5}] = Pawn{Color: types.White} board.position[types.Coordinate{Col: 1, Row: 5}] = King{Color: types.White} board.position[types.Coordinate{Col: 7, Row: 5}] = Queen{Color: types.Black} board.position[types.Coordinate{Col: 8, Row: 5}] = King{Color: types.Black} board.position[types.Coordinate{Col: 2, Row: 6}] = Pawn{Color: types.Black} move := types.Move{ StartSquare: types.Coordinate{Col: 2, Row: 5}, EndSquare: types.Coordinate{Col: 2, Row: 6}, } legalMove, _ := board.CheckAndPlay(&move) assert.False(t, legalMove) }) t.Run("pawn moves to the side", func(t *testing.T) { var board = newBoard() boardBeforeMove := board board.position[types.Coordinate{Col: 1, Row: 5}] = King{Color: types.White} board.position[types.Coordinate{Col: 8, Row: 5}] = King{Color: types.Black} board.position[types.Coordinate{Col: 2, Row: 5}] = Pawn{Color: types.White} move := types.Move{ StartSquare: types.Coordinate{Col: 2, Row: 5}, EndSquare: types.Coordinate{Col: 3, Row: 5}, } legal, _ := board.CheckAndPlay(&move) assert.False(t, legal) assert.Equal(t, boardBeforeMove, board) move = types.Move{ StartSquare: types.Coordinate{Col: 2, Row: 5}, EndSquare: types.Coordinate{Col: 1, Row: 5}, } legal, _ = board.CheckAndPlay(&move) assert.False(t, legal) assert.Equal(t, boardBeforeMove, board) move = types.Move{ StartSquare: types.Coordinate{Col: 2, Row: 5}, EndSquare: types.Coordinate{Col: 6, Row: 5}, } legal, _ = board.CheckAndPlay(&move) assert.False(t, legal) assert.Equal(t, boardBeforeMove, board) }) t.Run("king of moving color is in check after move", func(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 1, Row: 6}] = King{Color: types.White} board.position[types.Coordinate{Col: 2, Row: 6}] = Pawn{Color: types.White} board.position[types.Coordinate{Col: 7, Row: 6}] = Rook{Color: types.Black} board.position[types.Coordinate{Col: 8, Row: 8}] = King{Color: types.Black} boardBeforeMove := board move := types.Move{ StartSquare: types.Coordinate{Col: 2, Row: 6}, EndSquare: types.Coordinate{Col: 2, Row: 7}, } good, _ := board.CheckAndPlay(&move) assert.False(t, good) assert.Equal(t, boardBeforeMove, board) }) } func Test_CheckMove_validPromotion(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 1, Row: 7}] = Pawn{Color: types.White} board.position[types.Coordinate{Col: 1, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 7, Row: 7}] = King{Color: types.Black} shortName := types.WhiteQueenShortName.String() move := types.Move{ StartSquare: types.Coordinate{Col: 1, Row: 7}, EndSquare: types.Coordinate{Col: 1, Row: 8}, PromotionToPiece: &shortName, } good, reason := board.CheckAndPlay(&move) assert.Empty(t, reason) assert.True(t, good) assert.Equal(t, Queen{Color: types.White}, board.getPieceAt(types.Coordinate{Row: 8, Col: 1})) } func Test_CheckMove_HistoryWorks(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 3, Row: 7}] = Pawn{Color: types.Black} board.position[types.Coordinate{Col: 1, Row: 2}] = Pawn{Color: types.White} board.position[types.Coordinate{Col: 1, Row: 5}] = King{Color: types.White} board.position[types.Coordinate{Col: 8, Row: 5}] = King{Color: types.Black} firstMove := types.Move{ StartSquare: types.Coordinate{Col: 1, Row: 2}, EndSquare: types.Coordinate{Col: 1, Row: 3}, } secondMove := types.Move{ StartSquare: types.Coordinate{Col: 3, Row: 7}, EndSquare: types.Coordinate{Col: 3, Row: 5}, } thirdMove := types.Move{ StartSquare: types.Coordinate{Col: 1, Row: 3}, EndSquare: types.Coordinate{Col: 1, Row: 4}, } good, _ := board.CheckAndPlay(&firstMove) assert.True(t, good) good, _ = board.CheckAndPlay(&secondMove) assert.True(t, good) good, _ = board.CheckAndPlay(&thirdMove) assert.True(t, good) expectedHistory := []types.Move{ { StartSquare: types.Coordinate{Col: 1, Row: 2}, EndSquare: types.Coordinate{Col: 1, Row: 3}, PieceMoved: "P", ColorMoved: "white", }, { StartSquare: types.Coordinate{Col: 3, Row: 7}, EndSquare: types.Coordinate{Col: 3, Row: 5}, PieceMoved: "p", ColorMoved: "black", }, { StartSquare: types.Coordinate{Col: 1, Row: 3}, EndSquare: types.Coordinate{Col: 1, Row: 4}, PieceMoved: "P", ColorMoved: "white", }, } assert.Equal(t, expectedHistory, board.history) } func Test_Promotion_BlackKing(t *testing.T) { t.Run("valid promotion to the right", func(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black} 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}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 7, Row: 8}, } good, reason := board.CheckAndPlay(&move) assert.True(t, good) assert.Empty(t, reason) assert.Equal(t, King{Color: types.Black}, board.position[types.Coordinate{Col: 7, Row: 8}]) assert.Equal(t, Rook{Color: types.Black}, board.position[types.Coordinate{Col: 6, Row: 8}]) assert.Empty(t, board.position[types.Coordinate{Col: 5, Row: 8}]) assert.Empty(t, board.position[types.Coordinate{Col: 8, Row: 8}]) }) t.Run("valid promotion to the left", func(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black} 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}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } good, reason := board.CheckAndPlay(&move) assert.True(t, good) assert.Empty(t, reason) assert.Equal(t, King{Color: types.Black}, board.position[types.Coordinate{Col: 3, Row: 8}]) assert.Equal(t, Rook{Color: types.Black}, board.position[types.Coordinate{Col: 4, Row: 8}]) assert.Empty(t, board.position[types.Coordinate{Col: 1, Row: 8}]) assert.Empty(t, board.position[types.Coordinate{Col: 2, Row: 8}]) assert.Empty(t, board.position[types.Coordinate{Col: 5, Row: 8}]) }) t.Run("king moved already", func(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black} 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}}) //Move black 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}}) //Move black back 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}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } good, _ := board.CheckAndPlay(&move) assert.False(t, good) }) t.Run("promotion right with piece in between", func(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black} board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black} 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}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 7, Row: 8}, } good, _ := board.CheckAndPlay(&move) assert.False(t, good) }) t.Run("promotion left with piece next to king", func(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black} board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black} 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}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } good, _ := board.CheckAndPlay(&move) assert.False(t, good) }) t.Run("promotion left with piece on castling square", func(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black} board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black} 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}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } good, _ := board.CheckAndPlay(&move) assert.False(t, good) }) t.Run("promotion left with piece next to rook", func(t *testing.T) { var board = newBoard() board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White} board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black} board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black} board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black} 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}}) move := types.Move{ StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 3, Row: 8}, } 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) }