452 lines
16 KiB
Go
452 lines
16 KiB
Go
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)
|
|
}
|