//
// uci.cpp
// fornax3
//
// Created by Anders on 24/04/2019.
//
#ifndef GIT_DESC
#define GIT_DESC ""
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "uci.hpp"
#include "libs/require.h"
#include "libs/strings.h"
#include "libs/iolog.hpp"
#include "board.h"
#include "moving/makemove.hpp"
#include "moving/movegen.hpp"
#include "moving/movepick.hpp"
#include "search/search.hpp"
#include "eval/eval.hpp"
#include "parsing.hpp"
#include "tt/transpositions.hpp"
#include "supplementary/time_management.hpp"
#include "supplementary/opening_book.hpp"
#include "supplementary/testsuite.hpp"
#include "supplementary/perft.hpp"
static Board* curr_board;
static char** curr_tokens;
static std::future<move> currmove;
move get_currmove() {
return currmove.valid() ? currmove.get() : 0;
}
void uci_printversion() {
const char* mainVersion = "4.0";
if (!string_equals(GIT_DESC, "")) {
printf("%s %s", mainVersion, GIT_DESC);
}
else {
printf("%s", mainVersion);
}
}
void uci_init(void) {
curr_board = (Board*) malloc(sizeof(Board));
curr_tokens = (char**) malloc(sizeof(*curr_tokens) * 512);
}
void uci_destroy(void) {
free(curr_board);
free(curr_tokens);
}
static void uci_test(char** tokens, int length) {
if (string_starts_with(tokens[0], "all")) {
testsuite_perft();
testsuite_search(1);
}
else if (string_starts_with(tokens[0], "p")) {
testsuite_perft();
}
else if (string_starts_with(tokens[0], "s")) {
if (length > 1) {
int number = (int) string_to_long(tokens[1]);
testsuite_search(number);
}
else {
testsuite_search(1);
}
}
else if (string_starts_with(tokens[0], "e")) {
testuite_eval();
}
}
static void uci_tt(char** tokens, int length) {
if (length == 0) {
IO_PRINT("ttkey = %" PRIu64 "\n", board_get_hash(curr_board));
IO_FLUSH();
}
else if (string_starts_with(tokens[0], "clear")) {
transpositions_clear();
}
else if (string_starts_with(tokens[0], "clean")) {
transpositions_maintenance();
}
else if (string_starts_with(tokens[0], "status")) {
transpositions_status();
}
else if (string_starts_with(tokens[0], "print")) {
long size = 0;
if (length > 1) {
size = string_to_long(tokens[1]);
}
transpositions_print(size);
}
else if (string_starts_with(tokens[0], "row")) {
long size = 0;
if (length > 1) {
size = string_to_long(tokens[1]);
}
transpositions_print_bucket(board_get_hash(curr_board), size);
}
}
static void uci_play_moves(char** tokens, int length) {
int moves_to_play = length;
int i = 0;
while (moves_to_play > 0) {
moves_to_play--;
move m = parsing_move_from_uci(tokens[i]);
i++;
makemove_makef(curr_board, m);
}
}
static void uci_position(char** tokens, int length) {
if (length == 0) {
return;
}
if (string_equals("fen", tokens[0])) {
int fenEnd = 1;
int moveStart = -1;
for (; fenEnd < length - 1; ++fenEnd) {
if (string_equals(tokens[fenEnd], "moves")) {
moveStart = fenEnd;
break;
}
else if (fenEnd == 6) break;
}
Board b = parsing_from_fen_parts(tokens + 1, fenEnd - 1);
*curr_board = b;
if (moveStart >= 0) {
uci_play_moves(tokens + moveStart + 1, length - moveStart - 1);
}
}
else if (string_starts_with(tokens[0], "startpos")) {
Board b = parsing_from_fen(STARTPOS_FEN);
*curr_board = b;
if (length <= 2) return;
if (string_starts_with(tokens[1], "moves")) {
uci_play_moves(tokens + 2, length - 2);
}
}
}
static void uci_perft(char** tokens, int length) {
int depth = 1;
if (length > 0) {
require(isdigit(*tokens[0]), "'%s' was not a digit\n", tokens[0]);
depth = (int)string_to_long(tokens[0]);
}
perft(curr_board, depth, true);
}
static inline move uci_find_book_ponder(Board* board, move m) {
makemove_makef(board, m);
move bookmove = opening_book_consult(board);
makemove_takebackf(board, m);
return bookmove;
}
static void uci_go(char** tokens, int length) {
move bookmove = opening_book_consult(curr_board);
if (bookmove) {
#if DEBUGLOG
IO_PRINT("info depth 1 nodes 1 string - found book move\n");
IO_FLUSH();
#endif
currmove = move_now(bookmove);
parsing_printbestmove(bookmove, uci_find_book_ponder(curr_board, bookmove));
return;
}
if (length == 0) {
currmove = search_async_time(curr_board, 1000);
return;
}
else if (!strcmp(tokens[0], "depth")
&& length == 2
&& isdigit(*tokens[1])) {
currmove = search_async_depth(curr_board, (uint8_t) string_to_long(tokens[1]));
}
else if (string_starts_with(tokens[0], "infinite")) {
currmove = search_async_infinite(curr_board);
}
else if ((string_starts_with(tokens[0], "movetime")
|| string_starts_with(tokens[0], "time"))
&& length == 2
&& isdigit(*tokens[1])) {
currmove = search_async_time(curr_board, string_to_long(tokens[1]));
}
else {
currmove = time_management_parse_uci(curr_board, tokens, length);
}
}
static void uci_setoption(char** tokens, int length) {
if (length < 4 || !string_equals("name", tokens[0])) return;
if (length == 5
&& string_equals("Move", tokens[1])
&& string_equals("Overhead", tokens[2])) {
long overhead = string_to_long(tokens[4]);
time_management_set_overhead(overhead);
}
else if (length == 4 && string_equals("Contempt", tokens[1])) {
long contempt = string_to_long(tokens[3]);
eval_set_contempt_factor(contempt);
}
else if (length == 4 && string_equals("Hash", tokens[1])) {
long mb = string_to_long(tokens[3]);
transpositions_set_hash_size(mb);
}
else if (length == 4 && string_equals("Ponder", tokens[1])) {
bool enabled = string_equals("true", tokens[3]);
time_management_set_ponder_enabled(enabled);
}
else if (length == 4 && string_equals("OwnBook", tokens[1])) {
bool enabled = string_equals("true", tokens[3]);
opening_book_set_enabled(enabled);
}
else {
IO_PRINT("unknown option '%s'\n", tokens[1]);
IO_FLUSH();
}
}
static void uci_do(char** tokens, int length) {
if (length == 0 && currmove.valid()) {
makemove_makef(curr_board, currmove.get());
}
else {
uci_play_moves(tokens, length);
}
}
static bool uci_handleToken(char** tokens, int length) {
if (length == 0) {
return true;
}
char* token = tokens[0];
if (string_equals(token, "go")) {
uci_go(tokens + 1, length - 1);
}
else if (string_equals(token, "stop")) {
search_async_stop();
}
else if (string_equals(token, "ponderhit")) {
time_management_ponderhit(curr_board);
}
else if (string_equals(token, "position")) {
uci_position(tokens + 1, length - 1);
}
else if (string_equals(token, "ucinewgame")) {
transpositions_clear();
}
else if (string_equals(token, "uci")) {
IO_PRINT("id name fornax v");
uci_printversion();
IO_PRINT("\nid author Anders Fuglseth\n\n");
IO_PRINT("option name Move Overhead type spin default %d min %d max %d\n",
TIME_MANAGEMENT_OVERHEAD_DEFAULT, TIME_MANAGEMENT_OVERHEAD_MIN, TIME_MANAGEMENT_OVERHEAD_MAX);
IO_PRINT("option name Hash type spin default %d min %d max %d\n",
TRANSPOSITIONS_SIZE_MB_DEFAULT, TRANSPOSITIONS_SIZE_MB_MIN, TRANSPOSITIONS_SIZE_MB_MAX);
IO_PRINT("option name Ponder type check default false\n");
IO_PRINT("option name OwnBook type check default false\n");
IO_PRINT("option name Contempt type spin default %d min %d max %d\n", EVAL_CONTEMPT_DEFAULT, EVAL_CONTEMPT_MIN, EVAL_CONTEMPT_MAX);
IO_PRINT("uciok\n");
IO_FLUSH();
}
else if (string_equals(token, "isready")) {
IO_PRINT("readyok\n");
IO_FLUSH();
}
else if (string_equals(token, "quit")) {
return false;
}
else if (string_equals(token, "setoption")) {
uci_setoption(tokens + 1, length - 1);
}
//non uci commands
else if (string_equals(token, "d")) {
parsing_printboard(curr_board);
}
else if (string_equals(token, "info")) {
search_async_printinfo();
}
else if (string_equals(token, "do")) {
uci_do(tokens + 1, length - 1);
}
else if (string_equals(token, "perft")) {
uci_perft(tokens + 1, length - 1);
}
else if (string_equals(token, "test")) {
uci_test(tokens + 1, length - 1);
}
else if (string_equals(token, "bench")) {
testsuite_search(3);
}
else if (string_equals(token, "tt")) {
uci_tt(tokens + 1, length -1);
}
else if (string_equals(token, "eval")) {
if (length == 1) {
eval_evaluate_debug(curr_board);
}
else if (length == 2) {
move m = parsing_move_from_uci(tokens[1]);
if (curr_board->active) movepick_eval<BLACK, true>(curr_board, m);
else movepick_eval<WHITE, true>(curr_board, m);
}
}
else if (string_equals(token, "moves")) {
evalmove moves[128];
uint8_t size = movegen_generatef(curr_board, moves);
if (curr_board->active) movepick_annotate_list<BLACK>(curr_board, moves, size, 0);
else movepick_annotate_list<WHITE>(curr_board, moves, size, 0);
for (uint8_t n = 0; n < size; ++n) parsing_printmove_extra(curr_board, movepick_find_best(moves, size, n));
}
else if (string_equals(token, "pf")) {
Board b = parsing_from_fen_parts(tokens + 1, length - 1);
*curr_board = b;
}
else {
uci_handleToken(tokens + 1, length - 1);
}
return true;
}
bool uci_handlecommand(char* command) {
int token_length = strings_split(command, curr_tokens, " ");
if (token_length <= 0) return true;
assert(token_length < 512);
for (int i = 0; i < token_length; ++i) {
strings_trim(curr_tokens[i]);
}
int result = uci_handleToken(curr_tokens, token_length);
return result;
}