More UCI features

This commit is contained in:
srtk 2025-11-08 20:03:35 +05:30
parent d649358c12
commit e1370eab54
13 changed files with 296 additions and 182 deletions

View file

@ -3,22 +3,11 @@
#include "types.h" #include "types.h"
// Update occupancy bitboards
void update_occupancy(GameState *state); void update_occupancy(GameState *state);
// Initialize a standard chess starting position
void init_startpos(GameState *state); void init_startpos(GameState *state);
// Make a move
void make_move(GameState *state, Move move, int *captured_pc, int *old_castling, int *old_ep); 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); 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); int is_king_in_check(const GameState *state, int side);
// Print board representation
void print_board(const GameState *state); void print_board(const GameState *state);
#endif // BOARD_H #endif // BOARD_H

View file

@ -201,10 +201,6 @@ int evaluate_position(const GameState *state) {
// Add randomness to break symmetry in equal positions // Add randomness to break symmetry in equal positions
score += (int)(hash_position(state) % 10) - 5; 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 // Slight bonus for having pieces in the center
U64 center = 0x0000001818000000ULL; // e4, e5, d4, d5 U64 center = 0x0000001818000000ULL; // e4, e5, d4, d5
U64 extended_center = 0x00003C3C3C3C0000ULL; // c3-f3 to c6-f6 U64 extended_center = 0x00003C3C3C3C0000ULL; // c3-f3 to c6-f6

View file

@ -15,13 +15,8 @@ extern const int pst_queen[64];
extern const int pst_king_mid[64]; extern const int pst_king_mid[64];
extern const int pst_king_end[64]; extern const int pst_king_end[64];
// Evaluate material balance
int evaluate_material(const GameState *state); int evaluate_material(const GameState *state);
// Evaluate positional factors
int evaluate_position(const GameState *state); int evaluate_position(const GameState *state);
// Complete evaluation function
int evaluate(const GameState *state); int evaluate(const GameState *state);
#endif // EVALUATION_H #endif // EVALUATION_H

View file

@ -3,34 +3,15 @@
#include "types.h" #include "types.h"
// Check if a square is attacked by a given side
int is_square_attacked(const GameState *state, int sq, int 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); 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); 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); 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); 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); 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); int generate_king_moves(const GameState *state, Move *moves, int index);
// Generate all pseudo-legal moves
int generate_moves(const GameState *state, Move *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); int is_move_legal(GameState *state, Move move);
// Generate all legal moves
int generate_legal_moves(GameState *state, Move *moves); int generate_legal_moves(GameState *state, Move *moves);
#endif // MOVEGEN_H #endif // MOVEGEN_H

View file

@ -6,11 +6,26 @@
#include "movegen.h" #include "movegen.h"
char* move_to_coordinates(Move move) { char* move_to_coordinates(Move move) {
char *buf = malloc(5); // 4 characters + null terminator char *buf = malloc(6);
if (!buf) return NULL; if (!buf) return NULL;
snprintf(buf, 5, "%c%d%c%d",
'a' + (move.from % 8), 1 + (move.from / 8), // Move coordinates should be within valid chess board range
'a' + (move.to % 8), 1 + (move.to / 8)); 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; return buf;
} }

View file

@ -3,16 +3,9 @@
#include "types.h" #include "types.h"
// Convert a move to chessboard coordinates
char* move_to_coordinates(Move move); char* move_to_coordinates(Move move);
// Convert a move to SAN notation
char* move_to_san(const GameState *state, Move move); char* move_to_san(const GameState *state, Move move);
// Parse SAN notation into a Move
Move parse_san(const GameState *state, const char *san); Move parse_san(const GameState *state, const char *san);
// Print move in algebraic notation
void print_move(const GameState *state, Move move); void print_move(const GameState *state, Move move);
#endif // NOTATION_H #endif // NOTATION_H

View file

@ -3,31 +3,6 @@
#include "types.h" #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_init(PGN* pgn);
void pgn_set_header(PGN* pgn, const char* event, const char* site, const char* date, void pgn_set_header(PGN* pgn, const char* event, const char* site, const char* date,
const char* round, const char* white, const char* black); const char* round, const char* white, const char* black);

View file

@ -3,7 +3,6 @@
#include "types.h" #include "types.h"
// Function declarations
U64 hash_position(const GameState* state); U64 hash_position(const GameState* state);
void add_position_to_history(GameState* state); void add_position_to_history(GameState* state);
int check_threefold_repetition(const GameState* state); int check_threefold_repetition(const GameState* state);

View file

@ -1,19 +1,71 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include "search.h" #include "search.h"
#include "evaluation.h" #include "evaluation.h"
#include "movegen.h" #include "movegen.h"
#include "board.h" #include "board.h"
#include "uci.h" #include "uci.h"
// Order moves to improve alpha-beta pruning // Global search control
void order_moves(Move *moves, int num_moves, const GameState *state) { volatile int stop_search = 0;
// Simple move ordering: SearchInfo global_search_info;
// 1. Captures (with MVV-LVA: Most Valuable Victim - Least Valuable Aggressor)
// 2. Promotions void *info_thread(void *arg) {
// 3. Checks SearchInfo *info = (SearchInfo *)arg;
// 4. Other moves
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}; int scores[256] = {0};
for (int i = 0; i < num_moves; i++) { 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; captured_piece = state->side_to_move == 0 ? p : P;
} }
// Base score // MVV-LVA scoring
if (captured_piece != NO_PIECE) { if (captured_piece != NO_PIECE) {
// MVV-LVA: score = 10 * victim_value - attacker_value
int victim_value = abs(piece_value[captured_piece]) / 100; int victim_value = abs(piece_value[captured_piece]) / 100;
int attacker_value = abs(piece_value[moving_piece]) / 100; int attacker_value = abs(piece_value[moving_piece]) / 100;
scores[i] = 10 * victim_value - attacker_value + 1000; 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 // Promotion bonus
if (moves[i].promo) { 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) // Center control bonus
GameState check_state = *state; if ((to >= 27 && to <= 28) || (to >= 35 && to <= 36)) { // d4, e4, d5, e5
int captured, old_castling, old_ep; scores[i] += 50;
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; // 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++) { for (int i = 1; i < num_moves; i++) {
Move temp_move = moves[i]; Move temp_move = moves[i];
int temp_score = scores[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 // Enhanced minimax with PV tracking
int minimax(GameState *state, int depth, int alpha, int beta) { int minimax(GameState *state, int depth, int alpha, int beta, Move *pv, int *pv_length, SearchInfo *info) {
// Check if we're at a leaf node 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) { if (depth == 0) {
return evaluate(state); return evaluate(state);
} }
// Generate and count legal moves
Move moves[256]; Move moves[256];
int num_moves = generate_legal_moves(state, moves); int num_moves = generate_legal_moves(state, moves);
// Check for checkmate or stalemate
if (num_moves == 0) { if (num_moves == 0) {
if (is_king_in_check(state, state->side_to_move)) { 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 { } else {
return 0; // Stalemate return 0;
} }
} }
// Order moves to improve alpha-beta pruning
order_moves(moves, num_moves, state); order_moves(moves, num_moves, state);
int best_score = -32000; int best_score = -32000;
Move best_pv[64];
int best_pv_length = 0;
// Try each move
for (int i = 0; i < num_moves; i++) { for (int i = 0; i < num_moves; i++) {
if (stop_search) break;
int captured, old_castling, old_ep; 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); 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, &current_pv_length, info);
undo_move(state, moves[i], captured, old_castling, old_ep); undo_move(state, moves[i], captured, old_castling, old_ep);
if (score > best_score) { if (score > best_score) {
best_score = 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) { if (best_score > alpha) {
@ -125,40 +218,26 @@ int minimax(GameState *state, int depth, int alpha, int beta) {
} }
if (alpha >= 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; return best_score;
} }
// Thread function for search // Root search with multi-PV support
void *thread_search(void *arg) { int minimax_root(GameState *state, int depth, int alpha, int beta, Move *pv, int *pv_length, SearchInfo *info) {
ThreadArg *thread_arg = (ThreadArg *)arg; return minimax(state, depth, alpha, beta, pv, pv_length, info);
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;
} }
// Find best move using multithreaded search // Find best move with multi-PV support
Move find_best_move(GameState *state, int depth) { Move find_best_move(GameState *state, int depth, int multipv) {
Move moves[256]; Move moves[256];
int num_moves = generate_legal_moves(state, moves); 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}; return (Move){0, 0, 0, 0};
} }
// Order moves to search promising moves first
order_moves(moves, num_moves, state); order_moves(moves, num_moves, state);
// Output initial search info // Initialize search info
printf("info depth %d nodes 0 time 0\n", depth); SearchInfo info = {0};
fflush(stdout); info.start_time = get_time_ms();
info.depth = depth;
info.seldepth = 0;
// Set up threads MultiPVEntry multipv_entries[256];
pthread_t threads[256]; int multipv_count = (multipv > num_moves) ? num_moves : multipv;
ThreadArg thread_args[256];
// Shared variables for best move // Search each move for multi-PV
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; for (int pv_index = 0; pv_index < multipv_count; pv_index++) {
int best_eval = -32000; int best_score = -32000;
Move best_move = moves[0]; 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;
pthread_create(&threads[i], NULL, thread_search, &thread_args[i]); // 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;
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, &current_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 return multipv_entries[0].move;
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;
} }

View file

@ -4,16 +4,13 @@
#include <pthread.h> #include <pthread.h>
#include "types.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); void order_moves(Move *moves, int num_moves, const GameState *state);
int minimax(GameState *state, int depth, int alpha, int beta, Move *pv, int *pv_length, SearchInfo *info);
// Negamax with alpha-beta pruning
int minimax(GameState *state, int depth, int alpha, int beta);
// Thread function for search
void *thread_search(void *arg); void *thread_search(void *arg);
Move find_best_move(GameState *state, int depth, int multipv);
// Find best move using multithreaded search void print_search_info(SearchInfo *info, int multipv_index);
Move find_best_move(GameState *state, int depth);
#endif // SEARCH_H #endif // SEARCH_H

View file

@ -59,6 +59,29 @@ typedef struct {
GameHistory history; // Add this line GameHistory history; // Add this line
} GameState; } 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 // Thread argument structure
typedef struct { typedef struct {
GameState state; GameState state;
@ -68,8 +91,33 @@ typedef struct {
pthread_mutex_t *mutex; pthread_mutex_t *mutex;
int *best_eval; int *best_eval;
Move *best_move; Move *best_move;
SearchInfo *search_info;
} ThreadArg; } 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 // Helper macros
#define GET_BIT(bb, sq) (((bb) >> (sq)) & 1ULL) #define GET_BIT(bb, sq) (((bb) >> (sq)) & 1ULL)
#define SET_BIT(bb, sq) ((bb) |= (1ULL << (sq))) #define SET_BIT(bb, sq) ((bb) |= (1ULL << (sq)))

View file

@ -12,6 +12,7 @@
static int uci_depth = 6; static int uci_depth = 6;
static int uci_hash = 64; static int uci_hash = 64;
static int uci_threads = 4; static int uci_threads = 4;
static int uci_multipv = 3;
// Convert move to UCI format (e.g., "e2e4", "e7e8q") // Convert move to UCI format (e.g., "e2e4", "e7e8q")
char* move_to_uci(Move move) { char* move_to_uci(Move move) {
@ -36,7 +37,7 @@ char* move_to_uci(Move move) {
} }
// Parse UCI move format (e.g., "e2e4", "e7e8q") // 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}; Move move = {0, 0, 0, 0};
if (strlen(move_str) < 4) return move; if (strlen(move_str) < 4) return move;
@ -79,13 +80,12 @@ void uci_position(GameState *state, char *line) {
token = strtok(NULL, " "); token = strtok(NULL, " ");
} }
// Add support for FEN later if needed
// Handle moves // Handle moves
if (token && strcmp(token, "moves") == 0) { if (token && strcmp(token, "moves") == 0) {
token = strtok(NULL, " "); token = strtok(NULL, " ");
while (token) { while (token) {
Move move = parse_uci_move(state, token); Move move = parse_uci_move(token);
// Verify move is legal // Verify move is legal
Move legal_moves[256]; Move legal_moves[256];
@ -117,10 +117,11 @@ void uci_position(GameState *state, char *line) {
// Handle UCI go command // Handle UCI go command
void uci_go(GameState *state, char *line) { void uci_go(GameState *state, char *line) {
int depth = uci_depth; // Use UCI option default int depth = uci_depth;
int movetime = 0; int movetime = 0;
int use_movetime = 0;
// Parse go parameters (these override the UCI options) // Parse go parameters
char *token = strtok(line, " "); char *token = strtok(line, " ");
while (token) { while (token) {
if (strcmp(token, "depth") == 0) { if (strcmp(token, "depth") == 0) {
@ -128,15 +129,26 @@ void uci_go(GameState *state, char *line) {
if (token) depth = atoi(token); if (token) depth = atoi(token);
} else if (strcmp(token, "movetime") == 0) { } else if (strcmp(token, "movetime") == 0) {
token = strtok(NULL, " "); token = strtok(NULL, " ");
if (token) movetime = atoi(token); if (token) {
movetime = atoi(token);
use_movetime = 1;
}
} }
token = strtok(NULL, " "); token = strtok(NULL, " ");
} }
// Find best move // Reset search control
Move best_move = find_best_move(state, depth); 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)); printf("bestmove %s\n", move_to_uci(best_move));
fflush(stdout); 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 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 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 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"); printf("uciok\n");
fflush(stdout); fflush(stdout);
@ -206,6 +219,10 @@ void uci_loop() {
} else if (strcmp(option_name, "Threads") == 0) { } else if (strcmp(option_name, "Threads") == 0) {
uci_threads = atoi(option_value); uci_threads = atoi(option_value);
// We don't control thread count yet, but store the 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) { } else if (strcmp(line, "ucinewgame") == 0) {

View file

@ -8,7 +8,7 @@
void uci_loop(); void uci_loop();
void uci_position(GameState *state, char *line); void uci_position(GameState *state, char *line);
void uci_go(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); char* move_to_uci(Move move);
#endif // UCI_H #endif // UCI_H