diff --git a/about_Gikou_AperyEvalMix.txt b/about_Gikou_AperyEvalMix.txt index a807bea..c0bc23f 100644 --- a/about_Gikou_AperyEvalMix.txt +++ b/about_Gikou_AperyEvalMix.txt @@ -110,3 +110,7 @@  ・元々のGainsStatsは指し手の実現確率の算出で使用されているので、そのままにしておく。 +■Gikou_AperyEvalMixNoTurn_Stockfish7_20161008 +「Gikou_AperyEvalMixNoTurn_BothGains_20160918」からの変更点 + ・Stockfish7の探索部を取り込み中。 + diff --git a/gikou_ja.txt b/gikou_ja.txt index 1bf1390..ed160f9 100644 --- a/gikou_ja.txt +++ b/gikou_ja.txt @@ -15,3 +15,4 @@ Z01_AperyEvalJoban Aperyの評価値の割合(序盤、%) Z02_AperyEvalChuban Aperyの評価値の割合(中盤、%) Z03_AperyEvalShuban Aperyの評価値の割合(終盤、%) Z04_AperyEvalFolder Apery評価関数バイナリのフォルダ +Z10_UseProbabilityMinDepth 探索で実現確率を使用する深さの最小値 diff --git a/src/move_probability.cc b/src/move_probability.cc index f2f62f8..4743d3b 100644 --- a/src/move_probability.cc +++ b/src/move_probability.cc @@ -508,6 +508,46 @@ std::unordered_map MoveProbability::ComputeProbabilities(const return move_probabilities; } +// この処理の中では指し手生成を行わず、引数で受け取る。 +std::unordered_map MoveProbability::ComputeProbabilities(const Position& pos, + const HistoryStats& history, + const GainsStats& gains + , ExtMove* begin + , ExtMove* end) { + // 合法手を生成する + //SimpleMoveList legal_moves(pos); + + // 局面情報を収集する + PositionSample sample; + PositionInfo pos_info(pos, history, gains); + sample.progress = Progress::EstimateProgress(pos); + + //for (ExtMove em : legal_moves) { + // sample.features.push_back(ExtractMoveFeatures(em.move, pos, pos_info)); + //} + for (ExtMove* it = begin; it != end; ++it) { + sample.features.push_back(ExtractMoveFeatures(it->move, pos, pos_info)); + } + + // 確率を計算する + std::valarray p = ComputeMoveProbabilities(sample); + + // 指し手と確率を対応付ける + std::unordered_map move_probabilities; + + //for (size_t i = 0; i < legal_moves.size(); ++i) { + // move_probabilities.emplace(legal_moves[i].move.ToUint32(), p[i]); + //} + + size_t i = 0; + for (ExtMove* it = begin; it != end; ++it) { + move_probabilities.emplace(it->move.ToUint32(), p[i]); + i++; + } + + return move_probabilities; +} + void MoveProbability::Init() { g_weights.resize(kNumMoveFeatures); diff --git a/src/move_probability.h b/src/move_probability.h index 7e3c987..929726d 100644 --- a/src/move_probability.h +++ b/src/move_probability.h @@ -36,6 +36,16 @@ struct MoveProbability { const HistoryStats& history, const GainsStats& gains); + /** + * 指し手が指される確率を計算します. + * ・この処理の中では指し手生成を行わず、引数で受け取る。 + */ + static std::unordered_map ComputeProbabilities(const Position& pos, + const HistoryStats& history, + const GainsStats& gains + , ExtMove* begin + , ExtMove* end); + /** * 指し手が指される確率を棋譜から学習します. * diff --git a/src/movepick.cc b/src/movepick.cc index 9cc14bb..1c07dc2 100644 --- a/src/movepick.cc +++ b/src/movepick.cc @@ -51,6 +51,7 @@ inline bool is_greater(const ExtMove& lhs, const ExtMove& rhs) { } inline bool has_good_score(const ExtMove& em) { + // TODO ScoreMoves()でhistory_[move]を使用しない場合はここも変える必要があるので注意 return em.score > (HistoryStats::kMax / 2); } @@ -77,7 +78,8 @@ MovePicker::MovePicker(const Position& pos, const HistoryStats& history, const Array& killermoves, const Array& countermoves, const Array& followupmoves, - Search::Stack* const ss) + Search::Stack* const ss + , const Search& search, bool use_probability) : pos_(pos), history_(history), gains_(gains), @@ -85,7 +87,8 @@ MovePicker::MovePicker(const Position& pos, const HistoryStats& history, depth_(depth), killermoves_(killermoves), countermoves_(countermoves), - followupmoves_(followupmoves) { + followupmoves_(followupmoves) + , search_(search) { assert(hash_move.IsOk()); assert(depth > kDepthZero); assert(ss != nullptr); @@ -98,7 +101,8 @@ MovePicker::MovePicker(const Position& pos, const HistoryStats& history, // 指し手生成のカテゴリをセットする if (pos.in_check()) { stage_ = kEvasion; - } else if (depth >= 8 * kOnePly) { + //} else if (depth >= 8 * kOnePly) { + } else if (use_probability) { stage_ = kProbSearch; } else { stage_ = kMainSearch; @@ -112,11 +116,13 @@ MovePicker::MovePicker(const Position& pos, const HistoryStats& history, } MovePicker::MovePicker(const Position& pos, const HistoryStats& history, - const GainsStats& gains, Depth depth, Move hash_move) + const GainsStats& gains, Depth depth, Move hash_move + , const Search& search) : pos_(pos), history_(history), gains_(gains), - depth_(depth) { + depth_(depth) + , search_(search) { assert(hash_move.IsOk()); assert(depth <= kDepthZero); @@ -147,11 +153,13 @@ MovePicker::MovePicker(const Position& pos, const HistoryStats& history, } MovePicker::MovePicker(const Position& pos, const HistoryStats& history, - const GainsStats& gains, Move hash_move) + const GainsStats& gains, Move hash_move + , const Search& search) : pos_(pos), history_(history), gains_(gains), - stage_(kProbCut) { + stage_(kProbCut) + , search_(search) { // ポインタを初期化する cur_ = moves_.begin(); end_ = moves_.begin(); @@ -291,23 +299,81 @@ void MovePicker::ScoreMoves() { template<> void MovePicker::ScoreMoves() { + // Stockfish7対応 + const SfHistoryStats& sf_history = *(search_.sf_history_); + const FromToStats& from_to = *(search_.from_to_); + + const CounterMoveStats* cm = (ss_-1)->counterMoves; + const CounterMoveStats* fm = (ss_-2)->counterMoves; + const CounterMoveStats* f2 = (ss_-4)->counterMoves; + + Color c = pos_.side_to_move(); + for (ExtMove* it = moves_.begin(); it != end_; ++it) { - it->score = history_[it->move]; + Move move = it->move; + Piece pc = move.piece_after_move(); + Square sq = move.to(); + + //it->score = history_[it->move]; + it->score = history_[move] + + sf_history[pc][sq] + + (cm ? (*cm)[pc][sq] : kScoreZero) + + (fm ? (*fm)[pc][sq] : kScoreZero) + + (f2 ? (*f2)[pc][sq] : kScoreZero) + + from_to.get(c, move); + +// デバッグ用 +#if 0 + static int cnt = 0; + cnt++; + + if (cnt % 10000 == 0) { + cnt++; + + std::printf("-----\n"); + std::printf("thread_id_=%d\n", search_.thread_id()); + std::printf("Color=%d\n", c); + std::printf("move=%s\n", move.ToSfen().c_str()); + std::printf("pc=%s\n", pc.ToSfen().c_str()); + std::printf("sq=%s\n", sq.ToSfen().c_str()); + std::printf("history_[move] =%7d\n", history_[move]); + std::printf("sf_history[pc][sq] =%7d\n", sf_history[pc][sq]); + std::printf("(*cm)[pc][sq] =%7d\n", (cm ? (*cm)[pc][sq] : kScoreZero)); + std::printf("(*fm)[pc][sq] =%7d\n", (fm ? (*fm)[pc][sq] : kScoreZero)); + std::printf("(*f2)[pc][sq] =%7d\n", (f2 ? (*f2)[pc][sq] : kScoreZero)); + std::printf("from_to.get(c, move)=%7d\n", from_to.get(c, move)); + std::printf("it->score =%7d\n", it->score); + } +#endif } } template<> void MovePicker::ScoreMoves() { + // Stockfish7対応 + const SfHistoryStats& sf_history = *(search_.sf_history_); + const FromToStats& from_to = *(search_.from_to_); + + Color c = pos_.side_to_move(); + for (ExtMove* it = moves_.begin(); it != end_; ++it) { Move move = it->move; + Piece pc = move.piece_after_move(); + Score swap_score = Swap::Evaluate(move, pos_); if (swap_score < kScoreZero) { - it->score = swap_score - HistoryStats::kMax; // 末尾に + //it->score = swap_score - HistoryStats::kMax; // 末尾に + it->score = swap_score - 25000; // 末尾に } else if (!move.is_quiet()) { - it->score = GetMvvLvaScore(move) + HistoryStats::kMax; // 先頭に + //it->score = GetMvvLvaScore(move) + HistoryStats::kMax; // 先頭に + it->score = GetMvvLvaScore(move) + 25000; // 先頭に it->score += move.is_promotion(); } else { - it->score = history_[move]; + // Stockfish7対応 + //it->score = history_[move]; + it->score = history_[move] + + sf_history[pc][move.to()] + + from_to.get(c, move); } } } @@ -318,9 +384,13 @@ void MovePicker::GenerateNext() { cur_ = moves_.begin(); end_ = GenerateMoves(pos_, cur_); end_ = RemoveIllegalMoves(pos_, cur_, end_); + // 指し手の実現確率を計算する + //auto probabilities = MoveProbability::ComputeProbabilities(pos_, history_, + // gains_); auto probabilities = MoveProbability::ComputeProbabilities(pos_, history_, - gains_); + gains_, cur_, end_); + // 指し手の実現確率が高い順にソートする for (ExtMove* it = cur_; it != end_; ++it) { double p = probabilities[it->move.ToUint32()]; diff --git a/src/movepick.h b/src/movepick.h index cf77393..e9a22db 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -45,19 +45,22 @@ class MovePicker { const GainsStats& gains, Depth depth, Move hash_move, const Array& killermoves, const Array& countermoves, - const Array& followupmoves, Search::Stack* ss); + const Array& followupmoves, Search::Stack* ss + , const Search& search, bool use_probability); /** * 静止探索用のコンストラクタです. */ MovePicker(const Position& pos, const HistoryStats& history, - const GainsStats& gains, Depth depth, Move hash_move); + const GainsStats& gains, Depth depth, Move hash_move + , const Search& search); /** * ProbCut用のコンストラクタです. */ MovePicker(const Position& pos, const HistoryStats& history, - const GainsStats& gains, Move hash_move); + const GainsStats& gains, Move hash_move + , const Search& search); /** * 次の手(残りの手の中で、最もβカットの可能性が高い手)を返します. @@ -94,6 +97,9 @@ class MovePicker { const Array followupmoves_; Array killers_; Array moves_; + + // Stockfish7対応 + const Search& search_; }; #endif /* MOVEPICK_H_ */ diff --git a/src/search.cc b/src/search.cc index 9ea8f37..f9bc28f 100644 --- a/src/search.cc +++ b/src/search.cc @@ -42,6 +42,16 @@ #include "usi.h" #include "zobrist.h" +// 探索で実現確率を使用する深さの最小値 +int g_UseProbabilityMinDepth; + +// Stockfish7のStats +// TODO とりあえず配列の要素数は8固定。あとでスレッド数に応じて変更できるようにする予定。 +SfHistoryStats g_arySfHistory[8]; +CounterMoveHistoryStats g_aryCounterMoveHistory[8]; +FromToStats g_aryFromTo[8]; + + namespace { // 開発時に参照する統計データ @@ -53,15 +63,6 @@ Array g_cuts_by_move; Array g_reductions; // [pv][improving][depth][moveNumber] -// デバッグ用 -#if 0 -uint64_t g_cnt_1 = 0; -uint64_t g_cnt_2 = 0; -uint64_t g_cnt_3 = 0; -uint64_t g_cnt_4 = 0; -#endif - - const Array, 20> g_half_density = { {0, 1}, {1, 0}, @@ -85,10 +86,18 @@ const Array, 20> g_half_density = { {1, 0, 0, 0, 0, 1, 1 ,1}, }; +// Stockfish7対応 +const int razor_margin_score[4] = { 483, 570, 603, 554 }; + inline Score razor_margin(Depth depth) { - return static_cast(512 + 32 * (depth / kOnePly)); + // Stockfish7対応 + //return static_cast(512 + 32 * (depth / kOnePly)); + return (Score)razor_margin_score[depth / kOnePly]; } +// Stockfish7対応 +int FutilityMoveCounts[2][16]; // [improving][depth] + inline int futility_move_count(bool is_pv_node, Depth depth) { return (is_pv_node ? 8 : 6) * depth / kOnePly; } @@ -147,11 +156,25 @@ void Search::Init() { g_reductions[0][0][d][mc] += kOnePly / 2; } } + + + // Stockfish7対応 + for (int d = 0; d < 16; ++d) { + FutilityMoveCounts[0][d] = int(2.4 + 0.773 * pow(d + 0.00, 1.8)); + FutilityMoveCounts[1][d] = int(2.9 + 1.045 * pow(d + 0.49, 1.8)); + } + } Search::Search(SharedData& shared, size_t thread_id) : shared_(shared), thread_id_(thread_id) { + + // Stockfish7のStats + // TODO とりあえず配列の要素数は8固定。あとでスレッド数に応じて変更できるようにする予定。 + sf_history_ = &g_arySfHistory[thread_id_ % 8]; + counter_move_history_ = &g_aryCounterMoveHistory[thread_id_ % 8]; + from_to_ = &g_aryFromTo[thread_id_ % 8]; } std::vector Search::GetPv() const { @@ -443,6 +466,10 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, (ss+1)->reduction = kDepthZero; (ss+2)->killers[0] = (ss+2)->killers[1] = kMoveNone; + // Stockfish7対応 + ss->moveCount = 0; + ss->counterMoves = nullptr; + if (!kIsRoot) { // 最大手数に到達したら、探索を打ち切る if (shared_.signals.stop) { @@ -494,7 +521,11 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, && hash_move != kMoveNone && hash_move.is_quiet() && !in_check) { - UpdateStats(ss, hash_move, depth, nullptr, 0); + + // Stockfish7対応 + int d = depth / kOnePly; + int bonus = d * d + 2 * d - 2; + UpdateStats(ss, hash_move, depth, nullptr, 0, bonus, node); } return hash_score; } @@ -604,6 +635,9 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, ss->current_move = kMoveNull; assert(eval - beta >= 0); + // Stockfish7対応 + ss->counterMoves = nullptr; + // 削減する深さの決定 Depth R = 3 * kOnePly + depth / 4 + int(eval - beta) / 200 * kOnePly; @@ -635,7 +669,7 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, assert(rdepth >= kOnePly); assert((ss-1)->current_move.is_real_move()); - MovePicker mp(node, history_, gains_, hash_move); + MovePicker mp(node, history_, gains_, hash_move, *this); double dummy; for (Move move; (move = mp.NextMove(&dummy)) != kMoveNone;) @@ -646,6 +680,9 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, ss->current_move = move; + // Stockfish7対応 + ss->counterMoves = &(*counter_move_history_)[move.piece_after_move()][move.to()]; + node.MakeMove(move, node.MoveGivesCheck(move), key_after_move); Score score = -MainSearch(node, -rbeta, -rbeta + 1, rdepth, ply + 1, !cut_node); @@ -675,8 +712,19 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, const Array countermoves = countermoves_[(ss-1)->current_move]; const Array followupmoves = followupmoves_[(ss-2)->current_move]; + + // Stockfish7対応 + const CounterMoveStats* cmh = (ss-1)->counterMoves; + const CounterMoveStats* fmh = (ss-2)->counterMoves; + const CounterMoveStats* fmh2 = (ss-4)->counterMoves; + + // 実現確率を使用するか否か + bool use_probability = (depth >= g_UseProbabilityMinDepth * kOnePly); + + MovePicker move_picker(node, history_, gains_, depth, hash_move, - ss->killers, countermoves, followupmoves, ss); + ss->killers, countermoves, followupmoves, ss + , *this, use_probability); const bool improving = ss->static_score >= (ss-2)->static_score || ss->static_score == kScoreNone @@ -716,7 +764,10 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, // ルートノードにおいては、非合法手はすでにスキップされているはず。 assert(!kIsRoot || node.PseudoLegalMoveIsLegal(move)); - ++move_count; + // Stockfish7対応 + //++move_count; + ss->moveCount = ++move_count; + Piece moved_piece = move.piece_after_move(); if (kIsRoot && move_count == 1) { shared_.signals.first_move_completed = false; // 最善手の探索をまだ終えていない @@ -726,6 +777,10 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, const bool move_is_quiet = move.is_quiet(); const bool move_gives_check = node.MoveGivesCheck(move); + // Stockfish7対応 + bool moveCountPruning = depth < 16 * kOnePly + && move_count >= FutilityMoveCounts[improving][depth / kOnePly]; + // 王手延長 if (move_gives_check) { ext = !Swap::IsLosing(move, node) ? kOnePly : (kOnePly / 2); @@ -763,36 +818,6 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, /* && move != ttMove Already implicit in the next condition */ && best_score > kScoreMatedInMaxPly) { -// デバッグ用 -#if 0 - if ( depth < 16 * kOnePly - && move_count >= futility_move_count(kIsPv, depth) - && history_.HasNegativeScore(move)) { - - //std::printf("gains_ [%s] =%+6d\n", move.ToSfen().c_str(), gains_ [move]); - //std::printf("gains_gikou_[%s] =%+6d\n", move.ToSfen().c_str(), gains_gikou_[move]); - //std::printf("gains_apery_[%s] =%+6d\n", move.ToSfen().c_str(), gains_apery_[move]); - - g_cnt_1++; - - if (gains_[move] < kScoreZero) { - g_cnt_2++; - } - - if (gains_gikou_[move] < kScoreZero - && gains_apery_[move] < kScoreZero) { - g_cnt_3++; - } - - if (gains_[move] < kScoreZero - && gains_gikou_[move] < kScoreZero - && gains_apery_[move] < kScoreZero) { - g_cnt_4++; - } - - } -#endif - // Move count based pruning // 各々のソフトの評価値のGainsStatsを使用する //if ( depth < 16 * kOnePly @@ -810,10 +835,19 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, continue; } - Depth r = reduction(improving, depth, move_count); Depth predicted_depth = new_depth - r; +#if 0 + // Stockfish7対応 + if ( predicted_depth < 3 * kOnePly + && (!cmh || (*cmh )[moved_piece][move.to()] < kScoreZero) + && (!fmh || (*fmh )[moved_piece][move.to()] < kScoreZero) + && (!fmh2 || (*fmh2)[moved_piece][move.to()] < kScoreZero || (cmh && fmh))) { + continue; + } +#endif + // futility pruning if (predicted_depth < 7 * kOnePly) { Score futility_score = ss->static_score + futility_margin(predicted_depth) @@ -833,7 +867,9 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, // 合法手か否かをチェックする if ( !kIsRoot && !node.PseudoLegalMoveIsLegal(move)) { - move_count--; + // Stockfish7対応 + //move_count--; + ss->moveCount = move_count--; continue; } @@ -843,6 +879,10 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, const bool is_pv_move = kIsPv && move_count == 1; ss->current_move = move; + + // Stockfish7対応 + ss->counterMoves = &(*counter_move_history_)[moved_piece][move.to()]; + if (move_is_quiet && quiet_count < 64) { quiets_searched[quiet_count++] = move; } @@ -864,13 +904,15 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, // 本当はすべて実現確率にしたいところだが、実現確率の計算コストが高いため、残り深さが大きい // ところに限って実現確率を用いている if ( depth >= 3 * kOnePly - && (move_is_quiet || depth >= 8 * kOnePly) + //&& (move_is_quiet || depth >= 8 * kOnePly) + && (move_is_quiet || use_probability) && move_count >= 2 && move != ss->killers[0] && move != ss->killers[1]) { // 実現確率 - if (depth >= 8 * kOnePly) { + //if (depth >= 8 * kOnePly) { + if (use_probability) { // 指し手の確率に基づいて、何手減らすかを決定する const double kPvFactor = kIsPv ? 0.75 : 1.0; double consumption = kPvFactor * -std::log(probability) / std::log(2.0); @@ -996,11 +1038,42 @@ Score Search::MainSearch(Node& node, Score alpha, Score beta, const Depth depth, best_score = score_mated_in(ply); // 詰まされた } } - } else if (best_score >= beta && best_move.is_quiet() && !in_check) { - // 最善手が静かな手の場合は、キラー手等を更新する - UpdateStats(ss, best_move, depth, quiets_searched.begin(), quiet_count - 1); } + // Stockfish7対応 + //} else if (best_score >= beta && best_move.is_quiet() && !in_check) { + else if (best_move != kMoveNone) { + int d = depth / kOnePly; + + if (best_move.is_quiet()) { + // 最善手が静かな手の場合は、キラー手等を更新する + int bonus = d * d + 2 * d - 2; + UpdateStats(ss, best_move, depth, quiets_searched.begin(), quiet_count - 1, bonus, node); + } + + // Extra penalty for a quiet TT move in previous ply when it gets refuted + //if ((ss-1)->moveCount == 1 && !pos.captured_piece()) + if ((ss-1)->moveCount == 1 && (ss-1)->current_move.is_quiet()) { + int penalty = d * d + 4 * d + 1; + Square prevSq = (ss-1)->current_move.to(); + Piece prevPc = (ss-1)->current_move.piece_after_move(); + UpdateCmStats(ss-1, prevPc, prevSq, -penalty); + } + } + + // Bonus for prior countermove that caused the fail low + else if ( depth >= 3 * kOnePly + //&& !pos.captured_piece() + && (ss-1)->current_move.is_quiet() + && (ss-1)->current_move.is_real_move()) { + int d = depth / kOnePly; + int bonus = d * d + 2 * d - 2; + Square prevSq = (ss-1)->current_move.to(); + Piece prevPc = (ss-1)->current_move.piece_after_move(); + UpdateCmStats(ss-1, prevPc, prevSq, bonus); + } + + shared_.hash_table.Save(pos_key, best_move, ScoreToTt(best_score, ply), depth, best_score >= beta ? kBoundLower : kIsPv && best_move != kMoveNone ? kBoundExact : kBoundUpper, @@ -1115,7 +1188,7 @@ Score Search::QuiecenceSearch(Node& node, Score alpha, Score beta, assert(0.0 <= progress && progress <= 1.0); // MovePickerオブジェクトを更新する - MovePicker mp(node, history_, gains_, depth, hash_move); + MovePicker mp(node, history_, gains_, depth, hash_move, *this); Move best_move = kMoveNone; // βカットするか、残りの手がなくなるまで、探索する @@ -1241,8 +1314,29 @@ Score Search::QuiecenceSearch(Node& node, Score alpha, Score beta, return best_score; } + +// Stockfish7対応 +void Search::UpdateCmStats(Search::Stack* ss, Piece pc, Square sq, int bonus) { + CounterMoveStats* cmh = (ss-1)->counterMoves; + CounterMoveStats* fmh1 = (ss-2)->counterMoves; + CounterMoveStats* fmh2 = (ss-4)->counterMoves; + + if (cmh) { + cmh->update(pc, sq, bonus); + } + + if (fmh1) { + fmh1->update(pc, sq, bonus); + } + + if (fmh2) { + fmh2->update(pc, sq, bonus); + } +} + void Search::UpdateStats(Stack* const ss, Move move, Depth depth, - Move* const quiets, int quiets_count) { + Move* const quiets, int quiets_count + , int bonus, Node& node) { assert(ss != nullptr); assert(quiets != nullptr || quiets_count == 0); @@ -1267,6 +1361,28 @@ void Search::UpdateStats(Stack* const ss, Move move, Depth depth, && (ss-1)->current_move == (ss-1)->hash_move) { followupmoves_.Update((ss-2)->current_move, move); } + + + // Stockfish7対応 + Piece pc = move.piece_after_move(); + Square sq = move.to(); + Color c = node.side_to_move(); + + from_to_->update(c, move, bonus); + sf_history_->update(pc, sq, bonus); + UpdateCmStats(ss, pc, sq, bonus); + + + // Decrease all the other played quiet moves + for (int i = 0; i < quiets_count; ++i) { + Move q_move = quiets[i]; + Piece q_pc = q_move.piece_after_move(); + Square q_sq = q_move.to(); + + from_to_->update(c, q_move, -bonus); + sf_history_->update(q_pc, q_sq, -bonus); + UpdateCmStats(ss, q_pc, q_sq, -bonus); + } } void Search::SendUsiInfo(const Node& node, int depth, int64_t time, @@ -1331,14 +1447,4 @@ void Search::SendUsiInfo(const Node& node, int depth, int64_t time, // infoコマンドをまとめて標準出力へ出力する SYNCED_PRINTF("%s", buf.c_str()); - - -// デバッグ用 -#if 0 - std::printf("g_cnt_1=%d\n", g_cnt_1); - std::printf("g_cnt_2=%d\n", g_cnt_2); - std::printf("g_cnt_3=%d\n", g_cnt_3); - std::printf("g_cnt_4=%d\n", g_cnt_4); -#endif - } diff --git a/src/search.h b/src/search.h index ddf1698..1181c6f 100644 --- a/src/search.h +++ b/src/search.h @@ -61,6 +61,10 @@ class Search { Score static_score; bool skip_null_move; + // Stockfish7対応 + CounterMoveStats* counterMoves; + int moveCount; + // 各々のソフトの最終的な評価値 Score static_score_gikou; Score static_score_apery; @@ -96,6 +100,10 @@ class Search { return thread_id_ == 0; } + size_t thread_id() const { + return thread_id_; + } + uint64_t num_nodes_searched() const { return num_nodes_searched_; } @@ -142,8 +150,17 @@ class Search { void PrepareForNextSearch(); + + // Stockfish7のStats + SfHistoryStats* sf_history_; + CounterMoveHistoryStats* counter_move_history_; + FromToStats* from_to_; + + private: - static constexpr int kStackSize = kMaxPly + 6; + // Stockfish7対応 + //static constexpr int kStackSize = kMaxPly + 6; + static constexpr int kStackSize = kMaxPly + 9; template Score QuiecenceSearch(Node& node, Score alpha, Score beta, Depth depth, @@ -157,19 +174,28 @@ class Search { Score QuiecenceSearch(Node& node, Score alpha, Score beta, Depth depth, int ply); + // Stockfish7対応 + void UpdateCmStats(Search::Stack* ss, Piece pc, Square sq, int bonus); + + // Stockfish7対応 void UpdateStats(Search::Stack* ss, Move move, Depth depth, Move* quiets, - int quiets_count); + int quiets_count, int bonus, Node& node); void SendUsiInfo(const Node& node, int depth, int64_t time, uint64_t nodes, Bound bound = kBoundExact) const; void ResetSearchStack() { - std::memset(stack_.begin(), 0, 5 * sizeof(Stack)); + // Stockfish7対応 + //std::memset(stack_.begin(), 0, 5 * sizeof(Stack)); + std::memset(stack_.begin(), 0, 8 * sizeof(Stack)); } Stack* search_stack_at_ply(int ply) { assert(0 <= ply && ply <= kMaxPly); - return stack_.begin() + 2 + ply; // stack_at_ply(0) - 2 の参照を可能にするため + + // Stockfish7対応 + //return stack_.begin() + 2 + ply; // stack_at_ply(0) - 2 の参照を可能にするため + return stack_.begin() + 5 + ply; // stack_at_ply(0) - 5 の参照を可能にするため } SharedData& shared_; diff --git a/src/stats.h b/src/stats.h index d0099be..b73416d 100644 --- a/src/stats.h +++ b/src/stats.h @@ -167,4 +167,62 @@ class MovesStats { ArrayMap, Square, Piece> table_; }; + +// Stockfish7のStats +template +struct Stats { + + static const int Max = int(1 << 28); + + const T* operator[](Piece pc) const { return table[pc]; } + T* operator[](Piece pc) { return table[pc]; } + void clear() { std::memset(table, 0, sizeof(table)); } + void update(Piece pc, Square to, Move m) { table[pc][to] = m; } + void update(Piece pc, Square to, int v) { + + if (abs(int(v)) >= 324) + return; + + table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 936 : 324); + table[pc][to] += int(v) * 32; + } + +private: + T table[32][81]; +}; + +typedef Stats MoveStats; + +// オリジナルの技巧のHistoryStatsと区別するため、名前を「SfHistoryStats」とする。 +typedef Stats SfHistoryStats; + +typedef Stats CounterMoveStats; +typedef Stats CounterMoveHistoryStats; + + +// Stockfish7のFromToStats +struct FromToStats { + + int get(Color c, Move m) const { return table[c][m.is_drop() ? 81 : m.from()][m.to()]; } + void clear() { std::memset(table, 0, sizeof(table)); } + void update(Color c, Move m, int v) { + + if (abs(int(v)) >= 324) + return; + + int from = m.is_drop() ? 81 : m.from(); + Square to = m.to(); + + table[c][from][to] -= table[c][from][to] * abs(int(v)) / 324; + table[c][from][to] += int(v) * 32; + } + +private: + // [color][from][to] + // fromについて + //  ・駒打ち以外の場合、fromは0~80 + //  ・駒打ちの場合、fromは81 + int table[2][81+1][81]; +}; + #endif /* STATS_H_ */ diff --git a/src/thinking.cc b/src/thinking.cc index b0a4107..520e209 100644 --- a/src/thinking.cc +++ b/src/thinking.cc @@ -36,6 +36,15 @@ int g_AperyEvalEndGame; // Aperyの評価関数バイナリのフォルダ std::string g_AperyEvalFolder; +// 探索で実現確率を使用する深さの最小値 +extern int g_UseProbabilityMinDepth; + +// Stockfish7のStats +extern SfHistoryStats g_arySfHistory[8]; +extern CounterMoveHistoryStats g_aryCounterMoveHistory[8]; +extern FromToStats g_aryFromTo[8]; + + namespace { const char* kBookFile = "book.bin"; @@ -59,6 +68,18 @@ void Thinking::Initialize() { // Aperyの評価関数バイナリのフォルダ g_AperyEvalFolder = usi_options_["Z04_AperyEvalFolder"].str_value(); + + // 探索で実現確率を使用する深さの最小値 + g_UseProbabilityMinDepth = usi_options_["Z10_UseProbabilityMinDepth"]; + + // Stockfish7のStatsのクリア + // TODO とりあえず配列の要素数は8固定。あとでスレッド数に応じて変更できるようにする予定。 + for (int i = 0; i < 8; i++) { + g_arySfHistory[i].clear(); + g_aryCounterMoveHistory[i].clear(); + g_aryFromTo[i].clear(); + } + } void Thinking::StartNewGame() { diff --git a/src/usi.cc b/src/usi.cc index b977cfd..a9bfbfb 100644 --- a/src/usi.cc +++ b/src/usi.cc @@ -37,7 +37,7 @@ namespace { -const auto kProgramName = "Gikou AperyEvalMixNoTurn_BothGains 20160918"; +const auto kProgramName = "Gikou AperyEvalMixNoTurn_BothGains Stockfish7 20161008"; const auto kAuthorName = "Yosuke Demura"; const auto kBookFile = "book.bin"; @@ -362,6 +362,9 @@ UsiOptions::UsiOptions() { // Aperyの評価関数バイナリのフォルダ map_.emplace("Z04_AperyEvalFolder", UsiOption("./Apery_20160307", 0)); + + // 探索で実現確率を使用する深さの最小値 + map_.emplace("Z10_UseProbabilityMinDepth" , UsiOption(8, 0, 100)); } void UsiOptions::PrintListOfOptions() {