File size: 3,951 Bytes
a1b0768
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""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)
    )