void MovePicker::score<CAPTURES>() { // Winning and equal captures in the main search are ordered by MVV/LVA. // Suprisingly, this appears to perform slightly better than SEE based // move ordering. The reason is probably that in a position with a winning // capture, capturing a more valuable (but sufficiently defended) piece // first usually doesn't hurt. The opponent will have to recapture, and // the hanging piece will still be hanging (except in the unusual cases // where it is possible to recapture with the hanging piece). Exchanging // big pieces before capturing a hanging piece probably helps to reduce // the subtree size. // In main search we want to push captures with negative SEE values to the // badCaptures[] array, but instead of doing it now we delay until the move // has been picked up in pick_move_from_list(). This way we save some SEE // calls in case we get a cutoff. Move m; for (ExtMove* it = moves; it != end; ++it) { m = it->move; /* 評価値は取る駒の駒評価値から駒種をValueにキャストした数を引いている 引いている意味は不明 */ it->value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))); /* もし指し手パターンがアンパッサンだったら PAWNの評価値を追加する (アンパッサンの時のPAWNの動きではto座標に敵の駒がいないので) */ if (type_of(m) == ENPASSANT) it->value += PieceValue[MG][PAWN]; /* 指し手パターンが成りだったら、なった駒種の駒評価値からPAWNの駒評価値を引いたもの(昇格評価値)を追加している */ else if (type_of(m) == PROMOTION) it->value += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN]; } }
void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); for (auto& m : *this) if (Type == CAPTURES) m.value = PieceValue[MG][pos.piece_on(to_sq(m))] + Value((*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]); else if (Type == QUIETS) m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + (*contHistory[0])[pos.moved_piece(m)][to_sq(m)] + (*contHistory[1])[pos.moved_piece(m)][to_sq(m)] + (*contHistory[3])[pos.moved_piece(m)][to_sq(m)]; else // Type == EVASIONS { if (pos.capture(m)) m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))); else m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] - (1 << 28); } }
Move MovePicker::next_move() { 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; case GOOD_CAPTURES: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) { if (pos.see_ge(move, VALUE_ZERO)) return move; // Losing capture, move it to the beginning of the array *endBadCaptures++ = move; } } ++stage; move = ss->killers[0]; // First killer move if ( move != MOVE_NONE && move != ttMove && pos.pseudo_legal(move) && !pos.capture(move)) return move; case KILLERS: ++stage; move = ss->killers[1]; // Second killer move if ( move != MOVE_NONE && move != ttMove && pos.pseudo_legal(move) && !pos.capture(move)) return move; case COUNTERMOVE: ++stage; move = countermove; if ( move != MOVE_NONE && move != ttMove && move != ss->killers[0] && move != ss->killers[1] && pos.pseudo_legal(move) && !pos.capture(move)) return move; case QUIET_INIT: cur = endBadCaptures; endMoves = generate<QUIETS>(pos, cur); score<QUIETS>(); if (depth < 3 * ONE_PLY) { ExtMove* goodQuiet = std::partition(cur, endMoves, [](const ExtMove& m) { return m.value > VALUE_ZERO; }); insertion_sort(cur, goodQuiet); } else insertion_sort(cur, endMoves); ++stage; case QUIET: while (cur < endMoves) { move = *cur++; if ( move != ttMove && move != ss->killers[0] && move != ss->killers[1] && move != countermove) return move; } ++stage; cur = moves; // Point to beginning of bad captures case BAD_CAPTURES: if (cur < endBadCaptures) return *cur++; break; case EVASIONS_INIT: cur = moves; endMoves = generate<EVASIONS>(pos, cur); score<EVASIONS>(); ++stage; 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; 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; 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; 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; 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_move() { Move move; while (true) { while (cur == endMoves && stage != STOP) generate_next_stage(); switch (stage) { case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS: case QSEARCH_WITHOUT_CHECKS: case PROBCUT: ++cur; return ttMove; case GOOD_CAPTURES: move = pick_best(cur++, endMoves); if (move != ttMove) { if (pos.see_sign(move) >= VALUE_ZERO) return move; // Losing capture, move it to the tail of the array *endBadCaptures-- = move; } break; case KILLERS: move = *cur++; if ( move != MOVE_NONE && move != ttMove && pos.pseudo_legal(move) && !pos.capture(move)) return move; break; case GOOD_QUIETS: case BAD_QUIETS: move = *cur++; if ( move != ttMove && move != killers[0] && move != killers[1] && move != killers[2]) return move; break; case BAD_CAPTURES: return *cur--; case ALL_EVASIONS: case QCAPTURES_1: case QCAPTURES_2: move = pick_best(cur++, endMoves); if (move != ttMove) return move; break; case PROBCUT_CAPTURES: move = pick_best(cur++, endMoves); if (move != ttMove && pos.see(move) > threshold) return move; break; case RECAPTURES: move = pick_best(cur++, endMoves); if (to_sq(move) == recaptureSquare) return move; break; case CHECKS: move = *cur++; if (move != ttMove) return move; break; case STOP: return MOVE_NONE; default: assert(false); } } }
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; }
bool move_is_legal(const Position& pos, Move move) { Move m = move; assert(is_ok(m)); Color us = pos.side_to_move(); Square from = from_sq(m); Square to = to_sq(m); assert(color_of(pos.piece_moved(m)) == us); assert(pos.piece_on(pos.king_square(us)) == make_piece(us, KING)); PieceType pfr = type_of(pos.piece_on(from)); PieceType pto= type_of(pos.piece_on(to)); Bitboard pawns = pos.pieces(~us, PAWN); Bitboard knights = pos.pieces(~us, KNIGHT); Bitboard cannons = pos.pieces(~us, CANNON); Bitboard rooks = pos.pieces(~us, ROOK); Bitboard occ = pos.pieces(); Bitboard occl90 = pos.piecesl90(); occl90 ^= square_rotate_l90_bb(from); occ ^= from; if(pto == NO_PIECE_TYPE) { occl90 ^= square_rotate_l90_bb(to); occ ^= to; } Square ksq = pos.king_square(us); if(ksq == from) ksq = to; if (pto != NO_PIECE_TYPE) { switch(pto) { case PAWN: pawns ^= to; break; case KNIGHT: knights ^= to; break; case ROOK: rooks ^= to; break; case CANNON: cannons ^= to; break; } } if((PseudoAttacks[ROOK][ksq]& cannons) &&(cannon_control_bb(ksq, occ,occl90) & cannons)) return false; if((PseudoAttacks[ROOK][ksq]& rooks) && (rook_attacks_bb(ksq,occ,occl90)& rooks) ) return false; if( knight_attackers_to_bb(ksq, knights, occ) ) return false; if( pos.attacks_from_pawn_nomask(ksq, us) & pawns ) return false; if((PseudoAttacks[ROOK][ksq]& pos.king_square(~us)) && (rook_attacks_bb(ksq,occ,occl90)& pos.king_square(~us))) return false;//╤таЁ return true; }
Move MovePicker::next_move<false>() { Move move; while (true) { while (cur == end) generate_next(); switch (phase) { case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: cur++; return ttMove; case CAPTURES_S1: move = pick_best(cur++, end)->move; if (move != ttMove) { assert(captureThreshold <= 0); // Otherwise we cannot use see_sign() if (pos.see_sign(move) >= captureThreshold) return move; // Losing capture, move it to the tail of the array (endBadCaptures--)->move = move; } break; case KILLERS_S1: move = (cur++)->move; if ( move != MOVE_NONE && pos.is_pseudo_legal(move) && move != ttMove && !pos.is_capture(move)) return move; break; case QUIETS_1_S1: case QUIETS_2_S1: move = (cur++)->move; if ( move != ttMove && move != killers[0].move && move != killers[1].move) return move; break; case BAD_CAPTURES_S1: return (cur--)->move; case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4: move = pick_best(cur++, end)->move; if (move != ttMove) return move; break; case CAPTURES_S5: move = pick_best(cur++, end)->move; if (move != ttMove && pos.see(move) > captureThreshold) return move; break; case CAPTURES_S6: move = pick_best(cur++, end)->move; if (to_sq(move) == recaptureSquare) return move; break; case QUIET_CHECKS_S3: move = (cur++)->move; if (move != ttMove) return move; break; case STOP: return MOVE_NONE; default: assert(false); } } }
Move MovePicker::next_move<false>() { Move move; while (true) { /* cur == endが成立するのは指し手が0ということ */ while (cur == end) generate_next_stage(); switch (stage) { /* ここにくるということは手を新たに生成していない、ttMoveで十分ということなのでttMoveをかえす */ case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: ++cur; return ttMove; /* CAPTURES_S1で手が生成できたとき(他の指し手は生成していない)ここにくる pick_best関数でもっとも点数のよかった手を返す、 静止探索して評価値が0を下回らない(駒の取り合いに勝つか引き分け) 静止探索してマイナスになるようなら取る手の着手リストはendBadCapturesの指す 位置に移動し、最初から着手リストがなくなるまでつづける(cur == end)が成立するまで */ case CAPTURES_S1: move = pick_best(cur++, end)->move; if (move != ttMove) { if (pos.see_sign(move) >= VALUE_ZERO) return move; // Losing capture, move it to the tail of the array (endBadCaptures--)->move = move; } break; case KILLERS_S1: move = (cur++)->move; if ( move != MOVE_NONE && move != ttMove && pos.pseudo_legal(move) && !pos.capture(move)) return move; break; case QUIETS_1_S1: case QUIETS_2_S1: move = (cur++)->move; if ( move != ttMove && move != killers[0].move && move != killers[1].move && move != killers[2].move && move != killers[3].move && move != killers[4].move && move != killers[5].move) return move; break; case BAD_CAPTURES_S1: return (cur--)->move; case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4: move = pick_best(cur++, end)->move; if (move != ttMove) return move; break; case CAPTURES_S5: move = pick_best(cur++, end)->move; if (move != ttMove && pos.see(move) > captureThreshold) return move; break; case CAPTURES_S6: move = pick_best(cur++, end)->move; if (to_sq(move) == recaptureSquare) return move; break; case QUIET_CHECKS_S3: move = (cur++)->move; if (move != ttMove) return move; break; case STOP: return MOVE_NONE; default: assert(false); } } }
Value name_NT_InCheck(qsearch)(Pos* pos, Stack* ss, Value alpha, BETA_ARG Depth depth) { assert(InCheck == !!pos_checkers()); assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); assert(depth <= DEPTH_ZERO); Move pv[MAX_PLY+1]; TTEntry *tte; Key posKey; Move ttMove, move, bestMove; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; int ttHit, ttPv, givesCheck, evasionPrunable; Depth ttDepth; int moveCount; if (PvNode) { oldAlpha = alpha; // To flag BOUND_EXACT when eval above alpha and no available moves (ss+1)->pv = pv; ss->pv[0] = 0; } bestMove = 0; moveCount = 0; // Check for an instant draw or if the maximum ply has been reached if (is_draw(pos) || ss->ply >= MAX_PLY) return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); // Decide whether or not to include checks: this fixes also the type of // TT entry depth that we are going to use. Note that in qsearch we use // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; // Transposition table lookup posKey = pos_key(); tte = tt_probe(posKey, &ttHit); ttValue = ttHit ? value_from_tt(tte_value(tte), ss->ply) : VALUE_NONE; ttMove = ttHit ? tte_move(tte) : 0; ttPv = ttHit ? tte_is_pv(tte) : 0; if ( !PvNode && ttHit && tte_depth(tte) >= ttDepth && ttValue != VALUE_NONE // Only in case of TT access race && (ttValue >= beta ? (tte_bound(tte) & BOUND_LOWER) : (tte_bound(tte) & BOUND_UPPER))) return ttValue; // Evaluate the position statically if (InCheck) { ss->staticEval = VALUE_NONE; bestValue = futilityBase = -VALUE_INFINITE; } else { if (ttHit) { // Never assume anything on values stored in TT if ((ss->staticEval = bestValue = tte_eval(tte)) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos); // Can ttValue be used as a better position evaluation? if (ttValue != VALUE_NONE) if (tte_bound(tte) & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER)) bestValue = ttValue; } else ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Tempo; // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { if (!ttHit) tte_save(tte, posKey, value_to_tt(bestValue, ss->ply), ttPv, BOUND_LOWER, DEPTH_NONE, 0, ss->staticEval, tt_generation()); return bestValue; } if (PvNode && bestValue > alpha) alpha = bestValue; futilityBase = bestValue + 128; } ss->history = &(*pos->counterMoveHistory)[0][0]; // Initialize move picker data for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // be generated. mp_init_q(pos, ttMove, depth, to_sq((ss-1)->currentMove)); // Loop through the moves until no moves remain or a beta cutoff occurs while ((move = next_move(pos, 0))) { assert(move_is_ok(move)); givesCheck = gives_check(pos, ss, move); moveCount++; // Futility pruning if ( !InCheck && !givesCheck && futilityBase > -VALUE_KNOWN_WIN && !advanced_pawn_push(pos, move)) { assert(type_of_m(move) != ENPASSANT); // Due to !advanced_pawn_push futilityValue = futilityBase + PieceValue[EG][piece_on(to_sq(move))]; if (futilityValue <= alpha) { bestValue = max(bestValue, futilityValue); continue; } if (futilityBase <= alpha && !see_test(pos, move, 1)) { bestValue = max(bestValue, futilityBase); continue; } } // Detect non-capture evasions that are candidates to be pruned evasionPrunable = InCheck && (depth != DEPTH_ZERO || moveCount > 2) && bestValue > VALUE_MATED_IN_MAX_PLY && !is_capture(pos, move); // Don't search moves with negative SEE values if ( (!InCheck || evasionPrunable) && !see_test(pos, move, 0)) continue; // Speculative prefetch as early as possible prefetch(tt_first_entry(key_after(pos, move))); // Check for legality just before making the move if (!is_legal(pos, move)) { moveCount--; continue; } ss->currentMove = move; ss->history = &(*pos->counterMoveHistory)[moved_piece(move)][to_sq(move)]; // Make and search the move do_move(pos, move, givesCheck); #if PvNode value = givesCheck ? -qsearch_PV_true(pos, ss+1, -beta, -alpha, depth - ONE_PLY) : -qsearch_PV_false(pos, ss+1, -beta, -alpha, depth - ONE_PLY); #else value = givesCheck ? -qsearch_NonPV_true(pos, ss+1, -beta, depth - ONE_PLY) : -qsearch_NonPV_false(pos, ss+1, -beta, depth - ONE_PLY); #endif undo_move(pos, move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); // Check for a new best move if (value > bestValue) { bestValue = value; if (value > alpha) { bestMove = move; if (PvNode) // Update pv even in fail-high case update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha here! alpha = value; else break; // Fail high } } } // All legal moves have been searched. A special case: If we're in check // and no legal moves were found, it is checkmate. if (InCheck && bestValue == -VALUE_INFINITE) return mated_in(ss->ply); // Plies to mate from the root tte_save(tte, posKey, value_to_tt(bestValue, ss->ply), ttPv, bestValue >= beta ? BOUND_LOWER : PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, ttDepth, bestMove, ss->staticEval, tt_generation()); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); return bestValue; }
/// 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 }
void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); for (auto& m : *this) if (Type == CAPTURES) { #ifdef ATOMIC if (pos.is_atomic()) m.value = pos.see<ATOMIC_VARIANT>(m); else #endif #ifdef RACE if (pos.is_race()) m.value = PieceValue[pos.variant()][MG][pos.piece_on(to_sq(m))] - Value(200 * relative_rank(BLACK, to_sq(m))) + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))] / 8; else #endif m.value = PieceValue[pos.variant()][MG][pos.piece_on(to_sq(m))] + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))] / 8; } else if (Type == QUIETS) { m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]; #ifdef ANTI if (pos.is_anti() && pos.attackers_to(to_sq(m), pos.pieces() ^ from_sq(m)) & pos.pieces(~pos.side_to_move())) { m.value += (1 << 28); if (!(pos.attackers_to(from_sq(m)) & pos.pieces(~pos.side_to_move()))) m.value += (1 << 27); } #endif } else // Type == EVASIONS { if (pos.capture(m)) m.value = PieceValue[pos.variant()][MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))); else m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] - (1 << 28); } }
void MovePicker::score<QUIETS>() { for (auto& m : *this) m.value = history[pos.moved_piece(m)][to_sq(m)] + (*counterMoveHistory)[pos.moved_piece(m)][to_sq(m)]; }
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; if ( type_of(pos.piece_on(to_sq(move))) == KNIGHT && type_of(pos.moved_piece(move)) == BISHOP && (cur-1)->value > 1090) 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; }
// 利きのある場所への取れない近接王手からの3手詰め Move Position::weak_mate_n_ply(int ply) const { // 1手詰めであるならこれを返す Move m = mate1ply(); if (m) return m; // 詰まない if (ply <= 1) return MOVE_NONE; Color us = side_to_move(); Color them = ~us; Bitboard around8 = kingEffect(king_square(them)); // const剥がし Position* This = ((Position*)this); StateInfo si; StateInfo si2; // 近接王手で味方の利きがあり、敵の利きのない場所を探す。 for (auto m : MoveList<CHECKS>(*this)) { // 近接王手で、この指し手による駒の移動先に敵の駒がない。 Square to = to_sq(m); if ((around8 & to) #ifndef LONG_EFFECT_LIBRARY // toに利きがあるかどうか。mが移動の指し手の場合、mの元の利きを取り除く必要がある。 && (is_drop(m) ? effected_to(us, to) : (attackers_to(us, to, pieces() ^ from_sq(m)) ^ from_sq(m))) // 敵玉の利きは必ずtoにあるのでそれを除いた利きがあるかどうか。 && (attackers_to(them,to,pieces()) ^ king_square(them)) #else && (is_drop(m) ? effected_to(us, to) : board_effect[us].effect(to) >= 2 || (long_effect.directions_of(us, from_sq(m)) & Effect8::directions_of(from_sq(m), to)) != 0) // 敵玉の利きがあるので2つ以上なければそれで良い。 && (board_effect[them].effect(to) <= 1) #endif ) { if (!legal(m)) continue; ASSERT_LV3(gives_check(m)); This->do_move(m,si,true); ASSERT_LV3(in_check()); // この局面ですべてのevasionを試す for (auto m2 : MoveList<EVASIONS>(*this)) { if (!legal(m2)) continue; // この指し手で逆王手になるなら、不詰めとして扱う if (gives_check(m2)) goto NEXT_CHECK; This->do_move(m2, si2, false); ASSERT_LV3(!in_check()); if (!weak_mate_n_ply(ply-2)) { // 詰んでないので、m2で詰みを逃れている。 This->undo_move(m2); goto NEXT_CHECK; } This->undo_move(m2); } // すべて詰んだ This->undo_move(m); // mによって3手で詰む。 return m; NEXT_CHECK:; This->undo_move(m); } } return MOVE_NONE; }