611 lines
16 KiB
C
611 lines
16 KiB
C
#pragma once
|
|
|
|
#include "engine-types.h"
|
|
#include "engine-board.h"
|
|
|
|
/* `struct magic` is used by magic bitboard lookups, see
|
|
* rook_attacks_from_index and bishop_attacks_from_index */
|
|
struct magic {
|
|
Bb64 mask;
|
|
Bb64 magic;
|
|
};
|
|
|
|
static Bb64 cardinals_from_index(Sq8 p)
|
|
{
|
|
return (FILE_MASK(sq_to_file(p)) | RANK_MASK(sq_to_rank(p)));
|
|
}
|
|
|
|
static Bb64 cardinals(Bb64 p)
|
|
{
|
|
Bb64 b = 0ULL;
|
|
while (p) {
|
|
Sq8 const lsb = bitboard_pop_lsb(&p);
|
|
b |= cardinals_from_index(lsb);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
static Bb64 diagonals_from_index(Sq8 sq)
|
|
{
|
|
#ifdef CODEGEN
|
|
enum rank_index const rank = sq_to_rank(sq);
|
|
enum file_index const file = sq_to_file(sq);
|
|
|
|
Bb64 mask = 0ULL;
|
|
|
|
_Static_assert(((enum rank_index)-1) > 0, "rank_index must be unsigned");
|
|
_Static_assert(((enum file_index)-1) > 0, "file_index must be unsigned");
|
|
|
|
enum rank_index r;
|
|
enum file_index f;
|
|
for (r = rank+1, f = file+1;
|
|
r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
|
|
++r, ++f)
|
|
{
|
|
mask |= MASK_FROM_RF(r, f);
|
|
}
|
|
|
|
for (r = rank+1, f = file-1;
|
|
r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
|
|
++r, --f)
|
|
{
|
|
mask |= MASK_FROM_RF(r, f);
|
|
}
|
|
|
|
for (r = rank-1, f = file+1;
|
|
r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
|
|
--r, ++f)
|
|
{
|
|
mask |= MASK_FROM_RF(r, f);
|
|
}
|
|
|
|
for (r = rank-1, f = file-1;
|
|
r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
|
|
--r, --f)
|
|
{
|
|
mask |= MASK_FROM_RF(r, f);
|
|
}
|
|
return mask;
|
|
|
|
#else
|
|
#if ! __has_include("diagonals.h")
|
|
#error "compile with -DCODEGEN and run once to generate required header files"
|
|
#endif
|
|
#include "diagonals.h" /* defines static Bb64 diagonals[SQ_COUNT]; */
|
|
return diagonals[sq];
|
|
#endif
|
|
}
|
|
|
|
static Bb64 diagonals(Bb64 p)
|
|
{
|
|
Bb64 b = 0ULL;
|
|
while (p) {
|
|
Sq8 const lsb = bitboard_pop_lsb(&p);
|
|
b |= diagonals_from_index(lsb);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
/* PIECE ATTACKS
|
|
* ==================
|
|
*
|
|
* All piece attack functions rely on the caller masking their own pieces.
|
|
* e.g. `knight_attacks(knights) & ~our_occupied`
|
|
* */
|
|
|
|
static Bb64 knight_attacks(Bb64 p)
|
|
{
|
|
Bb64 const l1 = MASK_SHIFT_EAST_1(p /*& ~FILE_MASK_H*/);
|
|
Bb64 const l2 = MASK_SHIFT_EAST_2(p /*& ~(FILE_MASK_G | FILE_MASK_H)*/);
|
|
Bb64 const r1 = MASK_SHIFT_WEST_1 (p /*& ~FILE_MASK_A*/);
|
|
Bb64 const r2 = MASK_SHIFT_WEST_2 (p /*& ~(FILE_MASK_A | FILE_MASK_B)*/);
|
|
Bb64 const h1 = l1 | r1;
|
|
Bb64 const h2 = l2 | r2;
|
|
return MASK_SHIFT_NORTH_2(h1)
|
|
| MASK_SHIFT_SOUTH_2(h1)
|
|
| MASK_SHIFT_NORTH_1(h2)
|
|
| MASK_SHIFT_SOUTH_1(h2);
|
|
}
|
|
|
|
static Bb64 knight_attacks_from_index(Sq8 sq)
|
|
{
|
|
return knight_attacks(MASK_FROM_SQ(sq));
|
|
}
|
|
|
|
static Bb64 rook_attacks_from_index(Sq8 sq, Bb64 occ)
|
|
{
|
|
#ifdef CODEGEN
|
|
enum rank_index const rank = sq_to_rank(sq);
|
|
enum file_index const file = sq_to_file(sq);
|
|
|
|
Bb64 atk = 0ULL;
|
|
|
|
/* following loops assuming rank and file types are unsigned, which relies on C23 enums */
|
|
_Static_assert(((enum rank_index)-1) > 0, "rank_index must be unsigned");
|
|
_Static_assert(((enum file_index)-1) > 0, "file_index must be unsigned");
|
|
|
|
for (enum rank_index walk_rank = rank+1; walk_rank <= RANK_INDEX_8; ++walk_rank) {
|
|
Bb64 const sq = MASK_FROM_RF(walk_rank, file);
|
|
atk |= sq;
|
|
if (occ & sq) {
|
|
break;
|
|
}
|
|
}
|
|
for (enum rank_index walk_rank = rank-1; walk_rank <= RANK_INDEX_8; --walk_rank) {
|
|
Bb64 const sq = MASK_FROM_RF(walk_rank, file);
|
|
atk |= sq;
|
|
if (occ & sq) {
|
|
break;
|
|
}
|
|
}
|
|
for (enum file_index walk_file = file+1; walk_file <= FILE_INDEX_H; ++walk_file) {
|
|
Bb64 const sq = MASK_FROM_RF(rank, walk_file);
|
|
atk |= sq;
|
|
if (occ & sq) {
|
|
break;
|
|
}
|
|
}
|
|
for (enum file_index walk_file = file-1; walk_file <= FILE_INDEX_H; --walk_file) {
|
|
Bb64 const sq = MASK_FROM_RF(rank, walk_file);
|
|
atk |= sq;
|
|
if (occ & sq) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return atk;
|
|
|
|
#else
|
|
#if ! __has_include("mbb_rook.h")
|
|
#error "compile with -DCODEGEN and run once to generate required header files"
|
|
#else
|
|
|
|
/* defines
|
|
- `mbb_rook[SQ_COUNT]`
|
|
- `Bb64 rook_attacks[SQ_COUNT][1<<12ULL] */
|
|
#include "mbb_rook.h"
|
|
|
|
struct magic const m = mbb_rook[sq];
|
|
occ &= m.mask;
|
|
occ *= m.magic;
|
|
occ >>= (64ULL-12ULL);
|
|
assuming(rook_attacks[sq][occ] != MASK_FROM_SQ(sq));
|
|
return rook_attacks[sq][occ];
|
|
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static Bb64 rook_attacks(Bb64 p, Bb64 occ)
|
|
{
|
|
Bb64 b = 0ULL;
|
|
while (p) {
|
|
Sq8 const lsb = bitboard_pop_lsb(&p);
|
|
b |= rook_attacks_from_index(lsb, occ);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
static Bb64 bishop_attacks_from_index(Sq8 sq, Bb64 occ)
|
|
{
|
|
#ifdef CODEGEN
|
|
enum rank_index const rank = sq_to_rank(sq);
|
|
enum file_index const file = sq_to_file(sq);
|
|
|
|
Bb64 atk = 0ULL;
|
|
|
|
/* following loops assuming rank and file types are unsigned, which relies on C23 enums */
|
|
_Static_assert(((enum rank_index)-1) > 0, "rank_index must be unsigned");
|
|
_Static_assert(((enum file_index)-1) > 0, "file_index must be unsigned");
|
|
|
|
enum rank_index walk_rank;
|
|
enum file_index walk_file;
|
|
for (walk_rank = rank+1, walk_file = file+1;
|
|
walk_rank <= RANK_INDEX_8 && walk_file <= FILE_INDEX_H;
|
|
++walk_rank, ++walk_file) {
|
|
Bb64 const sq = MASK_FROM_RF(walk_rank, walk_file);
|
|
atk |= sq;
|
|
if (occ & sq) {
|
|
break;
|
|
}
|
|
}
|
|
for (walk_rank = rank+1, walk_file = file-1;
|
|
walk_rank <= RANK_INDEX_8 && walk_file <= FILE_INDEX_H;
|
|
++walk_rank, --walk_file) {
|
|
Bb64 const sq = MASK_FROM_RF(walk_rank, walk_file);
|
|
atk |= sq;
|
|
if (occ & sq) {
|
|
break;
|
|
}
|
|
}
|
|
for (walk_rank = rank-1, walk_file = file+1;
|
|
walk_rank <= RANK_INDEX_8 && walk_file <= FILE_INDEX_H;
|
|
--walk_rank, ++walk_file) {
|
|
Bb64 const sq = MASK_FROM_RF(walk_rank, walk_file);
|
|
atk |= sq;
|
|
if (occ & sq) {
|
|
break;
|
|
}
|
|
}
|
|
for (walk_rank = rank-1, walk_file = file-1;
|
|
walk_rank <= RANK_INDEX_8 && walk_file <= FILE_INDEX_H;
|
|
--walk_rank, --walk_file) {
|
|
Bb64 const sq = MASK_FROM_RF(walk_rank, walk_file);
|
|
atk |= sq;
|
|
if (occ & sq) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return atk;
|
|
#else
|
|
#if ! __has_include("mbb_bishop.h")
|
|
#error "compile with -DCODEGEN and run once to generate required header files"
|
|
#else
|
|
|
|
/* defines
|
|
- `mbb_bishop[SQ_COUNT]`
|
|
- `Bb64 bishop_attacks[SQ_COUNT][1<<9ULL] */
|
|
#include "mbb_bishop.h"
|
|
assuming(sq < SQ_COUNT);
|
|
struct magic const m = mbb_bishop[sq];
|
|
occ &= m.mask;
|
|
occ *= m.magic;
|
|
occ >>= (64ULL-9ULL);
|
|
return bishop_attacks[sq][occ];
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static Bb64 bishop_attacks(Bb64 p, Bb64 occ)
|
|
{
|
|
Bb64 b = 0ULL;
|
|
while (p) {
|
|
Sq8 const lsb = bitboard_pop_lsb(&p);
|
|
b |= bishop_attacks_from_index(lsb, occ);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
static Bb64 queen_attacks_from_index(Sq8 sq, Bb64 occ)
|
|
{
|
|
return bishop_attacks_from_index(sq, occ) | rook_attacks_from_index(sq, occ);
|
|
}
|
|
|
|
static Bb64 queen_attacks(Bb64 p, Bb64 occ)
|
|
{
|
|
Bb64 b = 0ULL;
|
|
while (p) {
|
|
Sq8 const lsb = bitboard_pop_lsb(&p);
|
|
b |= queen_attacks_from_index(lsb, occ);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
#define DEFINE_PAWN_MOVE_FUNCTIONS(identifier, side_enum, direction, starting_rank, next_rank) \
|
|
static inline Bb64 pawn_moves_##identifier(Bb64 p, Bb64 empty)\
|
|
{\
|
|
Bb64 const single_push = MASK_SHIFT_##direction##_1(p) & empty;\
|
|
Bb64 const double_push =\
|
|
MASK_SHIFT_##direction##_1(single_push & next_rank) & empty;\
|
|
return (single_push | double_push);\
|
|
}\
|
|
\
|
|
static inline Bb64 pawn_moves_##identifier##_from_index(Sq8 sq, Bb64 empty)\
|
|
{\
|
|
return pawn_moves_##identifier(MASK_FROM_SQ(sq), empty);\
|
|
}\
|
|
\
|
|
static inline Bb64 pawn_single_push_##identifier(Bb64 p, Bb64 empty)\
|
|
{\
|
|
return MASK_SHIFT_##direction##_1(p) & empty;\
|
|
}\
|
|
\
|
|
static inline Bb64 pawn_double_push_##identifier(Bb64 p, Bb64 empty)\
|
|
{\
|
|
Bb64 const once = MASK_SHIFT_##direction##_1(p) & empty;\
|
|
Bb64 const twice = MASK_SHIFT_##direction##_1(once) & empty;\
|
|
return twice;\
|
|
}\
|
|
\
|
|
static inline Bb64 pawn_attacks_right_##identifier(Bb64 p)\
|
|
{\
|
|
Bb64 const up = MASK_SHIFT_##direction##_1(p);\
|
|
return MASK_SHIFT_EAST_1(up);\
|
|
}\
|
|
\
|
|
static inline Bb64 pawn_attacks_left_##identifier(Bb64 p)\
|
|
{\
|
|
Bb64 const up = MASK_SHIFT_##direction##_1(p);\
|
|
return MASK_SHIFT_WEST_1(up);\
|
|
}\
|
|
\
|
|
static inline Bb64 pawn_attacks_##identifier(Bb64 p)\
|
|
{\
|
|
Bb64 const up = MASK_SHIFT_##direction##_1(p);\
|
|
Bb64 const captures_right = MASK_SHIFT_EAST_1(up);\
|
|
Bb64 const captures_left = MASK_SHIFT_WEST_1(up);\
|
|
\
|
|
return (captures_right | captures_left);\
|
|
}\
|
|
\
|
|
static inline Bb64 pawn_attacks_##identifier##_from_index(Sq8 sq)\
|
|
{\
|
|
return pawn_attacks_##identifier(MASK_FROM_SQ(sq));\
|
|
}
|
|
|
|
DEFINE_PAWN_MOVE_FUNCTIONS(white, SIDE_WHITE, NORTH, RANK_MASK_2, RANK_MASK_3)
|
|
DEFINE_PAWN_MOVE_FUNCTIONS(black, SIDE_BLACK, SOUTH, RANK_MASK_7, RANK_MASK_6)
|
|
|
|
/* temporary solution, annoying branch */
|
|
static inline Bb64 pawn_moves_dispatch(Bb64 p, Bb64 empty, Side8 attacker)
|
|
{
|
|
if (attacker == SIDE_WHITE) {
|
|
return pawn_moves_white(p, empty);
|
|
} else {
|
|
return pawn_moves_black(p, empty);
|
|
}
|
|
}
|
|
|
|
/* temporary solution, annoying branch */
|
|
static inline Bb64 pawn_moves_from_index_dispatch(Sq8 sq, Bb64 empty, Side8 attacker)
|
|
{
|
|
if (attacker == SIDE_WHITE) {
|
|
return pawn_moves_white_from_index(sq, empty);
|
|
} else {
|
|
return pawn_moves_black_from_index(sq, empty);
|
|
}
|
|
}
|
|
|
|
/* temporary solution, annoying branch */
|
|
static inline Bb64 pawn_attacks_from_index_dispatch(Sq8 sq, Side8 attacker)
|
|
{
|
|
if (attacker == SIDE_WHITE) {
|
|
return pawn_attacks_white_from_index(sq);
|
|
} else {
|
|
return pawn_attacks_black_from_index(sq);
|
|
}
|
|
}
|
|
|
|
static inline Bb64 pawn_attacks_dispatch(Bb64 b, Side8 attacker)
|
|
{
|
|
if (attacker == SIDE_WHITE) {
|
|
return pawn_attacks_white(b);
|
|
} else {
|
|
return pawn_attacks_black(b);
|
|
}
|
|
}
|
|
|
|
static Bb64 king_attacks(Bb64 sq)
|
|
{
|
|
/* potential untested improvements:
|
|
* - lookup table
|
|
* - union of three files, three ranks and ~sq */
|
|
Bb64 const n = MASK_SHIFT_NORTH_1(sq);
|
|
Bb64 const s = MASK_SHIFT_SOUTH_1(sq);
|
|
Bb64 const w = MASK_SHIFT_WEST_1(sq);
|
|
Bb64 const e = MASK_SHIFT_EAST_1(sq);
|
|
|
|
Bb64 const nw = MASK_SHIFT_WEST_1(n);
|
|
Bb64 const ne = MASK_SHIFT_EAST_1(n);
|
|
Bb64 const sw = MASK_SHIFT_WEST_1(s);
|
|
Bb64 const se = MASK_SHIFT_EAST_1(s);
|
|
|
|
return (n | s | w | e | nw | ne | sw | se);
|
|
}
|
|
|
|
static Bb64 king_attacks_from_index(Sq8 sq)
|
|
{
|
|
return king_attacks(MASK_FROM_SQ(sq));
|
|
}
|
|
|
|
static Bb64 non_pawn_piece_attacks(Piece8 piece, Bb64 p, Bb64 occ)
|
|
{
|
|
assuming(piece != PIECE_PAWN);
|
|
|
|
switch (piece) {
|
|
case PIECE_KNIGHT: {
|
|
return knight_attacks(p);
|
|
} break;
|
|
|
|
case PIECE_BISHOP: {
|
|
return bishop_attacks(p, occ);
|
|
} break;
|
|
|
|
case PIECE_ROOK: {
|
|
return rook_attacks(p, occ);
|
|
} break;
|
|
|
|
case PIECE_QUEEN: {
|
|
return queen_attacks(p, occ);
|
|
} break;
|
|
|
|
case PIECE_KING: {
|
|
return king_attacks(p);
|
|
} break;
|
|
|
|
default: {
|
|
assuming(false);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
static Bb64 non_pawn_piece_attacks_from_index(Piece8 piece, Sq8 sq, Bb64 occ)
|
|
{
|
|
assuming(piece != PIECE_PAWN);
|
|
|
|
switch (piece) {
|
|
case PIECE_KNIGHT: {
|
|
return knight_attacks_from_index(sq);
|
|
} break;
|
|
|
|
case PIECE_BISHOP: {
|
|
return bishop_attacks_from_index(sq, occ);
|
|
} break;
|
|
|
|
case PIECE_ROOK: {
|
|
return rook_attacks_from_index(sq, occ);
|
|
} break;
|
|
|
|
case PIECE_QUEEN: {
|
|
return queen_attacks_from_index(sq, occ);
|
|
} break;
|
|
|
|
case PIECE_KING: {
|
|
return king_attacks_from_index(sq);
|
|
} break;
|
|
|
|
default: {
|
|
assuming(false);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
static Bb64 between_mask(Sq8 a, Sq8 b)
|
|
{
|
|
#ifdef CODEGEN
|
|
enum rank_index const ra = sq_to_rank(a);
|
|
enum file_index const fa = sq_to_file(a);
|
|
enum rank_index const rb = sq_to_rank(b);
|
|
enum file_index const fb = sq_to_file(b);
|
|
|
|
/* directional differences */
|
|
int const dr = (int)rb - (int)ra;
|
|
int const df = (int)fb - (int)fa;
|
|
|
|
int step_r = 0;
|
|
int step_f = 0;
|
|
|
|
if (df == 0 && dr != 0) {
|
|
step_r = (dr > 0) ? 1 : -1;
|
|
step_f = 0;
|
|
}
|
|
else if (dr == 0 && df != 0) {
|
|
step_r = 0;
|
|
step_f = (df > 0) ? 1 : -1;
|
|
}
|
|
else if (dr != 0 && (dr > 0 ? dr : -dr) == (df > 0 ? df : -df)) {
|
|
step_r = (dr > 0) ? 1 : -1;
|
|
step_f = (df > 0) ? 1 : -1;
|
|
}
|
|
else {
|
|
return 0ULL;
|
|
}
|
|
|
|
Bb64 mask = 0ULL;
|
|
|
|
enum rank_index r = (enum rank_index)((int)ra + step_r);
|
|
enum file_index f = (enum file_index)((int)fa + step_f);
|
|
|
|
while (r != rb || f != fb) {
|
|
mask |= MASK_FROM_RF(r, f);
|
|
r = (enum rank_index)((int)r + step_r);
|
|
f = (enum file_index)((int)f + step_f);
|
|
}
|
|
|
|
return mask;
|
|
#else
|
|
#if ! __has_include("between_lookup.h")
|
|
#error "compile codegen.c and run once to generate required header files"
|
|
#else
|
|
|
|
/* defines static between_lookup[sq8_COUNT][sq8_COUNT] */
|
|
#include "between_lookup.h"
|
|
return between_lookup[a][b];
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static Bb64 attacks_to(struct pos const* pos,
|
|
Bb64 targets,
|
|
Bb64 pretend_occupied,
|
|
Bb64 pretend_empty)
|
|
{
|
|
Bb64 const occ_orig = pos->occupied[SIDE_WHITE] | pos->occupied[SIDE_BLACK];
|
|
Bb64 const occ = (occ_orig & ~pretend_empty) | pretend_occupied;
|
|
|
|
Bb64 const* pw = pos->pieces[SIDE_WHITE];
|
|
Bb64 const* pb = pos->pieces[SIDE_BLACK];
|
|
|
|
Bb64 const wps = pw[PIECE_PAWN];
|
|
|
|
Bb64 const bps = pb[PIECE_PAWN];
|
|
|
|
Bb64 const ns = (pw[PIECE_KNIGHT]
|
|
| pb[PIECE_KNIGHT])
|
|
;
|
|
Bb64 const ks = (pw[PIECE_KING]
|
|
| pb[PIECE_KING])
|
|
;
|
|
Bb64 const qs = (pw[PIECE_QUEEN]
|
|
| pb[PIECE_QUEEN])
|
|
;
|
|
Bb64 const qrs = (pw[PIECE_ROOK]
|
|
| pb[PIECE_ROOK]
|
|
| qs)
|
|
;
|
|
Bb64 const qbs = (pw[PIECE_BISHOP]
|
|
| pb[PIECE_BISHOP]
|
|
| qs)
|
|
;
|
|
|
|
return ((bps & pawn_attacks_white(targets))
|
|
| (wps & pawn_attacks_black(targets))
|
|
| (ns & knight_attacks(targets))
|
|
| (qbs & bishop_attacks(targets, occ))
|
|
| (qrs & rook_attacks(targets, occ))
|
|
| (ks & king_attacks(targets)))
|
|
& ~pretend_occupied;
|
|
}
|
|
|
|
static
|
|
Bb64 checkers(struct pos const* pos, Side8 us)
|
|
{
|
|
/* TODO: specialize */
|
|
return attacks_to(pos, pos->pieces[us][PIECE_KING], 0ULL, 0ULL) & ~pos->occupied[us];
|
|
}
|
|
|
|
static
|
|
Bb64 pinning_lines_to_target(struct pos const* pos, Side8 us, Sq8 tsq)
|
|
{
|
|
Side8 const them = other_side(us);
|
|
Bb64 const our_occ = pos->occupied[us];
|
|
Bb64 const their_occ = pos->occupied[them];
|
|
|
|
Bb64 const* p = pos->pieces[them];
|
|
Bb64 const bqs = p[PIECE_QUEEN] | p[PIECE_BISHOP];
|
|
Bb64 const rqs = p[PIECE_QUEEN] | p[PIECE_ROOK];
|
|
|
|
Bb64 pinners = (bqs & bishop_attacks_from_index(tsq, their_occ))
|
|
| (rqs & rook_attacks_from_index(tsq, their_occ));
|
|
|
|
Bb64 pinned = 0;
|
|
while (pinners) {
|
|
Sq8 const sq = bitboard_pop_lsb(&pinners);
|
|
Bb64 const blockers = between_mask(tsq, sq) & our_occ;
|
|
if (!bitboard_more_than_one(blockers)) {
|
|
pinned |= blockers;
|
|
}
|
|
}
|
|
|
|
return pinned;
|
|
}
|
|
|
|
static
|
|
Bb64 all_threats_from_side(struct pos const * pos, Side8 who)
|
|
{
|
|
Bb64 const occ = pos->occupied[SIDE_WHITE] | pos->occupied[SIDE_BLACK];
|
|
|
|
Bb64 const* p = pos->pieces[who];
|
|
Bb64 t = 0ULL;
|
|
t |= pawn_attacks_dispatch(p[PIECE_PAWN], who);
|
|
t |= knight_attacks(p[PIECE_KNIGHT]);
|
|
t |= bishop_attacks(p[PIECE_BISHOP], occ);
|
|
t |= rook_attacks(p[PIECE_ROOK], occ);
|
|
t |= queen_attacks(p[PIECE_QUEEN], occ);
|
|
t |= king_attacks(p[PIECE_KING]);
|
|
return t;
|
|
}
|
|
|