More UCI features
This commit is contained in:
parent
d649358c12
commit
e1370eab54
13 changed files with 296 additions and 182 deletions
11
src/board.h
11
src/board.h
|
|
@ -3,22 +3,11 @@
|
|||
|
||||
#include "types.h"
|
||||
|
||||
// Update occupancy bitboards
|
||||
void update_occupancy(GameState *state);
|
||||
|
||||
// Initialize a standard chess starting position
|
||||
void init_startpos(GameState *state);
|
||||
|
||||
// Make a move
|
||||
void make_move(GameState *state, Move move, int *captured_pc, int *old_castling, int *old_ep);
|
||||
|
||||
// Undo a move
|
||||
void undo_move(GameState *state, Move move, int captured_pc, int old_castling, int old_ep);
|
||||
|
||||
// Check if king is in check
|
||||
int is_king_in_check(const GameState *state, int side);
|
||||
|
||||
// Print board representation
|
||||
void print_board(const GameState *state);
|
||||
|
||||
#endif // BOARD_H
|
||||
|
|
|
|||
|
|
@ -201,10 +201,6 @@ int evaluate_position(const GameState *state) {
|
|||
// Add randomness to break symmetry in equal positions
|
||||
score += (int)(hash_position(state) % 10) - 5;
|
||||
|
||||
// Encourage piece activity
|
||||
int white_pieces = __builtin_popcountll(state->occ_white);
|
||||
int black_pieces = __builtin_popcountll(state->occ_black);
|
||||
|
||||
// Slight bonus for having pieces in the center
|
||||
U64 center = 0x0000001818000000ULL; // e4, e5, d4, d5
|
||||
U64 extended_center = 0x00003C3C3C3C0000ULL; // c3-f3 to c6-f6
|
||||
|
|
|
|||
|
|
@ -15,13 +15,8 @@ extern const int pst_queen[64];
|
|||
extern const int pst_king_mid[64];
|
||||
extern const int pst_king_end[64];
|
||||
|
||||
// Evaluate material balance
|
||||
int evaluate_material(const GameState *state);
|
||||
|
||||
// Evaluate positional factors
|
||||
int evaluate_position(const GameState *state);
|
||||
|
||||
// Complete evaluation function
|
||||
int evaluate(const GameState *state);
|
||||
|
||||
#endif // EVALUATION_H
|
||||
|
|
|
|||
|
|
@ -3,34 +3,15 @@
|
|||
|
||||
#include "types.h"
|
||||
|
||||
// Check if a square is attacked by a given side
|
||||
int is_square_attacked(const GameState *state, int sq, int side);
|
||||
|
||||
// Generate pawn moves
|
||||
int generate_pawn_moves(const GameState *state, Move *moves, int index);
|
||||
|
||||
// Generate knight moves
|
||||
int generate_knight_moves(const GameState *state, Move *moves, int index);
|
||||
|
||||
// Generate bishop moves
|
||||
int generate_bishop_moves(const GameState *state, Move *moves, int index);
|
||||
|
||||
// Generate rook moves
|
||||
int generate_rook_moves(const GameState *state, Move *moves, int index);
|
||||
|
||||
// Generate queen moves
|
||||
int generate_queen_moves(const GameState *state, Move *moves, int index);
|
||||
|
||||
// Generate king moves
|
||||
int generate_king_moves(const GameState *state, Move *moves, int index);
|
||||
|
||||
// Generate all pseudo-legal moves
|
||||
int generate_moves(const GameState *state, Move *moves);
|
||||
|
||||
// Check if a move is legal (doesn't leave king in check)
|
||||
int is_move_legal(GameState *state, Move move);
|
||||
|
||||
// Generate all legal moves
|
||||
int generate_legal_moves(GameState *state, Move *moves);
|
||||
|
||||
#endif // MOVEGEN_H
|
||||
|
|
|
|||
|
|
@ -6,11 +6,26 @@
|
|||
#include "movegen.h"
|
||||
|
||||
char* move_to_coordinates(Move move) {
|
||||
char *buf = malloc(5); // 4 characters + null terminator
|
||||
char *buf = malloc(6);
|
||||
if (!buf) return NULL;
|
||||
snprintf(buf, 5, "%c%d%c%d",
|
||||
'a' + (move.from % 8), 1 + (move.from / 8),
|
||||
'a' + (move.to % 8), 1 + (move.to / 8));
|
||||
|
||||
// Move coordinates should be within valid chess board range
|
||||
int from_file = move.from % 8;
|
||||
int from_rank = move.from / 8;
|
||||
int to_file = move.to % 8;
|
||||
int to_rank = move.to / 8;
|
||||
|
||||
// Validate ranges (0-7 for files and ranks)
|
||||
if (from_file < 0 || from_file > 7 || from_rank < 0 || from_rank > 7 ||
|
||||
to_file < 0 || to_file > 7 || to_rank < 0 || to_rank > 7) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(buf, 6, "%c%d%c%d",
|
||||
'a' + from_file, 1 + from_rank,
|
||||
'a' + to_file, 1 + to_rank);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,9 @@
|
|||
|
||||
#include "types.h"
|
||||
|
||||
// Convert a move to chessboard coordinates
|
||||
char* move_to_coordinates(Move move);
|
||||
|
||||
// Convert a move to SAN notation
|
||||
char* move_to_san(const GameState *state, Move move);
|
||||
|
||||
// Parse SAN notation into a Move
|
||||
Move parse_san(const GameState *state, const char *san);
|
||||
|
||||
// Print move in algebraic notation
|
||||
void print_move(const GameState *state, Move move);
|
||||
|
||||
#endif // NOTATION_H
|
||||
|
|
|
|||
25
src/pgn.h
25
src/pgn.h
|
|
@ -3,31 +3,6 @@
|
|||
|
||||
#include "types.h"
|
||||
|
||||
// PGN result types
|
||||
typedef enum {
|
||||
PGN_RESULT_ONGOING = 0,
|
||||
PGN_RESULT_WHITE_WINS,
|
||||
PGN_RESULT_BLACK_WINS,
|
||||
PGN_RESULT_DRAW
|
||||
} PGNResult;
|
||||
|
||||
// PGN structure to hold game data
|
||||
typedef struct {
|
||||
char event[64];
|
||||
char site[64];
|
||||
char date[32];
|
||||
char round[16];
|
||||
char white[64];
|
||||
char black[64];
|
||||
PGNResult result;
|
||||
|
||||
char moves[4096]; // String to store all moves
|
||||
int move_count;
|
||||
int current_move_number;
|
||||
int white_to_move;
|
||||
} PGN;
|
||||
|
||||
// Function declarations
|
||||
void pgn_init(PGN* pgn);
|
||||
void pgn_set_header(PGN* pgn, const char* event, const char* site, const char* date,
|
||||
const char* round, const char* white, const char* black);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "types.h"
|
||||
|
||||
// Function declarations
|
||||
U64 hash_position(const GameState* state);
|
||||
void add_position_to_history(GameState* state);
|
||||
int check_threefold_repetition(const GameState* state);
|
||||
|
|
|
|||
281
src/search.c
281
src/search.c
|
|
@ -1,19 +1,71 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include "search.h"
|
||||
#include "evaluation.h"
|
||||
#include "movegen.h"
|
||||
#include "board.h"
|
||||
#include "uci.h"
|
||||
|
||||
// Order moves to improve alpha-beta pruning
|
||||
void order_moves(Move *moves, int num_moves, const GameState *state) {
|
||||
// Simple move ordering:
|
||||
// 1. Captures (with MVV-LVA: Most Valuable Victim - Least Valuable Aggressor)
|
||||
// 2. Promotions
|
||||
// 3. Checks
|
||||
// 4. Other moves
|
||||
// Global search control
|
||||
volatile int stop_search = 0;
|
||||
SearchInfo global_search_info;
|
||||
|
||||
void *info_thread(void *arg) {
|
||||
SearchInfo *info = (SearchInfo *)arg;
|
||||
|
||||
while (!stop_search) {
|
||||
usleep(100000); // Report every 100ms
|
||||
if (info->nodes > 0) {
|
||||
print_search_info(info, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get current time in milliseconds
|
||||
long get_time_ms() {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
|
||||
}
|
||||
|
||||
// Print search information
|
||||
void print_search_info(SearchInfo *info, int multipv_index) {
|
||||
long elapsed = get_time_ms() - info->start_time;
|
||||
if (elapsed == 0) elapsed = 1;
|
||||
|
||||
int nps = (info->nodes * 1000) / elapsed;
|
||||
|
||||
printf("info depth %d seldepth %d nodes %d time %ld nps %d",
|
||||
info->depth, info->seldepth, info->nodes, elapsed, nps);
|
||||
|
||||
if (multipv_index > 0) {
|
||||
printf(" multipv %d", multipv_index);
|
||||
}
|
||||
|
||||
if (info->is_mate) {
|
||||
printf(" score mate %d", info->mate_score);
|
||||
} else {
|
||||
printf(" score cp %d", info->eval);
|
||||
}
|
||||
|
||||
// Print principal variation
|
||||
if (info->pv_length > 0) {
|
||||
printf(" pv");
|
||||
for (int i = 0; i < info->pv_length; i++) {
|
||||
printf(" %s", move_to_uci(info->pv[i]));
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// Enhanced move ordering
|
||||
void order_moves(Move *moves, int num_moves, const GameState *state) {
|
||||
int scores[256] = {0};
|
||||
|
||||
for (int i = 0; i < num_moves; i++) {
|
||||
|
|
@ -44,9 +96,8 @@ void order_moves(Move *moves, int num_moves, const GameState *state) {
|
|||
captured_piece = state->side_to_move == 0 ? p : P;
|
||||
}
|
||||
|
||||
// Base score
|
||||
// MVV-LVA scoring
|
||||
if (captured_piece != NO_PIECE) {
|
||||
// MVV-LVA: score = 10 * victim_value - attacker_value
|
||||
int victim_value = abs(piece_value[captured_piece]) / 100;
|
||||
int attacker_value = abs(piece_value[moving_piece]) / 100;
|
||||
scores[i] = 10 * victim_value - attacker_value + 1000;
|
||||
|
|
@ -54,19 +105,27 @@ void order_moves(Move *moves, int num_moves, const GameState *state) {
|
|||
|
||||
// Promotion bonus
|
||||
if (moves[i].promo) {
|
||||
scores[i] += 800 + moves[i].promo * 10; // Queen promo gets highest score
|
||||
scores[i] += 800 + moves[i].promo * 10;
|
||||
}
|
||||
|
||||
// Check if move gives check (simplified, could be more efficient)
|
||||
GameState check_state = *state;
|
||||
int captured, old_castling, old_ep;
|
||||
make_move(&check_state, moves[i], &captured, &old_castling, &old_ep);
|
||||
if (is_king_in_check(&check_state, check_state.side_to_move)) {
|
||||
scores[i] += 500;
|
||||
// Center control bonus
|
||||
if ((to >= 27 && to <= 28) || (to >= 35 && to <= 36)) { // d4, e4, d5, e5
|
||||
scores[i] += 50;
|
||||
}
|
||||
|
||||
// Piece development bonus
|
||||
if (moving_piece == N || moving_piece == n ||
|
||||
moving_piece == B || moving_piece == b) {
|
||||
if ((moving_piece == N || moving_piece == B) && from >= 8) {
|
||||
scores[i] += 30;
|
||||
}
|
||||
if ((moving_piece == n || moving_piece == b) && from < 56) {
|
||||
scores[i] += 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort moves based on scores (simple insertion sort)
|
||||
// Sort moves based on scores
|
||||
for (int i = 1; i < num_moves; i++) {
|
||||
Move temp_move = moves[i];
|
||||
int temp_score = scores[i];
|
||||
|
|
@ -83,41 +142,75 @@ void order_moves(Move *moves, int num_moves, const GameState *state) {
|
|||
}
|
||||
}
|
||||
|
||||
// Negamax with alpha-beta pruning
|
||||
int minimax(GameState *state, int depth, int alpha, int beta) {
|
||||
// Check if we're at a leaf node
|
||||
// Enhanced minimax with PV tracking
|
||||
int minimax(GameState *state, int depth, int alpha, int beta, Move *pv, int *pv_length, SearchInfo *info) {
|
||||
if (stop_search) return 0;
|
||||
|
||||
info->nodes++;
|
||||
*pv_length = 0;
|
||||
|
||||
// Update selective depth
|
||||
if (info->depth - depth > info->seldepth) {
|
||||
info->seldepth = info->depth - depth;
|
||||
}
|
||||
|
||||
// Check for time and report info periodically
|
||||
if (info->nodes % 10000 == 0) {
|
||||
long elapsed = get_time_ms() - info->start_time;
|
||||
if (elapsed > 0) {
|
||||
// Update global info for periodic reporting
|
||||
global_search_info = *info;
|
||||
global_search_info.pv_length = *pv_length;
|
||||
for (int i = 0; i < *pv_length; i++) {
|
||||
global_search_info.pv[i] = pv[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (depth == 0) {
|
||||
return evaluate(state);
|
||||
}
|
||||
|
||||
// Generate and count legal moves
|
||||
Move moves[256];
|
||||
int num_moves = generate_legal_moves(state, moves);
|
||||
|
||||
// Check for checkmate or stalemate
|
||||
if (num_moves == 0) {
|
||||
if (is_king_in_check(state, state->side_to_move)) {
|
||||
return -30000 + (10 - depth); // Checkmate (prefer faster checkmate)
|
||||
info->is_mate = 1;
|
||||
info->mate_score = -(30000 - (info->depth - depth));
|
||||
return -30000 + (info->depth - depth);
|
||||
} else {
|
||||
return 0; // Stalemate
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Order moves to improve alpha-beta pruning
|
||||
order_moves(moves, num_moves, state);
|
||||
|
||||
int best_score = -32000;
|
||||
Move best_pv[64];
|
||||
int best_pv_length = 0;
|
||||
|
||||
// Try each move
|
||||
for (int i = 0; i < num_moves; i++) {
|
||||
if (stop_search) break;
|
||||
|
||||
int captured, old_castling, old_ep;
|
||||
Move current_pv[64];
|
||||
int current_pv_length = 0;
|
||||
|
||||
make_move(state, moves[i], &captured, &old_castling, &old_ep);
|
||||
int score = -minimax(state, depth - 1, -beta, -alpha);
|
||||
int score = -minimax(state, depth - 1, -beta, -alpha, current_pv, ¤t_pv_length, info);
|
||||
undo_move(state, moves[i], captured, old_castling, old_ep);
|
||||
|
||||
if (score > best_score) {
|
||||
best_score = score;
|
||||
|
||||
// Update PV
|
||||
best_pv[0] = moves[i];
|
||||
best_pv_length = 1;
|
||||
for (int j = 0; j < current_pv_length; j++) {
|
||||
best_pv[j + 1] = current_pv[j];
|
||||
best_pv_length++;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_score > alpha) {
|
||||
|
|
@ -125,40 +218,26 @@ int minimax(GameState *state, int depth, int alpha, int beta) {
|
|||
}
|
||||
|
||||
if (alpha >= beta) {
|
||||
break; // Beta cutoff
|
||||
break; // Beta cutoff
|
||||
}
|
||||
}
|
||||
|
||||
// Copy best PV to output
|
||||
*pv_length = best_pv_length;
|
||||
for (int i = 0; i < best_pv_length; i++) {
|
||||
pv[i] = best_pv[i];
|
||||
}
|
||||
|
||||
return best_score;
|
||||
}
|
||||
|
||||
// Thread function for search
|
||||
void *thread_search(void *arg) {
|
||||
ThreadArg *thread_arg = (ThreadArg *)arg;
|
||||
GameState *state = &thread_arg->state;
|
||||
int captured, old_castling, old_ep;
|
||||
|
||||
// Make the move
|
||||
make_move(state, thread_arg->move, &captured, &old_castling, &old_ep);
|
||||
|
||||
// Search from this position
|
||||
thread_arg->eval = -minimax(state, thread_arg->depth - 1, -30000, 30000);
|
||||
|
||||
// Lock mutex before checking if this is the best move
|
||||
pthread_mutex_lock(thread_arg->mutex);
|
||||
|
||||
if (thread_arg->eval > *(thread_arg->best_eval)) {
|
||||
*(thread_arg->best_eval) = thread_arg->eval;
|
||||
*(thread_arg->best_move) = thread_arg->move;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(thread_arg->mutex);
|
||||
|
||||
return NULL;
|
||||
// Root search with multi-PV support
|
||||
int minimax_root(GameState *state, int depth, int alpha, int beta, Move *pv, int *pv_length, SearchInfo *info) {
|
||||
return minimax(state, depth, alpha, beta, pv, pv_length, info);
|
||||
}
|
||||
|
||||
// Find best move using multithreaded search
|
||||
Move find_best_move(GameState *state, int depth) {
|
||||
// Find best move with multi-PV support
|
||||
Move find_best_move(GameState *state, int depth, int multipv) {
|
||||
Move moves[256];
|
||||
int num_moves = generate_legal_moves(state, moves);
|
||||
|
||||
|
|
@ -166,45 +245,75 @@ Move find_best_move(GameState *state, int depth) {
|
|||
return (Move){0, 0, 0, 0};
|
||||
}
|
||||
|
||||
// Order moves to search promising moves first
|
||||
order_moves(moves, num_moves, state);
|
||||
|
||||
// Output initial search info
|
||||
printf("info depth %d nodes 0 time 0\n", depth);
|
||||
fflush(stdout);
|
||||
// Initialize search info
|
||||
SearchInfo info = {0};
|
||||
info.start_time = get_time_ms();
|
||||
info.depth = depth;
|
||||
info.seldepth = 0;
|
||||
|
||||
// Set up threads
|
||||
pthread_t threads[256];
|
||||
ThreadArg thread_args[256];
|
||||
MultiPVEntry multipv_entries[256];
|
||||
int multipv_count = (multipv > num_moves) ? num_moves : multipv;
|
||||
|
||||
// Shared variables for best move
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
int best_eval = -32000;
|
||||
Move best_move = moves[0];
|
||||
// Search each move for multi-PV
|
||||
for (int pv_index = 0; pv_index < multipv_count; pv_index++) {
|
||||
int best_score = -32000;
|
||||
int best_move_index = -1;
|
||||
|
||||
// Start a thread for each move
|
||||
for (int i = 0; i < num_moves; i++) {
|
||||
thread_args[i].state = *state;
|
||||
thread_args[i].move = moves[i];
|
||||
thread_args[i].depth = depth;
|
||||
thread_args[i].mutex = &mutex;
|
||||
thread_args[i].best_eval = &best_eval;
|
||||
thread_args[i].best_move = &best_move;
|
||||
// Find the best move not already in multi-PV list
|
||||
for (int i = 0; i < num_moves; i++) {
|
||||
// Skip moves already in multi-PV
|
||||
int already_searched = 0;
|
||||
for (int j = 0; j < pv_index; j++) {
|
||||
if (moves[i].from == multipv_entries[j].move.from &&
|
||||
moves[i].to == multipv_entries[j].move.to &&
|
||||
moves[i].promo == multipv_entries[j].move.promo) {
|
||||
already_searched = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (already_searched) continue;
|
||||
|
||||
pthread_create(&threads[i], NULL, thread_search, &thread_args[i]);
|
||||
int captured, old_castling, old_ep;
|
||||
Move current_pv[64];
|
||||
int current_pv_length = 0;
|
||||
|
||||
make_move(state, moves[i], &captured, &old_castling, &old_ep);
|
||||
int score = -minimax_root(state, depth - 1, -30000, 30000, current_pv, ¤t_pv_length, &info);
|
||||
undo_move(state, moves[i], captured, old_castling, old_ep);
|
||||
|
||||
if (score > best_score) {
|
||||
best_score = score;
|
||||
best_move_index = i;
|
||||
|
||||
// Store in multi-PV entry
|
||||
multipv_entries[pv_index].move = moves[i];
|
||||
multipv_entries[pv_index].eval = score;
|
||||
multipv_entries[pv_index].is_mate = info.is_mate;
|
||||
multipv_entries[pv_index].mate_score = info.mate_score;
|
||||
multipv_entries[pv_index].pv[0] = moves[i];
|
||||
multipv_entries[pv_index].pv_length = current_pv_length + 1;
|
||||
for (int k = 0; k < current_pv_length; k++) {
|
||||
multipv_entries[pv_index].pv[k + 1] = current_pv[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_move_index != -1) {
|
||||
// Print info for this PV line
|
||||
SearchInfo pv_info = info;
|
||||
pv_info.eval = multipv_entries[pv_index].eval;
|
||||
pv_info.is_mate = multipv_entries[pv_index].is_mate;
|
||||
pv_info.mate_score = multipv_entries[pv_index].mate_score;
|
||||
pv_info.pv_length = multipv_entries[pv_index].pv_length;
|
||||
for (int k = 0; k < pv_info.pv_length; k++) {
|
||||
pv_info.pv[k] = multipv_entries[pv_index].pv[k];
|
||||
}
|
||||
|
||||
print_search_info(&pv_info, pv_index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all threads to finish
|
||||
for (int i = 0; i < num_moves; i++) {
|
||||
pthread_join(threads[i], NULL);
|
||||
}
|
||||
|
||||
// Output final search info with evaluation
|
||||
printf("info depth %d score cp %d pv %s\n",
|
||||
depth, best_eval, move_to_uci(best_move));
|
||||
fflush(stdout);
|
||||
|
||||
pthread_mutex_destroy(&mutex);
|
||||
|
||||
return best_move;
|
||||
return multipv_entries[0].move;
|
||||
}
|
||||
15
src/search.h
15
src/search.h
|
|
@ -4,16 +4,13 @@
|
|||
#include <pthread.h>
|
||||
#include "types.h"
|
||||
|
||||
// Order moves to improve alpha-beta pruning
|
||||
extern volatile int stop_search;
|
||||
extern SearchInfo global_search_info;
|
||||
|
||||
void order_moves(Move *moves, int num_moves, const GameState *state);
|
||||
|
||||
// Negamax with alpha-beta pruning
|
||||
int minimax(GameState *state, int depth, int alpha, int beta);
|
||||
|
||||
// Thread function for search
|
||||
int minimax(GameState *state, int depth, int alpha, int beta, Move *pv, int *pv_length, SearchInfo *info);
|
||||
void *thread_search(void *arg);
|
||||
|
||||
// Find best move using multithreaded search
|
||||
Move find_best_move(GameState *state, int depth);
|
||||
Move find_best_move(GameState *state, int depth, int multipv);
|
||||
void print_search_info(SearchInfo *info, int multipv_index);
|
||||
|
||||
#endif // SEARCH_H
|
||||
|
|
|
|||
48
src/types.h
48
src/types.h
|
|
@ -59,6 +59,29 @@ typedef struct {
|
|||
GameHistory history; // Add this line
|
||||
} GameState;
|
||||
|
||||
// Search statistics
|
||||
typedef struct {
|
||||
int nodes;
|
||||
int depth;
|
||||
int seldepth;
|
||||
clock_t start_time;
|
||||
Move pv[64];
|
||||
int pv_length;
|
||||
int eval;
|
||||
int mate_score;
|
||||
int is_mate;
|
||||
} SearchInfo;
|
||||
|
||||
// Multi-PV entry
|
||||
typedef struct {
|
||||
Move move;
|
||||
int eval;
|
||||
int is_mate;
|
||||
int mate_score;
|
||||
Move pv[64];
|
||||
int pv_length;
|
||||
} MultiPVEntry;
|
||||
|
||||
// Thread argument structure
|
||||
typedef struct {
|
||||
GameState state;
|
||||
|
|
@ -68,8 +91,33 @@ typedef struct {
|
|||
pthread_mutex_t *mutex;
|
||||
int *best_eval;
|
||||
Move *best_move;
|
||||
SearchInfo *search_info;
|
||||
} ThreadArg;
|
||||
|
||||
// PGN result types
|
||||
typedef enum {
|
||||
PGN_RESULT_ONGOING = 0,
|
||||
PGN_RESULT_WHITE_WINS,
|
||||
PGN_RESULT_BLACK_WINS,
|
||||
PGN_RESULT_DRAW
|
||||
} PGNResult;
|
||||
|
||||
// PGN structure to hold game data
|
||||
typedef struct {
|
||||
char event[64];
|
||||
char site[64];
|
||||
char date[32];
|
||||
char round[16];
|
||||
char white[64];
|
||||
char black[64];
|
||||
PGNResult result;
|
||||
|
||||
char moves[4096]; // String to store all moves
|
||||
int move_count;
|
||||
int current_move_number;
|
||||
int white_to_move;
|
||||
} PGN;
|
||||
|
||||
// Helper macros
|
||||
#define GET_BIT(bb, sq) (((bb) >> (sq)) & 1ULL)
|
||||
#define SET_BIT(bb, sq) ((bb) |= (1ULL << (sq)))
|
||||
|
|
|
|||
35
src/uci.c
35
src/uci.c
|
|
@ -12,6 +12,7 @@
|
|||
static int uci_depth = 6;
|
||||
static int uci_hash = 64;
|
||||
static int uci_threads = 4;
|
||||
static int uci_multipv = 3;
|
||||
|
||||
// Convert move to UCI format (e.g., "e2e4", "e7e8q")
|
||||
char* move_to_uci(Move move) {
|
||||
|
|
@ -36,7 +37,7 @@ char* move_to_uci(Move move) {
|
|||
}
|
||||
|
||||
// Parse UCI move format (e.g., "e2e4", "e7e8q")
|
||||
Move parse_uci_move(const GameState *state, const char *move_str) {
|
||||
Move parse_uci_move(const char *move_str) {
|
||||
Move move = {0, 0, 0, 0};
|
||||
|
||||
if (strlen(move_str) < 4) return move;
|
||||
|
|
@ -79,13 +80,12 @@ void uci_position(GameState *state, char *line) {
|
|||
|
||||
token = strtok(NULL, " ");
|
||||
}
|
||||
// Add support for FEN later if needed
|
||||
|
||||
// Handle moves
|
||||
if (token && strcmp(token, "moves") == 0) {
|
||||
token = strtok(NULL, " ");
|
||||
while (token) {
|
||||
Move move = parse_uci_move(state, token);
|
||||
Move move = parse_uci_move(token);
|
||||
|
||||
// Verify move is legal
|
||||
Move legal_moves[256];
|
||||
|
|
@ -117,10 +117,11 @@ void uci_position(GameState *state, char *line) {
|
|||
|
||||
// Handle UCI go command
|
||||
void uci_go(GameState *state, char *line) {
|
||||
int depth = uci_depth; // Use UCI option default
|
||||
int depth = uci_depth;
|
||||
int movetime = 0;
|
||||
int use_movetime = 0;
|
||||
|
||||
// Parse go parameters (these override the UCI options)
|
||||
// Parse go parameters
|
||||
char *token = strtok(line, " ");
|
||||
while (token) {
|
||||
if (strcmp(token, "depth") == 0) {
|
||||
|
|
@ -128,15 +129,26 @@ void uci_go(GameState *state, char *line) {
|
|||
if (token) depth = atoi(token);
|
||||
} else if (strcmp(token, "movetime") == 0) {
|
||||
token = strtok(NULL, " ");
|
||||
if (token) movetime = atoi(token);
|
||||
if (token) {
|
||||
movetime = atoi(token);
|
||||
use_movetime = 1;
|
||||
}
|
||||
}
|
||||
token = strtok(NULL, " ");
|
||||
}
|
||||
|
||||
// Find best move
|
||||
Move best_move = find_best_move(state, depth);
|
||||
// Reset search control
|
||||
stop_search = 0;
|
||||
|
||||
// TODO: Implement time control logic
|
||||
if (use_movetime) {
|
||||
printf("info string movetime %d ms\n", movetime);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// Find best move with multi-PV support
|
||||
Move best_move = find_best_move(state, depth, uci_multipv);
|
||||
|
||||
// Output the move
|
||||
printf("bestmove %s\n", move_to_uci(best_move));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
|
@ -164,6 +176,7 @@ void uci_loop() {
|
|||
printf("option name Hash type spin default 64 min 1 max 1024\n");
|
||||
printf("option name Threads type spin default 1 min 1 max 64\n");
|
||||
printf("option name Depth type spin default 6 min 1 max 20\n");
|
||||
printf("option name MultiPV type spin default 3 min 1 max 10\n");
|
||||
|
||||
printf("uciok\n");
|
||||
fflush(stdout);
|
||||
|
|
@ -206,6 +219,10 @@ void uci_loop() {
|
|||
} else if (strcmp(option_name, "Threads") == 0) {
|
||||
uci_threads = atoi(option_value);
|
||||
// We don't control thread count yet, but store the value
|
||||
} else if (strcmp(option_name, "MultiPV") == 0) {
|
||||
uci_multipv = atoi(option_value);
|
||||
if (uci_multipv < 1) uci_multipv = 1;
|
||||
if (uci_multipv > 10) uci_multipv = 10;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(line, "ucinewgame") == 0) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
void uci_loop();
|
||||
void uci_position(GameState *state, char *line);
|
||||
void uci_go(GameState *state, char *line);
|
||||
Move parse_uci_move(const GameState *state, const char *move_str);
|
||||
Move parse_uci_move(const char *move_str);
|
||||
char* move_to_uci(Move move);
|
||||
|
||||
#endif // UCI_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue