diff --git a/test/base.py b/test/base.py index 2d33202..02b4d63 100644 --- a/test/base.py +++ b/test/base.py @@ -23,10 +23,10 @@ class ChessLibTestBase(unittest.TestCase): self.board = Board() - def load(self, fen: str): - rc = load_fen(self.board, fen) - self.assertEqual(rc, 0, f"load_fen failed rc={rc} for FEN: {fen}") - return self.board + def load_fen(self, fen, board=None): + if board: + return load_fen(board, fen) + return load_fen(self.board, fen) def gen(self, captures_only: bool = False, cap: int = 256): diff --git a/test/test_fen_loader.py b/test/test_fen_loader.py index 04a2758..37620b0 100644 --- a/test/test_fen_loader.py +++ b/test/test_fen_loader.py @@ -14,12 +14,6 @@ class TestFenLoading(ChessLibTestBase): return sum(1 << (r*8 + f) for f in range(8)) - def load_fen(self, fen, board=None): - if board: - return load_fen(board, fen) - return load_fen(self.board, fen) - - def test_startpos_fields_and_occupancies(self): fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" rc = self.load_fen(fen) diff --git a/test/test_movegen.py b/test/test_movegen.py index 6a71234..36048ca 100644 --- a/test/test_movegen.py +++ b/test/test_movegen.py @@ -1,10 +1,101 @@ import ctypes from test.base import ChessLibTestBase +from test.chess_ffi import gen_white_pawn_quiet_pushes +from test.chess_ffi import gen_white_pawn_push_promotions +from test.chess_ffi import gen_white_pawn_capture_promotions +from test.chess_ffi import gen_white_pawn_captures +from test.chess_ffi import Move +from test.chess_ffi import Board +MAX_MOVES = 256 class TestMoveGeneration(ChessLibTestBase): - @classmethod - def setUpClass(cls): - pass + + + def test_quiet_pawn_pushes_white(self): + cases = [ + ("8/pppppppp/8/8/8/8/PPPPPPPP/8 w - - 0 1", 16, "open ranks"), + ("8/8/8/8/8/8/P7/8 w - - 0 1", 2, "single pawn open"), + ("8/8/8/8/8/p7/P7/8 w - - 0 1", 0, "blocked single enemy"), + ("8/8/8/8/8/n7/P7/8 w - - 0 1", 0, "blocked single friendly"), + # Although legal move, we have a separate function that calculates this move type. + ("8/9P/8/8/8/8/8/8 w - - 0 1", 0, "no promotion push"), + ] + + for fen, expected, msg in cases: + with self.subTest(msg=msg, fen=fen): + cnt = ctypes.c_int(0) + moves = (Move * MAX_MOVES)() + b = Board() + + self.load_fen(fen, board=b) + gen_white_pawn_quiet_pushes(b, moves, ctypes.byref(cnt)) + + self.assertEqual(expected, cnt.value) + + + def test_quiet_pawn_promotions_white(self): + cases = [ + ("8/PPPPPPPP/8/8/8/8/8/8 w - - 0 1", 8, "all open on 7th rank"), + ("8/8/7P/8/8/8/8/8 w - - 0 1", 0, "no promotions"), + ("7n/7P/8/8/8/8/8/8 w - - 0 1", 0, "blocked by enemy"), + ("7N/7P/8/8/8/8/8/8 w - - 0 1", 0, "blocked by friendly"), + ] + + for fen, expected, msg in cases: + with self.subTest(msg=msg, fen=fen): + cnt = ctypes.c_int(0) + moves = (Move * MAX_MOVES)() + b = Board() + + self.load_fen(fen, board=b) + gen_white_pawn_push_promotions(b, moves, ctypes.byref(cnt)) + + self.assertEqual(expected, cnt.value) + + + def test_capture_pawn_promotions_white(self): + cases = [ + ("6n1/7P/8/8/8/8/8/8 w - - 0 1", 1, "one capture"), + ("5n1n/6P1/8/8/8/8/8/8 w - - 0 1", 2, "two captures"), + ("8/7P/8/8/8/8/8/8 w - - 0 1", 0, "no capture"), + ] + + for fen, expected, msg in cases: + with self.subTest(msg=msg, fen=fen): + cnt = ctypes.c_int(0) + moves = (Move * MAX_MOVES)() + b = Board() + + self.load_fen(fen, board=b) + gen_white_pawn_capture_promotions(b, moves, ctypes.byref(cnt)) + + self.assertEqual(expected, cnt.value) + + + def test_capture_pawn_white(self): + cases = [ + # normal diagonal captures + ("8/8/8/3n4/4P3/8/8/8 w - - 0 1", 1, "e4xd5"), + ("8/8/8/3n1n2/4P3/8/8/8 w - - 0 1", 2, "e4xd5 and e4xf5"), + ("8/8/8/1p6/P7/8/8/8 w - - 0 1", 1, "edge file: a4xb5"), + ("8/7P/8/8/8/8/8/8 w - - 0 1", 0, "no capture"), + + # en passant + ("8/8/8/3pP3/8/8/8/8 w - d6 0 1", 1, "EP available: e5xd6 e.p. (black pawn on d5)"), + ("8/8/5n2/3pP3/8/8/8/8 w - d6 0 1", 2, "EP e5xd6 and normal e5xf6"), + ("8/8/8/3p4/8/4P3/8/8 w - d6 0 1", 0, "EP square present but no white pawn can take"), + ] + + for fen, expected, msg in cases: + with self.subTest(msg=msg, fen=fen): + cnt = ctypes.c_int(0) + moves = (Move * MAX_MOVES)() + b = Board() + + self.load_fen(fen, board=b) + gen_white_pawn_captures(b, moves, ctypes.byref(cnt)) + + self.assertEqual(expected, cnt.value) \ No newline at end of file