//
// opening_book.cpp
// fornax3
//
// Created by Anders on 28/03/2020.
//
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <stdio.h>
#include "opening_book.hpp"
#include "../libs/require.h"
#include "../libs/iolog.hpp"
#include "parsing.hpp"
#include "../moving/movegen.hpp"
#include "../moving/makemove.hpp"
#define BOOK_SIZE_MAX 50//max positions in the book
#define MOVES_SIZE_MAX 20 //max alternative moves for each position
#define PLY_MAX 10// max depth before book is disabled
static bool enabled = false;
typedef struct {
uint64_t ttkey;
move m[MOVES_SIZE_MAX];
uint8_t count;
} Opening;
static Opening* openings;
static Opening* opening_book_find(const Board* board) {
int i = 0;
Opening* opening;
while ((opening = &openings[i++])->ttkey != 0) {
if (opening->ttkey == board_get_hash(board)) {
return opening;
}
}
return NULL;
}
static void opening_register(const Board* board, const char* ucimove) {
Board b = *board;
move m = parsing_move_from_uci(ucimove);
require(parsing_is_valid_move(&b, m), "illegal move %s\n", ucimove);
Opening* opening = opening_book_find(&b);
if (opening != NULL) {
require(opening->count < MOVES_SIZE_MAX, "too many moves for this position, size=%d\n", MOVES_SIZE_MAX);
opening->m[opening->count] = m;
opening->count++;
return;
}
int i = 0;
while ((openings[i]).ttkey != 0) i++;
require(i < BOOK_SIZE_MAX, "book was full, size=%d\n", BOOK_SIZE_MAX);
Opening o;
o.ttkey = b.plyinfo[b.ply].hash;
o.m[0] = m;
o.count = 1;
openings[i] = o;
}
static void opening_moves_to_board(Board* board, ...) {
const char* ucimove;
va_list arglist;
va_start(arglist, board);
while ((ucimove = va_arg(arglist, const char *)) != NULL) {
move m = parsing_move_from_uci(ucimove);
require(parsing_is_valid_move(board, m), "illegal move %s\n", ucimove);
makemove_makef(board, m);
}
va_end( arglist );
}
static void opening_register_multi(const Board* b, ...) {
va_list arglist;
va_start(arglist, b);
const char* ucimove;
while ((ucimove = va_arg(arglist, const char *)) != NULL) {
opening_register(b, ucimove);
}
va_end( arglist );
}
#define OPENING_START() {Board b = parsing_from_fen(STARTPOS_FEN);\
#define OPENING_MOVES(...) {Board b = parsing_from_fen(STARTPOS_FEN);\
opening_moves_to_board(&b, __VA_ARGS__, NULL);
#define OPENING_REGISTER(expr, ...) expr; \
opening_register_multi(&b, __VA_ARGS__, NULL);\
}
#define OPENING_REGISTER_FEN(fen, ...) {Board b = parsing_from_fen(fen);\
opening_register_multi(&b, __VA_ARGS__, NULL);\
}
static move opening_pick(const Opening* opening) {
int i = rand() % opening->count;
return opening->m[i];
}
void opening_whitebook(void);
void opening_blackbook(void);
void opening_book_init(void) {
if (!enabled) return;
openings = (Opening*) calloc(BOOK_SIZE_MAX, sizeof(*openings));
unsigned int seed = (unsigned int) time(NULL);
srand(seed);
opening_whitebook();
opening_blackbook();
}
void opening_book_set_enabled(bool is_enabled) {
bool init = !enabled && is_enabled;
bool destroy = enabled && !is_enabled;
enabled = is_enabled;
if (init) opening_book_init();
else if (destroy) opening_book_destroy();
#ifdef DEBUGLOG
IO_PRINT("ok OwnBook = %s\n", enabled ? "true" : "false");
#endif
}
move opening_book_consult(const Board* board) {
if (!enabled) return 0;
if (board->ply > PLY_MAX) return 0;
Opening* opening = opening_book_find(board);
if (opening == NULL) return 0;
return opening_pick(opening);
}
void opening_book_destroy(void) {
free(openings);
}
void opening_whitebook(void) {
OPENING_REGISTER(OPENING_START(),
"d2d4", "d2d4", "d2d4", "d2d4", "d2d4",
"e2e4", "e2e4", "e2e4",
"g1f3",
"c2c4",
"g2g3");
OPENING_REGISTER(OPENING_MOVES("d2d4", "g8f6"),
"c2c4");
OPENING_REGISTER(OPENING_MOVES("d2d4", "d7d5"),
"c2c4");
OPENING_REGISTER(OPENING_MOVES("d2d4", "e7e6"),
"c2c4");
OPENING_REGISTER(OPENING_MOVES("e2e4", "c7c5"),
"g1f3");
OPENING_REGISTER(OPENING_MOVES("e2e4", "c7c5", "g1f3", "d7d6"),
"d2d4", "d2d4", "f1b5");
OPENING_REGISTER(OPENING_MOVES("e2e4", "e7e5"),
"g1f3");
OPENING_REGISTER(OPENING_MOVES("e2e4", "e7e6"),
"d2d4");
OPENING_REGISTER(OPENING_MOVES("e2e4", "c7c6"),
"d2d4");
OPENING_REGISTER(OPENING_MOVES("e2e4", "d7d5"),
"e4d5", "e4d5", "e4e5");
}
void opening_blackbook(void) {
OPENING_REGISTER(OPENING_MOVES("e2e4"),
"c7c5", "c7c5", "e7e5");
OPENING_REGISTER(OPENING_MOVES("e2e4", "c7c5", "g1f3"),
"d7d6", "d7d6", "b8c6");
OPENING_REGISTER(OPENING_MOVES("e2e4", "c7c5", "g1f3", "d7d6", "d2d4"),
"c5d4", "c5d4", "g8f6");
OPENING_REGISTER(OPENING_MOVES("e2e4", "c7c5", "b1c3"),
"d7d6", "b8c6", "b8c6");
OPENING_REGISTER(OPENING_MOVES("e2e4", "e7e5", "g1f3"),
"d7d6", "b8c6", "b8c6");
OPENING_REGISTER(OPENING_MOVES("e2e4", "e7e5", "g1f3", "b8c6", "f1b5"),
"a7a6");
OPENING_REGISTER(OPENING_MOVES("d2d4"),
"d7d5", "g8f6", "g8f6");
OPENING_REGISTER(OPENING_MOVES("d2d4", "d7d5", "c2c4"),
"e7e6", "e7e6", "e7e6", "e7e5");
OPENING_REGISTER(OPENING_MOVES("d2d4", "g8f6", "g1f3"),
"g7g6", "g7g6", "c7c5");
OPENING_REGISTER(OPENING_MOVES("c2c4"),
"g8f6", "c7c5", "c7c5", "e7e6", "e7e5", "c7c6");
OPENING_REGISTER(OPENING_MOVES("c2c4", "c7c5", "b1c3"),
"g7g6", "g7g6", "b8c6");
OPENING_REGISTER(OPENING_MOVES("c2c4", "c7c5", "g1f3"),
"g7g6", "g7g6", "g8f6");
OPENING_REGISTER(OPENING_MOVES("g2g3"),
"d7d5", "d7d5", "g8f6", "g7g6", "e7e5");
OPENING_REGISTER(OPENING_MOVES("f2f4"),
"d7d5", "d7d5", "g8f6", "g7g6", "e7e5");
OPENING_REGISTER(OPENING_MOVES("e2e3"),
"d7d5", "d7d5", "g8f6", "g7g6", "e7e5");
OPENING_REGISTER(OPENING_MOVES("b1c3"),
"c7c5", "d7d5", "g8f6", "g7g6", "e7e5");
OPENING_REGISTER(OPENING_MOVES("g1f3"),
"d7d5","d7d5", "d7d5", "g8f6", "g7g6", "c7c5", "e7e6", "c7c6");
OPENING_REGISTER(OPENING_MOVES("b2b4"),
"c7c5", "d7d5", "g8f6", "g7g6", "e7e6");
}