Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
ceagle/test-support/chess-engine.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1413 lines (1292 sloc)
36.3 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); */ | |
/* } */ |