mirror of
https://github.com/sockspls/badfish
synced 2025-05-01 01:03:09 +00:00
Change the flow in wich moves are generated and picked
In MovePicker we get the next move with pick_move_from_list(), then check if the return value is equal to MOVE_NONE and in this case we update the state to the new phase. This patch reorders the flow so that now from pick_move_from_list() renamed get_next_move() we directly call go_next_phase() to generate and sort the next bunch of moves when there are no more move to try. This avoids to always check for pick_move_from_list() returned value and the flow is more linear and natural. Also use a local variable instead of a pointer dereferencing in a time critical switch statement in get_next_move() With this patch alone we have an incredible speed up of 3.2% !!! No functional change. Signed-off-by: Marco Costalba <mcostalba@gmail.com>
This commit is contained in:
parent
129cde008c
commit
6cf28d4aa7
2 changed files with 175 additions and 186 deletions
357
src/movepick.cpp
357
src/movepick.cpp
|
@ -43,12 +43,12 @@ namespace {
|
||||||
/// Variables
|
/// Variables
|
||||||
|
|
||||||
CACHE_LINE_ALIGNMENT
|
CACHE_LINE_ALIGNMENT
|
||||||
const MovegenPhaseT MainSearchPhaseTable[] = { PH_STOP, PH_NULL_MOVE, PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
|
const MovegenPhaseT MainSearchPhaseTable[] = { PH_NULL_MOVE, PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
|
||||||
const MovegenPhaseT MainSearchNoNullPhaseTable[] = { PH_STOP, PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
|
const MovegenPhaseT MainSearchNoNullPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
|
||||||
const MovegenPhaseT LowSearchPhaseTable[] = { PH_STOP, PH_TT_MOVES, PH_GOOD_CAPTURES, PH_NULL_MOVE, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
|
const MovegenPhaseT LowSearchPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_NULL_MOVE, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
|
||||||
const MovegenPhaseT EvasionsPhaseTable[] = { PH_STOP, PH_EVASIONS, PH_STOP};
|
const MovegenPhaseT EvasionsPhaseTable[] = { PH_EVASIONS, PH_STOP};
|
||||||
const MovegenPhaseT QsearchWithChecksPhaseTable[] = { PH_STOP, PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
|
const MovegenPhaseT QsearchWithChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
|
||||||
const MovegenPhaseT QsearchWithoutChecksPhaseTable[] = { PH_STOP, PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
|
const MovegenPhaseT QsearchWithoutChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,9 +75,14 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
|
||||||
} else
|
} else
|
||||||
ttMoves[1] = killers[0] = killers[1] = MOVE_NONE;
|
ttMoves[1] = killers[0] = killers[1] = MOVE_NONE;
|
||||||
|
|
||||||
movesPicked = numOfMoves = numOfBadCaptures = 0;
|
numOfBadCaptures = 0;
|
||||||
finished = false;
|
finished = false;
|
||||||
|
|
||||||
|
Color us = pos.side_to_move();
|
||||||
|
|
||||||
|
dc = p.discovered_check_candidates(us);
|
||||||
|
pinned = p.pinned_pieces(us);
|
||||||
|
|
||||||
if (p.is_check())
|
if (p.is_check())
|
||||||
phasePtr = EvasionsPhaseTable;
|
phasePtr = EvasionsPhaseTable;
|
||||||
else if (d >= Depth(3 * OnePly))
|
else if (d >= Depth(3 * OnePly))
|
||||||
|
@ -89,120 +94,73 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
|
||||||
else
|
else
|
||||||
phasePtr = QsearchWithoutChecksPhaseTable;
|
phasePtr = QsearchWithoutChecksPhaseTable;
|
||||||
|
|
||||||
Color us = pos.side_to_move();
|
phasePtr--;
|
||||||
|
go_next_phase();
|
||||||
dc = p.discovered_check_candidates(us);
|
|
||||||
pinned = p.pinned_pieces(us);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// MovePicker::get_next_move() is the most important method of the MovePicker
|
/// MovePicker::go_next_phase() generates, scores and sorts the next bunch
|
||||||
/// class. It returns a new legal move every time it is called, until there
|
/// of moves when there are no more moves to try for the currrent phase.
|
||||||
/// are no more moves left of the types we are interested in.
|
|
||||||
|
|
||||||
Move MovePicker::get_next_move() {
|
void MovePicker::go_next_phase() {
|
||||||
|
|
||||||
Move move;
|
movesPicked = 0;
|
||||||
|
phase = *(++phasePtr);
|
||||||
|
switch (phase) {
|
||||||
|
|
||||||
while (true)
|
case PH_NULL_MOVE:
|
||||||
{
|
case PH_TT_MOVES:
|
||||||
// If we already have a list of generated moves, pick the best move from
|
return;
|
||||||
// the list, and return it.
|
|
||||||
move = pick_move_from_list();
|
|
||||||
if (move != MOVE_NONE)
|
|
||||||
{
|
|
||||||
assert(move_is_ok(move));
|
|
||||||
return move;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next phase
|
case PH_GOOD_CAPTURES:
|
||||||
phasePtr++;
|
numOfMoves = generate_captures(pos, moves);
|
||||||
switch (*phasePtr) {
|
score_captures();
|
||||||
|
std::sort(moves, moves + numOfMoves);
|
||||||
|
return;
|
||||||
|
|
||||||
case PH_NULL_MOVE:
|
case PH_KILLERS:
|
||||||
return MOVE_NULL;
|
return;
|
||||||
|
|
||||||
case PH_TT_MOVES:
|
case PH_NONCAPTURES:
|
||||||
movesPicked = 0; // This is used as index to ttMoves[]
|
numOfMoves = generate_noncaptures(pos, moves);
|
||||||
break;
|
score_noncaptures();
|
||||||
|
std::sort(moves, moves + numOfMoves);
|
||||||
|
return;
|
||||||
|
|
||||||
case PH_GOOD_CAPTURES:
|
case PH_BAD_CAPTURES:
|
||||||
numOfMoves = generate_captures(pos, moves);
|
// Bad captures SEE value is already calculated so just sort them
|
||||||
score_captures();
|
// to get SEE move ordering.
|
||||||
std::sort(moves, moves + numOfMoves);
|
std::sort(badCaptures, badCaptures + numOfBadCaptures);
|
||||||
movesPicked = 0;
|
return;
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_KILLERS:
|
case PH_EVASIONS:
|
||||||
movesPicked = 0; // This is used as index to killers[]
|
assert(pos.is_check());
|
||||||
break;
|
numOfMoves = generate_evasions(pos, moves, pinned);
|
||||||
|
score_evasions();
|
||||||
|
std::sort(moves, moves + numOfMoves);
|
||||||
|
return;
|
||||||
|
|
||||||
case PH_NONCAPTURES:
|
case PH_QCAPTURES:
|
||||||
numOfMoves = generate_noncaptures(pos, moves);
|
numOfMoves = generate_captures(pos, moves);
|
||||||
score_noncaptures();
|
score_captures();
|
||||||
std::sort(moves, moves + numOfMoves);
|
std::sort(moves, moves + numOfMoves);
|
||||||
movesPicked = 0;
|
return;
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_BAD_CAPTURES:
|
case PH_QCHECKS:
|
||||||
// Bad captures SEE value is already calculated so just sort them
|
// Perhaps we should order moves move here? FIXME
|
||||||
// to get SEE move ordering.
|
numOfMoves = generate_non_capture_checks(pos, moves, dc);
|
||||||
std::sort(badCaptures, badCaptures + numOfBadCaptures);
|
return;
|
||||||
movesPicked = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_EVASIONS:
|
case PH_STOP:
|
||||||
assert(pos.is_check());
|
return;
|
||||||
numOfMoves = generate_evasions(pos, moves, pinned);
|
|
||||||
score_evasions();
|
|
||||||
std::sort(moves, moves + numOfMoves);
|
|
||||||
movesPicked = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_QCAPTURES:
|
default:
|
||||||
numOfMoves = generate_captures(pos, moves);
|
assert(false);
|
||||||
score_captures();
|
return;
|
||||||
std::sort(moves, moves + numOfMoves);
|
|
||||||
movesPicked = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_QCHECKS:
|
|
||||||
// Perhaps we should order moves move here? FIXME
|
|
||||||
numOfMoves = generate_non_capture_checks(pos, moves, dc);
|
|
||||||
movesPicked = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_STOP:
|
|
||||||
return MOVE_NONE;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
return MOVE_NONE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A variant of get_next_move() which takes a lock as a parameter, used to
|
|
||||||
/// prevent multiple threads from picking the same move at a split point.
|
|
||||||
|
|
||||||
Move MovePicker::get_next_move(Lock &lock) {
|
|
||||||
|
|
||||||
lock_grab(&lock);
|
|
||||||
if (finished)
|
|
||||||
{
|
|
||||||
lock_release(&lock);
|
|
||||||
return MOVE_NONE;
|
|
||||||
}
|
|
||||||
Move m = get_next_move();
|
|
||||||
if (m == MOVE_NONE)
|
|
||||||
finished = true;
|
|
||||||
|
|
||||||
lock_release(&lock);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// MovePicker::score_captures(), MovePicker::score_noncaptures(),
|
/// MovePicker::score_captures(), MovePicker::score_noncaptures(),
|
||||||
/// MovePicker::score_evasions() and MovePicker::score_qcaptures() assign a
|
/// MovePicker::score_evasions() and MovePicker::score_qcaptures() assign a
|
||||||
/// numerical move ordering score to each move in a move list. The moves
|
/// numerical move ordering score to each move in a move list. The moves
|
||||||
|
@ -276,100 +234,131 @@ void MovePicker::score_evasions() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// MovePicker::get_next_move() is the most important method of the MovePicker
|
||||||
|
/// class. It returns a new legal move every time it is called, until there
|
||||||
|
/// are no more moves left.
|
||||||
|
/// It picks the move with the biggest score from a list of generated moves taking
|
||||||
|
/// care not to return the tt move if that has already been serched previously.
|
||||||
|
|
||||||
/// MovePicker::pick_move_from_list() picks the move with the biggest score
|
Move MovePicker::get_next_move() {
|
||||||
/// from a list of generated moves (moves[] or badCaptures[], depending on
|
|
||||||
/// the current move generation phase). It takes care not to return the
|
|
||||||
/// transposition table move if that has already been serched previously.
|
|
||||||
|
|
||||||
Move MovePicker::pick_move_from_list() {
|
|
||||||
|
|
||||||
assert(movesPicked >= 0);
|
assert(movesPicked >= 0);
|
||||||
assert(!pos.is_check() || *phasePtr == PH_EVASIONS || *phasePtr == PH_STOP);
|
assert(!pos.is_check() || *phasePtr == PH_EVASIONS || *phasePtr == PH_STOP);
|
||||||
assert( pos.is_check() || *phasePtr != PH_EVASIONS);
|
assert( pos.is_check() || *phasePtr != PH_EVASIONS);
|
||||||
|
|
||||||
switch (*phasePtr) {
|
while (true)
|
||||||
|
{
|
||||||
|
switch (phase) {
|
||||||
|
|
||||||
case PH_TT_MOVES:
|
case PH_NULL_MOVE:
|
||||||
while (movesPicked < 2) {
|
go_next_phase();
|
||||||
Move move = ttMoves[movesPicked++];
|
return MOVE_NULL;
|
||||||
if ( move != MOVE_NONE
|
|
||||||
&& move_is_legal(pos, move, pinned))
|
|
||||||
return move;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_GOOD_CAPTURES:
|
case PH_TT_MOVES:
|
||||||
while (movesPicked < numOfMoves)
|
while (movesPicked < 2) {
|
||||||
{
|
Move move = ttMoves[movesPicked++];
|
||||||
Move move = moves[movesPicked++].move;
|
if ( move != MOVE_NONE
|
||||||
if ( move != ttMoves[0]
|
&& move_is_legal(pos, move, pinned))
|
||||||
&& move != ttMoves[1]
|
return move;
|
||||||
&& pos.pl_move_is_legal(move, pinned))
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PH_GOOD_CAPTURES:
|
||||||
|
while (movesPicked < numOfMoves)
|
||||||
{
|
{
|
||||||
// Check for a non negative SEE now
|
Move move = moves[movesPicked++].move;
|
||||||
int seeValue = pos.see_sign(move);
|
if ( move != ttMoves[0]
|
||||||
if (seeValue >= 0)
|
&& move != ttMoves[1]
|
||||||
return move;
|
&& pos.pl_move_is_legal(move, pinned))
|
||||||
|
{
|
||||||
|
// Check for a non negative SEE now
|
||||||
|
int seeValue = pos.see_sign(move);
|
||||||
|
if (seeValue >= 0)
|
||||||
|
return move;
|
||||||
|
|
||||||
// Losing capture, move it to the badCaptures[] array, note
|
// Losing capture, move it to the badCaptures[] array, note
|
||||||
// that move has now been already checked for legality.
|
// that move has now been already checked for legality.
|
||||||
assert(numOfBadCaptures < 63);
|
assert(numOfBadCaptures < 63);
|
||||||
badCaptures[numOfBadCaptures].move = move;
|
badCaptures[numOfBadCaptures].move = move;
|
||||||
badCaptures[numOfBadCaptures++].score = seeValue;
|
badCaptures[numOfBadCaptures++].score = seeValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PH_KILLERS:
|
||||||
|
while (movesPicked < 2) {
|
||||||
|
Move move = killers[movesPicked++];
|
||||||
|
if ( move != MOVE_NONE
|
||||||
|
&& move != ttMoves[0]
|
||||||
|
&& move != ttMoves[1]
|
||||||
|
&& move_is_legal(pos, move, pinned)
|
||||||
|
&& !pos.move_is_capture(move))
|
||||||
|
return move;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PH_NONCAPTURES:
|
||||||
|
while (movesPicked < numOfMoves)
|
||||||
|
{
|
||||||
|
Move move = moves[movesPicked++].move;
|
||||||
|
if ( move != ttMoves[0]
|
||||||
|
&& move != ttMoves[1]
|
||||||
|
&& move != killers[0]
|
||||||
|
&& move != killers[1]
|
||||||
|
&& pos.pl_move_is_legal(move, pinned))
|
||||||
|
return move;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PH_EVASIONS:
|
||||||
|
if (movesPicked < numOfMoves)
|
||||||
|
return moves[movesPicked++].move;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PH_BAD_CAPTURES:
|
||||||
|
if (movesPicked < numOfBadCaptures)
|
||||||
|
return badCaptures[movesPicked++].move;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PH_QCAPTURES:
|
||||||
|
case PH_QCHECKS:
|
||||||
|
while (movesPicked < numOfMoves)
|
||||||
|
{
|
||||||
|
Move move = moves[movesPicked++].move;
|
||||||
|
// Maybe postpone the legality check until after futility pruning?
|
||||||
|
if ( move != ttMoves[0]
|
||||||
|
&& pos.pl_move_is_legal(move, pinned))
|
||||||
|
return move;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PH_STOP:
|
||||||
|
return MOVE_NONE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
go_next_phase();
|
||||||
|
|
||||||
case PH_KILLERS:
|
|
||||||
while (movesPicked < 2) {
|
|
||||||
Move move = killers[movesPicked++];
|
|
||||||
if ( move != MOVE_NONE
|
|
||||||
&& move != ttMoves[0]
|
|
||||||
&& move != ttMoves[1]
|
|
||||||
&& move_is_legal(pos, move, pinned)
|
|
||||||
&& !pos.move_is_capture(move))
|
|
||||||
return move;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_NONCAPTURES:
|
|
||||||
while (movesPicked < numOfMoves)
|
|
||||||
{
|
|
||||||
Move move = moves[movesPicked++].move;
|
|
||||||
if ( move != ttMoves[0]
|
|
||||||
&& move != ttMoves[1]
|
|
||||||
&& move != killers[0]
|
|
||||||
&& move != killers[1]
|
|
||||||
&& pos.pl_move_is_legal(move, pinned))
|
|
||||||
return move;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_EVASIONS:
|
|
||||||
if (movesPicked < numOfMoves)
|
|
||||||
return moves[movesPicked++].move;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_BAD_CAPTURES:
|
|
||||||
if (movesPicked < numOfBadCaptures)
|
|
||||||
return badCaptures[movesPicked++].move;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PH_QCAPTURES:
|
|
||||||
case PH_QCHECKS:
|
|
||||||
while (movesPicked < numOfMoves)
|
|
||||||
{
|
|
||||||
Move move = moves[movesPicked++].move;
|
|
||||||
// Maybe postpone the legality check until after futility pruning?
|
|
||||||
if ( move != ttMoves[0]
|
|
||||||
&& pos.pl_move_is_legal(move, pinned))
|
|
||||||
return move;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return MOVE_NONE;
|
return MOVE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A variant of get_next_move() which takes a lock as a parameter, used to
|
||||||
|
/// prevent multiple threads from picking the same move at a split point.
|
||||||
|
|
||||||
|
Move MovePicker::get_next_move(Lock &lock) {
|
||||||
|
|
||||||
|
lock_grab(&lock);
|
||||||
|
if (finished)
|
||||||
|
{
|
||||||
|
lock_release(&lock);
|
||||||
|
return MOVE_NONE;
|
||||||
|
}
|
||||||
|
Move m = get_next_move();
|
||||||
|
if (m == MOVE_NONE)
|
||||||
|
finished = true;
|
||||||
|
|
||||||
|
lock_release(&lock);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
|
@ -75,13 +75,13 @@ private:
|
||||||
void score_captures();
|
void score_captures();
|
||||||
void score_noncaptures();
|
void score_noncaptures();
|
||||||
void score_evasions();
|
void score_evasions();
|
||||||
Move pick_move_from_list();
|
void go_next_phase();
|
||||||
|
|
||||||
const Position& pos;
|
const Position& pos;
|
||||||
const History& H;
|
const History& H;
|
||||||
Move ttMoves[2], killers[2];
|
Move ttMoves[2], killers[2];
|
||||||
const MovegenPhaseT* phasePtr;
|
const MovegenPhaseT* phasePtr;
|
||||||
int movesPicked, numOfMoves, numOfBadCaptures;
|
int phase, movesPicked, numOfMoves, numOfBadCaptures;
|
||||||
bool finished;
|
bool finished;
|
||||||
Bitboard dc, pinned;
|
Bitboard dc, pinned;
|
||||||
MoveStack moves[256], badCaptures[64];
|
MoveStack moves[256], badCaptures[64];
|
||||||
|
|
Loading…
Add table
Reference in a new issue