Spaces:
Sleeping
Sleeping
| """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) | |
| ) | |