WIP
This commit is contained in:
8
Makefile
8
Makefile
@@ -1,5 +1,5 @@
|
||||
|
||||
CFLAGS := -std=c23
|
||||
CFLAGS := -std=c23 -g
|
||||
|
||||
all: tests
|
||||
|
||||
@@ -7,10 +7,10 @@ codegen: codegen.c
|
||||
$(CC) -o $@ $(CFLAGS) -DCODEGEN $^
|
||||
|
||||
mbb_rook.h: codegen
|
||||
./codegen
|
||||
#./codegen
|
||||
|
||||
mbb_bishop.h: codegen
|
||||
./codegen
|
||||
#./codegen
|
||||
|
||||
tests: tests.c | mbb_rook.h mbb_bishop.h
|
||||
tests: tests.c | mbb_rook.h mbb_bishop.h base.h
|
||||
$(CC) -o $@ $(CFLAGS) $^
|
||||
|
||||
736
base.h
736
base.h
@@ -51,7 +51,6 @@ ULL
|
||||
#define BITBOARD_FMT PRIu64
|
||||
#define BITBOARD_FMT_X PRIx64
|
||||
|
||||
|
||||
typedef uint64_t index;
|
||||
#define INDEX(n) n##ULL
|
||||
#define INDEX_FMT PRIu64
|
||||
@@ -65,9 +64,9 @@ typedef uint64_t index;
|
||||
#define RANK_MASK(n) RANK_SHIFT_UP((index)0x00000000000000FFULL, n)
|
||||
|
||||
#define INDEX_FROM_RF(rank, file) ((index)(8ULL*(index)(rank) + (index)(file)))
|
||||
#define SQUARE_MASK_FROM_RF(rank, file) ((index)1ULL << INDEX_FROM_RF(rank,file))
|
||||
#define SQ_MASK_FROM_RF(rank, file) ((index)1ULL << INDEX_FROM_RF(rank,file))
|
||||
|
||||
#define SQUARE_MASK_FROM_INDEX(idx) (1ULL<<(idx))
|
||||
#define SQ_MASK_FROM_INDEX(idx) (1ULL<<(idx))
|
||||
|
||||
enum file_index : index {
|
||||
FILE_INDEX_BEGIN,
|
||||
@@ -95,48 +94,9 @@ enum rank_index : index {
|
||||
RANK_INDEX_COUNT,
|
||||
};
|
||||
|
||||
enum : bitboard {
|
||||
FILEMASK_A = FILE_MASK(FILE_INDEX_A),
|
||||
FILEMASK_B = FILE_MASK(FILE_INDEX_B),
|
||||
FILEMASK_C = FILE_MASK(FILE_INDEX_C),
|
||||
FILEMASK_D = FILE_MASK(FILE_INDEX_D),
|
||||
FILEMASK_E = FILE_MASK(FILE_INDEX_E),
|
||||
FILEMASK_F = FILE_MASK(FILE_INDEX_F),
|
||||
FILEMASK_G = FILE_MASK(FILE_INDEX_G),
|
||||
FILEMASK_H = FILE_MASK(FILE_INDEX_H),
|
||||
|
||||
RANKMASK_1 = RANK_MASK(RANK_INDEX_1),
|
||||
RANKMASK_2 = RANK_MASK(RANK_INDEX_2),
|
||||
RANKMASK_3 = RANK_MASK(RANK_INDEX_3),
|
||||
RANKMASK_4 = RANK_MASK(RANK_INDEX_4),
|
||||
RANKMASK_5 = RANK_MASK(RANK_INDEX_5),
|
||||
RANKMASK_6 = RANK_MASK(RANK_INDEX_6),
|
||||
RANKMASK_7 = RANK_MASK(RANK_INDEX_7),
|
||||
RANKMASK_8 = RANK_MASK(RANK_INDEX_8),
|
||||
|
||||
FILE_SHIFT_RIGHT_GUARD_0 = ~0ULL,
|
||||
FILE_SHIFT_RIGHT_GUARD_1 = ~FILEMASK_H,
|
||||
FILE_SHIFT_RIGHT_GUARD_2 = ~(FILEMASK_G | FILEMASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_3 = ~(FILEMASK_F | FILEMASK_G | FILEMASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_4 = ~(FILEMASK_E | FILEMASK_F | FILEMASK_G | FILEMASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_5 = ~(FILEMASK_D | FILEMASK_E | FILEMASK_F | FILEMASK_G | FILEMASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_6 = ~(FILEMASK_C | FILEMASK_D | FILEMASK_E | FILEMASK_F | FILEMASK_G | FILEMASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_7 = ~(FILEMASK_B | FILEMASK_C | FILEMASK_D | FILEMASK_E | FILEMASK_F | FILEMASK_G | FILEMASK_H),
|
||||
|
||||
FILE_SHIFT_LEFT_GUARD_0 = ~0ULL,
|
||||
FILE_SHIFT_LEFT_GUARD_1 = ~FILEMASK_A,
|
||||
FILE_SHIFT_LEFT_GUARD_2 = ~(FILEMASK_A | FILEMASK_B),
|
||||
FILE_SHIFT_LEFT_GUARD_3 = ~(FILEMASK_A | FILEMASK_B | FILEMASK_C),
|
||||
FILE_SHIFT_LEFT_GUARD_4 = ~(FILEMASK_A | FILEMASK_B | FILEMASK_C | FILEMASK_D),
|
||||
FILE_SHIFT_LEFT_GUARD_5 = ~(FILEMASK_A | FILEMASK_B | FILEMASK_C | FILEMASK_D | FILEMASK_E),
|
||||
FILE_SHIFT_LEFT_GUARD_6 = ~(FILEMASK_A | FILEMASK_B | FILEMASK_C | FILEMASK_D | FILEMASK_E | FILEMASK_F),
|
||||
FILE_SHIFT_LEFT_GUARD_7 = ~(FILEMASK_A | FILEMASK_B | FILEMASK_C | FILEMASK_D | FILEMASK_E | FILEMASK_F | FILEMASK_G),
|
||||
};
|
||||
|
||||
|
||||
#define STR(x) #x
|
||||
|
||||
#define SQUARE_MASK_PREFIX SQUARE_MASK_
|
||||
#define SQ_MASK_PREFIX SQ_MASK_
|
||||
#define SQUARES_LIST_BEGIN \
|
||||
X(A, 1)
|
||||
#define SQUARES_LIST \
|
||||
@@ -203,45 +163,85 @@ enum : bitboard {
|
||||
X(H, 5) \
|
||||
X(H, 6) \
|
||||
X(H, 7) \
|
||||
X(H, 8) \
|
||||
X(H, 8)
|
||||
|
||||
/* define mask constants: SQUARE_MASK_A1, SQUARE_MASK_A2, ..., SQUARE_MASK_H8 */
|
||||
/* define mask constants: SQ_MASK_A1, SQ_MASK_A2, ..., SQ_MASK_H8 */
|
||||
enum : bitboard {
|
||||
#define X(file, rank) SQUARE_MASK_##file##rank = SQUARE_MASK_FROM_RF(RANK_INDEX_##rank, FILE_INDEX_##file),
|
||||
#define X(file, rank) SQ_MASK_##file##rank = SQ_MASK_FROM_RF(RANK_INDEX_##rank, FILE_INDEX_##file),
|
||||
SQUARES_LIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
enum square_index : bitboard {
|
||||
#define X(file, rank) SQUARE_INDEX_##file##rank = INDEX_FROM_RF(RANK_INDEX_##rank, FILE_INDEX_##file),
|
||||
#define X(file, rank) SQ_INDEX_##file##rank = INDEX_FROM_RF(RANK_INDEX_##rank, FILE_INDEX_##file),
|
||||
SQUARES_LIST
|
||||
SQUARE_INDEX_COUNT,
|
||||
SQ_INDEX_COUNT,
|
||||
#undef X
|
||||
/* define iterator begin enum */
|
||||
#define X(file, rank) SQUARE_INDEX_BEGIN = SQUARE_INDEX_##file##rank,
|
||||
#define X(file, rank) SQ_INDEX_BEGIN = SQ_INDEX_##file##rank,
|
||||
SQUARES_LIST_BEGIN
|
||||
#undef X
|
||||
};
|
||||
|
||||
const char* square_index_display[SQUARE_INDEX_COUNT] = {
|
||||
const char* square_index_display[SQ_INDEX_COUNT] = {
|
||||
#define X(file, rank) \
|
||||
[SQUARE_INDEX_##file##rank] = STR(file##rank),
|
||||
[SQ_INDEX_##file##rank] = STR(file##rank),
|
||||
SQUARES_LIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
const char* square_index_str[SQUARE_INDEX_COUNT] = {
|
||||
const char* square_index_str[SQ_INDEX_COUNT] = {
|
||||
#define X(file, rank) \
|
||||
[SQUARE_INDEX_##file##rank] = STR(SQUARE_INDEX_##file##rank),
|
||||
[SQ_INDEX_##file##rank] = STR(SQ_INDEX_##file##rank),
|
||||
SQUARES_LIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
enum : bitboard {
|
||||
FILE_MASK_A = FILE_MASK(FILE_INDEX_A),
|
||||
FILE_MASK_B = FILE_MASK(FILE_INDEX_B),
|
||||
FILE_MASK_C = FILE_MASK(FILE_INDEX_C),
|
||||
FILE_MASK_D = FILE_MASK(FILE_INDEX_D),
|
||||
FILE_MASK_E = FILE_MASK(FILE_INDEX_E),
|
||||
FILE_MASK_F = FILE_MASK(FILE_INDEX_F),
|
||||
FILE_MASK_G = FILE_MASK(FILE_INDEX_G),
|
||||
FILE_MASK_H = FILE_MASK(FILE_INDEX_H),
|
||||
|
||||
RANK_MASK_1 = RANK_MASK(RANK_INDEX_1),
|
||||
RANK_MASK_2 = RANK_MASK(RANK_INDEX_2),
|
||||
RANK_MASK_3 = RANK_MASK(RANK_INDEX_3),
|
||||
RANK_MASK_4 = RANK_MASK(RANK_INDEX_4),
|
||||
RANK_MASK_5 = RANK_MASK(RANK_INDEX_5),
|
||||
RANK_MASK_6 = RANK_MASK(RANK_INDEX_6),
|
||||
RANK_MASK_7 = RANK_MASK(RANK_INDEX_7),
|
||||
RANK_MASK_8 = RANK_MASK(RANK_INDEX_8),
|
||||
|
||||
FILE_SHIFT_RIGHT_GUARD_0 = ~0ULL,
|
||||
FILE_SHIFT_RIGHT_GUARD_1 = ~FILE_MASK_H,
|
||||
FILE_SHIFT_RIGHT_GUARD_2 = ~(FILE_MASK_G | FILE_MASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_3 = ~(FILE_MASK_F | FILE_MASK_G | FILE_MASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_4 = ~(FILE_MASK_E | FILE_MASK_F | FILE_MASK_G | FILE_MASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_5 = ~(FILE_MASK_D | FILE_MASK_E | FILE_MASK_F | FILE_MASK_G | FILE_MASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_6 = ~(FILE_MASK_C | FILE_MASK_D | FILE_MASK_E | FILE_MASK_F | FILE_MASK_G | FILE_MASK_H),
|
||||
FILE_SHIFT_RIGHT_GUARD_7 = ~(FILE_MASK_B | FILE_MASK_C | FILE_MASK_D | FILE_MASK_E | FILE_MASK_F | FILE_MASK_G | FILE_MASK_H),
|
||||
|
||||
FILE_SHIFT_LEFT_GUARD_0 = ~0ULL,
|
||||
FILE_SHIFT_LEFT_GUARD_1 = ~FILE_MASK_A,
|
||||
FILE_SHIFT_LEFT_GUARD_2 = ~(FILE_MASK_A | FILE_MASK_B),
|
||||
FILE_SHIFT_LEFT_GUARD_3 = ~(FILE_MASK_A | FILE_MASK_B | FILE_MASK_C),
|
||||
FILE_SHIFT_LEFT_GUARD_4 = ~(FILE_MASK_A | FILE_MASK_B | FILE_MASK_C | FILE_MASK_D),
|
||||
FILE_SHIFT_LEFT_GUARD_5 = ~(FILE_MASK_A | FILE_MASK_B | FILE_MASK_C | FILE_MASK_D | FILE_MASK_E),
|
||||
FILE_SHIFT_LEFT_GUARD_6 = ~(FILE_MASK_A | FILE_MASK_B | FILE_MASK_C | FILE_MASK_D | FILE_MASK_E | FILE_MASK_F),
|
||||
FILE_SHIFT_LEFT_GUARD_7 = ~(FILE_MASK_A | FILE_MASK_B | FILE_MASK_C | FILE_MASK_D | FILE_MASK_E | FILE_MASK_F | FILE_MASK_G),
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define _FILE_SHIFT_RIGHT_GUARDED(b, n) \
|
||||
((((bitboard)(b)) & FILE_SHIFT_RIGHT_GUARD_##n) << ((index)(n) * 1ULL))
|
||||
(((bitboard)(b) & FILE_SHIFT_RIGHT_GUARD_##n) << (index)(n))
|
||||
|
||||
#define _FILE_SHIFT_LEFT_GUARDED(b, n) \
|
||||
((((bitboard)(b)) & FILE_SHIFT_LEFT_GUARD_##n) >> ((index)(n) * 1ULL))
|
||||
(((bitboard)(b) & FILE_SHIFT_LEFT_GUARD_##n) >> (index)(n))
|
||||
|
||||
/* FILE_SHIFT_RIGHT_n shifts the bitboard right by n ranks without wrapping */
|
||||
#define FILE_SHIFT_RIGHT_1(b) _FILE_SHIFT_RIGHT_GUARDED(b, 1)
|
||||
@@ -299,7 +299,14 @@ struct magic {
|
||||
|
||||
static index bitboard_lsb(bitboard p)
|
||||
{
|
||||
return __builtin_ffsll(p);
|
||||
return __builtin_ffsll(p) - 1;
|
||||
}
|
||||
|
||||
static index bitboard_pop_lsb(bitboard* p)
|
||||
{
|
||||
const index lsb = bitboard_lsb(*p);
|
||||
*p &= ~(1ULL<<(lsb));
|
||||
return lsb;
|
||||
}
|
||||
|
||||
static uint64_t bitboard_popcount(bitboard b)
|
||||
@@ -321,7 +328,7 @@ static bitboard cardinals_from_index(enum square_index p)
|
||||
{
|
||||
/* might benefit from a lookup table */
|
||||
return (FILE_MASK(index_to_file(p)) | RANK_MASK(index_to_rank(p)))
|
||||
& ~SQUARE_MASK_FROM_INDEX(p);
|
||||
& ~SQ_MASK_FROM_INDEX(p);
|
||||
}
|
||||
|
||||
static bitboard diagonals_from_index(enum square_index sq)
|
||||
@@ -341,7 +348,7 @@ static bitboard diagonals_from_index(enum square_index sq)
|
||||
r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
|
||||
++r, ++f)
|
||||
{
|
||||
mask |= SQUARE_MASK_FROM_RF(r, f);
|
||||
mask |= SQ_MASK_FROM_RF(r, f);
|
||||
}
|
||||
|
||||
/* NW (rank+1, file-1) */
|
||||
@@ -349,7 +356,7 @@ static bitboard diagonals_from_index(enum square_index sq)
|
||||
r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
|
||||
++r, --f)
|
||||
{
|
||||
mask |= SQUARE_MASK_FROM_RF(r, f);
|
||||
mask |= SQ_MASK_FROM_RF(r, f);
|
||||
}
|
||||
|
||||
/* SE (rank-1, file+1) */
|
||||
@@ -357,7 +364,7 @@ static bitboard diagonals_from_index(enum square_index sq)
|
||||
r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
|
||||
--r, ++f)
|
||||
{
|
||||
mask |= SQUARE_MASK_FROM_RF(r, f);
|
||||
mask |= SQ_MASK_FROM_RF(r, f);
|
||||
}
|
||||
|
||||
/* SW (rank-1, file-1) */
|
||||
@@ -365,7 +372,7 @@ static bitboard diagonals_from_index(enum square_index sq)
|
||||
r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
|
||||
--r, --f)
|
||||
{
|
||||
mask |= SQUARE_MASK_FROM_RF(r, f);
|
||||
mask |= SQ_MASK_FROM_RF(r, f);
|
||||
}
|
||||
|
||||
return mask;
|
||||
@@ -374,14 +381,18 @@ static bitboard diagonals_from_index(enum square_index sq)
|
||||
|
||||
|
||||
/* PIECE ATTACKS
|
||||
* ================== */
|
||||
* ==================
|
||||
*
|
||||
* All piece attack functions rely on the caller masking their own pieces.
|
||||
* e.g. `knight_attacks(knights) & ~own_occupied`
|
||||
* */
|
||||
|
||||
static bitboard knight_attacks(bitboard p)
|
||||
{
|
||||
const bitboard l1 = FILE_SHIFT_RIGHT_1(p & ~FILEMASK_H);
|
||||
const bitboard l2 = FILE_SHIFT_RIGHT_2(p & ~(FILEMASK_G | FILEMASK_H));
|
||||
const bitboard r1 = FILE_SHIFT_LEFT_1 (p & ~FILEMASK_A);
|
||||
const bitboard r2 = FILE_SHIFT_LEFT_2 (p & ~(FILEMASK_A | FILEMASK_B));
|
||||
const bitboard l1 = FILE_SHIFT_RIGHT_1(p & ~FILE_MASK_H);
|
||||
const bitboard l2 = FILE_SHIFT_RIGHT_2(p & ~(FILE_MASK_G | FILE_MASK_H));
|
||||
const bitboard r1 = FILE_SHIFT_LEFT_1 (p & ~FILE_MASK_A);
|
||||
const bitboard r2 = FILE_SHIFT_LEFT_2 (p & ~(FILE_MASK_A | FILE_MASK_B));
|
||||
const bitboard h1 = l1 | r1;
|
||||
const bitboard h2 = l2 | r2;
|
||||
return RANK_SHIFT_UP_2(h1)
|
||||
@@ -390,57 +401,6 @@ static bitboard knight_attacks(bitboard p)
|
||||
| RANK_SHIFT_DOWN_1(h2);
|
||||
}
|
||||
|
||||
/* rook_attacks_from_index computes rook attack sets from an index, and its
|
||||
* purpose is code generation (the magic bitboard).
|
||||
*
|
||||
* It does not discriminate between friendly and opponent pieces, the caller
|
||||
* should therefore do something like:
|
||||
* ```
|
||||
* rook_attacks_from_index(p, friendly_occ | opponent_occ) & ~friendly_occ
|
||||
* ```
|
||||
* */
|
||||
static bitboard rook_attacks_from_index_slow(enum square_index sq, bitboard occ)
|
||||
{
|
||||
const enum rank_index rank = index_to_rank(sq);
|
||||
const enum file_index file = index_to_file(sq);
|
||||
|
||||
bitboard atk = 0ULL;
|
||||
|
||||
/* following loops assume rank and file types are unsigned, which relies on C23 enums */
|
||||
_Static_assert(((enum rank_index)0) - ((enum rank_index)1) > ((enum rank_index)0));
|
||||
_Static_assert(((enum file_index)0) - ((enum file_index)1) > ((enum file_index)0));
|
||||
|
||||
for (enum rank_index walk_rank = rank+1; walk_rank <= RANK_INDEX_8; ++walk_rank) {
|
||||
const bitboard sq = SQUARE_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) {
|
||||
const bitboard sq = SQUARE_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) {
|
||||
const bitboard sq = SQUARE_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) {
|
||||
const bitboard sq = SQUARE_MASK_FROM_RF(rank, walk_file);
|
||||
atk |= sq;
|
||||
if (occ & sq) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return atk;
|
||||
}
|
||||
|
||||
static bitboard rook_attacks_from_index(enum square_index sq, bitboard occ)
|
||||
{
|
||||
@@ -452,8 +412,8 @@ static bitboard rook_attacks_from_index(enum square_index sq, bitboard occ)
|
||||
#else
|
||||
|
||||
/* defines
|
||||
- `mbb_rook[SQUARE_INDEX_COUNT]`
|
||||
- `bitboard rook_attacks[SQUARE_INDEX_COUNT][1<<12ULL] */
|
||||
- `mbb_rook[SQ_INDEX_COUNT]`
|
||||
- `bitboard rook_attacks[SQ_INDEX_COUNT][1<<12ULL] */
|
||||
#include "mbb_rook.h"
|
||||
|
||||
const struct magic m = mbb_rook[sq];
|
||||
@@ -470,75 +430,12 @@ static bitboard rook_attacks(bitboard p, bitboard occ)
|
||||
{
|
||||
bitboard b = 0ULL;
|
||||
while (p) {
|
||||
const index lsb = bitboard_lsb(p) - 1;
|
||||
p &= ~(1ULL<<(lsb));
|
||||
const index lsb = bitboard_pop_lsb(&p);
|
||||
b |= rook_attacks_from_index(lsb, occ);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/* bishop_attacks_from_index computes bishop attack sets from an index, and its
|
||||
* purpose is code generation (the magic bitboard).
|
||||
*
|
||||
* It does not discriminate between friendly and opponent pieces, the caller
|
||||
* should therefore do something like:
|
||||
* ```
|
||||
* bishop_attacks_from_index(p, occ) & ~friendly_occ
|
||||
* ```
|
||||
* */
|
||||
static bitboard bishop_attacks_from_index_slow(enum square_index sq, bitboard occ)
|
||||
{
|
||||
const enum rank_index rank = index_to_rank(sq);
|
||||
const enum file_index file = index_to_file(sq);
|
||||
|
||||
bitboard atk = 0ULL;
|
||||
|
||||
/* following loops assume rank and file types are unsigned, which relies on C23 enums */
|
||||
_Static_assert(((enum rank_index)0) - ((enum rank_index)1) > ((enum rank_index)0));
|
||||
_Static_assert(((enum file_index)0) - ((enum file_index)1) > ((enum file_index)0));
|
||||
|
||||
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) {
|
||||
const bitboard sq = SQUARE_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) {
|
||||
const bitboard sq = SQUARE_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) {
|
||||
const bitboard sq = SQUARE_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) {
|
||||
const bitboard sq = SQUARE_MASK_FROM_RF(walk_rank, walk_file);
|
||||
atk |= sq;
|
||||
if (occ & sq) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return atk;
|
||||
}
|
||||
|
||||
static bitboard bishop_attacks_from_index(enum square_index sq, bitboard occ)
|
||||
{
|
||||
#ifdef CODEGEN
|
||||
@@ -549,8 +446,8 @@ static bitboard bishop_attacks_from_index(enum square_index sq, bitboard occ)
|
||||
#else
|
||||
|
||||
/* defines
|
||||
- `mbb_bishop[SQUARE_INDEX_COUNT]`
|
||||
- `bitboard bishop_attacks[SQUARE_INDEX_COUNT][1<<9ULL] */
|
||||
- `mbb_bishop[SQ_INDEX_COUNT]`
|
||||
- `bitboard bishop_attacks[SQ_INDEX_COUNT][1<<9ULL] */
|
||||
#include "mbb_bishop.h"
|
||||
const struct magic m = mbb_bishop[sq];
|
||||
occ &= m.mask;
|
||||
@@ -565,76 +462,89 @@ static bitboard bishop_attacks(bitboard p, bitboard occ)
|
||||
{
|
||||
bitboard b = 0ULL;
|
||||
while (p) {
|
||||
const index lsb = bitboard_lsb(p) - 1;
|
||||
p &= ~(1ULL<<(lsb));
|
||||
const index lsb = bitboard_pop_lsb(&p);
|
||||
b |= bishop_attacks_from_index(lsb, occ);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
static inline bitboard pawn_white_moves(bitboard p, bitboard empty)
|
||||
static bitboard queen_attacks_from_index(enum square_index sq, bitboard occ)
|
||||
{
|
||||
return bishop_attacks_from_index(sq, occ) | rook_attacks_from_index(sq, occ);
|
||||
}
|
||||
|
||||
static bitboard queen_attacks(bitboard p, bitboard occ)
|
||||
{
|
||||
bitboard b = 0ULL;
|
||||
while (p) {
|
||||
const index lsb = bitboard_pop_lsb(&p);
|
||||
b |= queen_attacks_from_index(lsb, occ);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
static inline bitboard pawn_moves_white(bitboard p, bitboard empty)
|
||||
{
|
||||
/* Assumptions:
|
||||
- empty = ~(black_occupied | white_occupied);
|
||||
*/
|
||||
|
||||
const bitboard single_push = RANK_SHIFT_UP(p, 1) & empty;
|
||||
const bitboard single_push = RANK_SHIFT_UP_1(p) & empty;
|
||||
|
||||
const bitboard double_push =
|
||||
RANK_SHIFT_UP(single_push & RANKMASK_3, 1) & empty;
|
||||
RANK_SHIFT_UP_1(single_push & RANK_MASK_3) & empty;
|
||||
|
||||
return single_push | double_push;
|
||||
return (single_push | double_push);
|
||||
}
|
||||
|
||||
static inline bitboard pawn_white_attacks(bitboard p, bitboard ep_targets, bitboard black_occupied)
|
||||
static inline bitboard pawn_attacks_white(bitboard p)
|
||||
{
|
||||
const bitboard up = RANK_SHIFT_UP(p, 1);
|
||||
const bitboard captures_right = FILE_SHIFT_RIGHT(up, 1) & ~FILEMASK_H;
|
||||
const bitboard captures_left = FILE_SHIFT_LEFT(up, 1) & ~FILEMASK_A;
|
||||
const bitboard up = RANK_SHIFT_UP_1(p);
|
||||
const bitboard captures_right = FILE_SHIFT_RIGHT_1(up);
|
||||
const bitboard captures_left = FILE_SHIFT_LEFT_1(up);
|
||||
|
||||
return (captures_right | captures_left) & (black_occupied | ep_targets);
|
||||
return (captures_right | captures_left);
|
||||
}
|
||||
|
||||
static inline bitboard pawn_black_moves(bitboard p, bitboard empty)
|
||||
static inline bitboard pawn_moves_black(bitboard p, bitboard empty)
|
||||
{
|
||||
/* Assumptions:
|
||||
- empty = ~(black_occupied | white_occupied);
|
||||
*/
|
||||
|
||||
const bitboard single_push = RANK_SHIFT_DOWN(p, 1) & empty;
|
||||
const bitboard single_push = RANK_SHIFT_DOWN_1(p) & empty;
|
||||
|
||||
const bitboard double_push =
|
||||
RANK_SHIFT_DOWN(single_push & RANKMASK_6, 1) & empty;
|
||||
RANK_SHIFT_DOWN_1(single_push & RANK_MASK_6) & empty;
|
||||
|
||||
return single_push | double_push;
|
||||
return (single_push | double_push);
|
||||
}
|
||||
|
||||
static inline bitboard pawn_black_attacks(bitboard p, bitboard ep_targets, bitboard white_occupied)
|
||||
static inline bitboard pawn_attacks_black(bitboard p)
|
||||
{
|
||||
const bitboard down = RANK_SHIFT_UP(p, 1);
|
||||
const bitboard captures_right = FILE_SHIFT_RIGHT(down, 1) & ~FILEMASK_H;
|
||||
const bitboard captures_left = FILE_SHIFT_LEFT(down, 1) & ~FILEMASK_A;
|
||||
const bitboard down = RANK_SHIFT_DOWN_1(p);
|
||||
const bitboard captures_right = FILE_SHIFT_RIGHT_1(down);
|
||||
const bitboard captures_left = FILE_SHIFT_LEFT_1(down);
|
||||
|
||||
return (captures_right | captures_left) & (white_occupied | ep_targets);
|
||||
return (captures_right | captures_left);
|
||||
}
|
||||
|
||||
static bitboard king_attacks(enum square_index sq)
|
||||
{
|
||||
/* candidate for improvement */
|
||||
const bitboard n = RANK_SHIFT_UP(sq, 1);
|
||||
const bitboard s = RANK_SHIFT_DOWN(sq, 1);
|
||||
const bitboard w = FILE_SHIFT_LEFT(sq, 1);
|
||||
const bitboard e = FILE_SHIFT_RIGHT(sq, 1);
|
||||
const bitboard n = RANK_SHIFT_UP_1(sq);
|
||||
const bitboard s = RANK_SHIFT_DOWN_1(sq);
|
||||
const bitboard w = FILE_SHIFT_LEFT_1(sq);
|
||||
const bitboard e = FILE_SHIFT_RIGHT_1(sq);
|
||||
|
||||
const bitboard nw = FILE_SHIFT_LEFT(n, 1);
|
||||
const bitboard ne = FILE_SHIFT_RIGHT(n, 1);
|
||||
const bitboard sw = FILE_SHIFT_RIGHT(s, 1);
|
||||
const bitboard se = FILE_SHIFT_LEFT(s, 1);
|
||||
const bitboard nw = FILE_SHIFT_LEFT_1(n);
|
||||
const bitboard ne = FILE_SHIFT_RIGHT_1(n);
|
||||
const bitboard sw = FILE_SHIFT_RIGHT_1(s);
|
||||
const bitboard se = FILE_SHIFT_LEFT_1(s);
|
||||
|
||||
return n | s | w | e | nw | ne | sw | se;
|
||||
return (n | s | w | e | nw | ne | sw | se);
|
||||
}
|
||||
|
||||
|
||||
/* BOARD
|
||||
* ================= */
|
||||
enum player : index {
|
||||
@@ -644,80 +554,308 @@ enum player : index {
|
||||
PLAYER_COUNT,
|
||||
};
|
||||
|
||||
enum piece : index {
|
||||
PIECE_BEGIN,
|
||||
PIECE_PAWN = PIECE_BEGIN,
|
||||
PIECE_KING,
|
||||
PIECE_QUEEN,
|
||||
PIECE_BISHOP,
|
||||
PIECE_ROOK,
|
||||
PIECE_KNIGHT,
|
||||
static inline enum player opposite_player(enum player p)
|
||||
{
|
||||
return (p == PLAYER_WHITE ? PLAYER_BLACK : PLAYER_WHITE);
|
||||
}
|
||||
|
||||
const char* player_str[PLAYER_COUNT] = {
|
||||
[PLAYER_WHITE] = "PLAYER_WHITE",
|
||||
[PLAYER_BLACK] = "PLAYER_BLACK",
|
||||
};
|
||||
|
||||
#define PIECE_BEGIN PIECE_PAWN
|
||||
/* enum value white char white unicode black char black unicode */
|
||||
#define PIECES \
|
||||
X(PIECE_PAWN, 1.0, 'P', 0x2659, 'p', 0x265F) \
|
||||
X(PIECE_KING, 5.0, 'K', 0x2654, 'k', 0x265A) \
|
||||
X(PIECE_QUEEN, 9.0, 'Q', 0x2655, 'q', 0x265B) \
|
||||
X(PIECE_BISHOP, 3.0, 'Q', 0x2657, 'q', 0x265D) \
|
||||
X(PIECE_ROOK, 5.0, 'R', 0x2656, 'q', 0x265C) \
|
||||
X(PIECE_KNIGHT, 3.0, 'N', 0x2658, 'n', 0x265E)
|
||||
|
||||
enum piece : size_t {
|
||||
#define X(e, v, wc, wu, bc, bu) e,
|
||||
PIECES
|
||||
PIECE_COUNT,
|
||||
#undef X
|
||||
};
|
||||
|
||||
double piece_value[PIECE_COUNT] = {
|
||||
#define X(e, v, wc, wu, bc, bu) [e] = v,
|
||||
PIECES
|
||||
#undef X
|
||||
};
|
||||
|
||||
const char* piece_str[PIECE_COUNT] = {
|
||||
#define X(e, v, wc, wu, bc, bu) [e] = STR(e),
|
||||
PIECES
|
||||
#undef X
|
||||
};
|
||||
|
||||
const char piece_char[PLAYER_COUNT][PIECE_COUNT] = {
|
||||
[PLAYER_WHITE] = {
|
||||
[PIECE_PAWN] = 'P',
|
||||
[PIECE_KING] = 'K',
|
||||
[PIECE_QUEEN] = 'Q',
|
||||
[PIECE_BISHOP] = 'B',
|
||||
[PIECE_ROOK] = 'R',
|
||||
[PIECE_KNIGHT] = 'N',
|
||||
#define X(e, v, wc, wu, bc, bu) [e] = wc,
|
||||
PIECES
|
||||
#undef X
|
||||
},
|
||||
[PLAYER_BLACK] = {
|
||||
[PIECE_PAWN] = 'p',
|
||||
[PIECE_KING] = 'k',
|
||||
[PIECE_QUEEN] = 'q',
|
||||
[PIECE_BISHOP] = 'b',
|
||||
[PIECE_ROOK] = 'r',
|
||||
[PIECE_KNIGHT] = 'n',
|
||||
#define X(e, v, wc, wu, bc, bu) [e] = bc,
|
||||
PIECES
|
||||
#undef X
|
||||
}
|
||||
};
|
||||
|
||||
const int piece_unicode[PLAYER_COUNT][PIECE_COUNT] = {
|
||||
[PLAYER_WHITE] = {
|
||||
[PIECE_PAWN] = 0x2659,
|
||||
[PIECE_KING] = 0x2654,
|
||||
[PIECE_QUEEN] = 0x2655,
|
||||
[PIECE_BISHOP] = 0x2657,
|
||||
[PIECE_ROOK] = 0x2656,
|
||||
[PIECE_KNIGHT] = 0x2658,
|
||||
#define X(e, v, wc, wu, bc, bu) [e] = wu,
|
||||
PIECES
|
||||
#undef X
|
||||
},
|
||||
[PLAYER_BLACK] = {
|
||||
[PIECE_PAWN] = 0x265F,
|
||||
[PIECE_KING] = 0x265A,
|
||||
[PIECE_QUEEN] = 0x265B,
|
||||
[PIECE_BISHOP] = 0x265D,
|
||||
[PIECE_ROOK] = 0x265C,
|
||||
[PIECE_KNIGHT] = 0x265E,
|
||||
#define X(e, v, wc, wu, bc, bu) [e] = bu,
|
||||
PIECES
|
||||
#undef X
|
||||
}
|
||||
};
|
||||
|
||||
struct board {
|
||||
bitboard pieces[PLAYER_COUNT][PIECE_COUNT];
|
||||
bitboard enpassent_targets[PLAYER_COUNT];
|
||||
bitboard ep_targets;
|
||||
bitboard occupied[PLAYER_COUNT];
|
||||
|
||||
/* Used only for square->piece lookups, not central to logic.
|
||||
* Does not store which player owns the square piece */
|
||||
enum piece mailbox[SQ_INDEX_COUNT];
|
||||
};
|
||||
|
||||
#define BOARD_INITIAL (struct board) { \
|
||||
.pieces = { \
|
||||
[PLAYER_WHITE] = { \
|
||||
[PIECE_PAWN] = RANKMASK_2, \
|
||||
[PIECE_ROOK] = SQUARE_MASK_A1 | SQUARE_MASK_H1, \
|
||||
[PIECE_KNIGHT] = SQUARE_MASK_B1 | SQUARE_MASK_G1, \
|
||||
[PIECE_BISHOP] = SQUARE_MASK_C1 | SQUARE_MASK_F1, \
|
||||
[PIECE_QUEEN] = SQUARE_MASK_D1, \
|
||||
[PIECE_KING] = SQUARE_MASK_E1, \
|
||||
[PIECE_PAWN] = RANK_MASK_2, \
|
||||
[PIECE_ROOK] = SQ_MASK_A1 | SQ_MASK_H1, \
|
||||
[PIECE_KNIGHT] = SQ_MASK_B1 | SQ_MASK_G1, \
|
||||
[PIECE_BISHOP] = SQ_MASK_C1 | SQ_MASK_F1, \
|
||||
[PIECE_QUEEN] = SQ_MASK_D1, \
|
||||
[PIECE_KING] = SQ_MASK_E1, \
|
||||
}, \
|
||||
[PLAYER_BLACK] = { \
|
||||
[PIECE_PAWN] = RANKMASK_7, \
|
||||
[PIECE_ROOK] = SQUARE_MASK_A8 | SQUARE_MASK_H8, \
|
||||
[PIECE_KNIGHT] = SQUARE_MASK_B8 | SQUARE_MASK_G8, \
|
||||
[PIECE_BISHOP] = SQUARE_MASK_C8 | SQUARE_MASK_F8, \
|
||||
[PIECE_QUEEN] = SQUARE_MASK_D8, \
|
||||
[PIECE_KING] = SQUARE_MASK_E8, \
|
||||
} \
|
||||
[PIECE_PAWN] = RANK_MASK_7, \
|
||||
[PIECE_ROOK] = SQ_MASK_A8 | SQ_MASK_H8, \
|
||||
[PIECE_KNIGHT] = SQ_MASK_B8 | SQ_MASK_G8, \
|
||||
[PIECE_BISHOP] = SQ_MASK_C8 | SQ_MASK_F8, \
|
||||
[PIECE_QUEEN] = SQ_MASK_D8, \
|
||||
[PIECE_KING] = SQ_MASK_E8, \
|
||||
} \
|
||||
}, \
|
||||
.ep_targets = 0ULL, \
|
||||
.occupied = { \
|
||||
[PLAYER_WHITE] = \
|
||||
RANK_MASK_2 | SQ_MASK_A1 | SQ_MASK_H1 | \
|
||||
SQ_MASK_B1 | SQ_MASK_G1 | SQ_MASK_C1 | \
|
||||
SQ_MASK_F1 | SQ_MASK_D1 | SQ_MASK_E1, \
|
||||
[PLAYER_BLACK] = \
|
||||
RANK_MASK_7 | SQ_MASK_A8 | SQ_MASK_H8 | \
|
||||
SQ_MASK_B8 | SQ_MASK_G8| SQ_MASK_C8 | \
|
||||
SQ_MASK_F8| SQ_MASK_D8| SQ_MASK_E8, \
|
||||
}, \
|
||||
.mailbox = { \
|
||||
[SQ_INDEX_A1] = PIECE_ROOK, \
|
||||
[SQ_INDEX_B1] = PIECE_KNIGHT, \
|
||||
[SQ_INDEX_C1] = PIECE_BISHOP, \
|
||||
[SQ_INDEX_D1] = PIECE_QUEEN, \
|
||||
[SQ_INDEX_E1] = PIECE_KING, \
|
||||
[SQ_INDEX_F1] = PIECE_BISHOP, \
|
||||
[SQ_INDEX_G1] = PIECE_KNIGHT, \
|
||||
[SQ_INDEX_H1] = PIECE_ROOK, \
|
||||
[SQ_INDEX_A2] = PIECE_PAWN, \
|
||||
[SQ_INDEX_B2] = PIECE_PAWN, \
|
||||
[SQ_INDEX_C2] = PIECE_PAWN, \
|
||||
[SQ_INDEX_D2] = PIECE_PAWN, \
|
||||
[SQ_INDEX_E2] = PIECE_PAWN, \
|
||||
[SQ_INDEX_F2] = PIECE_PAWN, \
|
||||
[SQ_INDEX_G2] = PIECE_PAWN, \
|
||||
[SQ_INDEX_H2] = PIECE_PAWN, \
|
||||
[SQ_INDEX_A7] = PIECE_PAWN, \
|
||||
[SQ_INDEX_B7] = PIECE_PAWN, \
|
||||
[SQ_INDEX_C7] = PIECE_PAWN, \
|
||||
[SQ_INDEX_D7] = PIECE_PAWN, \
|
||||
[SQ_INDEX_E7] = PIECE_PAWN, \
|
||||
[SQ_INDEX_F7] = PIECE_PAWN, \
|
||||
[SQ_INDEX_G7] = PIECE_PAWN, \
|
||||
[SQ_INDEX_H7] = PIECE_PAWN, \
|
||||
[SQ_INDEX_A8] = PIECE_ROOK, \
|
||||
[SQ_INDEX_B8] = PIECE_KNIGHT, \
|
||||
[SQ_INDEX_C8] = PIECE_BISHOP, \
|
||||
[SQ_INDEX_D8] = PIECE_QUEEN, \
|
||||
[SQ_INDEX_E8] = PIECE_KING, \
|
||||
[SQ_INDEX_F8] = PIECE_BISHOP, \
|
||||
[SQ_INDEX_G8] = PIECE_KNIGHT, \
|
||||
[SQ_INDEX_H8] = PIECE_ROOK, \
|
||||
}, \
|
||||
}
|
||||
|
||||
double board_score_heuristic(const struct board* board, enum player player)
|
||||
{
|
||||
double score = 0.0;
|
||||
|
||||
enum player opp = opposite_player(player);
|
||||
|
||||
for (enum piece piece = PIECE_BEGIN; piece < PIECE_COUNT; ++piece) {
|
||||
score += (double)bitboard_popcount(board->pieces[player][piece]) * piece_value[piece];
|
||||
}
|
||||
for (enum piece piece = PIECE_BEGIN; piece < PIECE_COUNT; ++piece) {
|
||||
score -= (double)bitboard_popcount(board->pieces[opp][piece]) * piece_value[piece];
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
struct move {
|
||||
index from;
|
||||
index to;
|
||||
};
|
||||
|
||||
struct move move(index from, index to)
|
||||
{
|
||||
return (struct move){
|
||||
.from = from,
|
||||
.to = to
|
||||
};
|
||||
}
|
||||
|
||||
#define MOVE_MAX 32
|
||||
|
||||
static size_t all_moves_from(const struct board* board,
|
||||
enum player player,
|
||||
enum square_index from,
|
||||
struct move moves[static MOVE_MAX])
|
||||
{
|
||||
const enum piece piece = board->mailbox[from];
|
||||
|
||||
const enum player opp = opposite_player(player);
|
||||
|
||||
const bitboard own_occ = board->occupied[player];
|
||||
const bitboard opp_occ = board->occupied[opp];
|
||||
const bitboard occ = own_occ | opp_occ;
|
||||
|
||||
const bitboard sqmask = SQ_MASK_FROM_INDEX(from);
|
||||
|
||||
bitboard x = 0ULL;
|
||||
|
||||
switch (piece) {
|
||||
case PIECE_PAWN: {
|
||||
if (player == PLAYER_WHITE) {
|
||||
x |= pawn_moves_white(sqmask, ~occ);
|
||||
x |= pawn_attacks_white(sqmask) & (board->ep_targets | opp_occ);
|
||||
} else {
|
||||
x |= pawn_moves_black(sqmask, ~occ);
|
||||
x |= pawn_attacks_black(sqmask) & (board->ep_targets | opp_occ);
|
||||
}
|
||||
} break;
|
||||
|
||||
case PIECE_KNIGHT: {
|
||||
x |= knight_attacks(sqmask);
|
||||
} break;
|
||||
|
||||
case PIECE_BISHOP: {
|
||||
x |= bishop_attacks_from_index(from, occ);
|
||||
} break;
|
||||
|
||||
case PIECE_ROOK: {
|
||||
x |= rook_attacks_from_index(from, occ);
|
||||
} break;
|
||||
|
||||
case PIECE_QUEEN: {
|
||||
x |= queen_attacks_from_index(from, occ);
|
||||
} break;
|
||||
|
||||
case PIECE_KING: {
|
||||
x |= king_attacks(sqmask);
|
||||
} break;
|
||||
}
|
||||
|
||||
x &= ~board->occupied[player];
|
||||
|
||||
size_t move_count = 0;
|
||||
while (x) {
|
||||
const index to = bitboard_pop_lsb(&x);
|
||||
moves[move_count++] = (struct move){.from = from, .to = to};
|
||||
}
|
||||
return move_count;
|
||||
}
|
||||
|
||||
static size_t all_player_pieces(struct board* board,
|
||||
enum player player,
|
||||
enum square_index out[static SQ_INDEX_COUNT])
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
fprintf(stderr, "%s\n", player_str[player]);
|
||||
for (enum piece piece = PIECE_BEGIN; piece < PIECE_COUNT; ++piece) {
|
||||
bitboard x = board->pieces[player][piece];
|
||||
fprintf(stderr, "%s:\t%016"BITBOARD_FMT_X"\n", piece_str[piece], x);
|
||||
while (x) {
|
||||
out[count++] = (enum square_index)bitboard_pop_lsb(&x);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* caller is resposible for checking validity */
|
||||
static void board_move_piece(struct board* board,
|
||||
enum player player,
|
||||
struct move move)
|
||||
{
|
||||
const bitboard from_mask = SQ_MASK_FROM_INDEX(move.from);
|
||||
const bitboard to_mask = SQ_MASK_FROM_INDEX(move.to);
|
||||
|
||||
const enum player opp = opposite_player(player);
|
||||
const enum piece from_piece = board->mailbox[move.from];
|
||||
const enum piece to_piece = board->mailbox[move.to];
|
||||
|
||||
board->pieces[player][from_piece] &= ~from_mask;
|
||||
board->pieces[player][from_piece] |= to_mask;
|
||||
board->pieces[opp][to_piece] &= ~to_mask;
|
||||
|
||||
board->occupied[player] &= ~from_mask;
|
||||
board->occupied[player] |= to_mask;
|
||||
board->occupied[opp] &= ~to_mask;
|
||||
|
||||
/* mailboxes are solely used for fast lookups of pieces in already valid
|
||||
* moves, it's safe to ignore the from index */
|
||||
board->mailbox[move.to] = from_piece;
|
||||
}
|
||||
|
||||
static bool board_is_check(struct board* b, enum player player)
|
||||
{
|
||||
const bitboard kb = b->pieces[player][PIECE_KING];
|
||||
|
||||
const enum player opp = opposite_player(player);
|
||||
|
||||
const bitboard player_occ = bitboard->occupied[player];
|
||||
const bitboard opp_occ = bitboard->occupied[opp];
|
||||
const bitboard occ = player_occ | opp_occ;
|
||||
const bitboard* p = b->pieces[opp];
|
||||
|
||||
bitboard threats = 0ULL;
|
||||
if (player == PLAYER_WHITE) {
|
||||
threats |= pawn_attacks_white(p[PIECE_PAWN]);
|
||||
} else {
|
||||
threats |= pawn_attacks_black(p[PIECE_PAWN]);
|
||||
}
|
||||
threats |= bishop_attacks(p[PIECE_BISHOP], occ);
|
||||
threats |= rook_attacks(p[PIECE_ROOK], occ);
|
||||
threats |= king_attacks(p[PIECE_KING]);
|
||||
threats |= queen_attacks(p[PIECE_QUEEN], occ);
|
||||
threats |= knight_attacks(p[PIECE_KNIGHT]);
|
||||
threats &= ~player_occ;
|
||||
|
||||
if (threats & kb) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void board_print(struct board* b, FILE* out)
|
||||
@@ -765,9 +903,6 @@ void board_print(struct board* b, FILE* out)
|
||||
fprintf(out, " A B C D E F G H \n");
|
||||
}
|
||||
|
||||
/* debug functions
|
||||
* =============== */
|
||||
|
||||
void bitboard_print(bitboard b, FILE* out)
|
||||
{
|
||||
setlocale(LC_ALL, "");
|
||||
@@ -794,3 +929,76 @@ void bitboard_print(bitboard b, FILE* out)
|
||||
fprintf(out, " A B C D E F G H \n");
|
||||
}
|
||||
|
||||
void board_print_threats(struct board* b, FILE* out, enum player player)
|
||||
{
|
||||
int buf[8][8] = {0};
|
||||
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
buf[i][i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
enum player opp = opposite_player(player);
|
||||
bitboard opp_occ = 0ULL,
|
||||
own_occ = 0ULL;
|
||||
for (enum piece piece = PIECE_BEGIN; piece < PIECE_COUNT; ++piece) {
|
||||
opp_occ |= b->pieces[opp][piece];
|
||||
own_occ |= b->pieces[player][piece];
|
||||
}
|
||||
const bitboard occ = opp_occ | own_occ;
|
||||
|
||||
bitboard threats = 0ULL;
|
||||
|
||||
const bitboard* p = b->pieces[player];
|
||||
if (player == PLAYER_WHITE) {
|
||||
threats |= pawn_attacks_white(p[PIECE_PAWN]);
|
||||
} else {
|
||||
threats |= pawn_attacks_black(p[PIECE_PAWN]);
|
||||
}
|
||||
threats |= bishop_attacks(p[PIECE_BISHOP], occ);
|
||||
threats |= rook_attacks(p[PIECE_ROOK], occ);
|
||||
threats |= king_attacks(p[PIECE_KING]);
|
||||
threats |= queen_attacks(p[PIECE_QUEEN], occ);
|
||||
threats |= knight_attacks(p[PIECE_KNIGHT]);
|
||||
threats &= ~own_occ;
|
||||
|
||||
for (enum player player = PLAYER_BEGIN; player < PLAYER_COUNT; player++) {
|
||||
for (enum piece piece = PIECE_BEGIN; piece < PIECE_COUNT; piece++) {
|
||||
bitboard x = b->pieces[player][piece];
|
||||
|
||||
for (index i = 7; i < 8; i--) {
|
||||
for (index j = 0; j < 8; j++) {
|
||||
if (x & (1ULL<<(i*8+j))) {
|
||||
buf[i][j] = piece_unicode[player][piece];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* see: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors */
|
||||
setlocale(LC_ALL, "");
|
||||
for (index i = 7; i < 8; i--) {
|
||||
fprintf(out, "\033[0m"); /* reset background color */
|
||||
fprintf(out, "%"INDEX_FMT" ", i+1);
|
||||
for (index j = 0; j < 8; j++) {
|
||||
const index n = i*8+j;
|
||||
if ((threats >> n) & 1) {
|
||||
fprintf(out, "\033[%d;%dm", 30, 41);
|
||||
} else {
|
||||
/* 45: magenta, 47: white */
|
||||
fprintf(out, "\033[%d;%dm", 30, (i+j) % 2 ? 45 : 47);
|
||||
}
|
||||
if (buf[i][j]) {
|
||||
fprintf(out, "%lc ", buf[i][j]);
|
||||
} else {
|
||||
fprintf(out, " "); /* idk why this hack is needed but "%lc "
|
||||
is not working when buf[i][j] = ' ' */
|
||||
}
|
||||
}
|
||||
fprintf(out, "\033[0m"); /* reset background color */
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
fprintf(out, " A B C D E F G H \n");
|
||||
}
|
||||
|
||||
140
codegen.c
140
codegen.c
@@ -3,6 +3,102 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static bitboard bishop_attacks_from_index_slow(enum square_index sq, bitboard occ)
|
||||
{
|
||||
const enum rank_index rank = index_to_rank(sq);
|
||||
const enum file_index file = index_to_file(sq);
|
||||
|
||||
bitboard atk = 0ULL;
|
||||
|
||||
/* following loops assume rank and file types are unsigned, which relies on C23 enums */
|
||||
_Static_assert(((enum rank_index)0) - ((enum rank_index)1) > ((enum rank_index)0));
|
||||
_Static_assert(((enum file_index)0) - ((enum file_index)1) > ((enum file_index)0));
|
||||
|
||||
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) {
|
||||
const bitboard sq = 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) {
|
||||
const bitboard sq = 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) {
|
||||
const bitboard sq = 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) {
|
||||
const bitboard sq = SQ_MASK_FROM_RF(walk_rank, walk_file);
|
||||
atk |= sq;
|
||||
if (occ & sq) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return atk;
|
||||
}
|
||||
|
||||
static bitboard rook_attacks_from_index_slow(enum square_index sq, bitboard occ)
|
||||
{
|
||||
const enum rank_index rank = index_to_rank(sq);
|
||||
const enum file_index file = index_to_file(sq);
|
||||
|
||||
bitboard atk = 0ULL;
|
||||
|
||||
/* following loops assume rank and file types are unsigned, which relies on C23 enums */
|
||||
_Static_assert(((enum rank_index)0) - ((enum rank_index)1) > ((enum rank_index)0));
|
||||
_Static_assert(((enum file_index)0) - ((enum file_index)1) > ((enum file_index)0));
|
||||
|
||||
for (enum rank_index walk_rank = rank+1; walk_rank <= RANK_INDEX_8; ++walk_rank) {
|
||||
const bitboard sq = 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) {
|
||||
const bitboard sq = 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) {
|
||||
const bitboard sq = 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) {
|
||||
const bitboard sq = SQ_MASK_FROM_RF(rank, walk_file);
|
||||
atk |= sq;
|
||||
if (occ & sq) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return atk;
|
||||
}
|
||||
|
||||
/* gets the next number with the same bitcount, unless it overflows, in which
|
||||
* it returns the first number with popcount+1 */
|
||||
uint64_t next_magic(uint64_t seed) {
|
||||
@@ -38,19 +134,19 @@ uint64_t rand64() {
|
||||
|
||||
int main()
|
||||
{
|
||||
struct magic rook_magics[SQUARE_INDEX_COUNT] = {0ULL};
|
||||
bitboard rook_attacks[SQUARE_INDEX_COUNT][1<<12ULL] = {0};
|
||||
struct magic rook_magics[SQ_INDEX_COUNT] = {0ULL};
|
||||
bitboard rook_attacks[SQ_INDEX_COUNT][1<<12ULL] = {0};
|
||||
|
||||
index rook_relevant_bits[12] = {0};
|
||||
size_t rook_relevant_bits_count = 0;
|
||||
|
||||
struct magic bishop_magics[SQUARE_INDEX_COUNT] = {0ULL};
|
||||
bitboard bishop_attacks[SQUARE_INDEX_COUNT][1<<9ULL] = {0};
|
||||
struct magic bishop_magics[SQ_INDEX_COUNT] = {0ULL};
|
||||
bitboard bishop_attacks[SQ_INDEX_COUNT][1<<9ULL] = {0};
|
||||
|
||||
index bishop_relevant_bits[9] = {0};
|
||||
size_t bishop_relevant_bits_count = 0;
|
||||
|
||||
for (enum square_index sq_index = SQUARE_INDEX_BEGIN; sq_index < SQUARE_INDEX_COUNT; ++sq_index) {
|
||||
for (enum square_index sq_index = SQ_INDEX_BEGIN; sq_index < SQ_INDEX_COUNT; ++sq_index) {
|
||||
enum file_index file = index_to_file(sq_index);
|
||||
enum rank_index rank = index_to_rank(sq_index);
|
||||
fprintf(stderr, "%s ", square_index_display[sq_index]);
|
||||
@@ -61,16 +157,15 @@ int main()
|
||||
/* generate attack mask */
|
||||
bitboard atk_mask = 0;
|
||||
{
|
||||
atk_mask |= FILE_MASK(file) & ~(RANKMASK_1 | RANKMASK_8);
|
||||
atk_mask |= RANK_MASK(rank) & ~(FILEMASK_A | FILEMASK_H);
|
||||
atk_mask &= ~SQUARE_MASK_FROM_RF(rank, file);
|
||||
atk_mask |= FILE_MASK(file) & ~(RANK_MASK_1 | RANK_MASK_8);
|
||||
atk_mask |= RANK_MASK(rank) & ~(FILE_MASK_A | FILE_MASK_H);
|
||||
atk_mask &= ~SQ_MASK_FROM_RF(rank, file);
|
||||
|
||||
/* populate relevant bits array */
|
||||
rook_relevant_bits_count = 0;
|
||||
bitboard x = atk_mask;
|
||||
while (x) {
|
||||
const index lsb = bitboard_lsb(x) - 1;
|
||||
x &= ~(1ULL<<(lsb));
|
||||
const index lsb = bitboard_pop_lsb(&x);
|
||||
rook_relevant_bits[rook_relevant_bits_count++] = lsb;
|
||||
}
|
||||
}
|
||||
@@ -117,16 +212,15 @@ int main()
|
||||
bitboard atk_mask = 0;
|
||||
{
|
||||
atk_mask |= diagonals_from_index(sq_index);
|
||||
atk_mask &= ~(RANKMASK_1 | RANKMASK_8);
|
||||
atk_mask &= ~(FILEMASK_A | FILEMASK_H);
|
||||
atk_mask &= ~SQUARE_MASK_FROM_RF(rank, file);
|
||||
atk_mask &= ~(RANK_MASK_1 | RANK_MASK_8);
|
||||
atk_mask &= ~(FILE_MASK_A | FILE_MASK_H);
|
||||
atk_mask &= ~SQ_MASK_FROM_RF(rank, file);
|
||||
|
||||
/* populate relevant bits array */
|
||||
bishop_relevant_bits_count = 0;
|
||||
bitboard x = atk_mask;
|
||||
while (x) {
|
||||
const index lsb = bitboard_lsb(x) - 1;
|
||||
x &= ~(1ULL<<(lsb));
|
||||
const index lsb = bitboard_pop_lsb(&x);
|
||||
bishop_relevant_bits[bishop_relevant_bits_count++] = lsb;
|
||||
}
|
||||
}
|
||||
@@ -178,8 +272,8 @@ int main()
|
||||
}
|
||||
|
||||
fprintf(f, "#pragma once\n");
|
||||
fprintf(f, "static const struct magic mbb_rook[SQUARE_INDEX_COUNT] = {\n");
|
||||
for (enum square_index i = SQUARE_INDEX_BEGIN; i < SQUARE_INDEX_COUNT; i++) {
|
||||
fprintf(f, "static const struct magic mbb_rook[SQ_INDEX_COUNT] = {\n");
|
||||
for (enum square_index i = SQ_INDEX_BEGIN; i < SQ_INDEX_COUNT; i++) {
|
||||
fprintf(f,
|
||||
TAB "[%s] = (struct magic) {"
|
||||
NL TAB " .magic = 0x%016"BITBOARD_FMT_X"ULL,"
|
||||
@@ -194,8 +288,8 @@ int main()
|
||||
|
||||
fprintf(f,"\n");
|
||||
|
||||
fprintf(f, "static const bitboard rook_attacks[SQUARE_INDEX_COUNT][1ULL<<12ULL] = {\n");
|
||||
for (enum square_index sq = SQUARE_INDEX_BEGIN; sq < SQUARE_INDEX_COUNT; ++sq) {
|
||||
fprintf(f, "static const bitboard rook_attacks[SQ_INDEX_COUNT][1ULL<<12ULL] = {\n");
|
||||
for (enum square_index sq = SQ_INDEX_BEGIN; sq < SQ_INDEX_COUNT; ++sq) {
|
||||
fprintf(f, "[%s] = {\n", square_index_str[sq]);
|
||||
for (size_t i = 0; i < sizeof rook_attacks[sq] / sizeof *rook_attacks[sq]; i++) {
|
||||
fprintf(f, "0x%016"BITBOARD_FMT_X"ULL, \n", rook_attacks[sq][i]);
|
||||
@@ -215,8 +309,8 @@ int main()
|
||||
}
|
||||
|
||||
fprintf(f, "\n");
|
||||
fprintf(f, "static const struct magic mbb_bishop[SQUARE_INDEX_COUNT] = {\n");
|
||||
for (enum square_index i = SQUARE_INDEX_BEGIN; i < SQUARE_INDEX_COUNT; i++) {
|
||||
fprintf(f, "static const struct magic mbb_bishop[SQ_INDEX_COUNT] = {\n");
|
||||
for (enum square_index i = SQ_INDEX_BEGIN; i < SQ_INDEX_COUNT; i++) {
|
||||
fprintf(f,
|
||||
TAB "[%s] = (struct magic) {"
|
||||
NL TAB " .magic = 0x%016"BITBOARD_FMT_X"ULL,"
|
||||
@@ -231,8 +325,8 @@ int main()
|
||||
|
||||
fprintf(f,"\n");
|
||||
|
||||
fprintf(f, "static const bitboard bishop_attacks[SQUARE_INDEX_COUNT][1ULL<<9ULL] = {\n");
|
||||
for (enum square_index sq = SQUARE_INDEX_BEGIN; sq < SQUARE_INDEX_COUNT; ++sq) {
|
||||
fprintf(f, "static const bitboard bishop_attacks[SQ_INDEX_COUNT][1ULL<<9ULL] = {\n");
|
||||
for (enum square_index sq = SQ_INDEX_BEGIN; sq < SQ_INDEX_COUNT; ++sq) {
|
||||
fprintf(f, "[%s] = {\n", square_index_str[sq]);
|
||||
for (size_t i = 0; i < sizeof bishop_attacks[sq] / sizeof *bishop_attacks[sq]; i++) {
|
||||
fprintf(f, "0x%016"BITBOARD_FMT_X"ULL, \n", bishop_attacks[sq][i]);
|
||||
|
||||
196
tests.c
196
tests.c
@@ -1,4 +1,7 @@
|
||||
|
||||
#define _XOPEN_SOURCE 500
|
||||
#include <unistd.h> /* usleep */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -25,13 +28,13 @@ static void print_rook_test(const char *label,
|
||||
static void test_rooks()
|
||||
{
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_A1;
|
||||
const bitboard rook = SQUARE_MASK_A1;
|
||||
const enum square_index sq = SQ_INDEX_A1;
|
||||
const bitboard rook = SQ_MASK_A1;
|
||||
const bitboard all_occ = rook;
|
||||
const bitboard own_occ = rook;
|
||||
|
||||
/* Expected: full rank 1 and file A, except A1 */
|
||||
bitboard expected = (FILEMASK_A | RANKMASK_1) & ~SQUARE_MASK_A1;
|
||||
bitboard expected = (FILE_MASK_A | RANK_MASK_1) & ~SQ_MASK_A1;
|
||||
|
||||
print_rook_test("Test 1: Rook at A1, empty board", sq, all_occ, own_occ);
|
||||
|
||||
@@ -40,9 +43,9 @@ static void test_rooks()
|
||||
}
|
||||
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_A1;
|
||||
const bitboard rook = SQUARE_MASK_A1;
|
||||
const bitboard own_block = SQUARE_MASK_A2 | SQUARE_MASK_B1;
|
||||
const enum square_index sq = SQ_INDEX_A1;
|
||||
const bitboard rook = SQ_MASK_A1;
|
||||
const bitboard own_block = SQ_MASK_A2 | SQ_MASK_B1;
|
||||
const bitboard all_occ = rook | own_block;
|
||||
const bitboard own_occ = all_occ;
|
||||
|
||||
@@ -56,9 +59,9 @@ static void test_rooks()
|
||||
}
|
||||
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_A1;
|
||||
const bitboard rook = SQUARE_MASK_A1;
|
||||
const bitboard enemies = SQUARE_MASK_A3 | SQUARE_MASK_C1;
|
||||
const enum square_index sq = SQ_INDEX_A1;
|
||||
const bitboard rook = SQ_MASK_A1;
|
||||
const bitboard enemies = SQ_MASK_A3 | SQ_MASK_C1;
|
||||
const bitboard all_occ = rook | enemies;
|
||||
const bitboard own_occ = rook;
|
||||
|
||||
@@ -68,8 +71,8 @@ static void test_rooks()
|
||||
* - Along rank 1: B1, C1 (enemy at C1 capturable, stop there)
|
||||
*/
|
||||
bitboard expected = 0ULL;
|
||||
expected |= SQUARE_MASK_A2 | SQUARE_MASK_A3;
|
||||
expected |= SQUARE_MASK_B1 | SQUARE_MASK_C1;
|
||||
expected |= SQ_MASK_A2 | SQ_MASK_A3;
|
||||
expected |= SQ_MASK_B1 | SQ_MASK_C1;
|
||||
|
||||
print_rook_test("Test 3: Rook at A1, enemy blockers A3, C1", sq, all_occ, own_occ);
|
||||
|
||||
@@ -79,13 +82,13 @@ static void test_rooks()
|
||||
|
||||
/* Rook Test 6: center rook on empty board */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_E5;
|
||||
const bitboard rook = SQUARE_MASK_E5;
|
||||
const enum square_index sq = SQ_INDEX_E5;
|
||||
const bitboard rook = SQ_MASK_E5;
|
||||
const bitboard all_occ = rook;
|
||||
const bitboard own_occ = rook;
|
||||
|
||||
/* Full rank 5 and file E, except E5 itself */
|
||||
bitboard expected = (FILEMASK_E | RANKMASK_5) & ~SQUARE_MASK_E5;
|
||||
bitboard expected = (FILE_MASK_E | RANK_MASK_5) & ~SQ_MASK_E5;
|
||||
|
||||
print_rook_test("Rook Test 6: E5, empty board", sq, all_occ, own_occ);
|
||||
|
||||
@@ -95,12 +98,12 @@ static void test_rooks()
|
||||
|
||||
/* Rook Test 7: center rook, mixed blockers on rays */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_E5;
|
||||
const bitboard rook = SQUARE_MASK_E5;
|
||||
const enum square_index sq = SQ_INDEX_E5;
|
||||
const bitboard rook = SQ_MASK_E5;
|
||||
|
||||
/* Friendly: E7 and C5; Enemy: E3 and H5 */
|
||||
const bitboard friends = rook | SQUARE_MASK_E7 | SQUARE_MASK_C5;
|
||||
const bitboard enemies = SQUARE_MASK_E3 | SQUARE_MASK_H5;
|
||||
const bitboard friends = rook | SQ_MASK_E7 | SQ_MASK_C5;
|
||||
const bitboard enemies = SQ_MASK_E3 | SQ_MASK_H5;
|
||||
const bitboard all_occ = friends | enemies;
|
||||
const bitboard own_occ = friends;
|
||||
|
||||
@@ -112,10 +115,10 @@ static void test_rooks()
|
||||
* Right:F5, G5, H5 (enemy, included, then stop)
|
||||
*/
|
||||
bitboard expected = 0ULL;
|
||||
expected |= SQUARE_MASK_E6;
|
||||
expected |= SQUARE_MASK_E4 | SQUARE_MASK_E3;
|
||||
expected |= SQUARE_MASK_D5;
|
||||
expected |= SQUARE_MASK_F5 | SQUARE_MASK_G5 | SQUARE_MASK_H5;
|
||||
expected |= SQ_MASK_E6;
|
||||
expected |= SQ_MASK_E4 | SQ_MASK_E3;
|
||||
expected |= SQ_MASK_D5;
|
||||
expected |= SQ_MASK_F5 | SQ_MASK_G5 | SQ_MASK_H5;
|
||||
|
||||
print_rook_test("Rook Test 7: E5, friends E7/C5, enemies E3/H5", sq, all_occ, own_occ);
|
||||
|
||||
@@ -125,8 +128,8 @@ static void test_rooks()
|
||||
|
||||
/* Rook Test 8: edge rook on empty board (top edge, not corner) */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_C8;
|
||||
const bitboard rook = SQUARE_MASK_C8;
|
||||
const enum square_index sq = SQ_INDEX_C8;
|
||||
const bitboard rook = SQ_MASK_C8;
|
||||
const bitboard all_occ = rook;
|
||||
const bitboard own_occ = rook;
|
||||
|
||||
@@ -136,8 +139,8 @@ static void test_rooks()
|
||||
* Across rank 8: A8,B8,D8,E8,F8,G8,H8
|
||||
*/
|
||||
bitboard expected = 0ULL;
|
||||
expected |= (FILEMASK_C & ~SQUARE_MASK_C8);
|
||||
expected |= (RANKMASK_8 & ~SQUARE_MASK_C8);
|
||||
expected |= (FILE_MASK_C & ~SQ_MASK_C8);
|
||||
expected |= (RANK_MASK_8 & ~SQ_MASK_C8);
|
||||
|
||||
print_rook_test("Rook Test 8: C8, empty board", sq, all_occ, own_occ);
|
||||
|
||||
@@ -147,11 +150,11 @@ static void test_rooks()
|
||||
|
||||
/* Rook Test 9: rook completely boxed in by friendly orthogonal neighbors */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_D4;
|
||||
const bitboard rook = SQUARE_MASK_D4;
|
||||
const enum square_index sq = SQ_INDEX_D4;
|
||||
const bitboard rook = SQ_MASK_D4;
|
||||
const bitboard friends = rook |
|
||||
SQUARE_MASK_D5 | SQUARE_MASK_D3 |
|
||||
SQUARE_MASK_C4 | SQUARE_MASK_E4;
|
||||
SQ_MASK_D5 | SQ_MASK_D3 |
|
||||
SQ_MASK_C4 | SQ_MASK_E4;
|
||||
const bitboard enemies = 0ULL;
|
||||
const bitboard all_occ = friends | enemies;
|
||||
const bitboard own_occ = friends;
|
||||
@@ -168,14 +171,14 @@ static void test_rooks()
|
||||
|
||||
/* Rook Test 10: rook on file with non-interfering off-ray pieces */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_A4;
|
||||
const bitboard rook = SQUARE_MASK_A4;
|
||||
const enum square_index sq = SQ_INDEX_A4;
|
||||
const bitboard rook = SQ_MASK_A4;
|
||||
|
||||
/* Pieces placed off the rook's rank/file; they should have no effect */
|
||||
const bitboard off_ray = SQUARE_MASK_C1 | SQUARE_MASK_F6 | SQUARE_MASK_H8;
|
||||
const bitboard off_ray = SQ_MASK_C1 | SQ_MASK_F6 | SQ_MASK_H8;
|
||||
(void)off_ray;
|
||||
const bitboard friends = rook | SQUARE_MASK_C1;
|
||||
const bitboard enemies = SQUARE_MASK_F6 | SQUARE_MASK_H8;
|
||||
const bitboard friends = rook | SQ_MASK_C1;
|
||||
const bitboard enemies = SQ_MASK_F6 | SQ_MASK_H8;
|
||||
const bitboard all_occ = friends | enemies;
|
||||
const bitboard own_occ = friends;
|
||||
|
||||
@@ -186,7 +189,7 @@ static void test_rooks()
|
||||
* Pieces not on file A or rank 4 must not change attacks.
|
||||
*/
|
||||
bitboard expected = 0ULL;
|
||||
expected |= (FILEMASK_A | RANKMASK_4) & ~SQUARE_MASK_A4;
|
||||
expected |= (FILE_MASK_A | RANK_MASK_4) & ~SQ_MASK_A4;
|
||||
|
||||
print_rook_test("Rook Test 10: A4, random off-ray pieces C1/F6/H8",
|
||||
sq, all_occ, own_occ);
|
||||
@@ -217,8 +220,8 @@ static void test_bishops(void)
|
||||
{
|
||||
/* Test 1: Bishop at D4 on empty board (only bishop present) */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_D4;
|
||||
const bitboard bishop = SQUARE_MASK_D4;
|
||||
const enum square_index sq = SQ_INDEX_D4;
|
||||
const bitboard bishop = SQ_MASK_D4;
|
||||
const bitboard all_occ = bishop;
|
||||
const bitboard own_occ = bishop;
|
||||
|
||||
@@ -230,10 +233,10 @@ static void test_bishops(void)
|
||||
* SW: C3, B2, A1
|
||||
*/
|
||||
bitboard expected = 0ULL;
|
||||
expected |= SQUARE_MASK_E5 | SQUARE_MASK_F6 | SQUARE_MASK_G7 | SQUARE_MASK_H8;
|
||||
expected |= SQUARE_MASK_C5 | SQUARE_MASK_B6 | SQUARE_MASK_A7;
|
||||
expected |= SQUARE_MASK_E3 | SQUARE_MASK_F2 | SQUARE_MASK_G1;
|
||||
expected |= SQUARE_MASK_C3 | SQUARE_MASK_B2 | SQUARE_MASK_A1;
|
||||
expected |= SQ_MASK_E5 | SQ_MASK_F6 | SQ_MASK_G7 | SQ_MASK_H8;
|
||||
expected |= SQ_MASK_C5 | SQ_MASK_B6 | SQ_MASK_A7;
|
||||
expected |= SQ_MASK_E3 | SQ_MASK_F2 | SQ_MASK_G1;
|
||||
expected |= SQ_MASK_C3 | SQ_MASK_B2 | SQ_MASK_A1;
|
||||
|
||||
print_bishop_test("Bishop Test 1: D4, empty board", sq, all_occ, own_occ);
|
||||
|
||||
@@ -243,8 +246,8 @@ static void test_bishops(void)
|
||||
|
||||
/* Test 2: Bishop at C1 on empty board (only bishop present) */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_C1;
|
||||
const bitboard bishop = SQUARE_MASK_C1;
|
||||
const enum square_index sq = SQ_INDEX_C1;
|
||||
const bitboard bishop = SQ_MASK_C1;
|
||||
const bitboard all_occ = bishop;
|
||||
const bitboard own_occ = bishop;
|
||||
|
||||
@@ -255,9 +258,9 @@ static void test_bishops(void)
|
||||
* SE / SW: none (edge of board)
|
||||
*/
|
||||
bitboard expected = 0ULL;
|
||||
expected |= SQUARE_MASK_D2 | SQUARE_MASK_E3 | SQUARE_MASK_F4 |
|
||||
SQUARE_MASK_G5 | SQUARE_MASK_H6;
|
||||
expected |= SQUARE_MASK_B2 | SQUARE_MASK_A3;
|
||||
expected |= SQ_MASK_D2 | SQ_MASK_E3 | SQ_MASK_F4 |
|
||||
SQ_MASK_G5 | SQ_MASK_H6;
|
||||
expected |= SQ_MASK_B2 | SQ_MASK_A3;
|
||||
|
||||
print_bishop_test("Bishop Test 2: C1, empty board", sq, all_occ, own_occ);
|
||||
|
||||
@@ -267,9 +270,9 @@ static void test_bishops(void)
|
||||
|
||||
/* Test 3: Bishop at D4, friendly blockers at F6 and B2 (no enemies) */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_D4;
|
||||
const bitboard bishop = SQUARE_MASK_D4;
|
||||
const bitboard friends = bishop | SQUARE_MASK_F6 | SQUARE_MASK_B2;
|
||||
const enum square_index sq = SQ_INDEX_D4;
|
||||
const bitboard bishop = SQ_MASK_D4;
|
||||
const bitboard friends = bishop | SQ_MASK_F6 | SQ_MASK_B2;
|
||||
const bitboard enemies = 0ULL;
|
||||
const bitboard all_occ = friends | enemies;
|
||||
const bitboard own_occ = friends;
|
||||
@@ -282,10 +285,10 @@ static void test_bishops(void)
|
||||
* SE: E3, F2, G1 (no blockers)
|
||||
*/
|
||||
bitboard expected = 0ULL;
|
||||
expected |= SQUARE_MASK_E5;
|
||||
expected |= SQUARE_MASK_C3;
|
||||
expected |= SQUARE_MASK_C5 | SQUARE_MASK_B6 | SQUARE_MASK_A7;
|
||||
expected |= SQUARE_MASK_E3 | SQUARE_MASK_F2 | SQUARE_MASK_G1;
|
||||
expected |= SQ_MASK_E5;
|
||||
expected |= SQ_MASK_C3;
|
||||
expected |= SQ_MASK_C5 | SQ_MASK_B6 | SQ_MASK_A7;
|
||||
expected |= SQ_MASK_E3 | SQ_MASK_F2 | SQ_MASK_G1;
|
||||
|
||||
print_bishop_test("Bishop Test 3: D4, friendly blockers F6, B2", sq, all_occ, own_occ);
|
||||
|
||||
@@ -295,10 +298,10 @@ static void test_bishops(void)
|
||||
|
||||
/* Test 4: Bishop at D4, enemy blockers at F6 and B2 (no other friends) */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_D4;
|
||||
const bitboard bishop = SQUARE_MASK_D4;
|
||||
const enum square_index sq = SQ_INDEX_D4;
|
||||
const bitboard bishop = SQ_MASK_D4;
|
||||
const bitboard friends = bishop;
|
||||
const bitboard enemies = SQUARE_MASK_F6 | SQUARE_MASK_B2;
|
||||
const bitboard enemies = SQ_MASK_F6 | SQ_MASK_B2;
|
||||
const bitboard all_occ = friends | enemies;
|
||||
const bitboard own_occ = friends;
|
||||
|
||||
@@ -310,10 +313,10 @@ static void test_bishops(void)
|
||||
* SE: E3, F2, G1
|
||||
*/
|
||||
bitboard expected = 0ULL;
|
||||
expected |= SQUARE_MASK_E5 | SQUARE_MASK_F6;
|
||||
expected |= SQUARE_MASK_C3 | SQUARE_MASK_B2;
|
||||
expected |= SQUARE_MASK_C5 | SQUARE_MASK_B6 | SQUARE_MASK_A7;
|
||||
expected |= SQUARE_MASK_E3 | SQUARE_MASK_F2 | SQUARE_MASK_G1;
|
||||
expected |= SQ_MASK_E5 | SQ_MASK_F6;
|
||||
expected |= SQ_MASK_C3 | SQ_MASK_B2;
|
||||
expected |= SQ_MASK_C5 | SQ_MASK_B6 | SQ_MASK_A7;
|
||||
expected |= SQ_MASK_E3 | SQ_MASK_F2 | SQ_MASK_G1;
|
||||
|
||||
print_bishop_test("Bishop Test 4: D4, enemy blockers F6, B2", sq, all_occ, own_occ);
|
||||
|
||||
@@ -323,11 +326,11 @@ static void test_bishops(void)
|
||||
|
||||
/* Test 5: Bishop at D4, mixed friend/enemy + another friendly bishop elsewhere */
|
||||
{
|
||||
const enum square_index sq = SQUARE_INDEX_D4;
|
||||
const bitboard bishop1 = SQUARE_MASK_D4; /* tested bishop */
|
||||
const bitboard bishop2 = SQUARE_MASK_F4; /* another friendly bishop */
|
||||
const bitboard friends = bishop1 | bishop2 | SQUARE_MASK_F6;
|
||||
const bitboard enemies = SQUARE_MASK_B2;
|
||||
const enum square_index sq = SQ_INDEX_D4;
|
||||
const bitboard bishop1 = SQ_MASK_D4; /* tested bishop */
|
||||
const bitboard bishop2 = SQ_MASK_F4; /* another friendly bishop */
|
||||
const bitboard friends = bishop1 | bishop2 | SQ_MASK_F6;
|
||||
const bitboard enemies = SQ_MASK_B2;
|
||||
const bitboard all_occ = friends | enemies;
|
||||
const bitboard own_occ = friends;
|
||||
|
||||
@@ -340,10 +343,10 @@ static void test_bishops(void)
|
||||
* Bishop at F4 is irrelevant; it does not sit on a diagonal from D4.
|
||||
*/
|
||||
bitboard expected = 0ULL;
|
||||
expected |= SQUARE_MASK_E5;
|
||||
expected |= SQUARE_MASK_C3 | SQUARE_MASK_B2;
|
||||
expected |= SQUARE_MASK_C5 | SQUARE_MASK_B6 | SQUARE_MASK_A7;
|
||||
expected |= SQUARE_MASK_E3 | SQUARE_MASK_F2 | SQUARE_MASK_G1;
|
||||
expected |= SQ_MASK_E5;
|
||||
expected |= SQ_MASK_C3 | SQ_MASK_B2;
|
||||
expected |= SQ_MASK_C5 | SQ_MASK_B6 | SQ_MASK_A7;
|
||||
expected |= SQ_MASK_E3 | SQ_MASK_F2 | SQ_MASK_G1;
|
||||
|
||||
print_bishop_test("Bishop Test 5: D4, mixed friend/enemy + extra bishop F4", sq, all_occ, own_occ);
|
||||
|
||||
@@ -354,6 +357,7 @@ static void test_bishops(void)
|
||||
printf("\nAll bishop_attacks_from_index tests passed.\n");
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
test_rooks();
|
||||
@@ -365,39 +369,39 @@ int main()
|
||||
fprintf(stdout, "\033[0m\n"); /* reset background color */
|
||||
struct board board = BOARD_INITIAL;
|
||||
|
||||
board_print(&board, stdout);
|
||||
enum player player = PLAYER_WHITE;
|
||||
enum square_index pieces[SQ_INDEX_COUNT] = {0};
|
||||
struct move moves[MOVE_MAX] = {0};
|
||||
for (int i = 0; i < 40; ++i) {
|
||||
board_print_threats(&board, stdout, player);
|
||||
|
||||
printf("\nFile C3:\n");
|
||||
bitboard_print(FILEMASK_C, stdout);
|
||||
const size_t piece_count = all_player_pieces(&board, player, pieces);
|
||||
|
||||
printf("\nRank 2:\n");
|
||||
bitboard_print(RANKMASK_2, stdout);
|
||||
if (piece_count == 0) {
|
||||
printf("no pieces for %s, aborting\n", player_str[player]);
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\nKnight at C3 moves:\n");
|
||||
bitboard_print(knight_attacks(SQUARE_MASK_C3), stdout);
|
||||
size_t move_count;
|
||||
for (size_t i = 0; i < piece_count; ++i) {
|
||||
move_count = all_moves_from(&board, player, pieces[i], moves);
|
||||
|
||||
printf("\nKnight at B2 moves:\n");
|
||||
bitboard_print(knight_attacks(SQUARE_MASK_B2), stdout);
|
||||
if (move_count > 0) {
|
||||
board_move_piece(&board, player, moves[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("\nKnight at A1, H8 moves:\n");
|
||||
bitboard_print(knight_attacks(SQUARE_MASK_A1 | SQUARE_MASK_H8), stdout);
|
||||
if (move_count == 0) {
|
||||
printf("no moves for %s, aborting\n", player_str[player]);
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\nWhite pawn at E4 moves:\n");
|
||||
bitboard_print(
|
||||
pawn_white_moves(
|
||||
/* p */ SQUARE_MASK_E4,
|
||||
/* empty */ ~0ULL
|
||||
),
|
||||
stdout);
|
||||
player = opposite_player(player);
|
||||
|
||||
printf("\nWhite pawns at E2, F4, A3 moves:\n");
|
||||
bitboard_print(
|
||||
pawn_white_moves(
|
||||
/* p */ SQUARE_MASK_E2 | SQUARE_MASK_F4 | SQUARE_MASK_A3,
|
||||
/* empty */ ~0ULL
|
||||
),
|
||||
stdout);
|
||||
usleep(300000);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user