Skip to content

Commit fd84c7d

Browse files
committed
updated cmake lists, improved move ordering
-------------------------------------------------- Results of bread_engine_1.2.0 vs bread_engine_1.1.4 (10+0.1, 1t, MB, UHO_2024_8mvs_big_+115_+134.pgn): Elo: 9.01 +/- 7.14, nElo: 12.00 +/- 9.51 LOS: 99.33 %, DrawRatio: 38.01 %, PairsRatio: 1.11 Games: 5130, Wins: 1786, Losses: 1653, Draws: 1691, Points: 2631.5 (51.30 %) Ptnml(0-2): [191, 564, 975, 591, 244] LLR: 2.97 (-2.94, 2.94) [0.00, 10.00] -------------------------------------------------- Bench: 9655652
1 parent e9c27a9 commit fd84c7d

7 files changed

Lines changed: 94 additions & 35 deletions

File tree

CMakeLists.txt

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.20)
2-
project(bread_engine VERSION 1.1.4)
2+
project(bread_engine VERSION 1.2.0)
33

44
set(CMAKE_CXX_STANDARD 20)
55
set(CMAKE_CXX_STANDARD_REQUIRED True)
@@ -43,18 +43,26 @@ if ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL
4343
endif()
4444

4545
add_executable(generate_neural_net_header ${bread_UTILS}/generate_nn_header.cpp)
46-
target_compile_definitions(generate_neural_net_header PRIVATE bread_NN_HEADER_PATH="${bread_NN_HEADER_PATH}")
47-
target_compile_definitions(generate_neural_net_header PRIVATE bread_NNUE_MODEL_PATH="${bread_NNUE_MODEL_PATH}")
48-
49-
# add custom target that generates the header when needed
50-
add_custom_command(
51-
OUTPUT ${bread_NN_HEADER_PATH}
52-
COMMENT "generating neural network header"
53-
COMMAND ${CMAKE_COMMAND} -E make_directory ${bread_NN_HEADER_DIR}
46+
add_executable(clear_neural_net_header ${bread_UTILS}/clear_nn_header.cpp)
47+
48+
target_compile_definitions(
49+
generate_neural_net_header PRIVATE bread_NN_HEADER_PATH="${bread_NN_HEADER_PATH}")
50+
target_compile_definitions(
51+
generate_neural_net_header PRIVATE bread_NNUE_MODEL_PATH="${bread_NNUE_MODEL_PATH}")
52+
53+
target_compile_definitions(
54+
clear_neural_net_header PRIVATE bread_NN_HEADER_PATH="${bread_NN_HEADER_PATH}")
55+
target_compile_definitions(
56+
clear_neural_net_header PRIVATE bread_NNUE_MODEL_PATH="${bread_NNUE_MODEL_PATH}")
57+
58+
file(MAKE_DIRECTORY ${bread_NN_HEADER_DIR})
59+
60+
add_custom_target(run_generate_nn_header
5461
COMMAND $<TARGET_FILE:generate_neural_net_header>
55-
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
62+
DEPENDS generate_neural_net_header
63+
COMMENT "generating the neural network header"
64+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
5665
)
57-
add_custom_target(run_generate_neural_net_header DEPENDS ${bread_NN_HEADER_PATH})
5866

5967
function(bread_configure bread_TARGET)
6068
cmake_parse_arguments(bread "" "" "SOURCES" ${ARGN})
@@ -65,7 +73,13 @@ function(bread_configure bread_TARGET)
6573

6674
target_compile_definitions(${bread_TARGET} PRIVATE bread_VERSION="${CMAKE_PROJECT_VERSION}")
6775

68-
add_dependencies(${bread_TARGET} run_generate_neural_net_header)
76+
add_dependencies(${bread_TARGET} run_generate_nn_header)
77+
78+
add_custom_command(
79+
TARGET ${bread_TARGET} POST_BUILD
80+
COMMAND $<TARGET_FILE:clear_neural_net_header>
81+
COMMENT "clearing the neural network header"
82+
)
6983

7084
target_compile_definitions(${bread_TARGET} PRIVATE bread_TB_PATH="${bread_TB_PATH}")
7185

dependencies/sorted_move_gen.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class SortedMoveGen: public chess::Movelist {
3232

3333
private:
3434
int initial_size;
35-
int depth;
35+
int depth = -1;
3636
int move_idx = -1;
3737
int num_remaining_moves;
3838
bool checked_tt_move = false;

src/benchmark_engine.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ void benchmark_nn(){
4444
NnueBoard board = NnueBoard();
4545

4646
auto start = std::chrono::high_resolution_clock::now();
47-
int num_iter = 10'000;
47+
int num_iter = 10'000'000;
4848
for (int i = 0; i < num_iter; i++){
4949
board.evaluate();
5050
}
51-
int mean = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start).count()/num_iter;
52-
std::cout << "time taken: " << mean << " microseconds per call";
51+
int mean = 1000*std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start).count()/num_iter;
52+
std::cout << "time taken: " << mean << " nanoseconds per call\n";
5353
std::cout << "============================== \n";
5454
}
5555

@@ -74,7 +74,7 @@ void benchmark_engine(int depth){
7474
engine.transposition_table.info();
7575

7676
std::cout << "============================== \n";
77-
std::cout << "average time: " << sum(times)/fens.size() << "\n";
77+
std::cout << "average time: " << sum(times)/fens.size() << " ms\n";
7878
std::cout << "============================== \n";
7979
}
8080

src/bread_engine_core.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -296,17 +296,17 @@ int Engine::negamax(int depth, int color, int alpha, int beta){
296296
if (transposition->best_move != NO_MOVE) sorted_move_gen.set_tt_move(transposition->best_move);
297297
}
298298

299-
bool is_in_check = inner_board.inCheck();
299+
bool in_check = inner_board.inCheck();
300300

301301
int static_eval = is_hit ? transposition->evaluation : inner_board.evaluate();
302302
// razoring
303-
if (!pv && !is_in_check && (depth < 6) && static_eval + depth*800 + 1500 < alpha){
303+
if (!pv && !in_check && (depth < 6) && static_eval + depth*800 + 1500 < alpha){
304304
static_eval = qsearch<false>(alpha, beta, color, QSEARCH_MAX_DEPTH); // we update static eval to the better qsearch eval. //? tweak depth?
305305
if (static_eval <= alpha) return static_eval;
306306
}
307307

308308
// reverse futility pruning
309-
if (!pv && !is_in_check && (depth < 6) && (static_eval - depth*800 - 1500 >= beta)){
309+
if (!pv && !in_check && (depth < 6) && (static_eval - depth*800 - 1500 >= beta)){
310310
return static_eval;
311311
}
312312

@@ -315,7 +315,7 @@ int Engine::negamax(int depth, int color, int alpha, int beta){
315315
int null_move_eval;
316316
if (!pv &&
317317
(depth > 4) &&
318-
!is_in_check &&
318+
!in_check &&
319319
!inner_board.last_move_null() &&
320320
beta != BEST_EVAL)
321321
{
@@ -351,7 +351,7 @@ int Engine::negamax(int depth, int color, int alpha, int beta){
351351
bool is_killer = SortedMoveGen<chess::movegen::MoveGenType::ALL>::killer_moves[depth].in_buffer(move);
352352

353353
// lmp
354-
if ((!pv) && (!is_in_check) && (!is_capture) && (sorted_move_gen.index() > 3 + depth) && (!is_hit) && (static_eval < alpha)) continue;
354+
if ((!pv) && (!in_check) && (!is_capture) && (sorted_move_gen.index() > 3 + depth) && (!is_hit) && (static_eval < alpha)) continue;
355355

356356
inner_board.update_state(move);
357357

@@ -511,7 +511,6 @@ int Engine::qsearch(int alpha, int beta, int color, int depth){
511511
// SEE pruning
512512
if (!SEE::evaluate(inner_board, move, (alpha-stand_pat)/800 - 3)) continue;
513513

514-
515514
inner_board.update_state(move);
516515
pos_eval = -qsearch<pv>(-beta, -alpha, -color, depth-1);
517516
inner_board.restore_state(move);

src/sorted_move_gen.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,51 @@ void SortedMoveGen<chess::movegen::MoveGenType::ALL>::set_score(chess::Move& mov
2020
const chess::Square to = move.to();
2121
const chess::Piece piece = board.at(from);
2222
const chess::Piece to_piece = board.at(to);
23+
const int from_value = piece_value[static_cast<int>(piece.type())];
24+
25+
chess::Bitboard attacked_by_pawn = 0;
26+
27+
chess::Bitboard pawn_attackers = board.pieces(chess::PieceType::PAWN, ~board.sideToMove());
28+
29+
while (pawn_attackers)
30+
attacked_by_pawn |= chess::attacks::pawn(~board.sideToMove(), pawn_attackers.pop());
31+
2332
int score = 0;
2433

2534
if ((piece != chess::Piece::WHITEKING) && (piece != chess::Piece::BLACKKING)){
26-
score += psm.get_psm(piece, from, to);
35+
score += 100*psm.get_psm(piece, from, to);
2736
} else {
2837
bool is_endgame = board.occ().count() <= ENDGAME_PIECE_COUNT;
29-
score += psm.get_ksm(piece, is_endgame, to, from);
38+
score += 100*psm.get_ksm(piece, is_endgame, to, from);
3039
}
3140

41+
if (piece.type() != chess::PieceType::PAWN){
42+
score += bool(attacked_by_pawn & chess::Bitboard::fromSquare(from))
43+
* 40 * from_value * MATERIAL_CHANGE_MULTIPLIER;
44+
45+
score -= bool(attacked_by_pawn & chess::Bitboard::fromSquare(to))
46+
* 41 * from_value * MATERIAL_CHANGE_MULTIPLIER;
47+
}
48+
3249
// captures should be searched early, so
3350
// to_value = piece_value(to) - piece_value(from) doesn't seem to work.
3451
// however, find a way to make these captures even better ?
3552
if (to_piece != chess::Piece::NONE){
36-
score += piece_value[static_cast<int>(to_piece.type())] * MATERIAL_CHANGE_MULTIPLIER;
53+
score += 100 * piece_value[static_cast<int>(to_piece.type())] * MATERIAL_CHANGE_MULTIPLIER;
3754
}
3855

3956
if (move.typeOf() == chess::Move::PROMOTION){
40-
score += piece_value[static_cast<int>(move.promotionType())] * MATERIAL_CHANGE_MULTIPLIER;
57+
score += 100 * piece_value[static_cast<int>(move.promotionType())] * MATERIAL_CHANGE_MULTIPLIER;
4158
}
4259

43-
if (killer_moves[depth].in_buffer(move)){
44-
score += KILLER_SCORE;
60+
if (depth != -1 && killer_moves[depth].in_buffer(move)){
61+
score += 100 * KILLER_SCORE;
4562
}
4663

47-
score += history.get_history_bonus(from.index(), to.index(), board.sideToMove() == chess::Color::WHITE)/100; // cant be less than worst move score
48-
49-
assert(score < BEST_MOVE_SCORE);
50-
assert(score > WORST_MOVE_SCORE);
64+
score += history.get_history_bonus(from.index(), to.index(), board.sideToMove() == chess::Color::WHITE); // cant be less than worst move score
5165

66+
score = std::clamp(score, WORST_MOVE_SCORE + 1, BEST_MOVE_SCORE - 1);
67+
5268
move.setScore(score);
5369
}
5470

utils/clear_nn_header.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <iostream>
2+
#include <fstream>
3+
#include <cstdint>
4+
5+
int main(){
6+
std::ofstream output_file;
7+
output_file.open(bread_NN_HEADER_PATH);
8+
output_file << "#pragma once\n";
9+
output_file << "#include <cstdint>\n";
10+
output_file << "// this file has been auto generated. Please do not modify it by hand.\n\n";
11+
12+
output_file << "namespace nn_data {\n";
13+
14+
output_file << "inline int w1[] = {};\n";
15+
output_file << "inline int w2[] = {};\n";
16+
output_file << "inline int w3[] = {};\n";
17+
output_file << "inline int w4[] = {};\n";
18+
19+
output_file << "inline int* weights[] = {w1, w2, w3, w4};\n";
20+
21+
output_file << "inline int b1[] = {};\n";
22+
output_file << "inline int b2[] = {};\n";
23+
output_file << "inline int b3[] = {};\n";
24+
output_file << "inline int b4[] = {};\n";
25+
26+
output_file << "inline int* bias[] = {b1, b2, b3, b4};\n";
27+
output_file << "}";
28+
output_file.close();
29+
return 0;
30+
}

utils/generate_nn_header.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
// this functions writes the weights from input_path to output_path with comas separating the values
1010
template<typename weights_type>
11-
void parameters_to_header(std::string input_path, int weights_size, std::ofstream& output_path){
11+
void parameters_to_header(std::string input_path, int weights_size, std::ofstream& output_file){
1212
std::ifstream weights_stream;
1313
weights_type* weights = new weights_type[weights_size];
1414

@@ -19,7 +19,7 @@ void parameters_to_header(std::string input_path, int weights_size, std::ofstrea
1919
} else {std::cout << "error loading weights \n";}
2020

2121
for (int i = 0; i < weights_size; i++){
22-
output_path << static_cast<int>(weights[i]) << ", ";
22+
output_file << static_cast<int>(weights[i]) << ", ";
2323
}
2424
};
2525

0 commit comments

Comments
 (0)