Skip to content

Commit

Permalink
Added MoveOverhead option and improved time management.
Browse files Browse the repository at this point in the history
  • Loading branch information
konsolas committed Aug 31, 2019
1 parent 3b894cc commit d78913e
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 76 deletions.
20 changes: 10 additions & 10 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ int main(int argc, char *argv[]) {

// Parameters
size_t threads = 1;
int move_overhead = 50;
size_t syzygy_resolve = 512;
std::string tb_path;

Expand All @@ -64,14 +65,15 @@ int main(int argc, char *argv[]) {

// Print options
std::cout << "option name Hash type spin default 128 min 1 max 1048576" << std::endl;
std::cout << "option name MoveOverhead type spin default 50 min 0 max 10000" << std::endl;
std::cout << "option name Threads type spin default 1 min 1 max 128" << std::endl;
std::cout << "option name SyzygyPath type string default <empty>" << std::endl;
std::cout << "option name SyzygyResolve type spin default 512 min 1 max 1024" << std::endl;
std::cout << "option name Ponder type check default false" << std::endl;

std::cout << "uciok" << std::endl;
} else if (cmd == "setoption") {
if(search) {
if (search) {
std::cerr << "warn: setoption command rejected as search is in progress" << std::endl;
} else {
std::string name;
Expand All @@ -91,6 +93,10 @@ int main(int argc, char *argv[]) {
delete tt;
tt = new tt::hash_t(hash_size * MB);
}
} else if (name == "MoveOverhead") {
std::string value;
iss >> value;
iss >> move_overhead;
} else if (name == "Threads") {
std::string value;
iss >> value; // Skip value
Expand Down Expand Up @@ -235,15 +241,9 @@ int main(int argc, char *argv[]) {
search_abort = false;

search_limits_t limits = time_control.enabled ?
search_limits_t(board->now,
time_control.time,
time_control.inc,
time_control.moves)
:
search_limits_t(max_time,
max_depth,
max_nodes,
root_moves);
search_limits_t(time_control.time - move_overhead,
time_control.inc, time_control.moves) :
search_limits_t(max_time, max_depth, max_nodes, root_moves);

limits.threads = threads;
limits.syzygy_resolve = syzygy_resolve;
Expand Down
94 changes: 50 additions & 44 deletions search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include <utility>
#include <vector>
#include <algorithm>
#include <type_traits>

#include "search.h"
#include "move.h"
Expand All @@ -22,20 +21,18 @@ search_t::search_t(board_t board, tt::hash_t *tt, const processed_params_t &para

// Copy legal moves to root_moves
std::copy_if(buf, buf + pseudo_legal, std::back_inserter(root_moves),
[board](move_t move) { return board.is_legal(move); });
[board](move_t move) { return board.is_legal(move); });

// Create an evaluator for each thread
for(size_t i = 0; i < limits.threads; i++) {
for (size_t i = 0; i < limits.threads; i++) {
evaluators.emplace_back(params, 8 * MB);
}

// Find intersection of root moves with UCI searchmoves
if (!limits.search_moves.empty()) {
root_moves.erase(std::remove_if(root_moves.begin(), root_moves.end(),
[limits](move_t move) {
return std::find(limits.search_moves.begin(), limits.search_moves.end(), move) ==
limits.search_moves.end();
}), root_moves.end());
root_moves.erase(std::remove_if(root_moves.begin(), root_moves.end(),[limits](move_t move) {
return std::find(limits.search_moves.begin(), limits.search_moves.end(), move) == limits.search_moves.end();
}), root_moves.end());
}
}

Expand All @@ -58,7 +55,7 @@ search_result_t search_t::think(std::atomic_bool &aborted) {
} else {
tb_root_moves = root_probe_wdl(board, tb_wdl);

if(!tb_root_moves.empty()) {
if (!tb_root_moves.empty()) {
// WDL tablebases available: set root moves and only use tablebases during search if we're winning.
root_moves = std::move(tb_root_moves);
if (tb_wdl <= 0) {
Expand All @@ -68,13 +65,13 @@ search_result_t search_t::think(std::atomic_bool &aborted) {
}
}

if(root_moves.empty()) {
if (root_moves.empty()) {
std::cerr << "warn: no legal moves found at the root position" << std::endl;
return {EMPTY_MOVE, EMPTY_MOVE, 0, 0};
return {EMPTY_MOVE, EMPTY_MOVE};
}

// Instantly exit if there is only one legal move
if(limits.game_situation && root_moves.size() == 1) {
if (limits.game_situation && root_moves.size() == 1) {
// Try to obtain a ponder move from the transposition table
board.move(root_moves[0]);

Expand All @@ -86,18 +83,18 @@ search_result_t search_t::think(std::atomic_bool &aborted) {

board.unmove();

return {root_moves[0], ponder_move, 1, 0};
return {root_moves[0], ponder_move};
}

// Start threads
for(size_t tid = 0; tid < limits.threads; tid++) {
for (size_t tid = 0; tid < limits.threads; tid++) {
contexts.emplace_back(&boards[tid], &evaluators[tid], tt, use_tb);
}

// Start all the threads
std::vector<std::future<void>> futures;
futures.reserve(limits.threads);
for(size_t tid = 0; tid < limits.threads; tid++) {
for (size_t tid = 0; tid < limits.threads; tid++) {
futures.push_back(std::async(std::launch::async, &search_t::thread_start,
this, std::ref(contexts[tid]), std::ref(aborted), tid));
}
Expand All @@ -108,7 +105,7 @@ search_result_t search_t::think(std::atomic_bool &aborted) {
// Then abort and wait for all the helper threads
aborted = true;

for(size_t tid = 1; tid < limits.threads; tid++) {
for (size_t tid = 1; tid < limits.threads; tid++) {
futures[tid].wait();
}
} else {
Expand All @@ -121,7 +118,7 @@ search_result_t search_t::think(std::atomic_bool &aborted) {

if (pv.empty()) {
std::cerr << "warn: insufficient time to search to depth 1" << std::endl;
return {EMPTY_MOVE, EMPTY_MOVE, 0, 0};
return {EMPTY_MOVE, EMPTY_MOVE};
}

// If we don't have a ponder move (e.g. if we're currently failing high but the search was aborted), look in the tt.
Expand All @@ -139,7 +136,7 @@ search_result_t search_t::think(std::atomic_bool &aborted) {
pv.push_back(ponder_move);
}

return {pv[0], pv[1], main_root_depth, CHRONO_DIFF(start, engine_clock::now())};
return {pv[0], pv[1]};
}

void search_t::enable_timer() {
Expand All @@ -156,39 +153,43 @@ void search_t::wait_for_timer() {
while (!timer_started) timer_cnd.wait(lock);
}

void search_t::thread_start(pvs::context_t &context, const std::atomic_bool &aborted, size_t tid) {
void search_t::thread_start(pvs::context_t &context, const std::atomic_bool &aborted, const size_t tid) {
int prev_score = 0;

for(int depth = 1; depth <= MAX_PLY && (tid != 0 || keep_searching(depth)); depth++) {
int score = search_aspiration(context, prev_score, depth, aborted, tid);
if(aborted) break;
// Check if this is the main thread
if (tid == 0) {
int junk;

if(tid == 0) {
main_root_depth = depth;
}
// Scale search time based on the complexity of the position
double tapering_factor = evaluators[0].eval_material(*context.get_board(), junk, junk);
double complexity = std::clamp(root_moves.size() / 30.0, 0.5, 8.0) * tapering_factor + (1 - tapering_factor);
int adjusted_suggestion = static_cast<int>(complexity * limits.suggested_time_limit);

for (int depth = 1; depth <= MAX_PLY && (tid != 0 || keep_searching(depth)); depth++) {
int score = search_aspiration(context, prev_score, depth, aborted, tid);
if (aborted) break;

if(tid == 0) { // Main thread
// If in game situation, try and manage time
if (limits.game_situation && timer_started) {
auto elapsed = CHRONO_DIFF(timer_start, engine_clock::now());

// Scale the search time based on the complexity of the position
int junk;
double tapering_factor = evaluators[0].eval_material(*context.get_board(), junk, junk);
double complexity = std::clamp(root_moves.size() / 30.0, 0.5, 3.0) * tapering_factor + (1 - tapering_factor);
int adjusted_suggestion = static_cast<int>(complexity * limits.suggested_time_limit);

// See if we can justify ending the search early, as long as we're not doing badly
if (depth <= 5 || score > prev_score - 50) {
if (depth <= 5 || score > prev_score - 10) {
// If it's unlikely that we'll search deeper
if (elapsed > adjusted_suggestion * 0.7) {
break;
}
}
}
}

prev_score = score;
prev_score = score;
}
} else {
for (int depth = 1; depth <= MAX_PLY; depth++) {
int score = search_aspiration(context, prev_score, depth, aborted, tid);
if (aborted) break;
prev_score = score;
}
}
}

Expand All @@ -211,11 +212,16 @@ int search_t::search_aspiration(pvs::context_t &context, int prev_score, int dep

// Check if we need to search again
while (true) {
if(tid == 0 && time > 1000) {
if (tid == 0 && time > 1000) {
score = context.search_root(root_moves,
[this, tid, depth, &aborted](int score) { print_stats(boards[tid], score, depth, tt::EXACT, aborted); },
[](int num, move_t move) { std::cout << "info currmove " << move << " currmovenumber " << num << std::endl; },
alpha, beta, depth, aborted);
[this, tid, depth, &aborted](int score) {
print_stats(boards[tid], score, depth, tt::EXACT, aborted);
},
[](int num, move_t move) {
std::cout << "info currmove " << move << " currmovenumber " << num
<< std::endl;
},
alpha, beta, depth, aborted);
} else {
score = context.search_root(root_moves, nullptr, nullptr, alpha, beta, depth, aborted);
}
Expand All @@ -225,12 +231,12 @@ int search_t::search_aspiration(pvs::context_t &context, int prev_score, int dep
// Only save the principal variation if the bound is LOWER or EXACT
if (score >= beta) {
context.save_pv();
if(tid == 0) {
if (tid == 0) {
print_stats(boards[tid], score, depth, tt::LOWER, aborted);
}
beta = std::min(INF, beta + delta);
} else if (score <= alpha) {
if(tid == 0) {
if (tid == 0) {
print_stats(boards[tid], score, depth, tt::UPPER, aborted);
}
beta = (alpha + beta) / 2;
Expand All @@ -243,7 +249,7 @@ int search_t::search_aspiration(pvs::context_t &context, int prev_score, int dep
delta = delta + delta / 2;
}

if(tid == 0) {
if (tid == 0) {
print_stats(boards[tid], score, depth, tt::EXACT, aborted);
}

Expand All @@ -258,7 +264,7 @@ bool search_t::keep_searching(int depth) {

U64 search_t::count_nodes() {
U64 total_nodes = 0;
for(size_t tid = 0; tid < limits.threads; tid++) {
for (size_t tid = 0; tid < limits.threads; tid++) {
total_nodes += contexts[tid].get_nodes();
}

Expand All @@ -267,7 +273,7 @@ U64 search_t::count_nodes() {

U64 search_t::count_tb_hits() {
U64 total_tb_hits = 0;
for(size_t tid = 0; tid < limits.threads; tid++) {
for (size_t tid = 0; tid < limits.threads; tid++) {
total_tb_hits += contexts[tid].get_tb_hits();
}

Expand Down
32 changes: 10 additions & 22 deletions search.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,23 @@

struct search_limits_t {
// Game situation
explicit search_limits_t(int now, int time, int inc, int moves_to_go) {
explicit search_limits_t(int time, int inc, int moves_to_go) {
game_situation = true;

// Set other limits
depth_limit = MAX_PLY;
node_limit = UINT64_MAX;
search_moves = std::vector<move_t>();

if(moves_to_go > 0) {
// Repeating time controls
suggested_time_limit = (time / (moves_to_go + 1));
suggested_time_limit += inc / 2;
hard_time_limit = suggested_time_limit * 3;
if(moves_to_go > 1 && hard_time_limit > time / 2) {
hard_time_limit = time / 2;
}
} else {
// Other time controls
suggested_time_limit = (time / std::max(80 - now, 60));
suggested_time_limit += inc / 2;
hard_time_limit = suggested_time_limit * 3;
}
// Use the same time management for repeating and fixed time controls
if(moves_to_go == 0) moves_to_go = 60;

suggested_time_limit = (time / (moves_to_go + 1)) + inc;
hard_time_limit = std::min(suggested_time_limit * 8, time / std::min(4, moves_to_go));

// Clamp both time limits
suggested_time_limit = std::clamp(suggested_time_limit, 10, std::max(10, time - 50));
hard_time_limit = std::clamp(hard_time_limit, 10, std::max(10, time - 50));
hard_time_limit = std::max(10, std::min(hard_time_limit, time));
suggested_time_limit = std::max(1, std::min(suggested_time_limit, hard_time_limit));
}

// Custom search
Expand All @@ -76,10 +67,8 @@ struct search_limits_t {
};

struct search_result_t {
move_t best_move{};
move_t ponder{};
int root_depth = 0;
long long search_time = 0;
move_t best_move;
move_t ponder;
};

class search_t {
Expand Down Expand Up @@ -110,7 +99,6 @@ class search_t {
std::vector<evaluator_t> evaluators;
std::vector<board_t> boards;
std::vector<pvs::context_t> contexts;
int main_root_depth = 0;

// Root moves
std::vector<move_t> root_moves;
Expand Down

0 comments on commit d78913e

Please sign in to comment.