From dd7a44235040976f91867c8e6679551e9e14a824 Mon Sep 17 00:00:00 2001 From: Jonathan Rosenthal Date: Tue, 19 Feb 2019 01:18:45 -0500 Subject: [PATCH] Counter Move History (#3) Added counter move history which resulted in a boost of +40 +-8 Elo in self play at very fast TC. The regular History Heuristic as well as SFs Capture History were both tried without any real success. Non of these features were tuned and they were done in a rush, so there should be plenty of room for improvement. --- src/general/feature_indexes.h | 6 ++-- src/general/hardcoded_params.h | 10 ++++--- src/general/settings.h | 2 +- src/search.cc | 55 ++++++++++++++++++++++++++++++++++ src/search_thread.h | 25 ++++++++++++++++ 5 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/general/feature_indexes.h b/src/general/feature_indexes.h index af511bd..0a97baa 100644 --- a/src/general/feature_indexes.h +++ b/src/general/feature_indexes.h @@ -161,9 +161,10 @@ const int kPWIPawnRankDestination = kPWIForcingChanges + 4; const int kPWIPassedRankDestination = kPWIPawnRankDestination + 6; const int kPWIPawnAttack = kPWIPassedRankDestination + 6; const int kPWIPieceUnderAttack = kPWIPawnAttack + 1; -const int kNumMoveProbabilityFeatures = kPWIPieceUnderAttack + 2; +const int kPWICMH = kPWIPieceUnderAttack + 2; +const int kNumMoveProbabilityFeatures = kPWICMH+ 1; -const std::array kFeatureInfos = {{ +const std::array kFeatureInfos = {{ FeatureInfo("Hash Move", kPWIHashMove), FeatureInfo("Killer Move", kPWIKiller), FeatureInfo("Counter Move", kPWICounterMove), @@ -182,6 +183,7 @@ const std::array kFeatureInfos = {{ FeatureInfo("Rank of passed pawn destination", kPWIPassedRankDestination), FeatureInfo("Pawn move attacks a piece", kPWIPawnAttack), FeatureInfo("Piece under attack", kPWIPieceUnderAttack), + FeatureInfo("CMH", kPWICMH), FeatureInfo("Num Features Placeholder", kNumMoveProbabilityFeatures) }}; diff --git a/src/general/hardcoded_params.h b/src/general/hardcoded_params.h index 8d2321b..0bf4a2d 100644 --- a/src/general/hardcoded_params.h +++ b/src/general/hardcoded_params.h @@ -566,7 +566,7 @@ const std::array eval_weights_gmm = { // ------------------------------------------------------------------------------------------------ // Search parameters used for move ordering when not in check -const std::array search_params = { +const std::array search_params = { 2000, // Hash Move 142, // Killer Move 112, // Killer Move @@ -680,11 +680,12 @@ const std::array search_params = { 0, // Rank of passed pawn destination 128, // Pawn move attacks a piece 131, // Piece under attack - 206 // Piece under attack + 206, // Piece under attack + 1 }; // Search parameters used for move ordering when in check -const std::array search_params_in_check = { +const std::array search_params_in_check = { 2000, // Hash Move 123, // Killer Move 116, // Killer Move @@ -798,7 +799,8 @@ const std::array search_params_in_check = { 0, // Rank of passed pawn destination -15, // Pawn move attacks a piece 75, // Piece under attack - 11 // Piece under attack + 11, // Piece under attack + 1 }; } diff --git a/src/general/settings.h b/src/general/settings.h index 69eda47..b39615d 100644 --- a/src/general/settings.h +++ b/src/general/settings.h @@ -33,7 +33,7 @@ namespace settings { const std::string engine_name = "Winter"; -const std::string engine_version = "0.4.2"; +const std::string engine_version = "0.5"; const std::string engine_author = "Jonathan Rosenthal"; const int kNumClusters = 4; diff --git a/src/search.cc b/src/search.cc index f483aaf..6929c29 100644 --- a/src/search.cc +++ b/src/search.cc @@ -237,6 +237,19 @@ template<> inline void AddFeature(int &s, const int index) { s += search_weights_in_check[index]; } +template inline +void AddFeature(T &s, const int index, int val) { + s[index] = val; +} + +template<> inline void AddFeature(int &s, const int index, int val) { + s += search_weights[index] * val; +} + +template<> inline void AddFeature(int &s, const int index, int val) { + s += search_weights_in_check[index] * val; +} + inline Move get_last_move(const Board &board) { if (board.get_num_made_moves() > 0) { return board.get_last_move(); @@ -340,6 +353,13 @@ T GetMoveWeight(const Move move, search::Thread &t, const MoveOrderInfo &info) { if (move == t.counter_moves[t.board.get_turn()][last_moved_piece][last_destination]) { AddFeature(move_weight, kPWICounterMove); } + if (GetMoveType(move) < kCapture) { + const PieceType moving_piece = GetPieceType(t.board.get_piece(GetMoveSource(move))); + const Square destination = GetMoveDestination(move); + const int score = t.get_cmh_score(last_moved_piece, last_destination, + moving_piece, destination); + AddFeature(move_weight, kPWICMH, score / 100); + } } const PieceType moving_piece = GetPieceType(t.board.get_piece(GetMoveSource(move))); PieceType target = GetPieceType(t.board.get_piece(GetMoveDestination(move))); @@ -725,6 +745,27 @@ inline void bookkeeping_log(int NodeType, const Board &board, Move tt_entry, bool move_is_singular(Thread &t, const Depth depth, const std::vector &moves, const table::Entry &entry); +void update_counter_move_history(Thread &t, const std::vector &quiets, const Depth depth) { + if (t.board.get_num_made_moves() == 0 || t.board.get_last_move() == kNullMove) { + return; + } + const Move move = t.board.get_last_move(); + Square opp_des = GetMoveDestination(move); + PieceType opp_piecetype = GetPieceType(t.board.get_piece(opp_des)); + // TODO deal with promotion situations + int32_t score = std::min(depth * depth, 200); + + for (int i = 0; i < quiets.size() - 1; ++i) { + Square des = GetMoveDestination(quiets[i]); + PieceType piecetype = GetPieceType(t.board.get_piece(GetMoveSource(quiets[i]))); + t.update_cmh_scores(opp_piecetype, opp_des, piecetype, des, -score); + } + int i = quiets.size() - 1; + Square des = GetMoveDestination(quiets[i]); + PieceType piecetype = GetPieceType(t.board.get_piece(GetMoveSource(quiets[i]))); + t.update_cmh_scores(opp_piecetype, opp_des, piecetype, des, score); +} + template Score AlphaBeta(Thread &t, Score alpha, Score beta, Depth depth, int expected_node = 1) { assert(beta > alpha); @@ -756,6 +797,9 @@ Score AlphaBeta(Thread &t, Score alpha, Score beta, Depth depth, int expected_no bool valid_entry = table::ValidateHash(entry,t.board.get_hash()); if (NodeType != kPV && valid_entry && sufficient_bounds(t.board, entry, alpha, beta, depth) ) { +// if (entry.get_best_move() != kNullMove && GetMoveType(entry.get_best_move()) < kCapture) { +// update_counter_move_history(t, {{entry.get_best_move()}}, depth); +// } return entry.get_score(t.board); } @@ -850,6 +894,7 @@ Score AlphaBeta(Thread &t, Score alpha, Score beta, Depth depth, int expected_no //Ie no checks from special moves or discovered checks. Vec checking_squares = t.board.GetDirectCheckingSquares(); + std::vector quiets; //Move loop for (unsigned int i = 0; i < moves.size(); i++) { if (i == 1 && !moves_sorted) { @@ -899,6 +944,10 @@ Score AlphaBeta(Thread &t, Score alpha, Score beta, Depth depth, int expected_no } } + if (GetMoveType(move) < kCapture) { + quiets.emplace_back(move); + } + //Make moves, search and unmake t.board.Make(move); Score score; @@ -925,6 +974,9 @@ Score AlphaBeta(Thread &t, Score alpha, Score beta, Depth depth, int expected_no //Check if search failed high if (score >= beta) { + if (GetMoveType(move) < kCapture) { + update_counter_move_history(t, quiets, depth); + } //Save TT entry table::SaveEntry(t.board, move, score, depth); @@ -949,6 +1001,9 @@ Score AlphaBeta(Thread &t, Score alpha, Score beta, Depth depth, int expected_no //In PV nodes we might be improving Alpha without breaking Beta if (score > alpha) { + if (GetMoveType(move) < kCapture) { + update_counter_move_history(t, quiets, depth); + } //Update score and expected best move alpha = score; best_local_move = move; diff --git a/src/search_thread.h b/src/search_thread.h index 6fefa67..7ee7d12 100644 --- a/src/search_thread.h +++ b/src/search_thread.h @@ -57,6 +57,17 @@ struct Thread { } } } + + for (PieceType pt = kPawn; pt <= kKing; pt++) { + for (Square sq = 0; sq < 64; sq++) { + for (PieceType pt_i = kPawn; pt_i <= kKing; pt_i++) { + for (Square sq_i = 0; sq_i < 64; sq_i++) { + counter_move_history[pt][sq][pt_i][sq_i] = 0; + } + } + } + } + } void search(); @@ -77,6 +88,19 @@ struct Thread { static_scores[height] = score; } + int32_t get_cmh_score(const PieceType opp_piecetype, const Square opp_des, + const PieceType piecetype, const Square des) const { + return counter_move_history[opp_piecetype][opp_des][piecetype][des]; + } + + void update_cmh_scores(const PieceType opp_piecetype, const Square opp_des, + const PieceType piecetype, const Square des, const int32_t score) { + + counter_move_history[opp_piecetype][opp_des][piecetype][des] += 32 * score + - counter_move_history[opp_piecetype][opp_des][piecetype][des] + * std::abs(score) / 512; + } + //Multithreading objects int id; @@ -86,6 +110,7 @@ struct Thread { Depth current_depth; Array2d killers; Array3d counter_moves; + Array2d, 6, 64> counter_move_history; Depth root_height; std::array static_scores; std::atomic nodes;