From 90ef97dcbd204d621eaa79479aecd66a6392962d Mon Sep 17 00:00:00 2001 From: Hisayori Noda Date: Thu, 20 Jun 2019 00:25:40 +0900 Subject: [PATCH] Fixed crash bugs. --- src/evaluate.cpp | 11 ++++++++++ src/evaluate.h | 2 +- src/extra/sfen_packer.cpp | 37 ++++++++++++++++---------------- src/learn/learner.cpp | 10 +++++++++ src/position.cpp | 32 +++++++++++++++------------- src/search.cpp | 45 ++++++++++++++++++++++++++++++++------- src/search.h | 4 ++++ src/ucioption.cpp | 9 ++++++++ 8 files changed, 108 insertions(+), 42 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1d444819..350f905b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -22,6 +22,7 @@ #include #include // For std::memset #include +#include #include #include "bitboard.h" @@ -938,6 +939,16 @@ ExtBonaPiece kpp_board_index[PIECE_NB] = { // 注 : デバッグ用。遅い。 bool EvalList::is_valid(const Position& pos) { + std::set piece_numbers; + for (Square sq = SQ_A1; sq != SQUARE_NB; ++sq) { + auto piece_number = piece_no_of_board(sq); + if (piece_number == PIECE_NUMBER_NB) { + continue; + } + assert(!piece_numbers.count(piece_number)); + piece_numbers.insert(piece_number); + } + for (int i = 0; i < length(); ++i) { BonaPiece fw = pieceListFw[i]; diff --git a/src/evaluate.h b/src/evaluate.h index 6115eeb4..47dfbd34 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -187,7 +187,7 @@ public: // VPGATHERDDを使う都合、4の倍数でなければならない。 // また、KPPT型評価関数などは、39,40番目の要素がゼロであることを前提とした // アクセスをしている箇所があるので注意すること。 - static const int MAX_LENGTH = 40; + static const int MAX_LENGTH = 32; // 盤上の駒に対して、その駒番号(PieceNumber)を保持している配列 // 玉がSQUARE_NBに移動しているとき用に+1まで保持しておくが、 diff --git a/src/extra/sfen_packer.cpp b/src/extra/sfen_packer.cpp index df095ce1..f7c1d238 100644 --- a/src/extra/sfen_packer.cpp +++ b/src/extra/sfen_packer.cpp @@ -126,11 +126,12 @@ struct HuffmanedPiece HuffmanedPiece huffman_table[] = { - {0b000,1}, // NO_PIECE - {0b001,3}, // PAWN - {0b011,3}, // KNIGHT - {0b101,3}, // BISHOP - {0b111,3}, // ROOK + {0b0000,1}, // NO_PIECE + {0b0001,4}, // PAWN + {0b0011,4}, // KNIGHT + {0b0101,4}, // BISHOP + {0b0111,4}, // ROOK + {0b1001,4}, // QUEEN }; // sfen繧貞悸邵ョ/隗」蜃阪☆繧九◆繧√ョ繧ッ繝ゥ繧ケ @@ -269,7 +270,8 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre std::memset(this, 0, sizeof(Position)); std::memset(si, 0, sizeof(StateInfo)); - st = si; + std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); + st = si; // Active color sideToMove = (Color)stream.read_one_bit(); @@ -279,13 +281,7 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre // PieceList繧呈峩譁ー縺吶k荳翫〒縲√←縺ョ鬧偵′縺ゥ縺薙↓縺ゅk縺九r險ュ螳壹@縺ェ縺代l縺ー縺ェ繧峨↑縺縺後 // 縺昴l縺槭l縺ョ鬧偵r縺ゥ縺薙∪縺ァ菴ソ縺」縺溘°縺ョ繧ォ繧ヲ繝ウ繧ソ繝シ - PieceNumber piece_no_count[KING] = { - PIECE_NUMBER_ZERO, - PIECE_NUMBER_PAWN, - PIECE_NUMBER_KNIGHT, - PIECE_NUMBER_BISHOP, - PIECE_NUMBER_ROOK, - }; + PieceNumber next_piece_number = PIECE_NUMBER_ZERO; pieceList[W_KING][0] = SQUARE_NB; pieceList[B_KING][0] = SQUARE_NB; @@ -294,12 +290,12 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre if (mirror) { for (auto c : Colors) - board[Mir((Square)stream.read_n_bit(7))] = make_piece(c, KING); + board[Mir((Square)stream.read_n_bit(6))] = make_piece(c, KING); } else { for (auto c : Colors) - board[stream.read_n_bit(7)] = make_piece(c, KING); + board[stream.read_n_bit(6)] = make_piece(c, KING); } // Piece placement @@ -335,7 +331,7 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre PieceNumber piece_no = (pc == B_KING) ? PIECE_NUMBER_BKING : // 蜈域焔邇 (pc == W_KING) ? PIECE_NUMBER_WKING : // 蠕梧焔邇 - piece_no_count[type_of(pc)]++; // 縺昴l莉・螟 + next_piece_number++; // 縺昴l莉・螟 evalList.put_piece(piece_no, sq, pc); // sq縺ョ蜊縺ォpc縺ョ鬧偵r驟咲スョ縺吶k @@ -363,12 +359,12 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre } if (stream.read_one_bit()) { Square rsq; - for (rsq = relative_square(BLACK, SQ_H1); piece_on(rsq) != W_ROOK; --rsq) {} + for (rsq = relative_square(BLACK, SQ_H1); piece_on(rsq) != B_ROOK; --rsq) {} set_castling_right(BLACK, rsq); } if (stream.read_one_bit()) { Square rsq; - for (rsq = relative_square(BLACK, SQ_A1); piece_on(rsq) != W_ROOK; ++rsq) {} + for (rsq = relative_square(BLACK, SQ_A1); piece_on(rsq) != B_ROOK; ++rsq) {} set_castling_right(BLACK, rsq); } @@ -381,6 +377,9 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))) st->epSquare = SQ_NONE; } + else { + st->epSquare = SQ_NONE; + } // Halfmove clock st->rule50 = static_cast(stream.read_n_bit(6)); @@ -397,6 +396,8 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre thisThread = th; set_state(st); + //std::cout << *this << std::endl; + assert(pos_is_ok()); #if defined(EVAL_NNUE) assert(evalList.is_valid(*this)); diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index 0e904650..8babbee2 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -392,6 +392,16 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id) auto& pos = th->rootPos; pos.set(StartFEN, false, &si, th); + // Test cod for Packed SFEN. + //{ + // PackedSfen packed_sfen; + // pos.sfen_pack(packed_sfen); + // std::cout << pos << std::endl; + // pos.set_from_packed_sfen(packed_sfen, &si, th); + // std::string actual = pos.fen(); + // assert(actual == StartFEN); + //} + // 謗「邏「驛ィ縺ァ螳夂セゥ縺輔l縺ヲ縺繧毅ookMoveSelector縺ョ繝。繝ウ繝舌r蜿らァ縺吶k縲 //auto& book = ::book; diff --git a/src/position.cpp b/src/position.cpp index a3f05a87..b7d5096f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -249,14 +249,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th // PieceListを更新する上で、どの駒がどこにあるかを設定しなければならないが、 // それぞれの駒をどこまで使ったかのカウンター - PieceNumber piece_no_count[KING] = { - PIECE_NUMBER_ZERO, - PIECE_NUMBER_PAWN, - PIECE_NUMBER_KNIGHT, - PIECE_NUMBER_BISHOP, - PIECE_NUMBER_ROOK, - PIECE_NUMBER_QUEEN - }; + PieceNumber next_piece_number = PIECE_NUMBER_ZERO; #endif // defined(EVAL_NNUE) ss >> std::noskipws; @@ -279,7 +272,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th PieceNumber piece_no = (idx == W_KING) ? PIECE_NUMBER_WKING : // 先手玉 (idx == B_KING) ? PIECE_NUMBER_BKING : // 後手玉 - piece_no_count[type_of(Piece(idx))]++; // それ以外 + next_piece_number++; // それ以外 evalList.put_piece(piece_no, sq, pc); // sqの升にpcの駒を配置する #endif // defined(EVAL_NNUE) @@ -780,6 +773,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { Piece pc = piece_on(from); Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); + PieceNumber piece_no0 = PIECE_NUMBER_NB; + PieceNumber piece_no1 = PIECE_NUMBER_NB; + assert(color_of(pc) == us); assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); assert(type_of(captured) != KING); @@ -805,10 +801,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { { Square capsq = to; -#if defined(EVAL_NNUE) - PieceNumber piece_no1; -#endif // defined(EVAL_NNUE) - // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. if (type_of(captured) == PAWN) @@ -828,6 +820,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { #endif // defined(EVAL_NNUE) board[capsq] = NO_PIECE; // Not done by remove_piece() + evalList.piece_no_list_board[capsq] = PIECE_NUMBER_NB; } else { #if defined(EVAL_NNUE) @@ -893,7 +886,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) { #if defined(EVAL_NNUE) - PieceNumber piece_no0 = piece_no_of(from); + piece_no0 = piece_no_of(from); #endif // defined(EVAL_NNUE) move_piece(pc, from, to); @@ -901,6 +894,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { #if defined(EVAL_NNUE) dp.pieceNo[0] = piece_no0; dp.changed_piece[0].old_piece = evalList.bona_piece(piece_no0); + evalList.piece_no_list_board[from] = PIECE_NUMBER_NB; evalList.put_piece(piece_no0, to, pc); dp.changed_piece[0].new_piece = evalList.bona_piece(piece_no0); #endif // defined(EVAL_NNUE) @@ -928,9 +922,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { put_piece(promotion, to); #if defined(EVAL_NNUE) - PieceNumber piece_no0 = piece_no_of(to); + piece_no0 = piece_no_of(to); dp.pieceNo[0] = piece_no0; dp.changed_piece[0].old_piece = evalList.bona_piece(piece_no0); + assert(evalList.piece_no_list_board[from] == PIECE_NUMBER_NB); evalList.put_piece(piece_no0, to, promotion); dp.changed_piece[0].new_piece = evalList.bona_piece(piece_no0); #endif // defined(EVAL_NNUE) @@ -985,6 +980,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { } } + //std::cout << *this << std::endl; + assert(pos_is_ok()); #if defined(EVAL_NNUE) assert(evalList.is_valid(*this)); @@ -1038,6 +1035,7 @@ void Position::undo_move(Move m) { #if defined(EVAL_NNUE) PieceNumber piece_no0 = st->dirtyPiece.pieceNo[0]; evalList.put_piece(piece_no0, from, pc); + evalList.piece_no_list_board[to] = PIECE_NUMBER_NB; #endif // defined(EVAL_NNUE) if (st->capturedPiece) @@ -1118,16 +1116,20 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ if (Do) { dp.pieceNo[0] = piece_no0; dp.changed_piece[0].old_piece = evalList.bona_piece(piece_no0); + evalList.piece_no_list_board[from] = PIECE_NUMBER_NB; evalList.put_piece(piece_no0, to, make_piece(us, KING)); dp.changed_piece[0].new_piece = evalList.bona_piece(piece_no0); dp.pieceNo[1] = piece_no1; dp.changed_piece[1].old_piece = evalList.bona_piece(piece_no1); + evalList.piece_no_list_board[rfrom] = PIECE_NUMBER_NB; evalList.put_piece(piece_no1, rto, make_piece(us, ROOK)); dp.changed_piece[1].new_piece = evalList.bona_piece(piece_no1); } else { + evalList.piece_no_list_board[to] = PIECE_NUMBER_NB; evalList.put_piece(piece_no0, from, make_piece(us, KING)); + evalList.piece_no_list_board[rto] = PIECE_NUMBER_NB; evalList.put_piece(piece_no1, rfrom, make_piece(us, ROOK)); } #endif // defined(EVAL_NNUE) diff --git a/src/search.cpp b/src/search.cpp index 279c6d8a..df88564a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -871,7 +871,7 @@ moves_loop: // When in check, search starts from here ss->moveCount = ++moveCount; - if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) + if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000 && !Limits.silent) sync_cout << "info depth " << depth / ONE_PLY << " currmove " << UCI::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl; @@ -1382,7 +1382,9 @@ moves_loop: // When in check, search starts from here ss->continuationHistory = &thisThread->continuationHistory[pos.moved_piece(move)][to_sq(move)]; // Make and search the move + //std::cout << pos << std::endl; pos.do_move(move, st, givesCheck); + //std::cout << pos << std::endl; value = -qsearch(pos, ss+1, -beta, -alpha, depth - ONE_PLY); pos.undo_move(move); @@ -1740,7 +1742,7 @@ namespace Learner // RootNodeはss->ply == 0がその条件。 // ゼロクリアするので、ss->ply == 0となるので大丈夫…。 - memset(ss - 4, 0, 7 * sizeof(Stack)); + std::memset(ss - 7, 0, 10 * sizeof(Stack)); // Search::Limitsに関して // このメンバー変数はglobalなので他のスレッドに影響を及ぼすので気をつけること。 @@ -1751,7 +1753,7 @@ namespace Learner limits.infinite = true; // PVを表示されると邪魔なので消しておく。 - //limits.silent = true; + limits.silent = true; // これを用いると各スレッドのnodesを積算したものと比較されてしまう。ゆえに使用しない。 limits.nodes = 0; @@ -1789,8 +1791,23 @@ namespace Learner // history類を全部クリアする。この初期化は少し時間がかかるし、探索の精度はむしろ下がるので善悪はよくわからない。 // th->clear(); - for (int i = 4; i > 0; i--) - (ss - i)->continuationHistory = &th->continuationHistory[SQUARE_ZERO][NO_PIECE]; + int ct = int(Options["Contempt"]) * PawnValueEg / 100; // From centipawns + Color us = pos.side_to_move(); + + // In analysis mode, adjust contempt in accordance with user preference + if (Limits.infinite || Options["UCI_AnalyseMode"]) + ct = Options["Analysis Contempt"] == "Off" ? 0 + : Options["Analysis Contempt"] == "Both" ? ct + : Options["Analysis Contempt"] == "White" && us == BLACK ? -ct + : Options["Analysis Contempt"] == "Black" && us == WHITE ? -ct + : ct; + + // Evaluation score is from the white point of view + th->contempt = (us == WHITE ? make_score(ct, ct / 2) + : -make_score(ct, ct / 2)); + + for (int i = 7; i > 0; i--) + (ss - i)->continuationHistory = &th->continuationHistory[NO_PIECE][0]; // Use as sentinel // rootMovesの設定 auto& rootMoves = th->rootMoves; @@ -1831,7 +1848,7 @@ namespace Learner // 悪い影響があるので、窓の範囲を指定できるようにするのをやめることにした。 ValueAndPV qsearch(Position& pos) { - Stack stack[MAX_PLY + 7], * ss = stack + 4; + Stack stack[MAX_PLY + 10], * ss = stack + 7; Move pv[MAX_PLY + 1]; std::vector pvs; @@ -1881,7 +1898,7 @@ namespace Learner if (depth == DEPTH_ZERO) return qsearch(pos); - Stack stack[MAX_PLY + 7], * ss = stack + 4; + Stack stack[MAX_PLY + 10], * ss = stack + 7; Move pv[MAX_PLY + 1]; init_for_search(pos, ss); @@ -1892,6 +1909,7 @@ namespace Learner auto th = pos.this_thread(); auto& rootDepth = th->rootDepth; auto& pvIdx = th->pvIdx; + auto& pvLast = th->pvLast; auto& rootMoves = th->rootMoves; auto& completedDepth = th->completedDepth; auto& selDepth = th->selDepth; @@ -1919,9 +1937,20 @@ namespace Learner for (RootMove& rm : rootMoves) rm.previousScore = rm.score; - // MultiPV + size_t pvFirst = 0; + pvLast = 0; + + // MultiPV loop. We perform a full root search for each PV line for (pvIdx = 0; pvIdx < multiPV && !Threads.stop; ++pvIdx) { + if (pvIdx == pvLast) + { + pvFirst = pvLast; + for (pvLast++; pvLast < rootMoves.size(); pvLast++) + if (rootMoves[pvLast].tbRank != rootMoves[pvFirst].tbRank) + break; + } + // それぞれのdepthとPV lineに対するUSI infoで出力するselDepth selDepth = 0; diff --git a/src/search.h b/src/search.h index 92e124fc..7c6dcff7 100644 --- a/src/search.h +++ b/src/search.h @@ -86,6 +86,7 @@ struct LimitsType { time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); movestogo = depth = mate = perft = infinite = 0; nodes = 0; + silent = false; } bool use_time_management() const { @@ -96,6 +97,9 @@ struct LimitsType { TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; int movestogo, depth, mate, perft, infinite; int64_t nodes; + // 画面に出力しないサイレントモード(プロセス内での連続自己対戦のとき用) + // このときPVを出力しない。 + bool silent; }; extern LimitsType Limits; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index e549c6e0..87dbfa82 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -87,6 +87,15 @@ void init(OptionsMap& o) { // そこでこの隠しオプションでisready時の評価関数の読み込みを抑制して、 // test evalconvertコマンドを叩く。 o["SkipLoadingEval"] << Option(false); + // 定跡の指し手を何手目まで用いるか + o["BookMoves"] << Option(16, 0, 10000); + +#if defined(EVAL_LEARN) + // 評価関数の学習を行なうときは、評価関数の保存先のフォルダを変更できる。 + // デフォルトではevalsave。このフォルダは事前に用意されているものとする。 + // このフォルダ配下にフォルダを"0/","1/",…のように自動的に掘り、そこに評価関数ファイルを保存する。 + o["EvalSaveDir"] << Option("evalsave"); +#endif }