Skip to content

Commit

Permalink
Now selects a random opening line from an opening book (if the book i…
Browse files Browse the repository at this point in the history
…s present)
  • Loading branch information
Thomas Patterson committed Dec 30, 2020
1 parent 6cda986 commit 5737b65
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 12 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,6 @@ MigrationBackup/
FodyWeavers.xsd

*.sln
*.vcxproj*
*.vcxproj*

Dionysus/Book_Formula17/
1 change: 1 addition & 0 deletions Dionysus/board.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Board {
bool is_threatened(int, int, std::vector<Move>);

std::vector<int> get_squares();
int get_square(int ind);
bool is_white_to_move();
std::vector<std::vector<bool>> get_castling_rights();
int get_half_move_clock();
Expand Down
4 changes: 4 additions & 0 deletions Dionysus/board_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,10 @@ std::vector<int> Board::get_squares() {
return squares;
}

int Board::get_square(int ind) {
return squares[ind];
}

bool Board::is_white_to_move() {
return white_to_move;
}
Expand Down
2 changes: 2 additions & 0 deletions Dionysus/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@
#define RESET "\033[0m"
#define RED "\033[31m"
#define GREEN "\033[32m"

#define BOOK_NAME "Book_Formula17/Formula17.bin"
7 changes: 3 additions & 4 deletions Dionysus/dionysus.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#include <iostream>
#include <thread>
#include <stdio.h>

#include "board.h"
#include "move.h"
#include "utils.h"
#include "searcher.h"
#include "defs.h"

Board board;
Searcher searcher;
Expand Down Expand Up @@ -77,7 +79,6 @@ void process_UCI() {

//when recieve go, start searching on currently loaded position
else if (command == "go") {

//need to join thread from previous search before we can start this one
if (first_search) first_search = false;
else searching_thread.join();
Expand Down Expand Up @@ -105,8 +106,6 @@ void process_UCI() {
searching_thread.detach();
}



int main() {
process_UCI();
process_UCI();
}
137 changes: 132 additions & 5 deletions Dionysus/searcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

Searcher::Searcher() {
trans_table.clear();
if (using_opening_book) init_opening_book();
}

//used to sort moves before alpah beta pruning
Expand All @@ -33,6 +34,39 @@ bool compare_moves(Move a, Move b) {
return (a.prev_square % 6) > (b.prev_square % 6);
}

//load opening book moves into memory
void Searcher::init_opening_book() {
FILE* book;
errno_t res = fopen_s(&book, BOOK_NAME, "r");

//if opening the file failed
if (res != 0 || !book) {
std::cout << "Cant open file" << std::endl;
using_opening_book = false;
return;
}

//find out how many entries there are
long num_entries;
fseek(book, 0, SEEK_END);
num_entries = ftell(book) / sizeof(BookEntry);
rewind(book);

entries = (BookEntry*)malloc(sizeof(BookEntry) * num_entries);

if (!entries) {
std::cout << "Something went wrong" << std::endl;
using_opening_book = false;
return;
}

//actually read the data
int result = fread(entries, sizeof(BookEntry), num_entries, book);

using_opening_book = num_entries > 0;
book_size = num_entries;
}

//quiescence is run at each terminal node in negamax, to stabilise the position
//means we do not stop search halfway through a queen trade, and think we are a queen up/down
double Searcher::quiescence(double alpha, double beta, Board *board) {
Expand All @@ -49,7 +83,7 @@ double Searcher::quiescence(double alpha, double beta, Board *board) {
std::sort(valid_moves.begin(), valid_moves.end(), compare_moves);

//iterate through each capture, as in negamax
for (Move m : valid_moves) {
for (const Move &m : valid_moves) {
if (board->make_move(m)) {
double score = -quiescence(-beta, -alpha, board);
board->undo_move(m);
Expand Down Expand Up @@ -105,7 +139,7 @@ SearchResult Searcher::negamax(int depth, double alpha, double beta, Board *boar
if (first.player != -1) valid_moves.insert(valid_moves.begin(), first);

//iterate through each move
for (Move m : valid_moves) {
for (const Move &m : valid_moves) {
if (board->make_move(m)) {
SearchResult sr;

Expand Down Expand Up @@ -162,9 +196,10 @@ SearchResult Searcher::negamax(int depth, double alpha, double beta, Board *boar
}

//stops the search after milliseconds
void Searcher::stop_searching(int milliseconds) {
//only if we are still doing the same search - could be onto the next one if for example the other search was stopped early to play a book move
void Searcher::stop_searching(int milliseconds, int SID) {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
stop();
if (SID == searchID) stop();
}

void Searcher::stop() {
Expand All @@ -176,7 +211,42 @@ Move Searcher::get_best_move(int milliseconds, Board *board) {
searching = true;

//starts timer
std::thread stop_search([this, milliseconds] { stop_searching(milliseconds); });
searchID++;
std::thread stop_search([this, milliseconds] { stop_searching(milliseconds, searchID); });

//check for a book move
//have to swap the endianness of all the fields in each entry, since they are stored in the binary field as big endian
if (using_opening_book) {
std::vector<BookEntry*> matches;
int total_weight = 0;
//collect all the entries together which fit the current position
for (int i = 0; i < book_size; i++) {
if (board->get_zobrist_hash() == endian_swap_u64(entries[i].key)) {
matches.push_back(&entries[i]);
entries[i].weight = endian_swap_u16(entries[i].weight);
total_weight += entries[i].weight;
}
}
//if we found any entries for this position
//choose a random one based on the weighting of each entry
if (total_weight > 0) {
std::random_device rd; // obtain a random number from hardware
std::mt19937 gen(rd()); // seed the generator
std::uniform_int_distribution<> distr(0, total_weight); // define the range
int r = distr(gen); // generate a random integer
int cum_weight = 0;
for (const auto entry : matches) {
cum_weight += entry->weight;
//return the formatted move which we land on
if (cum_weight >= r) {
Move m = decipher_polyglot_move_code(endian_swap_u16(entry->move), board);
std::cout << "Using book move" << std::endl;
stop_search.detach();
return m;
}
}
}
}

trans_table.clear();

Expand Down Expand Up @@ -213,4 +283,61 @@ Move Searcher::get_random_move(Board *board) {
}

return valid_moves[i];
}

Move Searcher::decipher_polyglot_move_code(unsigned short code, Board *board) {

//extract eahc piece of information from bit field
int dst_file = (code >> 0) & 7;
int dst_row = (code >> 3) & 7;
int src_file = (code >> 6) & 7;
int src_row = (code >> 9) & 7;
int prom_pc = (code >> 12) & 7;

//convert row/file to the index in board's representation
int src_code = (7 - src_row) * 8 + src_file;
int dst_code = (7 - dst_row) * 8 + dst_file;

int prev_square = board->get_square(dst_code);

//determine the start and end piece types
int starting_piece = board->get_square(src_code) % 6;
int end_piece = starting_piece;
switch (prom_pc) {
case 0:
break;
case 1:
end_piece = KNIGHT;
break;
case 2:
end_piece = BISHOP;
break;
case 3:
end_piece = ROOK;
break;
case 4:
end_piece = QUEEN;
break;
}

int player = board->is_white_to_move() ? WHITE : BLACK;

//if castle, need to change target square to just 2 squares along instead of on the rook
if (starting_piece == KING) {
std::vector<std::pair<int, int>> castle_moves = { std::make_pair(4, 7), std::make_pair(4, 0), std::make_pair(60, 63), std::make_pair(60, 56) };

for (int i = 0; i < 4; i++) {
if (src_code == castle_moves[i].first && dst_code == castle_moves[i].second) {
prev_square = EMPTY_SQUARE;
dst_code = src_code + (dst_code > src_code ? 2 : -2);
break;
}
}
}

//return the extracted move
Move m = { player, src_code, dst_code, starting_piece, end_piece, prev_square };
return m;


}
15 changes: 14 additions & 1 deletion Dionysus/searcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,25 @@

class Searcher {

struct BookEntry {
unsigned long long key;
unsigned short move;
unsigned short weight;
unsigned int learn;
};

BookEntry* entries = nullptr;
int book_size = 0;
bool searching = false;
bool using_opening_book = true;
int searchID = 0;
TranspositionTable trans_table;

void init_opening_book();
double quiescence(double, double, Board*);
SearchResult negamax(int, double, double, Board*, Move first = { -1 });
void stop_searching(int);
void stop_searching(int, int);
Move decipher_polyglot_move_code(unsigned short code, Board* board);

public:
Searcher();
Expand Down
26 changes: 26 additions & 0 deletions Dionysus/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,30 @@ char get_char_from_piece(int piece) {
case KING:
return 'K';
}
}

unsigned short endian_swap_u16(unsigned short x) {
x = (x >> 8) |
(x << 8);
return x;
}

unsigned int endian_swap_u32(unsigned int x) {
x = (x >> 24) |
((x << 8) & 0x00FF0000) |
((x >> 8) & 0x0000FF00) |
(x << 24);
return x;
}

unsigned long long endian_swap_u64(unsigned long long x) {
x = (x >> 56) |
((x << 40) & 0x00FF000000000000) |
((x << 24) & 0x0000FF0000000000) |
((x << 8) & 0x000000FF00000000) |
((x >> 8) & 0x00000000FF000000) |
((x >> 24) & 0x0000000000FF0000) |
((x >> 40) & 0x000000000000FF00) |
(x << 56);
return x;
}
6 changes: 5 additions & 1 deletion Dionysus/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ int get_square_index_from_notation(std::string);
std::string get_notation_from_square_index(int);

int get_piece_from_char(char);
char get_char_from_piece(int);
char get_char_from_piece(int);

unsigned short endian_swap_u16(unsigned short x);
unsigned int endian_swap_u32(unsigned int x);
unsigned long long endian_swap_u64(unsigned long long x);

0 comments on commit 5737b65

Please sign in to comment.