Move pv[PLY_MAX_PLUS_2];
};
- // RootMoveList struct is just a vector of RootMove objects,
- // with an handful of methods above the standard ones.
+ // RootMoveList struct is mainly a std::vector of RootMove objects
struct RootMoveList : public std::vector<RootMove> {
-
- typedef std::vector<RootMove> Base;
-
void init(Position& pos, Move searchMoves[]);
- void sort() { insertion_sort<RootMove, Base::iterator>(begin(), end()); }
- void sort_first(int n) { insertion_sort<RootMove, Base::iterator>(begin(), begin() + n); }
-
int bestMoveChanges;
};
if (moveIsCheck && pos.see_sign(m) >= 0)
result += CheckExtension[PvNode];
- if (pos.type_of_piece_on(move_from(m)) == PAWN)
+ if (piece_type(pos.piece_on(move_from(m))) == PAWN)
{
Color c = pos.side_to_move();
if (relative_rank(c, move_to(m)) == RANK_7)
}
if ( captureOrPromotion
- && pos.type_of_piece_on(move_to(m)) != PAWN
+ && piece_type(pos.piece_on(move_to(m))) != PAWN
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- - pos.midgame_value_of_piece_on(move_to(m)) == VALUE_ZERO)
+ - piece_value_midgame(pos.piece_on(move_to(m))) == VALUE_ZERO)
&& !move_is_special(m))
{
result += PawnEndgameExtension[PvNode];
int64_t perft(Position& pos, Depth depth) {
- MoveStack mlist[MAX_MOVES];
StateInfo st;
- Move m;
int64_t sum = 0;
// Generate all legal moves
- MoveStack* last = generate<MV_LEGAL>(pos, mlist);
+ MoveList<MV_LEGAL> ml(pos);
// If we are at the last ply we don't need to do and undo
// the moves, just to count them.
if (depth <= ONE_PLY)
- return int(last - mlist);
+ return ml.size();
// Loop through all legal moves
CheckInfo ci(pos);
- for (MoveStack* cur = mlist; cur != last; cur++)
+ for ( ; !ml.end(); ++ml)
{
- m = cur->move;
- pos.do_move(m, st, ci, pos.move_gives_check(m, ci));
+ pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci));
sum += perft(pos, depth - ONE_PLY);
- pos.undo_move(m);
+ pos.undo_move(ml.move());
}
return sum;
}
if (PvNode && thread.maxPly < ss->ply)
thread.maxPly = ss->ply;
- if (SpNode)
+ // Step 1. Initialize node and poll. Polling can abort search
+ if (!SpNode)
+ {
+ ss->currentMove = ss->bestMove = threatMove = (ss+1)->excludedMove = MOVE_NONE;
+ (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
+ (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
+ }
+ else
{
sp = ss->sp;
tte = NULL;
goto split_point_start;
}
- // Step 1. Initialize node and poll. Polling can abort search
- ss->currentMove = ss->bestMove = threatMove = (ss+1)->excludedMove = MOVE_NONE;
- (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
- (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
-
if (pos.thread() == 0 && ++NodesSincePoll > NodesBetweenPolls)
{
NodesSincePoll = 0;
// TT value, so we use a different position key in case of an excluded move.
excludedMove = ss->excludedMove;
posKey = excludedMove ? pos.get_exclusion_key() : pos.get_key();
-
tte = TT.probe(posKey);
ttMove = tte ? tte->move() : MOVE_NONE;
assert(rdepth >= ONE_PLY);
- MovePicker mp(pos, ttMove, H, Position::see_value(pos.captured_piece_type()));
+ MovePicker mp(pos, ttMove, H, pos.captured_piece_type());
CheckInfo ci(pos);
while ((move = mp.get_next_move()) != MOVE_NONE)
if (move == excludedMove)
continue;
- // At PV and SpNode nodes we want the moves to be legal
+ // At PV and SpNode nodes we want all moves to be legal since the beginning
if ((PvNode || SpNode) && !pos.pl_move_is_legal(move, ci.pinned))
continue;
cout << "info" << speed_to_uci(pos.nodes_searched()) << endl;
}
- // For long searches send to GUI current move
+ // For long searches send current move info to GUI
if (current_search_time() > 2000)
cout << "info" << depth_to_uci(depth)
<< " currmove " << move << " currmovenumber " << moveCount << endl;
}
// At Root and at first iteration do a PV search on all the moves to score root moves
- isPvMove = (PvNode && moveCount <= (RootNode ? depth <= ONE_PLY ? MAX_MOVES : MultiPV : 1));
+ isPvMove = (PvNode && moveCount <= (!RootNode ? 1 : depth <= ONE_PLY ? MAX_MOVES : MultiPV));
givesCheck = pos.move_gives_check(move, ci);
- captureOrPromotion = pos.move_is_capture(move) || move_is_promotion(move);
+ captureOrPromotion = pos.move_is_capture_or_promotion(move);
// Step 12. Decide the new search depth
ext = extension<PvNode>(pos, move, captureOrPromotion, givesCheck, &dangerous);
}
ss->currentMove = move;
+ if (!SpNode && !captureOrPromotion)
+ movesSearched[playedMoveCount++] = move;
// Step 14. Make the move
pos.do_move(move, st, ci, givesCheck);
- if (!SpNode && !captureOrPromotion)
- movesSearched[playedMoveCount++] = move;
-
// Step extra. pv search (only in PV nodes)
// The first move in list is the expected PV
if (isPvMove)
// Step 15. Reduced depth search
// If the move fails high will be re-searched at full depth.
bool doFullDepthSearch = true;
- alpha = SpNode ? sp->alpha : alpha;
if ( depth > 3 * ONE_PLY
&& !captureOrPromotion
&& !dangerous
&& !move_is_castle(move)
&& ss->killers[0] != move
- && ss->killers[1] != move)
+ && ss->killers[1] != move
+ && (ss->reduction = reduction<PvNode>(depth, moveCount)) != DEPTH_ZERO)
{
- ss->reduction = reduction<PvNode>(depth, moveCount);
- if (ss->reduction)
- {
- Depth d = newDepth - ss->reduction;
- value = d < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
- : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d);
- doFullDepthSearch = (value > alpha);
- }
- ss->reduction = DEPTH_ZERO; // Restore original reduction
+ Depth d = newDepth - ss->reduction;
+ alpha = SpNode ? sp->alpha : alpha;
+
+ value = d < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
+ : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d);
+
+ ss->reduction = DEPTH_ZERO;
+ doFullDepthSearch = (value > alpha);
}
// Step 16. Full depth search
alpha = sp->alpha;
}
- if (value > bestValue && !(SpNode && thread.cutoff_occurred()))
+ if (value > bestValue)
{
bestValue = value;
+ ss->bestMove = move;
- if (SpNode)
- sp->bestValue = value;
+ if ( !RootNode
+ && PvNode
+ && value > alpha
+ && value < beta) // We want always alpha < beta
+ alpha = value;
- if (!RootNode && value > alpha)
+ if (SpNode && !thread.cutoff_occurred())
{
- if (PvNode && value < beta) // We want always alpha < beta
- {
- alpha = value;
-
- if (SpNode)
- sp->alpha = value;
- }
- else if (SpNode)
- sp->is_betaCutoff = true;
-
- ss->bestMove = move;
-
- if (SpNode)
- sp->ss->bestMove = move;
+ sp->bestValue = value;
+ sp->ss->bestMove = move;
+ sp->alpha = alpha;
+ sp->is_betaCutoff = (value >= beta);
}
}
if (isPvMove || value > alpha)
{
// Update PV
- ss->bestMove = move;
mp.current().pv_score = value;
mp.current().extract_pv_from_tt(pos);
// because all the values but the first are usually set to
// -VALUE_INFINITE and we want to keep the same order for all
// the moves but the new PV that goes to head.
- Rml.sort_first(moveCount);
+ sort<RootMove>(Rml.begin(), Rml.begin() + moveCount);
// Update alpha. In multi-pv we don't use aspiration window, so set
// alpha equal to minimum score among the PV lines searched so far.
// Update killers and history only for non capture moves that fails high
if ( bestValue >= beta
- && !pos.move_is_capture(move)
- && !move_is_promotion(move))
+ && !pos.move_is_capture_or_promotion(move))
{
if (move != ss->killers[0])
{
&& !pos.move_is_passed_pawn_push(move))
{
futilityValue = futilityBase
- + pos.endgame_value_of_piece_on(move_to(move))
+ + piece_value_endgame(pos.piece_on(move_to(move)))
+ (move_is_ep(move) ? PawnValueEndgame : VALUE_ZERO);
if (futilityValue < alpha)
&& !inCheck
&& givesCheck
&& move != ttMove
- && !pos.move_is_capture(move)
- && !move_is_promotion(move)
+ && !pos.move_is_capture_or_promotion(move)
&& ss->eval + PawnValueMidgame / 4 < beta
&& !check_is_dangerous(pos, move, futilityBase, beta, &bestValue))
{
newAtt = pos.attacks_from(pc, to, occ);
// Rule 1. Checks which give opponent's king at most one escape square are dangerous
- b = kingAtt & ~pos.pieces_of_color(them) & ~newAtt & ~(1ULL << to);
+ b = kingAtt & ~pos.pieces(them) & ~newAtt & ~(1ULL << to);
if (!(b && (b & (b - 1))))
return true;
// Rule 2. Queen contact check is very dangerous
- if ( type_of_piece(pc) == QUEEN
+ if ( piece_type(pc) == QUEEN
&& bit_is_set(kingAtt, to))
return true;
// Rule 3. Creating new double threats with checks
- b = pos.pieces_of_color(them) & newAtt & ~oldAtt & ~(1ULL << ksq);
+ b = pos.pieces(them) & newAtt & ~oldAtt & ~(1ULL << ksq);
while (b)
{
victimSq = pop_1st_bit(&b);
- futilityValue = futilityBase + pos.endgame_value_of_piece_on(victimSq);
+ futilityValue = futilityBase + piece_value_endgame(pos.piece_on(victimSq));
// Note that here we generate illegal "double move"!
if ( futilityValue >= beta
bool connected_moves(const Position& pos, Move m1, Move m2) {
Square f1, t1, f2, t2;
- Piece p;
+ Piece p1, p2;
+ Square ksq;
assert(m1 && move_is_ok(m1));
assert(m2 && move_is_ok(m2));
return true;
// Case 3: Moving through the vacated square
- if ( piece_is_slider(pos.piece_on(f2))
+ p2 = pos.piece_on(f2);
+ if ( piece_is_slider(p2)
&& bit_is_set(squares_between(f2, t2), f1))
return true;
// Case 4: The destination square for m2 is defended by the moving piece in m1
- p = pos.piece_on(t1);
- if (bit_is_set(pos.attacks_from(p, t1), t2))
+ p1 = pos.piece_on(t1);
+ if (bit_is_set(pos.attacks_from(p1, t1), t2))
return true;
// Case 5: Discovered check, checking piece is the piece moved in m1
- if ( piece_is_slider(p)
- && bit_is_set(squares_between(t1, pos.king_square(pos.side_to_move())), f2)
- && !bit_is_set(squares_between(t1, pos.king_square(pos.side_to_move())), t2))
+ ksq = pos.king_square(pos.side_to_move());
+ if ( piece_is_slider(p1)
+ && bit_is_set(squares_between(t1, ksq), f2))
{
- // discovered_check_candidates() works also if the Position's side to
- // move is the opposite of the checking piece.
- Color them = opposite_color(pos.side_to_move());
- Bitboard dcCandidates = pos.discovered_check_candidates(them);
-
- if (bit_is_set(dcCandidates, f2))
+ Bitboard occ = pos.occupied_squares();
+ clear_bit(&occ, f2);
+ if (bit_is_set(pos.attacks_from(p1, t1, occ), ksq))
return true;
}
return false;
assert(move_is_ok(m));
assert(threat && move_is_ok(threat));
- assert(!pos.move_is_capture(m) && !move_is_promotion(m));
+ assert(!pos.move_is_capture_or_promotion(m));
assert(!pos.move_is_passed_pawn_push(m));
Square mfrom, mto, tfrom, tto;
// Case 2: If the threatened piece has value less than or equal to the
// value of the threatening piece, don't prune moves which defend it.
if ( pos.move_is_capture(threat)
- && ( pos.midgame_value_of_piece_on(tfrom) >= pos.midgame_value_of_piece_on(tto)
- || pos.type_of_piece_on(tfrom) == KING)
+ && ( piece_value_midgame(pos.piece_on(tfrom)) >= piece_value_midgame(pos.piece_on(tto))
+ || piece_type(pos.piece_on(tfrom)) == KING)
&& pos.move_attacks_square(m, tto))
return true;
void RootMoveList::init(Position& pos, Move searchMoves[]) {
- MoveStack mlist[MAX_MOVES];
Move* sm;
-
- clear();
bestMoveChanges = 0;
+ clear();
// Generate all legal moves and add them to RootMoveList
- MoveStack* last = generate<MV_LEGAL>(pos, mlist);
- for (MoveStack* cur = mlist; cur != last; cur++)
+ for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
{
- // If we have a searchMoves[] list then verify cur->move
+ // If we have a searchMoves[] list then verify the move
// is in the list before to add it.
- for (sm = searchMoves; *sm && *sm != cur->move; sm++) {}
+ for (sm = searchMoves; *sm && *sm != ml.move(); sm++) {}
- if (searchMoves[0] && *sm != cur->move)
+ if (sm != searchMoves && *sm != ml.move())
continue;
RootMove rm;
- rm.pv[0] = cur->move;
+ rm.pv[0] = ml.move();
rm.pv[1] = MOVE_NONE;
rm.pv_score = -VALUE_INFINITE;
push_back(rm);
break;
}
- Rml.sort();
+ sort<RootMove>(Rml.begin(), Rml.end());
}
} // namespace