Add attacker and check logic with tests
All checks were successful
Python tests (make) / test (push) Successful in 11s
All checks were successful
Python tests (make) / test (push) Successful in 11s
This commit is contained in:
100
test/test_attack_to.py
Normal file
100
test/test_attack_to.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import ctypes
|
||||
from test.base import ChessLibTestBase
|
||||
from test.chess_ffi import get_attackers_to
|
||||
from test.chess_ffi import is_square_attacked
|
||||
from test.chess_ffi import is_in_check
|
||||
from test.chess_ffi import sq
|
||||
from test.chess_ffi import Board
|
||||
from test.chess_ffi import BLACK
|
||||
from test.chess_ffi import WHITE
|
||||
from test.chess_ffi import draw_bb
|
||||
|
||||
|
||||
class TestAttackers(ChessLibTestBase):
|
||||
def test_square_attacked(self):
|
||||
cases = [
|
||||
# Pawns: bp d5 attacks c4/e4; not d4
|
||||
("8/8/8/3p4/8/8/8/8 w - - 0 1", "c4", BLACK, True, "bp d5 -> c4"),
|
||||
("8/8/8/3p4/8/8/8/8 w - - 0 1", "e4", BLACK, True, "bp d5 -> e4"),
|
||||
("8/8/8/3p4/8/8/8/8 w - - 0 1", "d4", BLACK, False, "bp d5 not d4"),
|
||||
|
||||
# Knights
|
||||
("8/8/8/3n4/8/8/8/8 w - - 0 1", "f4", BLACK, True, "bn d5 -> f4"),
|
||||
("8/8/8/3n4/8/8/8/8 w - - 0 1", "e7", BLACK, True, "bn d5 -> e7"),
|
||||
("8/8/8/3n4/8/8/8/8 w - - 0 1", "e5", BLACK, False, "bn d5 not e5"),
|
||||
|
||||
# Bishops (wB d4)
|
||||
("8/8/8/8/3B4/8/8/8 w - - 0 1", "c5", WHITE, True, "wB d4 -> c5"),
|
||||
("8/8/8/8/3B4/8/8/8 w - - 0 1", "e5", WHITE, True, "wB d4 -> e5"),
|
||||
|
||||
# Rooks (wR e4)
|
||||
("8/8/8/8/4R3/8/8/8 w - - 0 1", "e8", WHITE, True, "wR e4 -> e8 clear"),
|
||||
("4k3/4p3/8/8/4R3/8/8/8 w - - 0 1", "e8", WHITE, False, "wR e4 blocked by pe7"),
|
||||
("4k3/4p3/8/8/4R3/8/8/8 w - - 0 1", "e7", WHITE, True, "wR e4 blocked can attack pawn on e7"),
|
||||
("8/8/8/8/4R3/8/8/8 w - - 0 1", "a4", WHITE, True, "wR e4 -> a4"),
|
||||
|
||||
# Queens (wQ d4)
|
||||
("8/8/8/8/3Q4/8/8/8 w - - 0 1", "h8", WHITE, True, "wQ d4 -> h8"),
|
||||
("8/8/8/8/3Q4/8/8/8 w - - 0 1", "d8", WHITE, True, "wQ d4 -> d8"),
|
||||
|
||||
# Kings (bk e4)
|
||||
("8/8/8/8/4k3/8/8/4K3 w - - 0 1", "e3", BLACK, True, "bk e4 -> e3"),
|
||||
("8/8/8/8/4k3/8/8/4K3 w - - 0 1", "g4", BLACK, False, "bk e4 not g4"),
|
||||
|
||||
# Edge knights (bn a1)
|
||||
("8/8/8/8/8/8/8/n7 w - - 0 1", "b3", BLACK, True, "bn a1 -> b3"),
|
||||
("8/8/8/8/8/8/8/n7 w - - 0 1", "c2", BLACK, True, "bn a1 -> c2"),
|
||||
("8/8/8/8/8/8/8/n7 w - - 0 1", "a2", BLACK, False, "bn a1 not a2"),
|
||||
]
|
||||
|
||||
for fen, sq_str, by, expected, msg in cases:
|
||||
with self.subTest(msg=msg, fen=fen, sq=sq_str, by=by):
|
||||
b = Board()
|
||||
self.load_fen(fen, board=b)
|
||||
got = bool(is_square_attacked(b, sq(sq_str), by))
|
||||
self.assertEqual(expected, got, msg)
|
||||
|
||||
|
||||
def test_in_check(self):
|
||||
cases = [
|
||||
# Unblocked slider (Qe2 checks e8)
|
||||
("4k3/8/8/8/8/8/4Q3/4K3 b - - 0 1", BLACK, True, "Qe2 -> e8"),
|
||||
# Blocked by pe7
|
||||
("4k3/4p3/8/8/8/8/4Q3/4K3 b - - 0 1", BLACK, False, "Qe2 blocked by pe7"),
|
||||
|
||||
# Knight check (Nc7 checks e8)
|
||||
("4k3/2N5/8/8/8/8/8/4K3 b - - 0 1", BLACK, True, "Nc7 -> e8"),
|
||||
|
||||
# Pawn check: bp e5 checks f4 (WK on f4)
|
||||
("4k3/8/8/4p3/5K2/8/8/8 b - - 0 1", WHITE, True, "bp e5 -> f4"),
|
||||
# Not checking WK e1
|
||||
("4k3/8/8/4p3/8/8/8/4K3 b - - 0 1", WHITE, False, "bp e5 not e1"),
|
||||
|
||||
# King adjacency (illegal but detected)
|
||||
# Ensure that this case does not make it through a legality filter.
|
||||
("8/8/8/8/8/8/4k3/4K3 w - - 0 1", WHITE, True, "BK e2 checks WK e1"),
|
||||
|
||||
# Double check (Qe2 + Nc7 vs BK e8)
|
||||
("4k3/2N5/8/8/8/8/4Q3/4K3 b - - 0 1", BLACK, True, "double check still true"),
|
||||
]
|
||||
|
||||
for fen, side, expected, msg in cases:
|
||||
with self.subTest(msg=msg, fen=fen, side=side):
|
||||
b = Board()
|
||||
self.load_fen(fen, board=b)
|
||||
actual = bool(is_in_check(b, side))
|
||||
self.assertEqual(expected, actual, msg)
|
||||
|
||||
def test_attackers_to_popcount(self):
|
||||
cases = [
|
||||
# Bishop on d4, adjacent enemies on each ray → 4 attackers
|
||||
("8/8/8/2p1p3/3B4/2p1p3/8/8 w - - 0 1", "d4", BLACK, 2, "adjacent enemies on c5,e5,c3,e3"),
|
||||
]
|
||||
|
||||
for fen, sq_str, by, expected_cnt, msg in cases:
|
||||
with self.subTest(msg=msg, fen=fen, sq=sq_str, by=by):
|
||||
b = Board()
|
||||
self.load_fen(fen, board=b)
|
||||
|
||||
mask = get_attackers_to(b, sq(sq_str), by)
|
||||
self.assertEqual(expected_cnt, int(mask).bit_count())
|
||||
Reference in New Issue
Block a user