Permalink
Cannot retrieve contributors at this time
#lang ceagle | |
// BEGIN C++ COMPATIBILITY | |
typedef unsigned __bits 64 uint64_t; | |
typedef unsigned int bool; | |
const bool false = 0; | |
const bool true = 1; | |
const int NEGATIVE_INFINITY = -1000000000; | |
// END C++ COMPATIBILITY | |
// bb is 'bitboard' | |
typedef struct gamestate { | |
uint64_t rooks_bb; | |
uint64_t knights_bb; | |
uint64_t bishops_bb; | |
uint64_t queens_bb; | |
uint64_t kings_bb; | |
uint64_t pawns_bb; | |
union { | |
uint64_t current_player_bb; | |
uint64_t current_piece_bb; // For iterators | |
}; | |
int en_passant_sq; | |
union { | |
uint64_t castle_flags; | |
uint64_t promotion_piece; // For iterators | |
}; | |
bool is_white; | |
} gamestate; | |
typedef struct move { | |
int from; | |
int to; | |
} move; | |
typedef gamestate iterator; | |
typedef int position; | |
const int PIECE_EMPTY = 0; | |
const int PIECE_ROOK = 1; | |
const int PIECE_KNIGHT = 2; | |
const int PIECE_BISHOP = 3; | |
const int PIECE_QUEEN = 4; | |
const int PIECE_KING = 5; | |
const int PIECE_PAWN = 6; | |
const int VALUE_PAWN = 100; | |
const int VALUE_KNIGHT = 300; | |
const int VALUE_BISHOP = 300; | |
const int VALUE_ROOK = 500; | |
const int VALUE_QUEEN = 900; | |
const int VALUE_AVAILABLE_MOVE = 5; // Points for each move available | |
const int VALUE_CHECKMATE = -1000000; | |
const int VALUE_NEGAMAX_START = NEGATIVE_INFINITY; | |
const int VALUE_CENTER = 10; // Points for holding the center | |
const int VALUE_ATTACK = 20; // Points for being able to attack enemy pieces. | |
const int DIRECTION_EAST = 0; | |
const int DIRECTION_WEST = 1; | |
const int DIRECTION_NORTH = 2; | |
const int DIRECTION_SOUTH = 3; | |
const int DIRECTION_NORTHEAST = 4; | |
const int DIRECTION_NORTHWEST = 5; | |
const int DIRECTION_SOUTHEAST = 6; | |
const int DIRECTION_SOUTHWEST = 7; | |
// CASTLE FLAGS | |
const int CASTLE_WHITE_KINGSIDE = 0x1; | |
const int CASTLE_WHITE_QUEENSIDE = 0x2; | |
const int CASTLE_BLACK_KINGSIDE = 0x4; | |
const int CASTLE_BLACK_QUEENSIDE = 0x8; | |
const int CASTLE_KINGSIDE_KPOS = 6; | |
const int CASTLE_QUEENSIDE_KPOS = 2; | |
const int CASTLE_KINGSIDE_RPOS = 5; | |
const int CASTLE_QUEENSIDE_RPOS = 3; | |
const int NUM_BOARD_SQUARES = 8*8; | |
const int RANK = 8; | |
const int POSITION_INVALID = 255; | |
const uint64_t RAY_A1A8 = 0x0101010101010101; | |
const uint64_t RAY_H1H8 = RAY_A1A8 << (RANK-1); | |
// Static functions | |
static uint64_t bit(uint64_t idx) { | |
if (idx >= 64 || idx < 0) { | |
return 0; | |
} | |
return ((uint64_t)(1) << idx); | |
} | |
static uint64_t clear_bit(uint64_t x, uint64_t idx) { return x & ~bit(idx); } | |
static bool is_bit_set(uint64_t x, uint64_t idx) { | |
if (idx >= 64) { return false; } | |
return x & bit(idx); | |
} | |
static uint64_t set_bit(uint64_t x, uint64_t idx) { return x | bit(idx); } | |
static uint64_t lsb_first_set(uint64_t x) { return __builtin_ctzll(x); } | |
static uint64_t msb_first_set(uint64_t x) { return (63 - __builtin_clzll(x)); } | |
static int rank(int x) { return x / 8; } | |
static int file(int x) { return x % 8; } | |
static uint64_t offset(uint64_t x, int os) | |
{ | |
if (os < 0) { | |
return (x >> -os); | |
} else { | |
return (x << os); | |
} | |
} | |
static gamestate zerostate() | |
{ | |
gamestate x; | |
x.current_piece_bb = 0; | |
x.rooks_bb = 0; | |
x.knights_bb = 0; | |
x.bishops_bb = 0; | |
x.queens_bb = 0; | |
x.kings_bb = 0; | |
x.pawns_bb = 0; | |
x.en_passant_sq = POSITION_INVALID; | |
x.castle_flags = 0; | |
x.is_white = true; | |
return x; | |
} | |
static uint64_t mkPosition(int file, int rank) | |
{ | |
return rank * RANK + file; | |
} | |
const uint64_t RAY_A1H8 = | |
bit(mkPosition(0,0)) | | |
bit(mkPosition(1,1)) | | |
bit(mkPosition(2,2)) | | |
bit(mkPosition(3,3)) | | |
bit(mkPosition(4,4)) | | |
bit(mkPosition(5,5)) | | |
bit(mkPosition(6,6)) | | |
bit(mkPosition(7,7)) | |
; | |
// https://chessprogramming.wikispaces.com/On+an+empty+Board#RayAttacks | |
static uint64_t diagonal_mask(int center) | |
{ | |
const uint64_t maindia = 0x8040201008040201; | |
int diag =8*(center & 7) - (center & 56); | |
int nort = -diag & ( diag >> 31); | |
int sout = diag & (-diag >> 31); | |
return (maindia >> sout) << nort; | |
} | |
static uint64_t antidiagonal_mask(int center) | |
{ | |
const uint64_t maindia = 0x0102040810204080; | |
int diag =56- 8*(center&7) - (center&56); | |
int nort = -diag & ( diag >> 31); | |
int sout = diag & (-diag >> 31); | |
return (maindia >> sout) << nort; | |
} | |
// https://chessprogramming.wikispaces.com/Flipping+Mirroring+and+Rotating | |
// See 'flipDiagA1H8' | |
static uint64_t flipDiagA1H8(uint64_t x) | |
{ | |
uint64_t t; | |
const uint64_t k1 = 0x5500550055005500; | |
const uint64_t k2 = 0x3333000033330000; | |
const uint64_t k4 = 0x0f0f0f0f00000000; | |
t = k4 & (x ^ (x << 28)); | |
x ^= t ^ (t >> 28) ; | |
t = k2 & (x ^ (x << 14)); | |
x ^= t ^ (t >> 14) ; | |
t = k1 & (x ^ (x << 7)); | |
x ^= t ^ (t >> 7) ; | |
return x; | |
} | |
static uint64_t mirror_horizontal (uint64_t x) { | |
const uint64_t k1 = 0x5555555555555555; | |
const uint64_t k2 = 0x3333333333333333; | |
const uint64_t k4 = 0x0f0f0f0f0f0f0f0f; | |
x = ((x >> 1) & k1) + 2*(x & k1); | |
x = ((x >> 2) & k2) + 4*(x & k2); | |
x = ((x >> 4) & k4) + 16*(x & k4); | |
return x; | |
} | |
static uint64_t flip_vertical(uint64_t x) | |
{ | |
return | |
( (x << 56) ) | | |
( (x << 40) & (0x00ff000000000000) ) | | |
( (x << 24) & (0x0000ff0000000000) ) | | |
( (x << 8) & (0x000000ff00000000) ) | | |
( (x >> 8) & (0x00000000ff000000) ) | | |
( (x >> 24) & (0x0000000000ff0000) ) | | |
( (x >> 40) & (0x000000000000ff00) ) | | |
( (x >> 56) ); | |
} | |
// We use a monochrome board, so white is always the one to move, and we flip the board after each move. | |
static uint64_t flip_bb(uint64_t x) | |
{ | |
return __builtin_bswap64(x); | |
//return mirror_horizontal(__builtin_bswap64(x)); | |
} | |
static uint64_t rotate_bb(uint64_t x) | |
{ | |
return flip_vertical(flipDiagA1H8(x)); | |
} | |
const uint64_t RAY_A8H1 = rotate_bb(RAY_A1H8); | |
static uint64_t get_piece_bb(gamestate x, int piece) | |
{ | |
switch (piece) { | |
case PIECE_ROOK: | |
return x.rooks_bb; | |
case PIECE_KNIGHT: | |
return x.knights_bb; | |
case PIECE_BISHOP: | |
return x.bishops_bb; | |
case PIECE_QUEEN: | |
return x.queens_bb; | |
case PIECE_KING: | |
return x.kings_bb; | |
case PIECE_PAWN: | |
return x.pawns_bb; | |
} | |
__builtin_trap(); | |
} | |
static gamestate set_piece_bb(gamestate x, int piece, uint64_t bb) | |
{ | |
switch (piece) { | |
case PIECE_ROOK: | |
x.rooks_bb = bb; | |
return x; | |
case PIECE_KNIGHT: | |
x.knights_bb = bb; | |
return x; | |
case PIECE_BISHOP: | |
x.bishops_bb = bb; | |
return x; | |
case PIECE_QUEEN: | |
x.queens_bb = bb; | |
return x; | |
case PIECE_KING: | |
x.kings_bb = bb; | |
return x; | |
case PIECE_PAWN: | |
x.pawns_bb = bb; | |
return x; | |
default: | |
__builtin_trap(); | |
} | |
} | |
static int next_iterator_piece(iterator x) | |
{ | |
if (x.rooks_bb) | |
return PIECE_ROOK; | |
if (x.knights_bb) | |
return PIECE_KNIGHT; | |
if (x.bishops_bb) | |
return PIECE_BISHOP; | |
if (x.queens_bb) | |
return PIECE_QUEEN; | |
if (x.kings_bb) | |
return PIECE_KING; | |
if (x.pawns_bb) | |
return PIECE_PAWN; | |
return PIECE_EMPTY; | |
} | |
static int iterator_position(iterator x) | |
{ | |
int piece = next_iterator_piece(x); | |
if (piece == PIECE_EMPTY) { | |
return POSITION_INVALID; | |
} | |
uint64_t piece_bb = get_piece_bb(x, piece); | |
int idx = lsb_first_set(piece_bb); | |
return idx; | |
} | |
static int promotion_piece(move m) | |
{ | |
return (m.to >> 6); | |
} | |
static move dereference_iterator(iterator i) | |
{ | |
move m; | |
m.from = iterator_position(i); | |
m.to = lsb_first_set(i.current_piece_bb); | |
m.to |= (i.promotion_piece << 6); | |
return m; | |
} | |
static uint64_t advance_bb_iterator(uint64_t x) | |
{ | |
return x & (x-1); | |
} | |
static bool is_iterator_finished(iterator x) | |
{ | |
return | |
! x.rooks_bb && | |
! x.knights_bb && | |
! x.bishops_bb && | |
! x.queens_bb && | |
! x.kings_bb && | |
! x.pawns_bb && | |
! x.current_piece_bb | |
; | |
} | |
// When a piece is moving west, it may wrap around to be east. | |
// So we mask out the eastmost column, since it's impossible anyways. | |
static uint64_t guard_west(uint64_t x) { | |
return x & ~RAY_H1H8; | |
} | |
static uint64_t guard_east(uint64_t x) { | |
return x & ~RAY_A1A8; | |
} | |
static int move_direction(int idx, int direction) | |
{ | |
if (idx < 0 || idx >= 64) { | |
return POSITION_INVALID; | |
} | |
switch (direction) { | |
case DIRECTION_EAST: | |
if (idx % 8 == 7) | |
return POSITION_INVALID; | |
return idx + 1; | |
case DIRECTION_WEST: | |
if (idx % 8 == 0) | |
return POSITION_INVALID; | |
return idx - 1; | |
case DIRECTION_NORTH: | |
return idx + RANK; | |
case DIRECTION_SOUTH: | |
return idx - RANK; | |
case DIRECTION_NORTHEAST: | |
if (file(idx) == 7) { | |
return POSITION_INVALID; | |
} | |
return (idx + RANK + 1); | |
case DIRECTION_NORTHWEST: | |
if (file(idx) == 0) { | |
return POSITION_INVALID; | |
} | |
return (idx + RANK - 1); | |
case DIRECTION_SOUTHEAST: | |
if (file(idx) == 7) { | |
return POSITION_INVALID; | |
} | |
return (idx - RANK + 1); | |
case DIRECTION_SOUTHWEST: | |
if (file(idx) == 0) { | |
return POSITION_INVALID; | |
} | |
return (idx - RANK - 1); | |
default: | |
__builtin_trap(); | |
} | |
} | |
static uint64_t all_pieces(gamestate x) | |
{ | |
return | |
x.rooks_bb | | |
x.knights_bb | | |
x.bishops_bb | | |
x.queens_bb | | |
x.kings_bb | | |
x.pawns_bb | |
; | |
} | |
static uint64_t enemy_pieces(gamestate x) | |
{ | |
return all_pieces(x) ^ x.current_player_bb; | |
} | |
static uint64_t valid_pawn_moves(gamestate x, int center) | |
{ | |
uint64_t moves_bb = 0; | |
// Non-captures | |
if (! is_bit_set(all_pieces(x), center + RANK)) { | |
uint64_t noncaptures_bb = bit(center + RANK); | |
uint64_t pcs = all_pieces(x); | |
if (rank(center) == 1 && ! is_bit_set(pcs, center + RANK) && ! is_bit_set(pcs, center + RANK*2)) { | |
noncaptures_bb |= bit(center + RANK + RANK); | |
} | |
moves_bb |= noncaptures_bb; | |
} | |
// Captures | |
{ | |
uint64_t captures_bb = 0; | |
if (file(center) > 0) { | |
captures_bb |= bit(move_direction(center + RANK, DIRECTION_WEST)); | |
} | |
if (file(center) < 7) { | |
captures_bb |= bit(move_direction(center + RANK, DIRECTION_EAST)); | |
} | |
captures_bb &= all_pieces(x) ^ x.current_player_bb; | |
if ((file(center) < 7 && x.en_passant_sq == center + RANK + 1) || | |
(file(center) > 0 && x.en_passant_sq == center + RANK - 1)) { | |
captures_bb |= bit(x.en_passant_sq); | |
} | |
moves_bb |= captures_bb; | |
} | |
return moves_bb; | |
} | |
static uint64_t valid_knight_moves(gamestate x, int idx) | |
{ | |
uint64_t moves_bb = 0; | |
moves_bb |= guard_west(bit(move_direction(idx + 2 * RANK, DIRECTION_WEST))); | |
moves_bb |= guard_east(bit(move_direction(idx + 2 * RANK, DIRECTION_EAST))); | |
moves_bb |= guard_west(bit(move_direction(idx - 2 * RANK, DIRECTION_WEST))); | |
moves_bb |= guard_east(bit(move_direction(idx - 2 * RANK, DIRECTION_EAST))); | |
moves_bb |= bit(move_direction(move_direction(move_direction(idx, DIRECTION_EAST), DIRECTION_EAST), DIRECTION_NORTH)); | |
moves_bb |= bit(move_direction(move_direction(move_direction(idx, DIRECTION_EAST), DIRECTION_EAST), DIRECTION_SOUTH)); | |
moves_bb |= bit(move_direction(move_direction(move_direction(idx, DIRECTION_WEST), DIRECTION_WEST), DIRECTION_NORTH)); | |
moves_bb |= bit(move_direction(move_direction(move_direction(idx, DIRECTION_WEST), DIRECTION_WEST), DIRECTION_SOUTH)); | |
moves_bb &= ~x.current_player_bb; | |
return moves_bb; | |
} | |
static uint64_t bitrange(int start, int end) | |
{ | |
uint64_t end_bb = bit(end+1)-1; | |
uint64_t start_bb = bit(start)-1; | |
return end_bb ^ start_bb; | |
} | |
static uint64_t mkRay(int center, int direction) | |
{ | |
int rOs, fOs; | |
switch (direction) { | |
case DIRECTION_NORTHWEST: rOs = 1; fOs = -1; break; | |
case DIRECTION_NORTH: rOs = 1; fOs = 0; break; | |
case DIRECTION_NORTHEAST: rOs = 1; fOs = 1; break; | |
case DIRECTION_EAST: rOs = 0; fOs = 1; break; | |
case DIRECTION_SOUTHEAST: rOs = -1; fOs = 1; break; | |
case DIRECTION_SOUTH: rOs = -1; fOs = 0; break; | |
case DIRECTION_SOUTHWEST: rOs = -1; fOs = -1; break; | |
case DIRECTION_WEST: rOs = 0; fOs = -1; break; | |
default: __builtin_trap(); | |
} | |
uint64_t ray; | |
uint64_t next = bit(center); | |
do { | |
ray = next; | |
next = offset(next, RANK * rOs); | |
next = offset(next, fOs); | |
switch (fOs) { | |
case 1: next &= ~RAY_A1A8; break; | |
case -1: next &= ~RAY_H1H8; break; | |
default: break; | |
} | |
next |= ray; | |
} while (ray != next); | |
ray = clear_bit(ray, center); | |
return ray; | |
} | |
static int closest_blocker(uint64_t blockers_ray, int direction) | |
{ | |
if (blockers_ray == 0) | |
return POSITION_INVALID; | |
switch (direction) { | |
case DIRECTION_NORTHWEST: | |
case DIRECTION_NORTH: | |
case DIRECTION_NORTHEAST: | |
case DIRECTION_EAST: | |
return lsb_first_set(blockers_ray); | |
case DIRECTION_SOUTHEAST: | |
case DIRECTION_SOUTH: | |
case DIRECTION_SOUTHWEST: | |
case DIRECTION_WEST: | |
return msb_first_set(blockers_ray); | |
default: | |
__builtin_trap(); | |
} | |
} | |
static uint64_t shoot_ray_until_blocker(gamestate state, int idx, int direction) | |
{ | |
uint64_t pieces = all_pieces(state); | |
uint64_t base_ray = mkRay(idx, direction); | |
uint64_t blockers = base_ray & pieces; | |
int blocker = closest_blocker(blockers, direction); | |
if (blocker == POSITION_INVALID) { | |
return base_ray; | |
} else { | |
uint64_t blocker_ray = mkRay(blocker, direction); | |
uint64_t movable_squares_without_capture = base_ray ^ blocker_ray; | |
bool allow_capture = ! is_bit_set(state.current_player_bb, blocker); | |
if (allow_capture) | |
return movable_squares_without_capture | bit(blocker); | |
else | |
return movable_squares_without_capture & ~bit(blocker); | |
} | |
} | |
static uint64_t valid_bishop_moves(gamestate x, int idx) | |
{ | |
return | |
shoot_ray_until_blocker(x, idx, DIRECTION_NORTHEAST) | | |
shoot_ray_until_blocker(x, idx, DIRECTION_NORTHWEST) | | |
shoot_ray_until_blocker(x, idx, DIRECTION_SOUTHEAST) | | |
shoot_ray_until_blocker(x, idx, DIRECTION_SOUTHWEST) | |
; | |
} | |
static uint64_t valid_rook_moves(gamestate x, int idx) | |
{ | |
return | |
shoot_ray_until_blocker(x, idx, DIRECTION_NORTH) | | |
shoot_ray_until_blocker(x, idx, DIRECTION_WEST) | | |
shoot_ray_until_blocker(x, idx, DIRECTION_EAST) | | |
shoot_ray_until_blocker(x, idx, DIRECTION_SOUTH) | |
; | |
} | |
static uint64_t valid_king_moves(gamestate g, int idx) | |
{ | |
uint64_t ret = | |
bit(move_direction(idx, DIRECTION_EAST)) | | |
bit(move_direction(idx, DIRECTION_WEST)) | | |
bit(move_direction(idx, DIRECTION_NORTH)) | | |
bit(move_direction(idx, DIRECTION_SOUTH)) | | |
bit(move_direction(idx, DIRECTION_NORTHEAST)) | | |
bit(move_direction(idx, DIRECTION_SOUTHEAST)) | | |
bit(move_direction(idx, DIRECTION_NORTHWEST)) | | |
bit(move_direction(idx, DIRECTION_SOUTHWEST)) | |
; | |
if ((g.castle_flags & CASTLE_WHITE_KINGSIDE)) { | |
uint64_t pcs = all_pieces(g); | |
if (is_bit_set(pcs, CASTLE_KINGSIDE_KPOS) || | |
is_bit_set(pcs, CASTLE_KINGSIDE_RPOS)) { | |
goto skip_kingside; | |
} | |
ret |= bit(CASTLE_KINGSIDE_KPOS); | |
} | |
skip_kingside: | |
if ((g.castle_flags & CASTLE_WHITE_QUEENSIDE)) { | |
uint64_t pcs = all_pieces(g); | |
if (is_bit_set(pcs, CASTLE_QUEENSIDE_RPOS) || | |
is_bit_set(pcs, CASTLE_QUEENSIDE_KPOS) || | |
is_bit_set(pcs, CASTLE_QUEENSIDE_KPOS-1)) { | |
goto skip_queenside; | |
} | |
ret |= bit(CASTLE_QUEENSIDE_KPOS); | |
} | |
skip_queenside: | |
ret &= ~ g.current_piece_bb; | |
return ret; | |
} | |
static uint64_t valid_queen_moves(gamestate x, int idx) | |
{ | |
return | |
valid_bishop_moves(x, idx) | | |
valid_rook_moves(x, idx) | |
; | |
} | |
static int get_piece(gamestate x, int idx) | |
{ | |
if (is_bit_set(x.rooks_bb, idx)) | |
return PIECE_ROOK; | |
if (is_bit_set(x.knights_bb, idx)) | |
return PIECE_KNIGHT; | |
if (is_bit_set(x.bishops_bb, idx)) | |
return PIECE_BISHOP; | |
if (is_bit_set(x.queens_bb, idx)) | |
return PIECE_QUEEN; | |
if (is_bit_set(x.kings_bb, idx)) | |
return PIECE_KING; | |
if (is_bit_set(x.pawns_bb, idx)) | |
return PIECE_PAWN; | |
return PIECE_EMPTY; | |
} | |
static uint64_t valid_piece_moves(gamestate x, int idx) | |
{ | |
int piece = get_piece(x, idx); | |
switch (piece) { | |
case PIECE_ROOK: | |
return valid_rook_moves(x, idx); | |
case PIECE_KNIGHT: | |
return valid_knight_moves(x, idx); | |
case PIECE_BISHOP: | |
return valid_bishop_moves(x, idx); | |
case PIECE_QUEEN: | |
return valid_queen_moves(x, idx); | |
case PIECE_KING: | |
return valid_king_moves(x, idx); | |
case PIECE_PAWN: | |
return valid_pawn_moves(x, idx); | |
default: | |
__builtin_trap(); | |
} | |
} | |
static iterator reset_iterator_promotion_piece(gamestate g, iterator i) | |
{ | |
if (is_iterator_finished(i)) { | |
return i; | |
} | |
int idx = iterator_position(i); | |
int piece = get_piece(g, idx); | |
if (piece == PIECE_PAWN && | |
rank(idx) == 6 && | |
i.current_piece_bb != 0) { | |
i.promotion_piece = PIECE_QUEEN; | |
} | |
return i; | |
} | |
static iterator reset_iterator_moves(gamestate g, iterator i) | |
{ | |
if (is_iterator_finished(i)) { | |
return zerostate(); | |
} else { | |
int idx = iterator_position(i); | |
uint64_t moves = valid_piece_moves(g, idx); | |
i.current_piece_bb = moves; | |
i = reset_iterator_promotion_piece(g, i); | |
return i; | |
} | |
} | |
static iterator advance_iterator(gamestate g, iterator i) | |
{ | |
if (i.current_piece_bb) { | |
if (i.promotion_piece) { | |
i.promotion_piece--; | |
} | |
if (!i.promotion_piece) { | |
i.current_piece_bb = advance_bb_iterator(i.current_piece_bb); | |
i = reset_iterator_promotion_piece(g, i); | |
} | |
} | |
while (! is_iterator_finished(i) && ! i.current_piece_bb) { | |
if (i.rooks_bb) { | |
i.rooks_bb = advance_bb_iterator(i.rooks_bb); | |
} else if (i.knights_bb) { | |
i.knights_bb = advance_bb_iterator(i.knights_bb); | |
} else if (i.bishops_bb) { | |
i.bishops_bb = advance_bb_iterator(i.bishops_bb); | |
} else if (i.queens_bb) { | |
i.queens_bb = advance_bb_iterator(i.queens_bb); | |
} else if (i.kings_bb) { | |
i.kings_bb = advance_bb_iterator(i.kings_bb); | |
} else if (i.pawns_bb) { | |
i.pawns_bb = advance_bb_iterator(i.pawns_bb); | |
} | |
i = reset_iterator_moves(g, i); | |
} | |
if (is_iterator_finished(i) && i.current_piece_bb != 0) { | |
__builtin_trap(); | |
} | |
return i; | |
} | |
static gamestate switch_sides(gamestate x) | |
{ | |
uint64_t enemy_pieces_bb = enemy_pieces(x); | |
x.current_player_bb = enemy_pieces_bb; | |
x.rooks_bb = __builtin_bswap64(x.rooks_bb); | |
x.knights_bb = __builtin_bswap64(x.knights_bb); | |
x.bishops_bb = __builtin_bswap64(x.bishops_bb); | |
x.queens_bb = __builtin_bswap64(x.queens_bb); | |
x.kings_bb = __builtin_bswap64(x.kings_bb); | |
x.pawns_bb = __builtin_bswap64(x.pawns_bb); | |
return x; | |
} | |
static iterator mkIterator(gamestate g) | |
{ | |
iterator x = g; | |
x.rooks_bb &= x.current_player_bb; | |
x.knights_bb &= x.current_player_bb; | |
x.bishops_bb &= x.current_player_bb; | |
x.queens_bb &= x.current_player_bb; | |
x.kings_bb &= x.current_player_bb; | |
x.pawns_bb &= x.current_player_bb; | |
x.promotion_piece = 0; | |
x = reset_iterator_moves(g, x); | |
if (! x.current_piece_bb) { | |
x = advance_iterator(g, x); | |
} | |
return x; | |
} | |
static uint64_t all_rotations(uint64_t bb) | |
{ | |
bb |= rotate_bb(bb); | |
bb |= rotate_bb(bb); | |
bb |= rotate_bb(bb); | |
return bb; | |
} | |
static gamestate new_game() | |
{ | |
gamestate x; | |
x.rooks_bb = | |
bit(mkPosition(0,0)) | | |
bit(mkPosition(7,0)) | | |
bit(mkPosition(0,7)) | | |
bit(mkPosition(7,7)); | |
x.knights_bb = | |
bit(mkPosition(1,0)) | | |
bit(mkPosition(6,0)) | | |
bit(mkPosition(1,7)) | | |
bit(mkPosition(6,7)); | |
x.bishops_bb = | |
bit(mkPosition(2,0)) | | |
bit(mkPosition(5,0)) | | |
bit(mkPosition(2,7)) | | |
bit(mkPosition(5,7)); | |
x.queens_bb = | |
bit(mkPosition(3,0)) | | |
bit(mkPosition(3,7)); | |
x.kings_bb = | |
bit(mkPosition(4,0)) | | |
bit(mkPosition(4,7)); | |
x.pawns_bb = | |
((uint64_t)0xFF << RANK) | | |
((uint64_t)0xFF << (6*RANK)); | |
x.current_player_bb = 0xFFFF; | |
x.en_passant_sq = POSITION_INVALID; | |
x.castle_flags = 0xF; | |
x.is_white = true; | |
return x; | |
} | |
// Flips the board so white is black and black is white. | |
// In our model, it is always white's turn to move. | |
static gamestate swap_board(gamestate g) | |
{ | |
g.current_player_bb ^= all_pieces(g); | |
g.rooks_bb = flip_bb(g.rooks_bb); | |
g.knights_bb = flip_bb(g.knights_bb); | |
g.bishops_bb = flip_bb(g.bishops_bb); | |
g.queens_bb = flip_bb(g.queens_bb); | |
g.kings_bb = flip_bb(g.kings_bb); | |
g.pawns_bb = flip_bb(g.pawns_bb); | |
g.current_player_bb = flip_bb(g.current_player_bb); | |
if (g.en_passant_sq != POSITION_INVALID) { | |
g.en_passant_sq = mkPosition(file(g.en_passant_sq), 7 - rank(g.en_passant_sq)); | |
} | |
{ | |
int flags = 0; | |
if (g.castle_flags & CASTLE_WHITE_KINGSIDE) | |
flags |= CASTLE_BLACK_KINGSIDE; | |
if (g.castle_flags & CASTLE_WHITE_QUEENSIDE) | |
flags |= CASTLE_BLACK_QUEENSIDE; | |
if (g.castle_flags & CASTLE_BLACK_KINGSIDE) | |
flags |= CASTLE_WHITE_KINGSIDE; | |
if (g.castle_flags & CASTLE_BLACK_QUEENSIDE) | |
flags |= CASTLE_WHITE_QUEENSIDE; | |
g.castle_flags = flags; | |
} | |
return g; | |
} | |
static move swap_move(move m) | |
{ | |
m.from = mkPosition(file(m.from), 7 - rank(m.from)); | |
m.to = mkPosition(file(m.to), 7 - rank(m.to)); | |
return m; | |
} | |
static bool is_kingside_castle(gamestate g, move m) | |
{ | |
int from_piece = get_piece(g, m.from); | |
return | |
from_piece == PIECE_KING && | |
m.to == CASTLE_KINGSIDE_KPOS && | |
(g.castle_flags & CASTLE_WHITE_KINGSIDE); | |
} | |
static bool is_queenside_castle(gamestate g, move m) | |
{ | |
int from_piece = get_piece(g, m.from); | |
return | |
from_piece == PIECE_KING && | |
m.to == CASTLE_QUEENSIDE_KPOS && | |
(g.castle_flags & CASTLE_WHITE_QUEENSIDE); | |
} | |
static bool is_promotion(move m) { return promotion_piece(m) == PIECE_EMPTY; } | |
static gamestate apply_move(gamestate g, move m) | |
{ | |
gamestate g_orig = g; | |
int from_piece = get_piece(g, m.from); | |
int prom_piece = promotion_piece(m); | |
m.to &= 0x3F; | |
int to_piece = get_piece(g, m.to); | |
if (to_piece != PIECE_EMPTY) { | |
uint64_t to_bb = get_piece_bb(g, to_piece); | |
g = set_piece_bb(g, to_piece, clear_bit(to_bb, m.to)); | |
} | |
// Non-promotion moves | |
if (prom_piece == PIECE_EMPTY) { | |
uint64_t from_bb = get_piece_bb(g, from_piece); | |
g = set_piece_bb(g, from_piece, clear_bit(from_bb, m.from) | bit(m.to)); | |
// Promotions | |
} else { | |
uint64_t piece_bb = get_piece_bb(g, prom_piece); | |
g.pawns_bb = clear_bit(g.pawns_bb, m.from); | |
g = set_piece_bb(g, prom_piece, piece_bb | bit(m.to)); | |
} | |
// Capture the pawn properly during En Passant capture | |
if (from_piece == PIECE_PAWN && m.to == g.en_passant_sq) { | |
g.pawns_bb = clear_bit(g.pawns_bb, g.en_passant_sq - RANK); | |
} | |
// Set En Passant target square on double-jump | |
if (from_piece == PIECE_PAWN && rank(m.to) - rank(m.from) == 2) { | |
g.en_passant_sq = m.from + RANK; | |
} else { | |
g.en_passant_sq = POSITION_INVALID; | |
} | |
// Check for kingside castle. | |
if (is_kingside_castle(g_orig, m)) { | |
g.castle_flags &= ~CASTLE_WHITE_KINGSIDE; | |
g.rooks_bb = clear_bit(g.rooks_bb, mkPosition(7,0)); | |
g.current_piece_bb = clear_bit(g.current_piece_bb, mkPosition(7,0)); | |
g.rooks_bb = set_bit(g.rooks_bb, CASTLE_KINGSIDE_RPOS); | |
g.current_piece_bb = set_bit(g.current_piece_bb, CASTLE_KINGSIDE_RPOS); | |
} | |
// Check for queenside castle | |
if (is_queenside_castle(g_orig,m)) { | |
g.castle_flags &= ~CASTLE_WHITE_QUEENSIDE; | |
g.rooks_bb = clear_bit(g.rooks_bb, mkPosition(0,0)); | |
g.current_piece_bb = clear_bit(g.current_piece_bb, mkPosition(0,0)); | |
g.rooks_bb = set_bit(g.rooks_bb, CASTLE_QUEENSIDE_RPOS); | |
g.current_piece_bb = set_bit(g.current_piece_bb, CASTLE_QUEENSIDE_RPOS); | |
} | |
// Clear kingside rook flags | |
if (g.castle_flags & CASTLE_WHITE_KINGSIDE && | |
from_piece == PIECE_ROOK && | |
m.from == mkPosition(7,0)) { | |
g.castle_flags &= ~CASTLE_WHITE_KINGSIDE; | |
} | |
// Clear queenside rook flags | |
if (g.castle_flags & CASTLE_WHITE_QUEENSIDE && | |
from_piece == PIECE_ROOK && | |
m.from == mkPosition(0,0)) { | |
g.castle_flags &= ~CASTLE_WHITE_QUEENSIDE; | |
} | |
// Clear eaten kingside rook flags | |
if (to_piece == PIECE_ROOK && | |
m.to == mkPosition(7,7)) { | |
g.castle_flags &= ~CASTLE_BLACK_KINGSIDE; | |
} | |
// Clear eaten queenside rook flags | |
if (to_piece == PIECE_ROOK && | |
m.to == mkPosition(0,7)) { | |
g.castle_flags &= ~CASTLE_BLACK_QUEENSIDE; | |
} | |
// Clear king's castling flags | |
if (from_piece == PIECE_KING) { | |
g.castle_flags &= ~(CASTLE_WHITE_KINGSIDE | CASTLE_WHITE_QUEENSIDE); | |
} | |
// Set colors | |
g.current_player_bb = clear_bit(g.current_player_bb, m.from) | bit(m.to); | |
g.is_white = ! g.is_white; | |
g = swap_board(g); | |
return g; | |
} | |
static uint64_t mkRank(int idx) { return ((uint64_t)0xFF << (idx*RANK)); } | |
static uint64_t mkFile(int idx) { | |
return | |
bit(mkPosition(idx,0)) | | |
bit(mkPosition(idx,1)) | | |
bit(mkPosition(idx,2)) | | |
bit(mkPosition(idx,3)) | | |
bit(mkPosition(idx,4)) | | |
bit(mkPosition(idx,5)) | | |
bit(mkPosition(idx,6)) | | |
bit(mkPosition(idx,7)); | |
} | |
static bool iter_lt(iterator x, iterator y) | |
{ | |
return | |
(x.rooks_bb < y.rooks_bb || | |
(x.rooks_bb == y.rooks_bb && | |
(x.knights_bb < y.knights_bb || | |
(x.knights_bb == y.knights_bb && | |
(x.bishops_bb < y.bishops_bb || | |
(x.bishops_bb == y.bishops_bb && | |
(x.queens_bb < y.queens_bb || | |
(x.queens_bb == y.queens_bb && | |
(x.kings_bb < y.kings_bb || | |
(x.kings_bb == y.kings_bb && | |
(x.pawns_bb < y.pawns_bb || | |
(x.pawns_bb == y.pawns_bb && | |
(x.current_piece_bb < y.current_piece_bb || | |
(x.current_piece_bb == y.current_piece_bb && | |
(x.promotion_piece < y.promotion_piece))))))))))))))); | |
} | |
static uint64_t movepoints(gamestate g) | |
{ | |
uint64_t movepoints = 0; | |
iterator i = mkIterator(g); | |
while (! is_iterator_finished(i)) { | |
movepoints |= i.current_piece_bb; | |
i.current_piece_bb = 0; | |
i = advance_iterator(g, i); | |
} | |
return movepoints; | |
} | |
static uint64_t vulnerables(gamestate g) | |
{ | |
g = swap_board(g); | |
uint64_t ret = movepoints(g) & enemy_pieces(g); | |
ret = flip_bb(ret); | |
return ret; | |
} | |
static bool is_in_check(gamestate g) | |
{ | |
uint64_t white_kings = g.kings_bb & g.current_player_bb; | |
return ((white_kings & vulnerables(g)) != 0); | |
} | |
static bool results_in_check(gamestate g, move m) | |
{ | |
gamestate g2 = swap_board(apply_move(g,m)); | |
return is_in_check(g2); | |
} | |
static bool is_legal(gamestate g, move m) | |
{ | |
if (results_in_check(g,m)) { | |
return false; | |
} | |
move kingside_castle_part; kingside_castle_part.from = mkPosition(4,0); kingside_castle_part.to = mkPosition(5,0); | |
move queenside_castle_part; queenside_castle_part.from = mkPosition(4,0); queenside_castle_part.to = mkPosition(3,0); | |
bool valid_kingside_castle = | |
is_kingside_castle(g, m) | |
? (! results_in_check(g, kingside_castle_part) && | |
! is_in_check(g)) | |
: true; | |
bool valid_queenside_castle = | |
is_queenside_castle(g, m) | |
? (! results_in_check(g, queenside_castle_part) && | |
! is_in_check(g)) | |
: true; | |
return | |
valid_kingside_castle && | |
valid_queenside_castle; | |
} | |
// Like advance_iterator, but skip moves that leave the king in check | |
static iterator advance_iterator_legal(gamestate g, iterator i) | |
{ | |
while (1) { | |
i = advance_iterator(g, i); | |
if (is_iterator_finished(i)) { | |
break; | |
} | |
move m = dereference_iterator(i); | |
if (is_legal(g, m)) { | |
break; | |
} | |
} | |
return i; | |
} | |
static iterator mkLegalIterator(gamestate g) | |
{ | |
iterator i = mkIterator(g); | |
move m = dereference_iterator(i); | |
if (! is_legal(g, m)) { | |
i = advance_iterator_legal(g, i); | |
} | |
return i; | |
} | |
static uint64_t legal_movepoints(gamestate g) | |
{ | |
uint64_t movepoints = 0; | |
iterator i = mkLegalIterator(g); | |
while (! is_iterator_finished(i)) { | |
move m = dereference_iterator(i); | |
movepoints |= bit(m.to); | |
i = advance_iterator_legal(g, i); | |
} | |
return movepoints; | |
} | |
static int num_legal_moves(gamestate g) | |
{ | |
iterator i = mkLegalIterator(g); | |
int count = 0; | |
while (! is_iterator_finished(i)) { | |
i = advance_iterator_legal(g, i); | |
count++; | |
} | |
return count; | |
} | |
static iterator legalize(gamestate g, iterator i) | |
{ | |
while (! is_iterator_finished(i)) { | |
move m = dereference_iterator(i); | |
if (is_legal(g, m)) { | |
break; | |
} | |
i = advance_iterator(g, i); | |
} | |
return i; | |
} | |
static uint64_t piece_legal_movepoints(gamestate g, int idx) | |
{ | |
iterator i = zerostate(); | |
int piece = get_piece(g, idx); | |
i = set_piece_bb(i, piece, bit(idx)); | |
i = reset_iterator_moves(g, i); | |
i = legalize(g, i); | |
uint64_t ret = 0; | |
while (! is_iterator_finished(i)) { | |
move m = dereference_iterator(i); | |
ret |= bit(m.to); | |
i = advance_iterator_legal(g, i); | |
} | |
return ret; | |
} | |
static move mkPromotion(int from, int piece) | |
{ | |
if (rank(from) != 6) | |
__builtin_trap(); | |
move m; | |
m.from = from; | |
m.to = from + RANK; | |
m.to |= piece << 6; | |
return m; | |
} | |
// Public functions | |
// we could use __builtin_popcountl, but we'd need -march=native to eliminate library call which is less portable. | |
static uint64_t popcount64(uint64_t x) | |
{ | |
x = (x & 0x5555555555555555ULL) + ((x >> 1) & 0x5555555555555555ULL); | |
x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); | |
x = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x >> 4) & 0x0F0F0F0F0F0F0F0FULL); | |
return (x * 0x0101010101010101ULL) >> 56; | |
} | |
static int num_bits(uint64_t x) { return popcount64(x); } | |
static int score_pieces(gamestate g) | |
{ | |
return | |
num_bits(g.pawns_bb & g.current_player_bb) * VALUE_PAWN + | |
num_bits(g.knights_bb & g.current_player_bb) * VALUE_KNIGHT + | |
num_bits(g.bishops_bb & g.current_player_bb) * VALUE_BISHOP + | |
num_bits(g.rooks_bb & g.current_player_bb) * VALUE_ROOK + | |
num_bits(g.queens_bb & g.current_player_bb) * VALUE_QUEEN; | |
} | |
static int score_availability(gamestate g) | |
{ | |
int num_moves = num_legal_moves(g); | |
if (num_moves == 0) | |
return VALUE_CHECKMATE; | |
return VALUE_AVAILABLE_MOVE * num_moves; | |
} | |
static int score_attacking(gamestate g) | |
{ | |
return num_bits(enemy_pieces(g) & movepoints(g)); | |
} | |
static uint64_t center() | |
{ | |
return | |
bit(mkPosition(2,2)) | | |
bit(mkPosition(2,3)) | | |
bit(mkPosition(2,4)) | | |
bit(mkPosition(2,5)) | | |
bit(mkPosition(3,2)) | | |
bit(mkPosition(3,3)) | | |
bit(mkPosition(3,4)) | | |
bit(mkPosition(3,5)) | | |
bit(mkPosition(4,2)) | | |
bit(mkPosition(4,3)) | | |
bit(mkPosition(4,4)) | | |
bit(mkPosition(4,5)) | | |
bit(mkPosition(5,2)) | | |
bit(mkPosition(5,3)) | | |
bit(mkPosition(5,4)) | | |
bit(mkPosition(5,5)); | |
} | |
static int score_center(gamestate g) | |
{ | |
int num_centers = num_bits(g.current_player_bb & center()); | |
return num_centers * VALUE_CENTER; | |
} | |
static int evaluate(gamestate g) | |
{ | |
return score_pieces(g) + score_availability(g) + score_attacking(g); | |
} | |
typedef struct negamax_ret { | |
move m; | |
int score; | |
} negamax_ret; | |
static int negamax(gamestate g, int depth, int color) | |
{ | |
if (depth == 0) | |
return color*evaluate(g); | |
int max = VALUE_NEGAMAX_START; | |
for (iterator i = mkLegalIterator(g); ! is_iterator_finished(i); i = advance_iterator_legal(g, i)) { | |
move m = dereference_iterator(i); | |
gamestate g2 = apply_move(g, m); | |
int score = -negamax(g2, depth-1, color*-1); | |
if (score > max) | |
max = score; | |
} | |
return max; | |
} | |
static move best_move(gamestate g, int depth) | |
{ | |
int max = VALUE_NEGAMAX_START; | |
move ret; ret.from = POSITION_INVALID; ret.to = POSITION_INVALID; | |
for (iterator i = mkLegalIterator(g); ! is_iterator_finished(i); i = advance_iterator_legal(g, i)) { | |
move m = dereference_iterator(i); | |
gamestate g2 = apply_move(g, m); | |
int score = -negamax(g2, depth, -1); | |
if (score > max) { | |
max = score; | |
ret = m; | |
} | |
} | |
return ret; | |
} | |
static uint64_t perft(gamestate g, int depth) | |
{ | |
if (depth == 0) { return 1; } | |
uint64_t n = 0; | |
for (iterator i = mkLegalIterator(g); ! is_iterator_finished(i); i = advance_iterator_legal(g, i)) { | |
move m = dereference_iterator(i); | |
gamestate g2 = apply_move(g, m); | |
n += perft(g2, depth-1); | |
} | |
return n; | |
} | |
typedef struct packed_move { | |
union { | |
move m; | |
uint64_t packed; | |
}; | |
} packed_move; | |
static gamestate parse_fen(const char* s) | |
{ | |
const char *orig_s = s; | |
// Pieces | |
gamestate g = zerostate(); | |
for (int r = 8; r --> 0;) { | |
for (int f = 0;; f++) { | |
int pos = mkPosition(f,r); | |
char c = *s++; | |
switch (c) { | |
case 'R': g.current_piece_bb |= bit(pos); | |
case 'r': g.rooks_bb |= bit(pos); break; | |
case 'N': g.current_piece_bb |= bit(pos); | |
case 'n': g.knights_bb |= bit(pos); break; | |
case 'B': g.current_piece_bb |= bit(pos); | |
case 'b': g.bishops_bb |= bit(pos); break; | |
case 'Q': g.current_piece_bb |= bit(pos); | |
case 'q': g.queens_bb |= bit(pos); break; | |
case 'K': g.current_piece_bb |= bit(pos); | |
case 'k': g.kings_bb |= bit(pos); break; | |
case 'P': g.current_piece_bb |= bit(pos); | |
case 'p': g.pawns_bb |= bit(pos); break; | |
case '8': f++; | |
case '7': f++; | |
case '6': f++; | |
case '5': f++; | |
case '4': f++; | |
case '3': f++; | |
case '2': f++; | |
case '1': break; | |
case '/': goto end_row; | |
case ' ': goto end_loop; | |
default: __builtin_trap(); goto end_loop; | |
} | |
} | |
end_row:; | |
} | |
end_loop: | |
// Color | |
switch (*s++) { | |
case 'w': g.is_white = true; break; | |
case 'b': g.is_white = false; break; | |
default: __builtin_trap(); | |
} | |
s++; | |
// Castle flags | |
while (*s != ' ') { | |
switch (*s++) { | |
case 'K': g.castle_flags |= CASTLE_WHITE_KINGSIDE; break; | |
case 'Q': g.castle_flags |= CASTLE_WHITE_QUEENSIDE; break; | |
case 'k': g.castle_flags |= CASTLE_BLACK_KINGSIDE; break; | |
case 'q': g.castle_flags |= CASTLE_BLACK_QUEENSIDE; break; | |
case '-': break; | |
default: __builtin_trap(); | |
} | |
} | |
if (!g.is_white) { | |
g = swap_board(g); | |
} | |
return g; | |
} | |
static char piece_char(int p, bool is_white) { | |
switch (p) { | |
case PIECE_EMPTY: return '.'; | |
case PIECE_ROOK: return is_white ? 'R' : 'r'; | |
case PIECE_KNIGHT: return is_white ? 'N' : 'n'; | |
case PIECE_BISHOP: return is_white ? 'B' : 'b'; | |
case PIECE_QUEEN: return is_white ? 'Q' : 'q'; | |
case PIECE_KING: return is_white ? 'K' : 'k'; | |
case PIECE_PAWN: return is_white ? 'P' : 'p'; | |
default: __builtin_trap(); | |
} | |
} | |
static void print_pos(int pos, char *buffer) | |
{ | |
*buffer++ = file(pos) + 'a'; | |
*buffer++ = rank(pos) + '1'; | |
} | |
static void print_fen(gamestate g, char *buffer) | |
{ | |
bool swap = ! g.is_white; | |
if (swap) | |
g = swap_board(g); | |
int num_empty_squares = 0; | |
for (int r = 8; r --> 0;) { | |
for (int f = 0; f < 8; f++) { | |
int pos = mkPosition(f, r); | |
int piece = get_piece(g, pos); | |
if (piece == PIECE_EMPTY) { | |
num_empty_squares++; | |
continue; | |
} else if (num_empty_squares > 0) { | |
*buffer++ = (num_empty_squares + '0'); | |
num_empty_squares = 0; | |
} | |
bool is_white = is_bit_set(g.current_player_bb, pos); | |
char c = piece_char(piece, is_white); | |
*buffer++ = c; | |
} | |
if (num_empty_squares > 0) { | |
*buffer++ = (num_empty_squares + '0'); | |
num_empty_squares = 0; | |
} | |
if (r > 0) { | |
*buffer++ = '/'; | |
} | |
} | |
skip_loop: | |
*buffer++ = ' '; | |
*buffer++ = g.is_white | |
? 'w' | |
: 'b'; | |
*buffer++ = ' '; | |
// Castle Flags | |
{ | |
if (g.castle_flags & CASTLE_WHITE_KINGSIDE) | |
*buffer++ = 'K'; | |
if (g.castle_flags & CASTLE_WHITE_QUEENSIDE) | |
*buffer++ = 'Q'; | |
if (g.castle_flags & CASTLE_BLACK_KINGSIDE) | |
*buffer++ = 'k'; | |
if (g.castle_flags & CASTLE_BLACK_QUEENSIDE) | |
*buffer++ = 'q'; | |
if (! g.castle_flags) | |
*buffer++ = '-'; | |
} | |
*buffer++ = ' '; | |
if (g.en_passant_sq == POSITION_INVALID) { | |
*buffer++ = '-'; | |
} else { | |
//printf("%d---", g.en_passant_sq); | |
print_pos(g.en_passant_sq, buffer); | |
buffer += 2; | |
} | |
*buffer++ = ' '; | |
*buffer++ = '0'; | |
*buffer++ = ' '; | |
*buffer++ = '1'; | |
*buffer++ = 0; | |
} | |
// Buffer must have at least 7 characters available | |
static void print_move(move m, char *buffer) | |
{ | |
print_pos(m.from, buffer); buffer+=2; | |
print_pos(m.to & 0x3F, buffer); buffer+=2; | |
int piece = promotion_piece(m); | |
switch (piece) { | |
case PIECE_EMPTY: break; | |
case PIECE_ROOK: *buffer++ = '/'; *buffer++ = 'R'; break; | |
case PIECE_KNIGHT: *buffer++ = '/'; *buffer++ = 'N'; break; | |
case PIECE_BISHOP: *buffer++ = '/'; *buffer++ = 'B'; break; | |
case PIECE_QUEEN: *buffer++ = '/'; *buffer++ = 'Q'; break; | |
default: __builtin_trap(); | |
} | |
*buffer++ = 0; | |
} | |
static int parse_pos(const char* buffer) | |
{ | |
int file = *buffer++ - 'a'; | |
int rank = *buffer++ - '1'; | |
return mkPosition(file, rank); | |
} | |
static move parse_move(const char* buffer) | |
{ | |
move m; | |
m.from = parse_pos(buffer); | |
buffer += 2; | |
m.to = parse_pos(buffer); | |
buffer += 2; | |
if (*buffer) { | |
buffer++; | |
switch (*buffer++) { | |
case 'R': m.to |= (PIECE_ROOK << 6); break; | |
case 'N': m.to |= (PIECE_KNIGHT << 6); break; | |
case 'B': m.to |= (PIECE_BISHOP << 6); break; | |
case 'Q': m.to |= (PIECE_QUEEN << 6); break; | |
default: __builtin_trap(); | |
} | |
} | |
return m; | |
} | |
// Used when generating pf_best_move | |
/* extern "C" void custom_main(const char *g_str, char *m_dest, int depth) */ | |
/* { */ | |
/* gamestate g = parse_fen(g_str); */ | |
/* move m = best_move(g, depth); */ | |
/* if (! g.is_white) */ | |
/* m = swap_move(m); */ | |
/* print_move(m, m_dest); */ | |
/* } */ | |
// Used when generating pf_apply_move | |
/* extern "C" void custom_main(const char *g_str, const char *m_str, char *g_dest) */ | |
/* { */ | |
/* gamestate g = parse_fen(g_str); */ | |
/* move m = parse_move(m_str); */ | |
/* if (! g.is_white) */ | |
/* m = swap_move(m); */ | |
/* gamestate g2 = apply_move(g, m); */ | |
/* print_fen(g2, g_dest); */ | |
/* } */ |