"""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) )