Move MovePicker::next_move(bool skipQuiets) { Move move; switch (stage) { case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS: case QSEARCH_NO_CHECKS: case PROBCUT: ++stage; return ttMove; case CAPTURES_INIT: endBadCaptures = cur = moves; endMoves = generate<CAPTURES>(pos, cur); score<CAPTURES>(); ++stage; /* fallthrough */ case GOOD_CAPTURES: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) { if (pos.see_ge(move)) return move; // Losing capture, move it to the beginning of the array *endBadCaptures++ = move; } } ++stage; move = killers[0]; // First killer move if ( move != MOVE_NONE && move != ttMove && pos.pseudo_legal(move) && !pos.capture(move)) return move; /* fallthrough */ case KILLERS: ++stage; move = killers[1]; // Second killer move if ( move != MOVE_NONE && move != ttMove && pos.pseudo_legal(move) && !pos.capture(move)) return move; /* fallthrough */ case COUNTERMOVE: ++stage; move = countermove; if ( move != MOVE_NONE && move != ttMove && move != killers[0] && move != killers[1] && pos.pseudo_legal(move) && !pos.capture(move)) return move; /* fallthrough */ case QUIET_INIT: cur = endBadCaptures; endMoves = generate<QUIETS>(pos, cur); score<QUIETS>(); partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY); ++stage; /* fallthrough */ case QUIET: while ( cur < endMoves && (!skipQuiets || cur->value >= VALUE_ZERO)) { move = *cur++; if ( move != ttMove && move != killers[0] && move != killers[1] && move != countermove) return move; } ++stage; cur = moves; // Point to beginning of bad captures /* fallthrough */ case BAD_CAPTURES: if (cur < endBadCaptures) return *cur++; break; case EVASIONS_INIT: cur = moves; endMoves = generate<EVASIONS>(pos, cur); score<EVASIONS>(); ++stage; /* fallthrough */ case ALL_EVASIONS: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) return move; } break; case PROBCUT_INIT: cur = moves; endMoves = generate<CAPTURES>(pos, cur); score<CAPTURES>(); ++stage; /* fallthrough */ case PROBCUT_CAPTURES: while (cur < endMoves) { move = pick_best(cur++, endMoves); if ( move != ttMove && pos.see_ge(move, threshold)) return move; } break; case QCAPTURES_1_INIT: case QCAPTURES_2_INIT: cur = moves; endMoves = generate<CAPTURES>(pos, cur); score<CAPTURES>(); ++stage; /* fallthrough */ case QCAPTURES_1: case QCAPTURES_2: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) return move; } if (stage == QCAPTURES_2) break; cur = moves; endMoves = generate<QUIET_CHECKS>(pos, cur); ++stage; /* fallthrough */ case QCHECKS: while (cur < endMoves) { move = cur++->move; if (move != ttMove) return move; } break; case QSEARCH_RECAPTURES: cur = moves; endMoves = generate<CAPTURES>(pos, cur); score<CAPTURES>(); ++stage; /* fallthrough */ case QRECAPTURES: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (to_sq(move) == recaptureSquare) return move; } break; default: assert(false); } return MOVE_NONE; }
Move MovePicker::next_move2() { #endif Move move; // 以下、caseのfall throughを駆使して書いてある。 switch (stage) { // 置換表の指し手を返すフェーズ case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS: case QSEARCH_NO_CHECKS: case PROBCUT: ++stage; return ttMove; case CAPTURES_INIT: endBadCaptures = cur = moves; endMoves = generateMoves<CAPTURES_PRO_PLUS>(pos, cur); score<CAPTURES>(); // CAPTUREの指し手の並べ替え。 ++stage; /* fallthrough */ // 置換表の指し手を返したあとのフェーズ // (killer moveの前のフェーズなのでkiller除去は不要) // SSEの符号がマイナスのものはbad captureのほうに回す。 case GOOD_CAPTURES: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) { // ここでSSEの符号がマイナスならbad captureのほうに回す。 // ToDo: moveは駒打ちではないからsee()の内部での駒打ちは判定不要なのだが。 if (pos.see_ge(move, VALUE_ZERO)) return move; // 損をするCAPTUREの指し手は、後回しにする。 *endBadCaptures++ = move; } } ++stage; // 1つ目のkiller move // ※ killer[]は32bit化されている(上位に移動後の駒が格納されている)と仮定している。 move = killers[0]; if ( move != MOVE_NONE && move != ttMove && pos.pseudo_legal_s<false>(move) && !pos.capture_or_pawn_promotion(move)) return move; /* fallthrough */ // killer moveを返すフェーズ // (直前に置換表の指し手を返しているし、CAPTURES_PRO_PLUSでの指し手も返しているのでそれらの指し手は除外されるべき) case KILLERS: ++stage; move = killers[1]; // 2つ目のkiller move if ( move != MOVE_NONE // ss->killer[0],[1]からコピーしただけなのでMOVE_NONEの可能性がある && move != ttMove // 置換表の指し手を重複除去しないといけない && pos.pseudo_legal_s<false>(move) // pseudo_legalでない指し手以外に歩や大駒の不成なども除外 && !pos.capture_or_pawn_promotion(move)) // 直前にCAPTURES_PRO_PLUSで生成している指し手を除外 return move; /* fallthrough */ // counter moveを返すフェーズ case COUNTERMOVE: ++stage; move = countermove; if ( move != MOVE_NONE && move != ttMove && move != killers[0] && move != killers[1] && pos.pseudo_legal_s<false>(move) && !pos.capture_or_pawn_promotion(move)) return move; /* fallthrough */ case QUIET_INIT: cur = endBadCaptures; endMoves = generateMoves<NON_CAPTURES_PRO_MINUS>(pos, cur); score<QUIETS>(); // 指し手を部分的にソートする。depthに線形に依存する閾値で。 partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY); ++stage; /* fallthrough */ // 捕獲しない指し手を返す。 // (置換表の指し手とkillerの指し手は返したあとなのでこれらの指し手は除外する必要がある) // ※ これ、指し手の数が多い場合、AVXを使って一気に削除しておいたほうが良いのでは.. case QUIET: while (cur < endMoves && (!skipQuiets || cur->value >= VALUE_ZERO)) { move = *cur++; if (move != ttMove && move != killers[0] && move != killers[1] && move != countermove) return move; } ++stage; // bad capturesの先頭を指すようにする。これは指し手生成バッファの先頭付近を再利用している。 cur = moves; /* fallthrough */ // see()が負の指し手を返す。 case BAD_CAPTURES: if (cur < endBadCaptures) return *cur++; break; // ここでcaseのfall throughは終わり。 // 回避手の生成 case EVASIONS_INIT: cur = moves; endMoves = generateMoves<EVASIONS>(pos, cur); score<EVASIONS>(); ++stage; /* fallthrough */ // 王手回避の指し手を返す case ALL_EVASIONS: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) return move; } break; case PROBCUT_INIT: cur = moves; endMoves = generateMoves<CAPTURES_PRO_PLUS>(pos, cur); score<CAPTURES>(); ++stage; /* fallthrough */ // 通常探索のProbCutの処理から呼び出されるとき用。 // 直前に捕獲された駒の価値以上のcaptureの指し手のみを生成する。 case PROBCUT_CAPTURES: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove && pos.see_ge(move, threshold)) return move; } break; // 捕獲する指し手のみを生成 case QCAPTURES_1_INIT: case QCAPTURES_2_INIT: cur = moves; endMoves = generateMoves<CAPTURES_PRO_PLUS>(pos, cur); score<CAPTURES>(); ++stage; /* fallthrough */ // 残りの指し手を生成するフェーズ(共通処理) case QCAPTURES_1: case QCAPTURES_2: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) return move; } if (stage == QCAPTURES_2) break; cur = moves; // CAPTURES_PRO_PLUSで生成していたので、歩の成る指し手は除外された成る指し手+王手の指し手生成が必要。 // QUIET_CHECKS_PRO_MINUSがあれば良いのだが、実装が難しいので、このあと除外する。 endMoves = generateMoves<QUIET_CHECKS>(pos, cur); ++stage; /* fallthrough */ // 王手になる指し手を一手ずつ返すフェーズ // (置換表の指し手は返したあとなのでこの指し手は除外する必要がある) case QCHECKS: while (cur < endMoves) { move = cur++->move; if (move != ttMove && !pos.pawn_promotion(move) ) return move; } break; case QSEARCH_RECAPTURES: cur = moves; endMoves = generateMoves<RECAPTURES>(pos, moves, recaptureSquare); score<CAPTURES>(); // CAPTUREの指し手の並べ替え ++stage; /* fallthrough */ // 取り返す指し手。これはすでにrecaptureの指し手だけが生成されているのでそのまま返す。 case QRECAPTURES: while (cur < endMoves) { // recaptureの指し手が2つ以上あることは稀なのでここでオーダリングしてもあまり意味をなさないが、 // 生成される指し手自体が少ないなら、pick_best()のコストはほぼ無視できるのでこれはやらないよりはマシ。 move = pick_best(cur++, endMoves); //if (to_sq(move) == recaptureSquare) // return move; // → recaptureの指し手のみを生成しているのでこの判定は不要。 ASSERT_LV3(to_sq(move) == recaptureSquare); return move; } break; default: UNREACHABLE; return MOVE_NONE; } return MOVE_NONE; }
/// MovePicker::next_move() is the most important method of the MovePicker class. It /// returns a new pseudo legal move every time it is called until there are no more /// moves left, picking the move with the highest score from a list of generated moves. Move MovePicker::next_move(bool skipQuiets) { top: switch (stage) { case MAIN_TT: case EVASION_TT: case QSEARCH_TT: case PROBCUT_TT: ++stage; return ttMove; case CAPTURE_INIT: case PROBCUT_INIT: case QCAPTURE_INIT: cur = endBadCaptures = moves; endMoves = generate<CAPTURES>(pos, cur); score<CAPTURES>(); ++stage; goto top; case GOOD_CAPTURE: if (select<Best>([&](){ return pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)) ? // Move losing capture to endBadCaptures to be tried later true : (*endBadCaptures++ = move, false); })) return move; // Prepare the pointers to loop over the refutations array cur = std::begin(refutations); endMoves = std::end(refutations); // If the countermove is the same as a killer, skip it if ( refutations[0].move == refutations[2].move || refutations[1].move == refutations[2].move) --endMoves; ++stage; /* fallthrough */ case REFUTATION: if (select<Next>([&](){ return move != MOVE_NONE && !pos.capture(move) && pos.pseudo_legal(move); })) return move; ++stage; /* fallthrough */ case QUIET_INIT: cur = endBadCaptures; endMoves = generate<QUIETS>(pos, cur); score<QUIETS>(); partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY); ++stage; /* fallthrough */ case QUIET: if ( !skipQuiets && select<Next>([&](){return move != refutations[0] && move != refutations[1] && move != refutations[2];})) return move; // Prepare the pointers to loop over the bad captures cur = moves; endMoves = endBadCaptures; ++stage; /* fallthrough */ case BAD_CAPTURE: return select<Next>(Any); case EVASION_INIT: cur = moves; endMoves = generate<EVASIONS>(pos, cur); score<EVASIONS>(); ++stage; /* fallthrough */ case EVASION: return select<Best>(Any); case PROBCUT: return select<Best>([&](){ return pos.see_ge(move, threshold); }); case QCAPTURE: if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES || to_sq(move) == recaptureSquare; })) return move; // If we did not find any move and we do not try checks, we have finished if (depth != DEPTH_QS_CHECKS) return MOVE_NONE; ++stage; /* fallthrough */ case QCHECK_INIT: cur = moves; endMoves = generate<QUIET_CHECKS>(pos, cur); ++stage; /* fallthrough */ case QCHECK: return select<Next>(Any); } assert(false); return MOVE_NONE; // Silence warning }