diff --git a/src/Makefile b/src/Makefile index 47333b1..d5600ca 100644 --- a/src/Makefile +++ b/src/Makefile @@ -21,7 +21,7 @@ OLDWFLAGS = -DNDEBUG -Wall -Wcast-qual -Wextra -Wshadow -pedantic -std=c++11 -m DFLAGS = -g -Wall -Wcast-qual -Wextra -Wshadow -pedantic -std=c++11 -m64 -msse3 -mpopcnt -flto -D__DEBUG__ TARGET = Defenchess OPT = -O3 -VERSION = 2.1 +VERSION = 2.2 OBJECTS = bitboard.o data.o eval.o move.o move_utils.o params.o position.o pst.o search.o see.o target.o test.o timecontrol.o thread.o tt.o tune.o uci.o magic.o main.o movegen.o tb.o fathom/tbprobe.o all: $(TARGET) diff --git a/src/bitboard.h b/src/bitboard.h index 24829e6..f8f423f 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -48,6 +48,10 @@ inline bool more_than_one(Bitboard b) { return b & (b - 1); } +inline bool only_one(Bitboard b) { + return b && !more_than_one(b); +} + inline Bitboard shift(Bitboard b, int offset) { if (offset > 0) { return b << offset; diff --git a/src/const.h b/src/const.h index 0701265..0e00d96 100644 --- a/src/const.h +++ b/src/const.h @@ -188,14 +188,15 @@ const uint64_t _knight_targets = 0x0000005088008850; const uint64_t _king_targets = 0x0000000070507000; const int - KNOWN_WIN = 10000, MATE = 32000, INFINITE = 32001, UNDEFINED = 32002, TIMEOUT = 32003, MATE_IN_MAX_PLY = MATE - MAX_PLY, - MATED_IN_MAX_PLY = -MATE + MAX_PLY; + MATED_IN_MAX_PLY = -MATE + MAX_PLY, + + TB_WIN = MATE_IN_MAX_PLY - MAX_PLY - 1; const int RANK_1 = 0, RANK_2 = 1, @@ -307,12 +308,13 @@ typedef struct CopiedInfo { const int info_size = sizeof(CopiedInfo); typedef struct Metadata { - int ply; - Move current_move; - int static_eval; - Move killers[2]; - Move pv[MAX_PLY + 1]; - Move excluded_move; + int ply; + Move current_move; + int static_eval; + Move killers[2]; + Move pv[MAX_PLY + 1]; + Move excluded_move; + Piece moved_piece; } Metadata; struct Position { @@ -353,14 +355,7 @@ typedef struct Evaluation { enum EndgameType { NORMAL_ENDGAME, - DRAW_ENDGAME, - KNOWN_ENDGAME_KPK, - KNOWN_ENDGAME_KXK -}; - -enum ScalingFactorType { - NO_SCALING, - KRPKR_SCALING + DRAW_ENDGAME }; const int SCALE_NORMAL = 32; @@ -369,7 +364,6 @@ typedef struct Material { int phase; int score; EndgameType endgame_type; - ScalingFactorType scaling_factor_type; } Material; const int material_balance[NUM_PIECE] ={ @@ -443,7 +437,6 @@ struct MoveGen { Position *position; Move tte_move; Move counter_move; - Move prev_move; int stage; uint8_t head; uint8_t tail; @@ -456,7 +449,6 @@ const MoveGen blank_movegen = { nullptr, // Position no_move, // tte_move no_move, // counter move - no_move, // prev move 0, // stage 0, // head 0, // tail @@ -474,6 +466,7 @@ struct SearchThread { Move counter_moves[NUM_PIECE][64]; int history[2][64][64]; int counter_move_history[NUM_PIECE][64][NUM_PIECE][64]; + int followup_history[NUM_PIECE][64][NUM_PIECE][64]; int selply; PawnTTEntry pawntt[pawntt_size]; int depth; diff --git a/src/data.cpp b/src/data.cpp index 54c7361..7e4f493 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -66,6 +66,7 @@ uint64_t polyglotCombined[NUM_PIECE][64]; int num_threads = 1; int move_overhead = 100; +SearchThread main_thread; SearchThread *search_threads; int mvvlva_values[12][NUM_PIECE]; @@ -467,11 +468,8 @@ void init_imbalance(){ int all_minor = white_minor + black_minor; int all_major = white_major + black_major; bool no_pawns = wp == 0 && bp == 0; - bool only_one_pawn = wp + bp == 1; - // Default endgame and scaling factor types material->endgame_type = NORMAL_ENDGAME; - material->scaling_factor_type = NO_SCALING; if (wp + bp + all_minor + all_major == 0) { material->endgame_type = DRAW_ENDGAME; @@ -481,15 +479,6 @@ void init_imbalance(){ } else if (no_pawns && all_major == 0 && all_minor == 2 && (wn == 2 || bn == 2)) { material->endgame_type = DRAW_ENDGAME; - } - else if (only_one_pawn && !all_minor && !all_major) { - material->endgame_type = KNOWN_ENDGAME_KPK; - } - else if (no_pawns && !all_minor && all_major == 1) { - material->endgame_type = KNOWN_ENDGAME_KXK; - } - else if (br == 1 && wr == 1 && only_one_pawn && !all_minor && wq == 0 && bq == 0) { - material->scaling_factor_type = KRPKR_SCALING; } } } diff --git a/src/data.h b/src/data.h index d1cbd3d..9f33a28 100644 --- a/src/data.h +++ b/src/data.h @@ -82,22 +82,27 @@ bool scored_move_compare_greater(ScoredMove lhs, ScoredMove rhs); inline bool is_main_thread(Position *p) {return p->my_thread->thread_id == 0;} +extern SearchThread main_thread; extern SearchThread *search_threads; +inline SearchThread *get_thread(int thread_id) { return thread_id == 0 ? &main_thread : &search_threads[thread_id - 1]; } + extern int num_threads; extern int move_overhead; inline void initialize_nodes() { for (int i = 0; i < num_threads; ++i) { - search_threads[i].nodes = 0; - search_threads[i].tb_hits = 0; + SearchThread *t = get_thread(i); + t->nodes = 0; + t->tb_hits = 0; } } inline uint64_t sum_nodes() { uint64_t s = 0; for (int i = 0; i < num_threads; ++i) { - s += search_threads[i].nodes; + SearchThread *t = get_thread(i); + s += t->nodes; } return s; } @@ -105,7 +110,8 @@ inline uint64_t sum_nodes() { inline uint64_t sum_tb_hits() { uint64_t s = 0; for (int i = 0; i < num_threads; ++i) { - s += search_threads[i].tb_hits; + SearchThread *t = get_thread(i); + s += t->tb_hits; } return s; } diff --git a/src/eval.cpp b/src/eval.cpp index 8cc2e52..c2a2a48 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -134,28 +134,36 @@ void evaluate_pawns(Evaluation *eval, Position *p) { Score evaluate_pieces(Evaluation *eval, Position *p, Color color) { Score piece_score = {0, 0}; + Bitboard capturable, king_threats; + Bitboard my_bishops = p->bbs[bishop(color)]; Bitboard my_knights = p->bbs[knight(color)]; Bitboard my_rooks = p->bbs[rook(color)]; Bitboard my_queens = p->bbs[queen(color)]; Bitboard pinned = p->info->pinned[color]; + Bitboard opponent_bishops = p->bbs[bishop(~color)]; + Bitboard opponent_knights = p->bbs[knight(~color)]; + Bitboard opponent_rooks = p->bbs[rook(~color)]; + Bitboard opponent_queens = p->bbs[queen(~color)]; + eval->targets[bishop(color)] = 0; while (my_bishops) { Square sq = pop(&my_bishops); Bitboard bishop_targets = generate_bishop_targets(p->board ^ p->bbs[queen(color)], sq); if (pinned & bfi[sq]) { - bishop_targets &= BETWEEN_MASK[p->king_index[color]][sq]; + bishop_targets &= FROMTO_MASK[p->king_index[color]][sq]; } - int mobility = count(bishop_targets & eval->mobility_area[color]); + capturable = opponent_bishops | opponent_rooks | opponent_queens; + int mobility = count(bishop_targets & (eval->mobility_area[color] | capturable)); piece_score += mobility_bonus[BISHOP][mobility]; // Bishop with same colored pawns piece_score -= bishop_pawn_penalty * count(COLOR_MASKS[TILE_COLOR[sq]] & p->bbs[pawn(color)]); // King threats - Bitboard king_threats = bishop_targets & eval->king_zone[~color]; + king_threats = bishop_targets & eval->king_zone[~color]; if (king_threats) { ++eval->num_king_attackers[~color]; eval->king_zone_score[~color] += ATTACK_VALUES[BISHOP]; @@ -171,14 +179,16 @@ Score evaluate_pieces(Evaluation *eval, Position *p, Color color) { Bitboard knight_targets = generate_knight_targets(sq); if (pinned & bfi[sq]) { - knight_targets &= BETWEEN_MASK[p->king_index[color]][sq]; + // Pinned knights cannot move + knight_targets = 0; } - int mobility = count(knight_targets & eval->mobility_area[color]); + capturable = opponent_knights | opponent_bishops | opponent_rooks | opponent_queens; + int mobility = count(knight_targets & (eval->mobility_area[color] | capturable)); piece_score += mobility_bonus[KNIGHT][mobility]; // King threats - Bitboard king_threats = knight_targets & eval->king_zone[~color]; + king_threats = knight_targets & eval->king_zone[~color]; if (king_threats) { ++eval->num_king_attackers[~color]; eval->king_zone_score[~color] += ATTACK_VALUES[KNIGHT]; @@ -194,10 +204,11 @@ Score evaluate_pieces(Evaluation *eval, Position *p, Color color) { Bitboard rook_targets = generate_rook_targets(p->board ^ (p->bbs[queen(color)] | p->bbs[rook(color)]), sq); if (pinned & bfi[sq]) { - rook_targets &= BETWEEN_MASK[p->king_index[color]][sq]; + rook_targets &= FROMTO_MASK[p->king_index[color]][sq]; } - int mobility = count(rook_targets & eval->mobility_area[color]); + capturable = opponent_rooks | opponent_queens; + int mobility = count(rook_targets & (eval->mobility_area[color] | capturable)); piece_score += mobility_bonus[ROOK][mobility]; // Bonus for being on a semiopen or open file @@ -206,7 +217,7 @@ Score evaluate_pieces(Evaluation *eval, Position *p, Color color) { } // King threats - Bitboard king_threats = rook_targets & eval->king_zone[~color]; + king_threats = rook_targets & eval->king_zone[~color]; if (king_threats) { ++eval->num_king_attackers[~color]; eval->king_zone_score[~color] += ATTACK_VALUES[ROOK]; @@ -222,14 +233,15 @@ Score evaluate_pieces(Evaluation *eval, Position *p, Color color) { Bitboard queen_targets = generate_queen_targets(p->board, sq); if (pinned & bfi[sq]) { - queen_targets &= BETWEEN_MASK[p->king_index[color]][sq]; + queen_targets &= FROMTO_MASK[p->king_index[color]][sq]; } - int mobility = count(queen_targets & eval->mobility_area[color]); + capturable = opponent_queens; + int mobility = count(queen_targets & (eval->mobility_area[color] | capturable)); piece_score += mobility_bonus[QUEEN][mobility]; // King threats - Bitboard king_threats = queen_targets & eval->king_zone[~color]; + king_threats = queen_targets & eval->king_zone[~color]; if (king_threats) { ++eval->num_king_attackers[~color]; eval->king_zone_score[~color] += ATTACK_VALUES[QUEEN]; @@ -256,6 +268,10 @@ Score evaluate_king(Evaluation *eval, Position *p, Color color) { king_score.endgame -= pawn_distance_penalty * pawn_distance; } + Bitboard flank_attacks = eval->targets[~color] & flank_ranks[color] & flank_files[file_of(king_sq)]; + Bitboard flank_attacks2 = flank_attacks & eval->double_targets[~color]; + king_score.midgame -= king_flank_penalty * (count(flank_attacks) + count(flank_attacks2)); + if (eval->num_king_attackers[color] > (1 - eval->num_queens[~color])) { Bitboard weak = eval->targets[king(color)] & eval->targets[~color] & ~eval->double_targets[color]; Bitboard weak_zone = eval->targets[~color] & ~eval->targets[color] & eval->king_zone[color] & ~p->bbs[~color]; @@ -307,24 +323,30 @@ Score evaluate_threat(Evaluation *eval, Position *p, Color color) { Bitboard very_supported = eval->targets[pawn(~color)] | (eval->double_targets[~color] & ~eval->double_targets[color]); Bitboard not_supported = p->bbs[~color] & ~very_supported & eval->targets[color]; + Bitboard attacked; if (opponent_non_pawns | not_supported) { - Bitboard attacked_by_minor = (opponent_non_pawns | not_supported) & (eval->targets[knight(color)] | eval->targets[bishop(color)]); - while (attacked_by_minor) { - Square sq = pop(&attacked_by_minor); + attacked = (opponent_non_pawns | not_supported) & (eval->targets[knight(color)] | eval->targets[bishop(color)]); + while (attacked) { + Square sq = pop(&attacked); threat_score += minor_threat_bonus[piece_type(p->pieces[sq])]; } - Bitboard attacked_by_rook = (p->bbs[queen(~color)] | not_supported) & eval->targets[rook(color)]; - while (attacked_by_rook) { - Square sq = pop(&attacked_by_rook); + attacked = (p->bbs[queen(~color)] | not_supported) & eval->targets[rook(color)]; + while (attacked) { + Square sq = pop(&attacked); threat_score += rook_threat_bonus[piece_type(p->pieces[sq])]; } - Bitboard attacked_by_pawn = opponent_non_pawns & eval->targets[pawn(color)]; - while (attacked_by_pawn) { - Square sq = pop(&attacked_by_pawn); + attacked = opponent_non_pawns & eval->targets[pawn(color)]; + while (attacked) { + Square sq = pop(&attacked); threat_score += pawn_threat_bonus[piece_type(p->pieces[sq])]; } + + attacked = not_supported & eval->targets[king(color)]; + if (attacked) { + threat_score += king_threat_bonus[more_than_one(attacked)]; + } } Bitboard pawn_moves = (color == white ? p->bbs[pawn(color)] << 8 : p->bbs[pawn(color)] >> 8) & ~p->board; @@ -405,6 +427,25 @@ void pre_eval(Evaluation *eval, Position *p) { eval->num_queens[white] = eval->num_queens[black] = 0; } +int scaling_factor(Position *p) { + if (only_one(p->bbs[white_bishop]) && + only_one(p->bbs[black_bishop]) && + only_one(COLOR_MASKS[white] & (p->bbs[white_bishop] | p->bbs[black_bishop]))) { + if (p->info->non_pawn_material[white] == BISHOP_MID && p->info->non_pawn_material[black] == BISHOP_MID) { + return SCALE_PURE_OCB; + } else { + return SCALE_OCB_WITH_PIECES; + } + } + + Color winner = p->score.endgame > 0 ? white : black; + if (!p->bbs[pawn(winner)] && p->info->non_pawn_material[winner] <= p->info->non_pawn_material[~winner] + piece_values[white_bishop]) { + return SCALE_NO_PAWNS; + } + + return SCALE_NORMAL; +} + int evaluate(Position *p) { assert(!is_checked(p)); Evaluation eval; @@ -426,6 +467,7 @@ int evaluate(Position *p) { evaluate_threat(&eval, p, white) - evaluate_threat(&eval, p, black) + evaluate_passers(&eval, p, white) - evaluate_passers(&eval, p, black); - int ret = (eval.score.midgame * eval_material->phase + eval.score.endgame * (256 - eval_material->phase)) / 256; + int scale = scaling_factor(p); + int ret = (eval.score.midgame * eval_material->phase + eval.score.endgame * (256 - eval_material->phase) * scale / SCALE_NORMAL) / 256; return (p->color == white ? ret : -ret) + tempo; } diff --git a/src/eval.h b/src/eval.h index e81b47a..1c661fc 100644 --- a/src/eval.h +++ b/src/eval.h @@ -21,6 +21,26 @@ #include "const.h" +const Bitboard flank_ranks[2] = { + RANK_1BB | RANK_2BB | RANK_3BB | RANK_4BB, + RANK_8BB | RANK_7BB | RANK_6BB | RANK_5BB +}; + +const Bitboard queenside_flank = FILE_ABB | FILE_BBB | FILE_CBB | FILE_DBB; +const Bitboard center_flank = FILE_CBB | FILE_DBB | FILE_EBB | FILE_FBB; +const Bitboard kingside_flank = FILE_EBB | FILE_FBB | FILE_GBB | FILE_HBB; + +const Bitboard flank_files[8] = { + queenside_flank, + queenside_flank, + queenside_flank, + center_flank, + center_flank, + kingside_flank, + kingside_flank, + kingside_flank +}; + int evaluate(Position *p); #endif diff --git a/src/fathom/tbcore.cpp b/src/fathom/tbcore.cpp index 1fa460a..a10861d 100644 --- a/src/fathom/tbcore.cpp +++ b/src/fathom/tbcore.cpp @@ -434,7 +434,7 @@ void init_tablebases(const char *path) init_tb(str); } -// printf("Found %d tablebases.\n", TBnum_piece + TBnum_pawn); + printf("info string found %d tablebases.\n", TBnum_piece + TBnum_pawn); } static const signed char offdiag[] = { diff --git a/src/main.cpp b/src/main.cpp index d87da64..e5ede98 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,9 +18,17 @@ #include "data.h" #include "uci.h" +#include "search.h" +#include "thread.h" -int main() { +int main(int argc, char* argv[]) { init(); + get_ready(); + if (argc > 1 && strcmp(argv[1], "bench") == 0) { + // Calling bench exits the program + bench(); + return 0; + } loop(); return 0; } diff --git a/src/movegen.cpp b/src/movegen.cpp index 05ab6e8..4337b91 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -34,21 +34,21 @@ void print_movegen(MoveGen *movegen) { std::cout << std::endl; } -void score_moves(MoveGen *movegen, ScoreType score_type) { +void score_moves(MoveGen *movegen, Metadata *md, ScoreType score_type) { if (score_type == SCORE_CAPTURE) { for (uint8_t i = movegen->head; i < movegen->tail; ++i) { movegen->moves[i].score = score_capture_mvvlva(movegen->position, movegen->moves[i].move); } } else if (score_type == SCORE_QUIET) { for (uint8_t i = movegen->head; i < movegen->tail; ++i) { - movegen->moves[i].score = score_quiet(movegen, movegen->moves[i].move); + movegen->moves[i].score = score_quiet(movegen->position, md, movegen->moves[i].move); } } else { // Evasions for (uint8_t i = movegen->head; i < movegen->tail; ++i) { if (is_capture(movegen->position, movegen->moves[i].move)) { movegen->moves[i].score = score_capture_mvvlva(movegen->position, movegen->moves[i].move); } else { - movegen->moves[i].score = score_quiet(movegen, movegen->moves[i].move) - (1 << 20); + movegen->moves[i].score = score_quiet(movegen->position, md, movegen->moves[i].move) - (1 << 20); } } } @@ -79,7 +79,7 @@ Move next_move(MoveGen *movegen, Metadata *md, int depth) { case GOOD_CAPTURES_SORT: generate_moves(movegen, movegen->position); - score_moves(movegen, SCORE_CAPTURE); + score_moves(movegen, md, SCORE_CAPTURE); ++movegen->stage; /* fallthrough */ @@ -135,7 +135,7 @@ Move next_move(MoveGen *movegen, Metadata *md, int depth) { case QUIETS_SORT: movegen->head = movegen->tail = movegen->end_bad_captures; generate_moves(movegen, movegen->position); - score_moves(movegen, SCORE_QUIET); + score_moves(movegen, md, SCORE_QUIET); insertion_sort(movegen->moves, movegen->head, movegen->tail); ++movegen->stage; /* fallthrough */ @@ -165,7 +165,7 @@ Move next_move(MoveGen *movegen, Metadata *md, int depth) { case EVASIONS_SORT: generate_evasions(movegen, movegen->position); - score_moves(movegen, SCORE_EVASION); + score_moves(movegen, md, SCORE_EVASION); ++movegen->stage; /* fallthrough */ @@ -181,7 +181,7 @@ Move next_move(MoveGen *movegen, Metadata *md, int depth) { case QUIESCENCE_CAPTURES_SORT: generate_moves(movegen, movegen->position); - score_moves(movegen, SCORE_CAPTURE); + score_moves(movegen, md, SCORE_CAPTURE); ++movegen->stage; /* fallthrough */ @@ -216,7 +216,7 @@ Move next_move(MoveGen *movegen, Metadata *md, int depth) { case PROBCUT_CAPTURES_SORT: generate_moves(movegen, movegen->position); - score_moves(movegen, SCORE_CAPTURE); + score_moves(movegen, md, SCORE_CAPTURE); ++movegen->stage; /* fallthrough */ @@ -256,21 +256,19 @@ MoveGen new_movegen(Position *p, Metadata *md, Move tte_move, uint8_t type, int } } - Move prev_move = (md-1)->current_move; - Square prev_to = move_to(prev_move); + Square prev_to = move_to((md-1)->current_move); MoveGen movegen = { {}, // Moves p, // Position tm, // tte_move p->my_thread->counter_moves[p->pieces[prev_to]][prev_to], // counter move - prev_move, // prev move movegen_stage, // stage 0, // head 0, // tail 0, // end bad captures threshold // threshold }; - assert(!(md->ply == 0 && prev_move != no_move)); + assert(!(md->ply == 0 && (md-1)->current_move != no_move)); return movegen; } diff --git a/src/movegen.h b/src/movegen.h index c47c89b..eea9063 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -31,20 +31,24 @@ void generate_evasions(MoveGen *movegen, Position *p); void generate_king_evasions(MoveGen *movegen, Position *p); Move next_move(MoveGen *movegen, Metadata *md, int depth); -inline int score_quiet(MoveGen *movegen, Move move) { - Position *p = movegen->position; - +inline int score_quiet(Position *p, Metadata *md, Move move) { Square from = move_from(move); Square to = move_to(move); - Move prev_move = movegen->prev_move; - + Move prev_move = (md-1)->current_move; Piece piece = p->pieces[from]; Square prev_to = move_to(prev_move); - Piece prev_piece = p->pieces[prev_to]; + Piece prev_piece = (md-1)->moved_piece; + + Move followup_move = (md-2)->current_move; + Square followup_to = move_to(followup_move); + Piece followup_piece = (md-2)->moved_piece; + assert(prev_piece == no_piece || (prev_piece >= white_pawn && prev_piece <= black_king)); + assert(followup_piece == no_piece || (followup_piece >= white_pawn && followup_piece <= black_king)); return p->my_thread->history[p->color][from][to] + - p->my_thread->counter_move_history[prev_piece][prev_to][piece][to]; + p->my_thread->counter_move_history[prev_piece][prev_to][piece][to] + + p->my_thread->followup_history[followup_piece][followup_to][piece][to]; } inline int score_capture_mvvlva(Position *p, Move move) { diff --git a/src/params.cpp b/src/params.cpp index 2438dd4..72a9b5c 100644 --- a/src/params.cpp +++ b/src/params.cpp @@ -39,6 +39,7 @@ int king_danger_shelter_bonus = 22, king_zone_attack_penalty = 73, king_danger_weak_penalty = 86, king_danger_weak_zone_penalty = 69, + king_flank_penalty = 3, queen_check_penalty = 460, knight_check_penalty = 521, rook_check_penalty = 547, @@ -68,6 +69,10 @@ int tempo = 12; int ATTACK_VALUES[6] = {0, 0, 53, 40, 40, 22}; int bishop_pair = 53; +int SCALE_PURE_OCB = 16; +int SCALE_OCB_WITH_PIECES = 27; +int SCALE_NO_PAWNS = 0; + // connected_bonus[opposed][adjacent][rank] // cannot be opposed on rank 7 // cannot be not adjacent on rank 2 @@ -89,12 +94,35 @@ Score connected_bonus[2][2][8] = { }; Score mobility_bonus[6][32] = { - {}, // NO PIECE - {}, // PAWN - { {-47, -24}, {-38, -57}, {-21, -27}, {-11, -13}, {-6, -1}, {0, 4}, {10, 10}, {13, 15}, {15, 15} }, // KNIGHT - { {-50, 3}, {-23, -22}, {1, -7}, {4, 7}, {15, 17}, {24, 25}, {31, 31}, {33, 33}, {37, 37}, {52, 33}, {54, 32}, {72, 24}, {64, 18}, {108, 11} }, // BISHOP - { {-38, 34}, {-19, -8}, {2, 7}, {3, 28}, {4, 39}, {4, 53}, {4, 59}, {8, 63}, {12, 70}, {15, 76}, {18, 81}, {25, 84}, {34, 80}, {44, 80}, {66, 86} }, // ROOK - { {-15, 94}, {-44, 16}, {-20, 0}, {6, -31}, {4, 8}, {21, 7}, {22, 40}, {27, 41}, {31, 49}, {34, 54}, {39, 59}, {41, 67}, {39, 77}, {40, 85}, {45, 86}, {41, 93}, {39, 90}, {36, 92}, {40, 88}, {52, 84}, {50, 71}, {65, 56}, {112, 42}, {147, 21}, {81, 38}, {119, 26}, {62, 7}, {38, 5}, {92, 145} } // QUEEN + {}, // NO PIECE + {}, // PAWN + { // KNIGHT + {-41, -65}, {-38, -120}, {-35, -47}, {-30, -23}, + {-14, -15}, { -9, -1}, { -1, 9}, { 5, 13}, + { 14, -2} + }, + { // BISHOP + {-40, -37}, {-40, -47}, {-10, -40}, {-6, -14}, + { 9, 5}, { 18, 19}, { 25, 28}, {26, 34}, + { 27, 42}, { 28, 41}, { 24, 43}, {62, 17}, + { 23, 31}, {221, -98} + }, + { // ROOK + {-88, 19}, {-14, -22}, { 1, 1}, { 0, 22}, + { 4, 35}, { 1, 55}, { 4, 56}, {10, 63}, + { 17, 74}, { 23, 78}, { 19, 87}, {15, 96}, + { 31, 90}, { 49, 82}, {133, 53} + }, + { // QUEEN + {-662, -587}, {-18, 125}, {-14, 38}, { -9, -20}, + { 6, -29}, { 5, 4}, { 11, 20}, { 18, 29}, + { 24, 53}, { 32, 52}, { 41, 53}, { 46, 66}, + { 51, 73}, { 49, 75}, { 47, 83}, { 46, 89}, + { 46, 86}, { 36, 92}, { 28, 88}, { 50, 79}, + { 55, 69}, { 67, 53}, { 78, 42}, {126, 6}, + { 127, -2}, {115, 6}, {166, 36}, { 84, -12}, + { 189, 298} + } }; Score passed_pawn_bonus[8] = { @@ -149,6 +177,8 @@ Score pawn_threat_bonus[6] = { // { 0, 0} // King should never be called }; +Score king_threat_bonus[2] = {{-8, 27}, {53, 64}}; + Score pst[NUM_PIECE][64]; int bonusPawn[2][32] = { diff --git a/src/params.h b/src/params.h index 57dee35..a214846 100644 --- a/src/params.h +++ b/src/params.h @@ -41,6 +41,7 @@ extern int king_danger_init, king_danger_weak_penalty, king_danger_weak_zone_penalty, + king_flank_penalty, queen_check_penalty, knight_check_penalty, rook_check_penalty, @@ -55,6 +56,10 @@ extern int tempo; extern int ATTACK_VALUES[6]; extern int bishop_pair; +extern int SCALE_PURE_OCB; +extern int SCALE_OCB_WITH_PIECES; +extern int SCALE_NO_PAWNS; + extern Score connected_bonus[2][2][8]; extern Score mobility_bonus[6][32]; extern Score passed_pawn_bonus[8]; @@ -70,6 +75,7 @@ extern Score pawn_push_threat_bonus; extern Score minor_threat_bonus[6]; extern Score rook_threat_bonus[6]; extern Score pawn_threat_bonus[6]; +extern Score king_threat_bonus[2]; extern Score pst[NUM_PIECE][64]; diff --git a/src/position.cpp b/src/position.cpp index 39e31e8..de54fe7 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -147,12 +147,12 @@ Position* import_fen(std::string fen, int thread_id){ halfmove_clock = 1; } - SearchThread *main_thread = &search_threads[thread_id]; - main_thread->root_ply = 2 * (halfmove_clock - 1) + color; - Info *info = &main_thread->infos[main_thread->root_ply]; - Position *p = &main_thread->position; + SearchThread *t = get_thread(thread_id); + t->root_ply = 2 * (halfmove_clock - 1) + color; + Info *info = &t->infos[t->root_ply]; + Position *p = &t->position; p->info = info; - main_thread->search_ply = main_thread->root_ply; + t->search_ply = t->root_ply; p->color = color; p->bbs[white_occupy] = 0; @@ -274,14 +274,13 @@ Position* import_fen(std::string fen, int thread_id){ calculate_score(p); calculate_hash(p); calculate_material(p); - p->my_thread = main_thread; + p->my_thread = t; return p; } Position* start_pos(){ - SearchThread *main_thread = &search_threads[0]; - Info *info = &main_thread->infos[0]; - Position *p = &main_thread->position; + Info *info = &main_thread.infos[0]; + Position *p = &main_thread.position; p->info = info; Bitboard white_occupied_bb = 0x000000000000FFFF; @@ -326,14 +325,14 @@ Position* start_pos(){ info->castling = 15; info->enpassant = no_sq; info->hash = info->pawn_hash = 0; - main_thread->search_ply = main_thread->root_ply = 0; + main_thread.search_ply = main_thread.root_ply = 0; info->pinned[white] = pinned_piece_squares(p, white); info->pinned[black] = pinned_piece_squares(p, black); info->previous = nullptr; calculate_score(p); calculate_hash(p); calculate_material(p); - p->my_thread = main_thread; + p->my_thread = &main_thread; info->last_irreversible = 0; return p; } diff --git a/src/search.cpp b/src/search.cpp index 9cb341c..1d3e7b0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -32,14 +32,16 @@ #include "tb.h" #include "tt.h" -uint8_t timer_count = 0; -int myremain = 10000; -int total_remaining = 10000; struct timeval curr_time, start_ts; -volatile bool is_timeout = false; -int think_depth_limit = MAX_PLY; -bool quit_application = false; -bool volatile is_searching = false; + +int timer_count = 1024, + myremain = 10000, + total_remaining = 10000, + think_depth_limit = MAX_PLY; + +volatile bool is_timeout = false, + quit_application = false, + is_searching = false; Move main_pv[MAX_PLY + 1]; std::vector root_moves = {}; @@ -60,7 +62,7 @@ int score_at_depth[MAX_PLY * 2]; void print_pv(Metadata *md) { int i = 0; while (md->pv[i] != no_move) { - std::cout << move_to_str(main_pv[i++]) << " "; + std::cout << move_to_str(md->pv[i++]) << " "; } } @@ -84,22 +86,19 @@ void set_main_pv(Metadata *md) { } bool is_draw(Position *p) { - Info *info = p->info; - if (info->last_irreversible > 3){ - if (info->last_irreversible > 99) + if (p->info->last_irreversible > 3){ + if (p->info->last_irreversible > 99) { return true; + } int repetition_count = 0; - int index = p->my_thread->search_ply - 2; - while (index >= p->my_thread->search_ply - info->last_irreversible) { - index -= 2; - if (index < 0) { - break; - } - - Info *info2 = &p->my_thread->infos[index]; - if (info->hash == info2->hash) { - if (index >= p->my_thread->root_ply) { + int min_index = std::max(p->my_thread->search_ply - p->info->last_irreversible, 0); + for (int i = p->my_thread->search_ply - 4; i >= min_index; i -= 2) { + // The i >= 0 condition isn't necessary but would prevent undefined + // behavior given an invalid fen. + Info *info = &p->my_thread->infos[i]; + if (p->info->hash == info->hash) { + if (i >= p->my_thread->root_ply) { return true; } @@ -118,16 +117,14 @@ bool is_draw(Position *p) { return false; } -void update_history(SearchThread *thread, Color color, Square from, Square to, int bonus) { - int value = thread->history[color][from][to]; - thread->history[color][from][to] += bonus - value * std::abs(bonus) / 16384; - assert(thread->history[color][from][to] <= 16384); +int draw_score(Position *p) { + // Idea from Stockfish, prevents making losing moves during 3fold repetitions. + return 1 - (p->my_thread->nodes & 2); } -void update_counter_move_history(SearchThread *thread, Piece prev_piece, Square prev_to, Piece piece, Square to, int bonus) { - int value = thread->counter_move_history[prev_piece][prev_to][piece][to]; - thread->counter_move_history[prev_piece][prev_to][piece][to] += bonus - value * std::abs(bonus) / 16384; - assert(thread->counter_move_history[prev_piece][prev_to][piece][to] <= 16384); +void update_history(int *history, int bonus) { + *history += bonus - (*history) * std::abs(bonus) / 16384; + assert(*history <= 16384 && *history >= -16384); } void save_killer(Position *p, Metadata *md, Move move, int depth, Move *quiets, int quiets_count) { @@ -140,40 +137,58 @@ void save_killer(Position *p, Metadata *md, Move move, int depth, Move *quiets, int bonus = std::min(32 * depth * depth, 16384); Square from = move_from(move); Square to = move_to(move); - update_history(my_thread, p->color, from, to, bonus); + update_history(&my_thread->history[p->color][from][to], bonus); + Move q; for (int i = 0; i < quiets_count; ++i) { - Move q = quiets[i]; - update_history(my_thread, p->color, move_from(q), move_to(q), -bonus); + q = quiets[i]; + update_history(&my_thread->history[p->color][move_from(q)][move_to(q)], -bonus); } if (is_move_valid((md-1)->current_move)) { Piece piece = p->pieces[from]; Square prev_to = move_to((md-1)->current_move); - Piece prev_piece = p->pieces[prev_to]; + Piece prev_piece = (md-1)->moved_piece; + assert(prev_piece >= white_pawn && prev_piece <= black_king); my_thread->counter_moves[prev_piece][prev_to] = move; - update_counter_move_history(my_thread, prev_piece, prev_to, piece, to, bonus); + update_history(&my_thread->counter_move_history[prev_piece][prev_to][piece][to], bonus); for (int i = 0; i < quiets_count; ++i) { - Move q = quiets[i]; - update_counter_move_history(my_thread, prev_piece, prev_to, p->pieces[move_from(q)], move_to(q), -bonus); + q = quiets[i]; + update_history(&my_thread->counter_move_history[prev_piece][prev_to][p->pieces[move_from(q)]][move_to(q)], -bonus); + } + } + + if (is_move_valid((md-2)->current_move)) { + Piece piece = p->pieces[from]; + Square prev_to = move_to((md-2)->current_move); + Piece prev_piece = (md-2)->moved_piece; + assert(prev_piece >= white_pawn && prev_piece <= black_king); + + update_history(&my_thread->followup_history[prev_piece][prev_to][piece][to], bonus); + for (int i = 0; i < quiets_count; ++i) { + q = quiets[i]; + update_history(&my_thread->followup_history[prev_piece][prev_to][p->pieces[move_from(q)]][move_to(q)], -bonus); } } } -bool check_time(Position *p) { - if (is_main_thread(p)) { - if (timer_count == 0) { - gettimeofday(&curr_time, nullptr); - if (time_passed() > total_remaining) { - is_timeout = true; - return true; - } +void check_time(Position *p) { + if (!is_main_thread(p)) { + return; + } + + --timer_count; + + if (timer_count == 0) { + gettimeofday(&curr_time, nullptr); + if (time_passed() >= total_remaining) { + is_timeout = true; } - ++timer_count; + + timer_count = 1024; } - return false; } int alpha_beta_quiescence(Position *p, Metadata *md, int alpha, int beta, int depth, bool in_check) { @@ -186,16 +201,18 @@ int alpha_beta_quiescence(Position *p, Metadata *md, int alpha, int beta, int de if (is_pv) { md->pv[0] = no_move; } + if (ply >= MAX_PLY) { return !in_check ? evaluate(p) : 0; } if (is_draw(p)) { - return 0; + return draw_score(p); } Info *info = p->info; md->current_move = no_move; + md->moved_piece = no_piece; (md+1)->ply = ply + 1; int tte_depth = in_check || depth >= 0 ? 0 : -1; @@ -218,7 +235,7 @@ int alpha_beta_quiescence(Position *p, Metadata *md, int alpha, int beta, int de int best_score; if (!in_check) { - bool is_null = ply > 0 && (md-1)->current_move == null_move; + bool is_null = (md-1)->current_move == null_move; if (tt_hit && tte->static_eval != UNDEFINED) { md->static_eval = best_score = tte->static_eval; } else if (is_null) { @@ -261,13 +278,9 @@ int alpha_beta_quiescence(Position *p, Metadata *md, int alpha, int beta, int de make_move(p, move); ++p->my_thread->nodes; md->current_move = move; + md->moved_piece = p->pieces[move_to(move)]; int score = -alpha_beta_quiescence(p, md+1, -beta, -alpha, depth - 1, is_checked(p)); undo_move(p, move); - assert(is_timeout || (score >= -MATE && score <= MATE)); - - if (is_timeout && search_threads[0].depth > 1) { - return TIMEOUT; - } if (score > best_score) { best_score = score; @@ -308,22 +321,31 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i if (is_pv) { md->pv[0] = no_move; } - if (ply >= MAX_PLY) { - return !in_check ? evaluate(p) : 0; - } - - if (is_draw(p)) { - return 0; - } bool root_node = ply == 0; + check_time(p); + if (!root_node) { + if (is_timeout) { + return TIMEOUT; + } + + if (ply >= MAX_PLY) { + return !in_check ? evaluate(p) : 0; + } + + if (is_draw(p)) { + return draw_score(p); + } + } + if (is_pv && ply > p->my_thread->selply) { p->my_thread->selply = ply; } Info *info = p->info; md->current_move = (md+1)->excluded_move = no_move; + md->moved_piece = no_piece; Move excluded_move = md->excluded_move; uint64_t pos_hash = info->hash ^ uint64_t(excluded_move << 16); @@ -355,19 +377,12 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i int tb_score = wdl == SYZYGY_LOSS ? MATED_IN_MAX_PLY + ply + 1 : wdl == SYZYGY_WIN ? MATE_IN_MAX_PLY - ply - 1 : 0; - uint8_t flag = wdl == SYZYGY_LOSS ? FLAG_ALPHA - : wdl == SYZYGY_WIN ? FLAG_BETA : FLAG_EXACT; - - if (flag == FLAG_EXACT || - (flag == FLAG_BETA && tb_score >= beta) || - (flag == FLAG_ALPHA && tb_score <= alpha)) { - set_tte(pos_hash, tte, 0, std::min(depth + SYZYGY_LARGEST, MAX_PLY - 1), score_to_tt(tb_score, ply), UNDEFINED, flag); - return tb_score; - } + set_tte(pos_hash, tte, no_move, std::min(depth + SYZYGY_LARGEST, MAX_PLY - 1), score_to_tt(tb_score, ply), UNDEFINED, FLAG_EXACT); + return tb_score; } } - bool is_null = ply > 0 && (md-1)->current_move == null_move; + bool is_null = (md-1)->current_move == null_move; if (!in_check) { if (tt_hit && tte->static_eval != UNDEFINED) { md->static_eval = tte->static_eval; @@ -397,6 +412,7 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i make_null_move(p); md->current_move = null_move; + md->moved_piece = no_piece; ++p->my_thread->nodes; int null_eval = -alpha_beta(p, md+1, -beta, -beta + 1, depth - R, false); undo_null_move(p); @@ -422,6 +438,8 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i while ((move = next_move(&movegen, md, depth)) != no_move) { if (move != excluded_move && is_legal(p, move)) { make_move(p, move); + md->current_move = move; + md->moved_piece = p->pieces[move_to(move)]; int value = -alpha_beta(p, md+1, -rbeta, -rbeta + 1, depth - 4, is_checked(p)); undo_move(p, move); @@ -468,7 +486,7 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i move == tte_move && !root_node && excluded_move == no_move && - std::abs(tte_score) < MATE_IN_MAX_PLY && + std::abs(tte_score) < TB_WIN && (tte_flag(tte) & FLAG_BETA) && tte->depth >= depth - 2 && is_legal(p, move)) @@ -489,7 +507,7 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i continue; } - if (!important && depth < 9 && !see_capture(p, move, -20 * depth * depth)) { + if (!important && depth < 9 && !see_capture(p, move, -10 * depth * depth)) { continue; } @@ -502,9 +520,13 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i continue; } + // History score has to be obtained before calling make move which changes the position + int quiet_score = score_quiet(p, md, move); + make_move(p, move); ++p->my_thread->nodes; md->current_move = move; + md->moved_piece = p->pieces[move_to(move)]; if (!capture_or_promo && quiets_count < 64) { quiets[quiets_count++] = move; } @@ -513,7 +535,7 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i int score; if (is_pv && num_moves == 1) { - score = -alpha_beta(p, md+1, -beta, -alpha, depth - 1, checks); + score = -alpha_beta(p, md+1, -beta, -alpha, new_depth, checks); } else { // late move reductions int reduction = 0; @@ -527,7 +549,7 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i --reduction; } - reduction = std::max(reduction, 0); + reduction = std::max(reduction - quiet_score / 12288, 0); } score = -alpha_beta(p, md+1, -alpha - 1, -alpha, new_depth - reduction, checks); @@ -543,7 +565,7 @@ int alpha_beta(Position *p, Metadata *md, int alpha, int beta, int depth, bool i undo_move(p, move); assert(is_timeout || (score >= -MATE && score <= MATE)); - if ((is_timeout || check_time(p)) && search_threads[0].depth > 1) { + if (is_timeout) { return TIMEOUT; } @@ -608,10 +630,12 @@ void print_info(SearchThread *my_thread, Metadata *md, int depth, int score, int std::cout << " hashfull " << hashfull(); uint64_t nodes = sum_nodes(); - std::cout << " nodes " << nodes << " nps " << nodes*1000/(time_taken+1) << " time " << time_taken; + std::cout << " nodes " << nodes << " nps " << nodes*1000/(time_taken+1) << " time " << time_taken << " pv "; if (pv_printed) { - std::cout << " pv "; print_pv(md); + } else { + // For fail lows, only print the first move of the main pv + std::cout << move_to_str(main_pv[0]); } std::cout << std::endl; } @@ -629,8 +653,7 @@ void thread_think(SearchThread *my_thread, bool in_check) { while (++depth <= think_depth_limit) { if (!is_main) { - int main_thread_depth = search_threads[0].depth; - depth = main_thread_depth + depth_increments[my_thread->thread_id]; + depth = main_thread.depth + depth_increments[my_thread->thread_id]; } my_thread->depth = depth; @@ -654,16 +677,16 @@ void thread_think(SearchThread *my_thread, bool in_check) { set_main_pv(md); } - if (is_main && score != TIMEOUT && (score <= alpha || score >= beta) && depth > 12) { - print_info(my_thread, md, depth, score, alpha, beta, false); - } - // This timeout has to be after setting main pv, otherwise // it'll cause a1a1 to be outputted as the pv if (is_timeout) { break; } + if (is_main && score != TIMEOUT && (score <= alpha || score >= beta) && depth > 12) { + print_info(my_thread, md, depth, score, alpha, beta, false); + } + if (score <= alpha) { beta = (alpha + beta) / 2; alpha = std::max(score - aspiration, -MATE); @@ -722,8 +745,7 @@ void think(Position *p, std::vector word_list) { // First check TB Move tb_move; bool in_check = is_checked(p); - SearchThread *main_thread = p->my_thread; - Metadata *md = &main_thread->metadatas[2]; // Start from 2 so that we can do (md-2) without checking + Metadata *md = &main_thread.metadatas[2]; // Start from 2 so that we can do (md-2) without checking // Clear root moves root_moves.clear(); @@ -738,7 +760,6 @@ void think(Position *p, std::vector word_list) { } root_moves.push_back(tb_move); } else { - Material *eval_material = get_material(p); MoveGen movegen = new_movegen(p, md, no_move, NORMAL_SEARCH, 0, in_check); Move move; while ((move = next_move(&movegen, md, 0)) != no_move) { @@ -746,7 +767,7 @@ void think(Position *p, std::vector word_list) { root_moves.push_back(move); } } - if (eval_material->endgame_type == DRAW_ENDGAME || root_moves.size() == 1) { + if (root_moves.size() == 1) { std::cout << "bestmove " << move_to_str(root_moves[0]) << std::endl; return; } @@ -764,8 +785,10 @@ void think(Position *p, std::vector word_list) { initialize_nodes(); is_searching = true; - for (int i = 0; i < num_threads; ++i) { - SearchThread *t = &search_threads[i]; + threads[0] = std::thread(thread_think, &main_thread, in_check); + + for (int i = 1; i < num_threads; ++i) { + SearchThread *t = get_thread(i); threads[i] = std::thread(thread_think, t, in_check); } @@ -801,7 +824,7 @@ void bench() { myremain = 3600000; think(p, empty_word_list); - nodes += search_threads[0].nodes; + nodes += main_thread.nodes; clear_tt(); } @@ -815,6 +838,5 @@ void bench() { std::cout << "Time : " << time_taken << std::endl; std::cout << "Nodes : " << nodes << std::endl; std::cout << "NPS : " << nodes * 1000 / (time_taken + 1) << std::endl; - exit(EXIT_SUCCESS); } diff --git a/src/search.h b/src/search.h index 21f5341..387fdb6 100644 --- a/src/search.h +++ b/src/search.h @@ -36,16 +36,17 @@ inline int lmr(bool is_pv, int depth, int num_moves) { return reductions[is_pv][std::min(depth, 63)][std::min(num_moves, 63)]; } -extern uint8_t timer_count; -extern int myremain; -extern int total_remaining; -extern volatile bool is_timeout; -extern int think_depth_limit; -extern bool quit_application; -extern volatile bool is_searching; - extern struct timeval curr_time, start_ts; +extern int timer_count, + myremain, + total_remaining, + think_depth_limit; + +extern volatile bool is_timeout, + quit_application, + is_searching; + inline int time_passed() { return (((curr_time.tv_sec - start_ts.tv_sec) * 1000000) + (curr_time.tv_usec - start_ts.tv_usec)) / 1000; } @@ -56,7 +57,7 @@ inline int bench_time(struct timeval s, struct timeval e) { inline void init_time(Position *p, std::vector word_list) { - timer_count = 0; + timer_count = 1024; is_timeout = false; if (word_list.size() <= 1) { @@ -98,15 +99,14 @@ inline void init_time(Position *p, std::vector word_list) { moves_to_go = stoi(word_list[i + 1]); } - if (white_remaining > 0 || black_remaining > 0) { - TTime t = get_myremain( - p->color == white ? white_increment : black_increment, - p->color == white ? white_remaining : black_remaining, - moves_to_go - ); - myremain = t.optimum_time; - total_remaining = t.maximum_time; - } + TTime t = get_myremain( + p->color == white ? white_increment : black_increment, + p->color == white ? white_remaining : black_remaining, + moves_to_go, + p->my_thread->root_ply + ); + myremain = t.optimum_time; + total_remaining = t.maximum_time; } } diff --git a/src/test.cpp b/src/test.cpp index 9e46215..12a6264 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -194,7 +194,7 @@ uint64_t fastPerft(int depth, Position *p, bool root, bool in_check) { uint64_t move_nodes = 0, nodes = 0; const bool is_leaf = depth == 2; - Metadata *md = &p->my_thread->metadatas[0]; + Metadata *md = &p->my_thread->metadatas[2]; MoveGen movegen = new_movegen(p, md, no_move, NORMAL_SEARCH, 0, in_check); Move move; while ((move = next_move(&movegen, md, 0)) != no_move) { @@ -260,7 +260,7 @@ uint64_t Perft(int depth, Position *p, bool root, bool in_check) { } uint64_t move_nodes = 0, nodes = 0; - Metadata *md = &p->my_thread->metadatas[0]; + Metadata *md = &p->my_thread->metadatas[2]; MoveGen movegen = new_movegen(p, md, no_move, NORMAL_SEARCH, 0, in_check); Move move; while ((move = next_move(&movegen, md, 0)) != no_move) { diff --git a/src/thread.cpp b/src/thread.cpp index 94c6c36..72f9180 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -23,26 +23,25 @@ #include "thread.h" void get_ready() { - SearchThread *main_thread = &search_threads[0]; - main_thread->root_ply = main_thread->search_ply; + main_thread.root_ply = main_thread.search_ply; // Copy over the root position for (int i = 1; i < num_threads; ++i) { - SearchThread *t = &search_threads[i]; - t->root_ply = t->search_ply = main_thread->root_ply; + SearchThread *t = get_thread(i); + t->root_ply = t->search_ply = main_thread.root_ply; // Need to fully copy the position and info - std::memcpy(&t->position, &main_thread->position, sizeof(Position)); + std::memcpy(&t->position, &main_thread.position, sizeof(Position)); Position *t_position = &t->position; // Also copy the info - std::memcpy(t->infos + main_thread->root_ply, main_thread->infos + main_thread->root_ply, sizeof(Info)); - t_position->info = &t->infos[main_thread->root_ply]; + std::memcpy(t->infos + main_thread.root_ply, main_thread.infos + main_thread.root_ply, sizeof(Info)); + t_position->info = &t->infos[main_thread.root_ply]; t_position->my_thread = t; } for (int i = 0; i < num_threads; ++i) { - SearchThread *t = &search_threads[i]; + SearchThread *t = get_thread(i); t->depth = 1; // Clear the metadata @@ -55,53 +54,47 @@ void get_ready() { md->killers[1] = no_move; md->pv[0] = no_move; md->excluded_move = no_move; + md->moved_piece = no_piece; } } } void clear_threads() { for (int i = 0; i < num_threads; ++i) { - SearchThread *search_thread = &search_threads[i]; + SearchThread *search_thread = get_thread(i); + // Clear history - for (int j = 0; j < 64; ++j) { - for (int k = 0; k < 64; ++k) { - search_thread->history[white][j][k] = 0; - search_thread->history[black][j][k] = 0; - } - } + std::memset(&search_thread->history, 0, sizeof(search_thread->history)); + std::memset(&search_thread->counter_move_history, 0, sizeof(search_thread->counter_move_history)); + std::memset(&search_thread->followup_history, 0, sizeof(search_thread->followup_history)); + // Clear counter moves for (int j = 0; j < NUM_PIECE; ++j) { for (int k = 0; k < 64; ++k) { search_thread->counter_moves[j][k] = no_move; } } - // Clear counter move history - for (int j = 0; j < NUM_PIECE; ++j) { - for (int k = 0; k < 64; ++k) { - for (int l = 0; l < NUM_PIECE; ++l) { - for (int m = 0; m < 64; ++m) { - search_thread->counter_move_history[j][k][l][m] = 0; - } - } - } - } } } void reset_threads() { - search_threads = (SearchThread*) realloc(search_threads, num_threads * sizeof(SearchThread)); + // Don't touch the main thread + search_threads = (SearchThread*) realloc(search_threads, (num_threads - 1) * sizeof(SearchThread)); for (int i = 0; i < num_threads; ++i) { - search_threads[i].thread_id = i; + SearchThread *t = get_thread(i); + t->thread_id = i; } clear_threads(); + get_ready(); } void init_threads() { - search_threads = (SearchThread*) malloc(num_threads * sizeof(SearchThread)); + search_threads = (SearchThread*) malloc((num_threads - 1) * sizeof(SearchThread)); for (int i = 0; i < num_threads; ++i) { - search_threads[i].thread_id = i; + SearchThread *t = get_thread(i); + t->thread_id = i; } clear_threads(); } diff --git a/src/timecontrol.cpp b/src/timecontrol.cpp index 8bd2f39..07bfa11 100644 --- a/src/timecontrol.cpp +++ b/src/timecontrol.cpp @@ -16,43 +16,49 @@ along with Defenchess. If not, see . */ -#include "timecontrol.h" #include #include -TTime moves_in_time(int increment, int remaining, int movestogo){ - int importance; - importance = 5 * std::sqrt(movestogo); +#include "timecontrol.h" +TTime moves_in_time(int increment, int remaining, int movestogo, int root_ply){ + int move_num = (root_ply + 1) / 2; int average_time = remaining / movestogo; - int extra = average_time * importance * 3 / 200; - int spend = average_time + extra + increment * (movestogo - 1) / movestogo; + int extra = average_time * std::max(30 - move_num, 0) / 200; + int spend = average_time + extra + increment; int max_usage = std::min(spend * 6, (remaining - move_overhead) / 4); + + if (max_usage < increment) { + max_usage = remaining > 2 * move_overhead ? remaining - 2 * move_overhead : remaining - 1; + } + return {spend, max_usage}; } -TTime no_movestogo(int increment, int remaining) { +TTime no_movestogo(int increment, int remaining, int root_ply) { int min_to_go = increment == 0 ? 10 : 3; - int move_num = (search_threads[0].root_ply + 1) / 2; - int movestogo = std::max(10 + 4 * (50 - move_num) / 5 , min_to_go); + int move_num = (root_ply + 1) / 2; + + int extra_movestogo = increment == 0 ? 10 : 0; + int movestogo = std::max(50 + extra_movestogo - 4 * move_num / 5 , min_to_go); int average_time = remaining / movestogo; int extra = average_time * std::max(30 - move_num, 0) / 200; int spend = average_time + extra + increment; int max_usage = std::min(spend * 6, (remaining - move_overhead) / 4); if (max_usage < increment) { - max_usage = remaining - 2 * move_overhead; + max_usage = remaining > 2 * move_overhead ? remaining - 2 * move_overhead : remaining - 1; } return {spend, max_usage}; } -TTime get_myremain(int increment, int remaining, int movestogo){ +TTime get_myremain(int increment, int remaining, int movestogo, int root_ply){ if (movestogo == 1) { return TTime{remaining - move_overhead, remaining - move_overhead}; } else if (movestogo == 0) { - return no_movestogo(increment, remaining); + return no_movestogo(increment, remaining, root_ply); } else { - return moves_in_time(increment, remaining, movestogo); + return moves_in_time(increment, remaining, movestogo, root_ply); } } diff --git a/src/timecontrol.h b/src/timecontrol.h index dd5494e..ce9c29b 100644 --- a/src/timecontrol.h +++ b/src/timecontrol.h @@ -26,6 +26,6 @@ typedef struct TTime { int maximum_time; } TTime; -TTime get_myremain(int increment, int remaining, int movestogo); +TTime get_myremain(int increment, int remaining, int movestogo, int root_ply); #endif diff --git a/src/tune.cpp b/src/tune.cpp index f707814..05f01b9 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -67,7 +67,7 @@ void single_error(int thread_id) { continue; } - Metadata *md = &p->my_thread->metadatas[0]; + Metadata *md = &p->my_thread->metadatas[2]; md->current_move = no_move; md->static_eval = UNDEFINED; md->ply = 0; @@ -277,7 +277,6 @@ void tune() { if (new_error < best_error) { best_error = new_error; best_guess = new_guess; - best_guess[pi].increasing = true; improving = true; cout << new_guess[pi].name << "[" << new_guess[pi].value << "]:\t" << new_error << " (best)" << endl; best_guess[pi].stability = 1; @@ -292,7 +291,7 @@ void tune() { if (new_error < best_error) { best_error = new_error; best_guess = new_guess; - best_guess[pi].increasing = false; + best_guess[pi].increasing = !best_guess[pi].increasing; improving = true; cout << new_guess[pi].name << "[" << new_guess[pi].value << "]:\t" << new_error << " (best)" << endl; best_guess[pi].stability = 1; @@ -319,33 +318,6 @@ void tune() { } void init_mobility(vector ¶meters) { - for (int i = 0; i < 4; ++i) { - for (int j = RANK_1; j < RANK_8; ++j) { - parameters.push_back({&pawn_shelter[i][j], pawn_shelter[i][j], "pawn_shelter[" + to_string(i) + "][" + to_string(j) + "]", true, 1}); - } - } - for (int b = 0; b <= 1; ++b) { - for (int i = 0; i < 4; ++i) { - for (int j = (b ? 2 : 0); j < 8; ++j) { - parameters.push_back({&pawn_storm[b][i][j], pawn_storm[b][i][j], "pawn_storm[" + to_string(b) + "][" + to_string(i) + "][" + to_string(j) + "]", true, 1}); - } - } - } - return; - - for (int o = 0; o <= 1; ++o) { - for (int adj = 0; adj <= 1; ++adj) { - for (int i = RANK_2; i < RANK_8; ++i) { - if ((o && i == RANK_7) || (!adj && i == RANK_2)) { - continue; - } - parameters.push_back({&connected_bonus[o][adj][i].midgame, connected_bonus[o][adj][i].midgame, "connected_bonus[" + to_string(o) + "][" + to_string(adj) + "][" + to_string(i) + "].midgame", true, 1}); - parameters.push_back({&connected_bonus[o][adj][i].endgame, connected_bonus[o][adj][i].endgame, "connected_bonus[" + to_string(o) + "][" + to_string(adj) + "][" + to_string(i) + "].endgame", true, 1}); - } - } - } - return; - for (int i = 0; i <= 8; ++i) { parameters.push_back({&mobility_bonus[KNIGHT][i].midgame, mobility_bonus[KNIGHT][i].midgame, "mobility_bonus[KNIGHT][" + to_string(i) + "].midgame", true, 1}); parameters.push_back({&mobility_bonus[KNIGHT][i].endgame, mobility_bonus[KNIGHT][i].endgame, "mobility_bonus[KNIGHT][" + to_string(i) + "].endgame", true, 1}); @@ -406,6 +378,9 @@ void init_pst(vector ¶meters) { } void init_parameters(vector ¶meters) { + parameters.push_back({&SCALE_NO_PAWNS, SCALE_NO_PAWNS, "SCALE_NO_PAWNS", true, 1}); + return; + // DO NOT TUNE THIS parameters.push_back({&PAWN_MID, PAWN_MID, "PAWN_MID", true}); parameters.push_back({&PAWN_END, PAWN_END, "PAWN_END", true, 1}); diff --git a/src/uci.cpp b/src/uci.cpp index e1c8f44..09fd51d 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -80,7 +80,7 @@ bool word_equal(int index, string comparison_str) { } void uci() { - cout << "id name Defenchess 2.1 x64" << endl << "id author Can Cetin & Dogac Eldenk" << endl; + cout << "id name Defenchess 2.2 x64" << endl << "id author Can Cetin & Dogac Eldenk" << endl; #ifndef NDEBUG cout << "debug mode on" << std::endl; #endif @@ -102,7 +102,7 @@ void perft() { void debug() { cout << bitstring(root_position->board); - Metadata *md = &search_threads[0].metadatas[2]; + Metadata *md = &main_thread.metadatas[2]; MoveGen movegen = new_movegen(root_position, md, no_move, NORMAL_SEARCH, 0, is_checked(root_position)); Move move; while ((move = next_move(&movegen, md, 0)) != no_move) { @@ -232,6 +232,27 @@ void setoption() { } } +void so() { + // Quick set option without name and value + string name = word_list[1]; + string value = word_list[2]; + + if (name == "Hash") { + int mb = stoi(value); + if (!mb || more_than_one(uint64_t(mb))) { + cout << "info Hash value needs to be a power of 2!" << endl; + } + reset_tt(mb); + } else if (name == "Threads") { + num_threads = std::min(MAX_THREADS, stoi(value)); + reset_threads(); + } else if (name == "SyzygyPath") { + init_syzygy(value); + } else if (name == "MoveOverhead") { + move_overhead = stoi(value); + } +} + void ucinewgame() { clear_threads(); clear_tt(); @@ -250,6 +271,8 @@ void run_command(string s) { go(); if (s == "setoption") setoption(); + if (s == "so") + so(); if (s == "isready") isready(); if (s == "uci") @@ -277,7 +300,7 @@ void run_command(string s) { } void loop() { - cout << "Defenchess 2.1 x64 by Can Cetin and Dogac Eldenk" << endl; + cout << "Defenchess 2.2 x64 by Can Cetin and Dogac Eldenk" << endl; string in_str; #ifdef __TUNE__