Skip to content

Commit ed9f1b6

Browse files
authored
Merge pull request #20 from konsolas/refactor
Refactor search and add back Lazy SMP
2 parents 4348afd + 0bcfabf commit ed9f1b6

File tree

16 files changed

+1189
-1081
lines changed

16 files changed

+1189
-1081
lines changed

CMakeLists.txt

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ cmake_minimum_required(VERSION 3.8)
22
project(Topple)
33

44
set(CMAKE_CXX_STANDARD 17)
5-
set(CMAKE_CXX_FLAGS "-march=skylake -static -Ofast -pedantic -Wall")
5+
set(TOPPLE_VERSION 0.6.0)
66

7+
# Source files for different targets
78
set(SOURCE_FILES
89
board.h board.cpp
910
endgame.h endgame.cpp
@@ -14,7 +15,7 @@ set(SOURCE_FILES
1415
move.h
1516
eval.h eval.cpp
1617
search.h search.cpp
17-
smp.h smp.cpp
18+
pvs.h pvs.cpp
1819
syzygy/tbcore.h
1920
syzygy/tbprobe.h syzygy/tbprobe.cpp syzygy/tbresolve.h syzygy/tbresolve.cpp)
2021
set(TEST_FILES testing/catch.hpp testing/runner.cpp testing/util.h testing/util.cpp
@@ -24,8 +25,40 @@ set(TEST_FILES testing/catch.hpp testing/runner.cpp testing/util.h testing/util.
2425
testing/tests/test_see.cpp)
2526
set(TUNE_FILES tuning/tunemain.cpp tuning/tuner.cpp tuning/tuner.h)
2627

28+
# Add version definitions
29+
add_definitions(-DTOPPLE_VER="${TOPPLE_VERSION}")
30+
2731
add_executable(Topple ${SOURCE_FILES} main.cpp)
2832
add_executable(ToppleTest ${SOURCE_FILES} ${TEST_FILES})
2933
add_executable(ToppleTune ${SOURCE_FILES} ${TUNE_FILES})
3034

31-
set_target_properties(ToppleTune PROPERTIES COMPILE_FLAGS "-DTOPPLE_TUNE")
35+
# Max optimisation for tuning
36+
set_target_properties(ToppleTune PROPERTIES COMPILE_FLAGS "-DTOPPLE_TUNE -Ofast -march=native")
37+
38+
# Link pthreads on linux
39+
set(THREADS_PREFER_PTHREAD_FLAG ON)
40+
find_package(Threads REQUIRED)
41+
target_link_libraries(Topple Threads::Threads)
42+
target_link_libraries(ToppleTest Threads::Threads)
43+
target_link_libraries(ToppleTune Threads::Threads)
44+
45+
# Need static compile for MinGW on windows, but cannot compile statically on unix.
46+
if (MINGW)
47+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
48+
endif ()
49+
50+
# Set -march for the Topple target
51+
target_compile_options(Topple PUBLIC -march=native)
52+
53+
# Configure the "Release" target
54+
if (CMAKE_BUILD_TYPE STREQUAL "Release")
55+
add_custom_target(Release)
56+
add_executable(Topple_${TOPPLE_VERSION}_legacy ${SOURCE_FILES} main.cpp)
57+
target_compile_options(Topple_${TOPPLE_VERSION}_legacy PUBLIC -s)
58+
add_dependencies(Release Topple_${TOPPLE_VERSION}_legacy)
59+
foreach(RELEASE_ARCH core2 nehalem westmere sandybridge ivybridge haswell broadwell skylake znver1 bdver4)
60+
add_executable(Topple_${TOPPLE_VERSION}_${RELEASE_ARCH} ${SOURCE_FILES} main.cpp)
61+
target_compile_options(Topple_${TOPPLE_VERSION}_${RELEASE_ARCH} PUBLIC -s -march=${RELEASE_ARCH})
62+
add_dependencies(Release Topple_${TOPPLE_VERSION}_${RELEASE_ARCH})
63+
endforeach(RELEASE_ARCH)
64+
endif ()

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
# Topple
22

33
Topple is a UCI-compatible chess engine.
4-
Topple v0.2.1 is rated 2457 in CCRL 40/40 and 2472 in CCRL 40/40.
4+
Topple v0.5.0 is rated 2801 in CCRL 40/40 and 2845 in CCRL 40/4.
55

66
## Usage
77
Topple requires a GUI that supports the UCI protocol to be used comfortably, although it can be used from the command line.
8-
Three configuration options are made available: `Hash`, `Threads` and `Clear Hash`.
8+
Five configuration options are made available: `Hash`, `Threads`, `SyzygyPath`, `SyzygyResolve` and `Ponder`.
99

1010
The `Hash` option sets the size of the main transposition table in MiB. If the size given is not a power of two, Topple will round it down to next lowest power of 2 to maximise probing efficiency. For example, if a value of 1000 is specified, Topple will only use a 512 MiB hash table. `Hash` does not control the value of the other tables in Topple, such as those used for move generation, evaluation and other data structures.
1111

1212
The `Threads` option sets the number of search threads that Topple will use. Topple may use additional threads for keeping track of inputs (such as the UCI `stop` command). Topple utilises additional threads by using Lazy SMP, so the `Hash` value should be increased to improve scaling with additional threads.
1313

14-
The `Clear Hash` option deletes and reallocates Topple's main transposition table.
14+
The `SyzygyPath` option sets the location in which Topple should search for Syzygy tablebases. These can be used to significantly improve playing strength in the endgame. Multiple paths should be delimited by a semicolon on Windows and a colon on other operating systems.
15+
16+
The `SyzygyResolve` option allows Topple to prettify searches which end in a tablebase position by playing out a DTZ optimal line to mate, and returning an appropriate mate score. The value of this option determines the maximum length of the playout.
17+
18+
The `Ponder` option has no effect, but is used to indicate that Topple has the ability to think during their opponent's time.
1519

1620
## Techniques used
1721
- Alpha-beta Principal Variation Search
@@ -46,4 +50,6 @@ The `Clear Hash` option deletes and reallocates Topple's main transposition tabl
4650
- Extensions to UCI:
4751
- "eval" command returns a static evaluation of the position
4852
- "print" displays a textual representation of the board and previous moves
49-
- "position moves ..." is stateful and can be used to continue an existing position
53+
- "mirror" flips the colours in the current position
54+
- "position moves ..." is stateful and can be used to continue an existing position
55+
- "tbprobe dtz|wdl" can be used to directly probe Syzygy tablebases for the current position

board.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <vector>
77
#include <iterator>
88
#include <iostream>
9+
#include <cstring>
910

1011
#include "board.h"
1112
#include "testing/catch.hpp"
@@ -158,7 +159,7 @@ void board_t::unmove() {
158159
}
159160
}
160161

161-
board_t::board_t(std::string fen) {
162+
board_t::board_t(const std::string &fen) {
162163
std::istringstream iss(fen);
163164
std::vector<std::string> split((std::istream_iterator<std::string>(iss)), std::istream_iterator<std::string>());
164165

@@ -591,7 +592,7 @@ void board_t::mirror() {
591592
memcpy(old_data, sq_data, 64 * sizeof(sq_data_t));
592593
for (uint8_t sq = 0; sq < 64; sq++) {
593594
if(old_data[sq].occupied) switch_piece<true>(old_data[sq].team, old_data[sq].piece, sq);
594-
};
595+
}
595596
for (uint8_t sq = 0; sq < 64; sq++) {
596597
if(old_data[sq].occupied) switch_piece<true>(Team(!old_data[sq].team), old_data[sq].piece, MIRROR_TABLE[sq]);
597598
}

board.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ struct sq_data_t {
4848
*/
4949
struct board_t {
5050
board_t() = default;
51-
explicit board_t(std::string fen);
51+
explicit board_t(const std::string &fen);
5252

5353
void move(move_t move);
5454
void unmove();
@@ -130,6 +130,8 @@ inline std::ostream &operator<<(std::ostream &stream, const board_t &board) {
130130
case KING:
131131
stream << (board.sq_data[square_index(file, rank)].team ? "k" : "K");
132132
break;
133+
default:
134+
assert(false);
133135
}
134136
} else {
135137
stream << ".";

main.cpp

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <sstream>
44
#include <iomanip>
55
#include <future>
6+
#include <climits>
67

78
#include "board.h"
89
#include "movegen.h"
@@ -12,8 +13,6 @@
1213

1314
#include "syzygy/tbprobe.h"
1415

15-
#define TOPPLE_VER "0.5.0"
16-
1716
U64 perft(board_t &, int);
1817

1918
int main(int argc, char *argv[]) {
@@ -35,13 +34,13 @@ int main(int argc, char *argv[]) {
3534
std::unique_ptr<search_t> search = nullptr;
3635
std::atomic_bool search_abort;
3736
std::future<void> future;
37+
search_result_t last_result;
3838

3939
// Evaluation
4040
std::unique_ptr<evaluator_t> evaluator = std::make_unique<evaluator_t>(eval_params_t(), 64 * MB);
4141

4242
// Parameters
4343
size_t threads = 1;
44-
int smp_split_depth = 10;
4544
size_t syzygy_resolve = 512;
4645
std::string tb_path;
4746

@@ -66,7 +65,6 @@ int main(int argc, char *argv[]) {
6665
// Print options
6766
std::cout << "option name Hash type spin default 128 min 1 max 1048576" << std::endl;
6867
std::cout << "option name Threads type spin default 1 min 1 max 128" << std::endl;
69-
std::cout << "option name SMPSplitDepth type spin default 10 min 1 max 128" << std::endl;
7068
std::cout << "option name SyzygyPath type string default <empty>" << std::endl;
7169
std::cout << "option name SyzygyResolve type spin default 512 min 1 max 1024" << std::endl;
7270
std::cout << "option name Ponder type check default false" << std::endl;
@@ -87,10 +85,6 @@ int main(int argc, char *argv[]) {
8785
// Resize hash
8886
delete tt;
8987
tt = new tt::hash_t(hash_size * MB);
90-
} else if (name == "SMPSplitDepth") {
91-
std::string value;
92-
iss >> value; // Skip value
93-
iss >> smp_split_depth;
9488
} else if (name == "Threads") {
9589
std::string value;
9690
iss >> value; // Skip value
@@ -244,18 +238,41 @@ int main(int argc, char *argv[]) {
244238
max_nodes,
245239
root_moves);
246240

241+
if(limits.game_situation && !ponder
242+
&& limits.suggested_time_limit < last_result.search_time) {
243+
tt::entry_t h = {};
244+
if (tt->probe(board->record[board->now].hash, h)) {
245+
move_t hash_move = board->to_move(h.move);
246+
247+
if(h.bound() == tt::EXACT && h.depth() >= last_result.root_depth) {
248+
board->move(hash_move);
249+
tt::entry_t ponder_h = {};
250+
move_t ponder_move = EMPTY_MOVE;
251+
if (tt->probe(board->record[board->now].hash, ponder_h)) {
252+
ponder_move = board->to_move(h.move);
253+
}
254+
board->unmove();
255+
256+
std::cout << "bestmove " << hash_move;
257+
if(ponder_move != EMPTY_MOVE) {
258+
std::cout << " ponder " << ponder_move;
259+
}
260+
std::cout << std::endl;
261+
continue;
262+
}
263+
}
264+
}
265+
247266
limits.threads = threads;
248267
limits.syzygy_resolve = syzygy_resolve;
249268

250-
limits.split_depth = smp_split_depth;
251-
252269
search = std::make_unique<search_t>(*board, tt, *evaluator, limits);
253270

254271
if (!ponder) search->enable_timer();
255272

256273
// Start search
257274
future = std::async(std::launch::async,
258-
[&tt, &search, ponder, &search_abort, limits] {
275+
[&last_result, &tt, &search, ponder, &search_abort, limits] {
259276
std::future<search_result_t> bm = std::async(std::launch::async,
260277
&search_t::think,
261278
search.get(),
@@ -275,6 +292,8 @@ int main(int argc, char *argv[]) {
275292
result = bm.get();
276293
}
277294

295+
last_result = result;
296+
278297
std::cout << "bestmove " << result.best_move;
279298
if(result.ponder != EMPTY_MOVE) {
280299
std::cout << " ponder " << result.ponder;

movegen.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
#include "move.h"
99
#include "board.h"
10-
#include "search.h"
1110

1211
struct board_t;
1312

movesort.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
#include "movesort.h"
66
#include "move.h"
77

8-
movesort_t::movesort_t(GenMode mode, const search_t::context_t &context, move_t hash_move, move_t refutation, int ply) :
9-
mode(mode), context(context), hash_move(hash_move), refutation(refutation), ply(ply), gen(movegen_t(context.board)) {
10-
killer_1 = context.heur.killers.primary(ply);
11-
killer_2 = context.heur.killers.secondary(ply);
8+
movesort_t::movesort_t(GenMode mode, const heuristic_set_t &heuristics, const board_t &board, move_t hash_move, move_t refutation, int ply) :
9+
mode(mode), heur(heuristics), board(board), hash_move(hash_move), refutation(refutation), gen(movegen_t(board)) {
10+
killer_1 = heur.killers.primary(ply);
11+
killer_2 = heur.killers.secondary(ply);
1212
if(ply > 2) {
13-
killer_3 = context.heur.killers.primary(ply - 2);
13+
killer_3 = heur.killers.primary(ply - 2);
1414
} else {
1515
killer_3 = EMPTY_MOVE;
1616
}
@@ -33,7 +33,7 @@ move_t movesort_t::next(GenStage &stage, int &score) {
3333
switch (stage) {
3434
case GEN_NONE:
3535
stage = GEN_HASH;
36-
if (context.board.is_pseudo_legal(hash_move)) {
36+
if (board.is_pseudo_legal(hash_move)) {
3737
score = INF;
3838
return hash_move;
3939
}
@@ -43,9 +43,9 @@ move_t movesort_t::next(GenStage &stage, int &score) {
4343

4444
// Score captures (SEE)
4545
for (int i = 0; i < capt_buf_size; i++) {
46-
int see_score = context.board.see(capt_buf[i]);
46+
int see_score = board.see(capt_buf[i]);
4747
capt_scores[i] = see_score >= 0 ? (see_score +
48-
(context.board.record[context.board.now].next_move ?
48+
(board.record[board.now].next_move ?
4949
rank_index(capt_buf[i].info.to) + 1 : 8 - rank_index(capt_buf[i].info.to)))
5050
: see_score;
5151
if(refutation.info.is_capture && main_buf[i].info.from == refutation.info.to) {
@@ -80,17 +80,17 @@ move_t movesort_t::next(GenStage &stage, int &score) {
8080
// Generate killers
8181
if(mode != QUIESCENCE) stage = GEN_KILLER_1;
8282
else return EMPTY_MOVE;
83-
if(context.board.is_pseudo_legal(killer_1)) {
83+
if(board.is_pseudo_legal(killer_1)) {
8484
return killer_1;
8585
}
8686
case GEN_KILLER_1:
8787
stage = GEN_KILLER_2;
88-
if(context.board.is_pseudo_legal(killer_2)) {
88+
if(board.is_pseudo_legal(killer_2)) {
8989
return killer_2;
9090
}
9191
case GEN_KILLER_2:
9292
stage = GEN_KILLER_3;
93-
if(context.board.is_pseudo_legal(killer_3)) {
93+
if(board.is_pseudo_legal(killer_3)) {
9494
return killer_3;
9595
}
9696
case GEN_KILLER_3:
@@ -124,7 +124,7 @@ move_t movesort_t::next(GenStage &stage, int &score) {
124124

125125
// Score quiets
126126
for (int i = 0; i < main_buf_size; i++) {
127-
main_scores[i] = context.heur.history.get(main_buf[i]);
127+
main_scores[i] = heur.history.get(main_buf[i]);
128128
if(refutation.info.is_capture && main_buf[i].info.from == refutation.info.to) {
129129
main_scores[i] += 100;
130130
}

0 commit comments

Comments
 (0)