github-actions[bot]
deploy prod from 06dbd16a01ddcfe02b2d936c681e3e4eaa9b141f
8e756fd
Raw
History Blame Contribute Delete
3.95 kB
"""Attack generation (ray-cast sliders; tables for leapers)."""
from __future__ import annotations
from . import bitboards as bb
from .types import BISHOP, BLACK, ROOK, WHITE
KNIGHT_DELTAS = (
(-2, -1), (-2, 1), (-1, -2), (-1, 2),
(1, -2), (1, 2), (2, -1), (2, 1),
)
KING_DELTAS = (
(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1),
)
BISHOP_DELTAS = ((-1, -1), (-1, 1), (1, -1), (1, 1))
ROOK_DELTAS = ((-1, 0), (1, 0), (0, -1), (0, 1))
PAWN_ATTACKS: list[list[int]] = [[0] * 64 for _ in range(2)]
KNIGHT_ATTACKS: list[int] = [0] * 64
KING_ATTACKS: list[int] = [0] * 64
def _valid(rank: int, file: int) -> bool:
return 0 <= rank < 8 and 0 <= file < 8
def _slider_attacks(sq: int, occupied: int, deltas) -> int:
result = 0
rank, file = bb.rank_of(sq), bb.file_of(sq)
for dr, df in deltas:
r, f = rank + dr, file + df
while _valid(r, f):
s = bb.square(r, f)
result |= 1 << s
if (occupied >> s) & 1:
break
r += dr
f += df
return result
def _init_tables() -> None:
for sq in range(64):
KNIGHT_ATTACKS[sq] = 0
for dr, df in KNIGHT_DELTAS:
r, f = bb.rank_of(sq) + dr, bb.file_of(sq) + df
if _valid(r, f):
KNIGHT_ATTACKS[sq] |= 1 << bb.square(r, f)
KING_ATTACKS[sq] = 0
for dr, df in KING_DELTAS:
r, f = bb.rank_of(sq) + dr, bb.file_of(sq) + df
if _valid(r, f):
KING_ATTACKS[sq] |= 1 << bb.square(r, f)
if bb.rank_of(sq) < 7:
if bb.file_of(sq) > 0:
PAWN_ATTACKS[WHITE][sq] |= 1 << (sq + 7)
if bb.file_of(sq) < 7:
PAWN_ATTACKS[WHITE][sq] |= 1 << (sq + 9)
if bb.rank_of(sq) > 0:
if bb.file_of(sq) > 0:
PAWN_ATTACKS[BLACK][sq] |= 1 << (sq - 9)
if bb.file_of(sq) < 7:
PAWN_ATTACKS[BLACK][sq] |= 1 << (sq - 7)
_init_tables()
def pawn_attacks(colour: int, sq: int) -> int:
return PAWN_ATTACKS[colour][sq]
def knight_attacks(sq: int) -> int:
return KNIGHT_ATTACKS[sq]
def bishop_attacks(sq: int, occupied: int) -> int:
return _slider_attacks(sq, occupied, BISHOP_DELTAS)
def rook_attacks(sq: int, occupied: int) -> int:
return _slider_attacks(sq, occupied, ROOK_DELTAS)
def queen_attacks(sq: int, occupied: int) -> int:
return bishop_attacks(sq, occupied) | rook_attacks(sq, occupied)
def king_attacks(sq: int) -> int:
return KING_ATTACKS[sq]
def pawn_left_attacks(pawns: int, targets: int, colour: int) -> int:
if colour == WHITE:
return targets & ((pawns << 7) & ~bb.FILE_H)
return targets & ((pawns >> 7) & ~bb.FILE_A)
def pawn_right_attacks(pawns: int, targets: int, colour: int) -> int:
if colour == WHITE:
return targets & ((pawns << 9) & ~bb.FILE_A)
return targets & ((pawns >> 9) & ~bb.FILE_H)
def pawn_attack_span(pawns: int, targets: int, colour: int) -> int:
return pawn_left_attacks(pawns, targets, colour) | pawn_right_attacks(pawns, targets, colour)
def pawn_attack_double(pawns: int, targets: int, colour: int) -> int:
return pawn_left_attacks(pawns, targets, colour) & pawn_right_attacks(pawns, targets, colour)
def pawn_advance(pawns: int, occupied: int, colour: int) -> int:
if colour == WHITE:
return (~occupied) & (pawns << 8) & 0xFFFFFFFFFFFFFF00
return (~occupied) & (pawns >> 8) & 0x00FFFFFFFFFFFFFF
def discovered_attacks(board, sq: int, us: int) -> int:
enemy = board.colours[1 - us]
occupied = board.colours[us] | enemy
r_att = rook_attacks(sq, occupied)
b_att = bishop_attacks(sq, occupied)
rooks = (enemy & board.pieces[ROOK]) & ~r_att
bishops = (enemy & board.pieces[BISHOP]) & ~b_att
return (rooks & rook_attacks(sq, occupied & ~r_att)) | (
bishops & bishop_attacks(sq, occupied & ~b_att)
)