2024-09-18 02:33:58 +00:00
|
|
|
package chess_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path"
|
2024-09-26 07:34:23 +00:00
|
|
|
"strings"
|
2024-09-18 02:33:58 +00:00
|
|
|
"testing"
|
|
|
|
|
2024-09-18 13:52:40 +00:00
|
|
|
"github.com/ekzyis/chessbot/chess"
|
2024-09-18 02:33:58 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
// change working directory to the root of the project
|
|
|
|
// so assets/ can be found
|
|
|
|
wd, _ := os.Getwd()
|
|
|
|
os.Chdir(path.Dir(wd))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBoardInitial(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-18 02:33:58 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertPiece(t, b, "a1", chess.Rook, chess.Light)
|
|
|
|
assertPiece(t, b, "b1", chess.Knight, chess.Light)
|
|
|
|
assertPiece(t, b, "c1", chess.Bishop, chess.Light)
|
|
|
|
assertPiece(t, b, "d1", chess.Queen, chess.Light)
|
|
|
|
assertPiece(t, b, "e1", chess.King, chess.Light)
|
|
|
|
assertPiece(t, b, "f1", chess.Bishop, chess.Light)
|
|
|
|
assertPiece(t, b, "g1", chess.Knight, chess.Light)
|
|
|
|
assertPiece(t, b, "h1", chess.Rook, chess.Light)
|
|
|
|
|
|
|
|
assertPiece(t, b, "a2", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "b2", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "c2", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "d2", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "e2", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "f2", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "g2", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "h2", chess.Pawn, chess.Light)
|
|
|
|
|
|
|
|
assertPiece(t, b, "a8", chess.Rook, chess.Dark)
|
|
|
|
assertPiece(t, b, "b8", chess.Knight, chess.Dark)
|
|
|
|
assertPiece(t, b, "c8", chess.Bishop, chess.Dark)
|
|
|
|
assertPiece(t, b, "d8", chess.Queen, chess.Dark)
|
|
|
|
assertPiece(t, b, "e8", chess.King, chess.Dark)
|
|
|
|
assertPiece(t, b, "f8", chess.Bishop, chess.Dark)
|
|
|
|
assertPiece(t, b, "g8", chess.Knight, chess.Dark)
|
|
|
|
assertPiece(t, b, "h8", chess.Rook, chess.Dark)
|
|
|
|
|
|
|
|
assertPiece(t, b, "a7", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "b7", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "c7", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "d7", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "e7", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "f7", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "g7", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "h7", chess.Pawn, chess.Dark)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBoardMovePawn(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-18 02:33:58 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "e4")
|
2024-09-18 02:33:58 +00:00
|
|
|
|
|
|
|
assertNoPiece(t, b, "e2")
|
|
|
|
assertPiece(t, b, "e4", chess.Pawn, chess.Light)
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "e5")
|
2024-09-18 02:33:58 +00:00
|
|
|
|
|
|
|
assertNoPiece(t, b, "e7")
|
|
|
|
assertPiece(t, b, "e5", chess.Pawn, chess.Dark)
|
|
|
|
}
|
|
|
|
|
2024-09-18 03:40:10 +00:00
|
|
|
func TestBoardMovePawnInvalid(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-18 03:40:10 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertMoveError(t, b, "a5", "no pawn found that can move to a5")
|
|
|
|
assertMoveError(t, b, "b5", "no pawn found that can move to b5")
|
|
|
|
assertMoveError(t, b, "c5", "no pawn found that can move to c5")
|
|
|
|
assertMoveError(t, b, "d5", "no pawn found that can move to d5")
|
|
|
|
assertMoveError(t, b, "e5", "no pawn found that can move to e5")
|
|
|
|
assertMoveError(t, b, "f5", "no pawn found that can move to f5")
|
|
|
|
assertMoveError(t, b, "g5", "no pawn found that can move to g5")
|
|
|
|
assertMoveError(t, b, "h5", "no pawn found that can move to h5")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "d4")
|
2024-09-18 03:40:10 +00:00
|
|
|
|
|
|
|
assertMoveError(t, b, "a4", "no pawn found that can move to a4")
|
|
|
|
assertMoveError(t, b, "b4", "no pawn found that can move to b4")
|
|
|
|
assertMoveError(t, b, "c4", "no pawn found that can move to c4")
|
|
|
|
assertMoveError(t, b, "d4", "no pawn found that can move to d4")
|
|
|
|
assertMoveError(t, b, "e4", "no pawn found that can move to e4")
|
|
|
|
assertMoveError(t, b, "f4", "no pawn found that can move to f4")
|
|
|
|
assertMoveError(t, b, "g4", "no pawn found that can move to g4")
|
|
|
|
assertMoveError(t, b, "h4", "no pawn found that can move to h4")
|
|
|
|
}
|
|
|
|
|
2024-09-18 05:59:15 +00:00
|
|
|
func TestBoardMovePawnCapture(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-18 05:59:15 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "e4 d5 exd5")
|
2024-09-26 00:39:53 +00:00
|
|
|
|
|
|
|
assertNoPiece(t, b, "e4")
|
|
|
|
assertPiece(t, b, "d5", chess.Pawn, chess.Light)
|
|
|
|
|
|
|
|
// test ambiguous capture
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "c4 d5 e4 e5 exd5")
|
2024-09-18 05:59:15 +00:00
|
|
|
|
|
|
|
assertNoPiece(t, b, "e4")
|
|
|
|
assertPiece(t, b, "d5", chess.Pawn, chess.Light)
|
2024-09-26 00:39:53 +00:00
|
|
|
assertPiece(t, b, "c4", chess.Pawn, chess.Light)
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "c4 d5 e4 e5 cxd5")
|
2024-09-26 00:39:53 +00:00
|
|
|
|
|
|
|
assertNoPiece(t, b, "c4")
|
|
|
|
assertPiece(t, b, "d5", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "e4", chess.Pawn, chess.Light)
|
2024-09-18 05:59:15 +00:00
|
|
|
}
|
|
|
|
|
2024-09-26 05:32:43 +00:00
|
|
|
func TestBoardPawnPromotion(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
b := chess.NewBoard()
|
|
|
|
b.Parse("a4 e6 a5 e5 a6 e4 axb7 e3 bxa8=Q")
|
|
|
|
|
|
|
|
assertPiece(t, b, "a8", chess.Queen, chess.Light)
|
|
|
|
assertNoPiece(t, b, "b7")
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
b.Parse("a4 e6 a5 e5 a6 e4 axb7 e3 bxa8=R")
|
|
|
|
|
|
|
|
assertPiece(t, b, "a8", chess.Rook, chess.Light)
|
|
|
|
assertNoPiece(t, b, "b7")
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
b.Parse("a4 e6 a5 e5 a6 e4 axb7 e3 bxa8=B")
|
|
|
|
|
|
|
|
assertPiece(t, b, "a8", chess.Bishop, chess.Light)
|
|
|
|
assertNoPiece(t, b, "b7")
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
b.Parse("a4 e6 a5 e5 a6 e4 axb7 e3 bxa8=N")
|
|
|
|
|
|
|
|
assertPiece(t, b, "a8", chess.Knight, chess.Light)
|
|
|
|
assertNoPiece(t, b, "b7")
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
b.Parse("a4 e6 a5 e5 a6 e4 axb7 e3")
|
|
|
|
|
|
|
|
assertMoveError(t, b, "bxa8=K", "invalid promotion: K")
|
|
|
|
}
|
|
|
|
|
2024-09-18 04:18:07 +00:00
|
|
|
func TestBoardMoveKnight(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-18 04:18:07 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Nf3")
|
2024-09-18 04:18:07 +00:00
|
|
|
assertPiece(t, b, "f3", chess.Knight, chess.Light)
|
|
|
|
assertNoPiece(t, b, "g1")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Nh6")
|
2024-09-18 04:23:51 +00:00
|
|
|
assertPiece(t, b, "h6", chess.Knight, chess.Dark)
|
2024-09-18 04:18:07 +00:00
|
|
|
assertNoPiece(t, b, "g8")
|
2024-09-25 22:38:23 +00:00
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Nc3")
|
2024-09-25 22:38:23 +00:00
|
|
|
assertPiece(t, b, "c3", chess.Knight, chess.Light)
|
|
|
|
assertNoPiece(t, b, "b1")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Na6")
|
2024-09-25 22:38:23 +00:00
|
|
|
assertPiece(t, b, "a6", chess.Knight, chess.Dark)
|
|
|
|
assertNoPiece(t, b, "b8")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Nh4")
|
2024-09-25 22:38:23 +00:00
|
|
|
assertPiece(t, b, "h4", chess.Knight, chess.Light)
|
|
|
|
assertNoPiece(t, b, "f3")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Nf5")
|
2024-09-25 22:38:23 +00:00
|
|
|
assertPiece(t, b, "f5", chess.Knight, chess.Dark)
|
|
|
|
assertNoPiece(t, b, "h6")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Na4")
|
2024-09-25 22:38:23 +00:00
|
|
|
assertPiece(t, b, "a4", chess.Knight, chess.Light)
|
|
|
|
assertNoPiece(t, b, "c3")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Nc5")
|
2024-09-25 22:38:23 +00:00
|
|
|
assertPiece(t, b, "c5", chess.Knight, chess.Dark)
|
|
|
|
assertNoPiece(t, b, "a6")
|
2024-09-18 04:18:07 +00:00
|
|
|
}
|
|
|
|
|
2024-09-25 22:13:21 +00:00
|
|
|
func TestBoardMoveKnightInvalid(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-25 22:13:21 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
// out of reach
|
|
|
|
assertMoveError(t, b, "Ng3", "no knight found that can move to g3")
|
|
|
|
assertMoveError(t, b, "Nb3", "no knight found that can move to b3")
|
|
|
|
|
|
|
|
// blocked by own piece
|
2024-09-25 22:38:23 +00:00
|
|
|
assertMoveError(t, b, "Ng1", "g1 blocked by white knight")
|
2024-09-25 22:13:21 +00:00
|
|
|
assertMoveError(t, b, "Nd2", "d2 blocked by white pawn")
|
|
|
|
assertMoveError(t, b, "Ne2", "e2 blocked by white pawn")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Nf3")
|
2024-09-25 22:13:21 +00:00
|
|
|
|
|
|
|
assertMoveError(t, b, "Ng6", "no knight found that can move to g6")
|
|
|
|
assertMoveError(t, b, "Nb6", "no knight found that can move to b6")
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Ne7", "e7 blocked by black pawn")
|
|
|
|
assertMoveError(t, b, "Nd7", "d7 blocked by black pawn")
|
2024-09-26 04:59:24 +00:00
|
|
|
|
|
|
|
// ambiguous moves
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
assertParse(t, b, "e4 e5 Nf3 d6 Nc3 d5 Nb5 d4")
|
|
|
|
assertMoveError(t, b, "Nxd4", "move ambiguous: 2 knights can move to d4")
|
|
|
|
assertMoveError(t, b, "N4xd4", "no knight found that can move to d4")
|
|
|
|
// disambiguate via file
|
|
|
|
assertParse(t, b, "Nfxd4")
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
assertParse(t, b, "e4 e5 Nf3 d6 Nc3 d5 Nb5 d4")
|
|
|
|
// disambiguate via rank
|
|
|
|
assertParse(t, b, "N3xd4")
|
2024-09-25 22:13:21 +00:00
|
|
|
}
|
|
|
|
|
2024-09-25 22:38:23 +00:00
|
|
|
func TestBoardMoveKnightCapture(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-25 22:38:23 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "e4 Nf6 d4 Nxe4")
|
2024-09-25 22:38:23 +00:00
|
|
|
assertPiece(t, b, "e4", chess.Knight, chess.Dark)
|
|
|
|
assertPiece(t, b, "d4", chess.Pawn, chess.Light)
|
|
|
|
assertNoPiece(t, b, "g8")
|
|
|
|
assertNoPiece(t, b, "e2")
|
|
|
|
assertNoPiece(t, b, "d2")
|
2024-09-26 00:27:51 +00:00
|
|
|
|
|
|
|
// test ambiguous capture
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "e4 e5 Nf3 d6 Nc3 d5 Nb5 d4 Nbxd4")
|
2024-09-26 00:27:51 +00:00
|
|
|
|
|
|
|
assertPiece(t, b, "e4", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "e5", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "d4", chess.Knight, chess.Light)
|
|
|
|
assertPiece(t, b, "f3", chess.Knight, chess.Light)
|
|
|
|
assertNoPiece(t, b, "g1")
|
|
|
|
assertNoPiece(t, b, "b1")
|
|
|
|
assertNoPiece(t, b, "c3")
|
|
|
|
assertNoPiece(t, b, "d6")
|
|
|
|
assertNoPiece(t, b, "d5")
|
|
|
|
assertNoPiece(t, b, "b5")
|
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "e4 e5 Nf3 d6 Nc3 d5 Nb5 d4 Nfxd4")
|
2024-09-26 00:27:51 +00:00
|
|
|
|
|
|
|
assertPiece(t, b, "e4", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "e5", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "d4", chess.Knight, chess.Light)
|
|
|
|
assertPiece(t, b, "b5", chess.Knight, chess.Light)
|
|
|
|
assertNoPiece(t, b, "g1")
|
|
|
|
assertNoPiece(t, b, "b1")
|
|
|
|
assertNoPiece(t, b, "c3")
|
|
|
|
assertNoPiece(t, b, "d6")
|
|
|
|
assertNoPiece(t, b, "d5")
|
|
|
|
assertNoPiece(t, b, "f3")
|
2024-09-25 22:38:23 +00:00
|
|
|
}
|
|
|
|
|
2024-09-18 04:44:59 +00:00
|
|
|
func TestBoardMoveBishop(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-18 04:44:59 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Bc4", "no bishop found that can move to c4")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "e3")
|
|
|
|
assertParse(t, b, "e6")
|
2024-09-18 04:44:59 +00:00
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Bc4")
|
2024-09-18 04:44:59 +00:00
|
|
|
|
|
|
|
assertPiece(t, b, "c4", chess.Bishop, chess.Light)
|
|
|
|
assertNoPiece(t, b, "f1")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Bc5")
|
2024-09-18 04:44:59 +00:00
|
|
|
|
|
|
|
assertPiece(t, b, "c5", chess.Bishop, chess.Dark)
|
|
|
|
assertNoPiece(t, b, "f8")
|
|
|
|
}
|
|
|
|
|
2024-09-25 22:38:23 +00:00
|
|
|
func TestBoardMoveBishopInvalid(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-25 22:38:23 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Bc3", "no bishop found that can move to c3")
|
|
|
|
assertMoveError(t, b, "Bc2", "c2 blocked by white pawn")
|
|
|
|
assertMoveError(t, b, "Bb2", "b2 blocked by white pawn")
|
2024-09-26 01:50:36 +00:00
|
|
|
|
|
|
|
// path blocked by white pawn at e2
|
|
|
|
assertMoveError(t, b, "Bd3", "no bishop found that can move to d3")
|
2024-09-25 22:38:23 +00:00
|
|
|
}
|
|
|
|
|
2024-09-26 02:02:28 +00:00
|
|
|
func TestBoardMoveBishopCapture(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertParse(t, b, "e4 e5 Bc4 d5 Bxd5")
|
|
|
|
|
|
|
|
assertPiece(t, b, "e4", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "e5", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "d5", chess.Bishop, chess.Light)
|
|
|
|
assertNoPiece(t, b, "f1")
|
|
|
|
assertNoPiece(t, b, "e2")
|
|
|
|
assertNoPiece(t, b, "e7")
|
|
|
|
assertNoPiece(t, b, "d7")
|
|
|
|
|
|
|
|
// bishop captures are never ambiguous because they can only move on same color
|
|
|
|
}
|
|
|
|
|
2024-09-18 04:49:58 +00:00
|
|
|
func TestBoardMoveRook(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-18 04:49:58 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Ra3", "no rook found that can move to a3")
|
|
|
|
|
2024-09-26 04:25:40 +00:00
|
|
|
assertParse(t, b, "a4 a5 Ra3")
|
2024-09-18 04:49:58 +00:00
|
|
|
|
|
|
|
assertPiece(t, b, "a3", chess.Rook, chess.Light)
|
|
|
|
assertNoPiece(t, b, "a1")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Ra6")
|
2024-09-18 04:49:58 +00:00
|
|
|
|
|
|
|
assertPiece(t, b, "a6", chess.Rook, chess.Dark)
|
|
|
|
assertNoPiece(t, b, "a8")
|
|
|
|
}
|
|
|
|
|
2024-09-26 04:25:40 +00:00
|
|
|
func TestBoardMoveRookCapture(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertParse(t, b, "a4 e6 h4 e5 Ra3 e4 Rhh3 e3 Raxe3")
|
|
|
|
|
|
|
|
assertPiece(t, b, "e3", chess.Rook, chess.Light)
|
|
|
|
assertPiece(t, b, "h3", chess.Rook, chess.Light)
|
|
|
|
assertNoPiece(t, b, "a3")
|
|
|
|
}
|
|
|
|
|
2024-09-25 22:38:23 +00:00
|
|
|
func TestBoardMoveRookInvalid(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-25 22:38:23 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Rb2", "b2 blocked by white pawn")
|
|
|
|
assertMoveError(t, b, "Rb1", "b1 blocked by white knight")
|
|
|
|
assertMoveError(t, b, "Ra2", "a2 blocked by white pawn")
|
|
|
|
assertMoveError(t, b, "Ra3", "no rook found that can move to a3")
|
2024-09-26 01:50:36 +00:00
|
|
|
|
|
|
|
assertParse(t, b, "e3 e6 a4 d6 Ra3")
|
|
|
|
|
|
|
|
// path blocked by pawn at d3
|
|
|
|
assertMoveError(t, b, "Rh3", "no rook found that can move to h3")
|
2024-09-26 04:48:07 +00:00
|
|
|
|
|
|
|
// ambiguous moves
|
|
|
|
b = chess.NewBoard()
|
|
|
|
|
|
|
|
assertParse(t, b, "a4 e6 h4 e5 Ra3 e4")
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Rh3", "move ambiguous: 2 rooks can move to h3")
|
|
|
|
assertParse(t, b, "Rhh3")
|
2024-09-25 22:38:23 +00:00
|
|
|
}
|
|
|
|
|
2024-09-18 05:00:09 +00:00
|
|
|
func TestBoardMoveQueen(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-18 05:00:09 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Qd3", "no queen found that can move to d3")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "d4")
|
|
|
|
assertParse(t, b, "d5")
|
2024-09-18 05:00:09 +00:00
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Qd3")
|
2024-09-18 05:00:09 +00:00
|
|
|
|
|
|
|
assertPiece(t, b, "d3", chess.Queen, chess.Light)
|
|
|
|
assertNoPiece(t, b, "d1")
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "Qd6")
|
2024-09-18 05:00:09 +00:00
|
|
|
|
|
|
|
assertPiece(t, b, "d6", chess.Queen, chess.Dark)
|
|
|
|
assertNoPiece(t, b, "d8")
|
|
|
|
}
|
|
|
|
|
2024-09-25 22:38:23 +00:00
|
|
|
func TestBoardMoveQueenInvalid(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-25 22:38:23 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Qd2", "d2 blocked by white pawn")
|
|
|
|
assertMoveError(t, b, "Qd1", "d1 blocked by white queen")
|
|
|
|
assertMoveError(t, b, "Qe1", "e1 blocked by white king")
|
|
|
|
assertMoveError(t, b, "Qc1", "c1 blocked by white bishop")
|
2024-09-26 01:50:36 +00:00
|
|
|
|
|
|
|
// path blocked by white pawn at d2
|
2024-09-25 22:38:23 +00:00
|
|
|
assertMoveError(t, b, "Qd3", "no queen found that can move to d3")
|
2024-09-26 05:10:58 +00:00
|
|
|
|
|
|
|
// TODO: ambiguous queen moves require pawn promotion
|
2024-09-25 22:38:23 +00:00
|
|
|
}
|
|
|
|
|
2024-09-25 22:49:24 +00:00
|
|
|
func TestBoardMoveKing(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-25 22:49:24 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
assertParse(t, b, "e4 e5 Ke2 Ke7 Kf3 Kd6 Kg3 Kc6")
|
2024-09-25 22:49:24 +00:00
|
|
|
assertNoPiece(t, b, "e1")
|
|
|
|
assertNoPiece(t, b, "e8")
|
|
|
|
assertNoPiece(t, b, "e2")
|
|
|
|
assertNoPiece(t, b, "e7")
|
|
|
|
assertNoPiece(t, b, "f3")
|
|
|
|
assertNoPiece(t, b, "d6")
|
|
|
|
|
|
|
|
assertPiece(t, b, "g3", chess.King, chess.Light)
|
|
|
|
assertPiece(t, b, "c6", chess.King, chess.Dark)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBoardMoveKingInvalid(t *testing.T) {
|
2024-09-26 00:51:35 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2024-09-25 22:49:24 +00:00
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Ke1", "e1 blocked by white king")
|
|
|
|
assertMoveError(t, b, "Ke2", "e2 blocked by white pawn")
|
|
|
|
assertMoveError(t, b, "Ke3", "no king found that can move to e3")
|
|
|
|
}
|
|
|
|
|
2024-09-26 07:34:23 +00:00
|
|
|
func TestBoardCheck(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assert.False(t, b.InCheck())
|
|
|
|
|
|
|
|
assertParse(t, b, "e4 e5 Qh5 Nc6 Qxf7")
|
|
|
|
|
|
|
|
assert.True(t, b.InCheck())
|
|
|
|
assert.True(t, strings.HasSuffix(b.Moves[len(b.Moves)-1], "+"), "check move should end with +")
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Nf6", "invalid move Nf6: king is in check")
|
|
|
|
assertMoveError(t, b, "Ke7", "invalid move Ke7: king is in check")
|
|
|
|
|
|
|
|
assertParse(t, b, "Kxf7")
|
|
|
|
}
|
|
|
|
|
2024-10-16 18:18:42 +00:00
|
|
|
func TestBoardCheckmate(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assert.False(t, b.InCheck())
|
|
|
|
|
|
|
|
// fool's mate
|
|
|
|
assertParse(t, b, "f3 e6 g4 Qh4#")
|
|
|
|
|
|
|
|
assert.True(t, b.InCheck())
|
|
|
|
assert.True(t, strings.HasSuffix(b.Moves[len(b.Moves)-1], "#"), "checkmate move should end with #")
|
|
|
|
|
|
|
|
assertMoveError(t, b, "a3", "invalid move a3: king is in check")
|
|
|
|
}
|
|
|
|
|
2024-09-26 07:48:12 +00:00
|
|
|
func TestBoardPin(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertParse(t, b, "d4 e5 Nc3 Bb4")
|
|
|
|
|
|
|
|
assertMoveError(t, b, "Ne4", "invalid move Ne4: king is in check")
|
|
|
|
}
|
|
|
|
|
2024-09-26 08:21:09 +00:00
|
|
|
func TestBoardCastle(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertParse(t, b, "e4 e5 Nf3 Nf6 Be2 Be7 O-O")
|
|
|
|
|
|
|
|
assertPiece(t, b, "f1", chess.Rook, chess.Light)
|
|
|
|
assertPiece(t, b, "g1", chess.King, chess.Light)
|
|
|
|
assertNoPiece(t, b, "h1")
|
|
|
|
assertNoPiece(t, b, "e1")
|
|
|
|
|
|
|
|
assertParse(t, b, "O-O")
|
|
|
|
|
|
|
|
assertPiece(t, b, "f8", chess.Rook, chess.Dark)
|
|
|
|
assertPiece(t, b, "g8", chess.King, chess.Dark)
|
|
|
|
assertNoPiece(t, b, "h8")
|
|
|
|
assertNoPiece(t, b, "e8")
|
2024-09-26 08:38:02 +00:00
|
|
|
|
|
|
|
b = chess.NewBoard()
|
|
|
|
|
|
|
|
assertParse(t, b, "e4 e5 Qg4 d6 d3 d5 Be3 d4 Nc3 dxe3 O-O-O")
|
|
|
|
|
|
|
|
assertPiece(t, b, "d1", chess.Rook, chess.Light)
|
|
|
|
assertPiece(t, b, "c1", chess.King, chess.Light)
|
|
|
|
assertNoPiece(t, b, "a1")
|
|
|
|
assertNoPiece(t, b, "e1")
|
2024-09-26 08:21:09 +00:00
|
|
|
}
|
|
|
|
|
2024-09-26 16:36:03 +00:00
|
|
|
func TestBoardParseAlgebraicNotation(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
b := chess.NewBoard()
|
|
|
|
|
|
|
|
assertParse(t, b, "1.d4 d5 2.c4 e6")
|
|
|
|
|
|
|
|
assertPiece(t, b, "d4", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "d5", chess.Pawn, chess.Dark)
|
|
|
|
assertPiece(t, b, "c4", chess.Pawn, chess.Light)
|
|
|
|
assertPiece(t, b, "e6", chess.Pawn, chess.Dark)
|
|
|
|
|
|
|
|
assertNoPiece(t, b, "d2")
|
|
|
|
assertNoPiece(t, b, "d7")
|
|
|
|
assertNoPiece(t, b, "c2")
|
|
|
|
assertNoPiece(t, b, "e7")
|
|
|
|
}
|
|
|
|
|
2024-09-26 00:49:20 +00:00
|
|
|
func assertParse(t *testing.T, b *chess.Board, moves string) {
|
|
|
|
assert.NoError(t, b.Parse(moves))
|
|
|
|
}
|
|
|
|
|
2024-09-18 02:33:58 +00:00
|
|
|
func assertPiece(t *testing.T, b *chess.Board, position string, name chess.PieceName, color chess.Color) {
|
|
|
|
p := b.At(position)
|
|
|
|
|
|
|
|
c := "white"
|
|
|
|
if color == chess.Dark {
|
|
|
|
c = "black"
|
|
|
|
}
|
|
|
|
|
|
|
|
msg := fmt.Sprintf("expected %s %s at %s", c, name, position)
|
|
|
|
|
2024-09-18 03:39:57 +00:00
|
|
|
if assert.NotNil(t, p, msg) {
|
|
|
|
assert.Equal(t, name, p.Name, msg)
|
|
|
|
assert.Equal(t, color, p.Color, msg)
|
|
|
|
}
|
2024-09-18 02:33:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func assertNoPiece(t *testing.T, b *chess.Board, position string) {
|
|
|
|
p := b.At(position)
|
|
|
|
|
|
|
|
assert.Nil(t, p, "expected no piece at %s", position)
|
|
|
|
}
|
2024-09-18 03:40:10 +00:00
|
|
|
|
|
|
|
func assertMoveError(t *testing.T, b *chess.Board, position string, message string) {
|
|
|
|
assert.ErrorContains(t, b.Move(position), message)
|
|
|
|
}
|