Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/core/board_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,12 @@ enum Phases : uint8_t {
GamePhaseEg,
};

// clang-format off
constexpr static inline std::array<uint8_t, s_amountPieces> s_piecePhaseValues {
0, 1, 1, 2, 4, 0, /* white */
0, 1, 1, 2, 4, 0, /* black */
};
// clang-format on

constexpr static inline uint8_t s_maxSearchDepth { 128 };
constexpr static inline uint8_t s_amountSquares { 64 };
Expand Down
250 changes: 160 additions & 90 deletions src/evaluation/evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class Evaluator {
TimeManager::start(board);
}

return scanForBestMove(depthInput.value_or(s_maxSearchDepth), board);
return startIterativeDeepening(depthInput.value_or(s_maxSearchDepth), board);
}

constexpr bool startPondering(const BitBoard& board)
Expand Down Expand Up @@ -189,145 +189,215 @@ class Evaluator {
return static_cast<double>(pvNodes) / totalNodes;
}

constexpr movegen::Move scanForBestMove(uint8_t depth, const BitBoard& board)
constexpr movegen::Move startIterativeDeepening(uint8_t depth, const BitBoard& board)
{
Score alpha = s_minScore;
Score beta = s_maxScore;
const movegen::Move bestMove = m_searchers.size() == 1
? iterativeDeepeningSingle(depth, board)
: iterativeDeepeningMulti(depth, board);

stop();

return bestMove;
}

struct AspirationWindow {
Score alpha;
Score beta;
Score delta;
uint8_t depthReduction;
const uint8_t depth;

explicit AspirationWindow(uint8_t depth, Score prevScore)
: depth(depth)
{
if (depth >= spsa::aspirationMinDepth) {
alpha = std::max<Score>(s_minScore, prevScore - spsa::aspirationWindow);
beta = std::min<Score>(s_maxScore, prevScore + spsa::aspirationWindow);
} else {
alpha = s_minScore;
beta = s_maxScore;
}
delta = spsa::aspirationWindow;
depthReduction = 0;
}

inline void widenOnFailLow()
{
alpha = std::max<Score>(s_minScore, alpha - delta);
Comment thread
hansbinderup marked this conversation as resolved.
beta = (alpha + beta) / 2;
depthReduction = 0;
}

inline void widenOnFailHigh()
{
beta = std::min<Score>(s_maxScore, beta + delta);
depthReduction++;
}

inline void grow()
{
delta *= 2;
if (delta > spsa::aspirationMaxWindow) {
alpha = s_minScore;
beta = s_maxScore;
depthReduction = 0;
}
}

inline uint8_t searchDepth() const
{
/* noia check - don't want to overflow nor search depth 0 */
Comment thread
hansbinderup marked this conversation as resolved.
Comment thread
hansbinderup marked this conversation as resolved.
return depthReduction < depth
? depth - depthReduction
: 1;
}
};

/*
* iterative deeping - with aspiration window
* https://web.archive.org/web/20070705134903/www.seanet.com/%7Ebrucemo/topics/aspiration.htm
*/
constexpr movegen::Move iterativeDeepeningSingle(uint8_t depth, const BitBoard& board)
{
const auto& singleSearcher = m_searchers.front();
Score bestScore = 0;
movegen::Move bestMove;
uint8_t d = 1;

while (d <= depth) {
for (uint8_t d = 1; d <= depth; d++) {
if (!TimeManager::timeForAnotherSearch(d)) {
break;
}

if (m_searchers.size() == 1) {
const auto& singleSearcher = m_searchers.front();
AspirationWindow window(d, bestScore);

while (true) {
Searcher::setSearchStopped(false);
const auto score = singleSearcher->startSearch(d, board, alpha, beta);

/* use previous search if we timed out - it means that the current search was incomplete */
const auto score = singleSearcher->startSearch(window.searchDepth(), board, window.alpha, window.beta);

if (TimeManager::hasTimedOut()) {
break;
}

if ((score <= alpha) || (score >= beta)) {
alpha = s_minScore;
beta = s_maxScore;
if (score <= window.alpha) {
window.widenOnFailLow();
} else if (score >= window.beta) {
window.widenOnFailHigh();
} else {
bestScore = score;
interface::printSearchInfo(singleSearcher, score, d, getNodes(), getTbHits());
bestMove = singleSearcher->getPvMove();
m_ponderMove = singleSearcher->getPonderMove();
TimeManager::updateMoveStability(bestMove, score, pvMoveNodeFraction(bestMove));

continue;
break;
}

/* prepare window for next iteration */
alpha = score - spsa::aspirationWindow;
beta = score + spsa::aspirationWindow;
window.grow();
}
}

return bestMove;
}

interface::printSearchInfo(singleSearcher, score, d, getNodes(), getTbHits());
/* currently using a more primitive iterative deepening with fixed window search
* update to use similar implementation as single threaded search */
constexpr movegen::Move iterativeDeepeningMulti(uint8_t depth, const BitBoard& board)
{
movegen::Move bestMove;
uint8_t d = 1;
Score alpha = s_minScore;
Score beta = s_maxScore;

bestMove = singleSearcher->getPvMove();
m_ponderMove = singleSearcher->getPonderMove();
while (d <= depth) {
if (!TimeManager::timeForAnotherSearch(d)) {
break;
}

TimeManager::updateMoveStability(bestMove, score, pvMoveNodeFraction(bestMove));
} else {
Searcher::setSearchStopped(false);
Searcher::setSearchStopped(false);

/* Thread voting: https://www.chessprogramming.org/Lazy_SMP */
std::array<SearcherResult, s_maxThreads> searchResults {};
uint8_t numSearchResults {};
/* Thread voting: https://www.chessprogramming.org/Lazy_SMP */
std::array<SearcherResult, s_maxThreads> searchResults {};
uint8_t numSearchResults {};

for (auto& searcher : m_searchers) {
searcher->startSearchAsync(m_threadPool, d, board, alpha, beta);
}
for (auto& searcher : m_searchers) {
searcher->startSearchAsync(m_threadPool, d, board, alpha, beta);
}

for (auto& searcher : m_searchers) {
const auto& result = searcher->getSearchResult();
if (result.has_value()) {
// If didn't fall out of window and wasn't an immediate early termination, push back to results
if ((result.value().score > alpha) && (result.value().score < beta) && result.value().searchedDepth > 0) {
searchResults.at(numSearchResults) = result.value();
++numSearchResults;
}
for (auto& searcher : m_searchers) {
const auto& result = searcher->getSearchResult();
if (result.has_value()) {
// If didn't fall out of window and wasn't an immediate early termination, push back to results
if ((result.value().score > alpha) && (result.value().score < beta) && result.value().searchedDepth > 0) {
searchResults.at(numSearchResults) = result.value();
++numSearchResults;
}
}
}

/* use previous search if we timed out - it means that the current search was incomplete */
if (TimeManager::hasTimedOut()) {
break;
}
/* use previous search if we timed out - it means that the current search was incomplete */
if (TimeManager::hasTimedOut()) {
break;
}

if (numSearchResults == 0) {
alpha = s_minScore;
beta = s_maxScore;
if (numSearchResults == 0) {
alpha = s_minScore;
beta = s_maxScore;

continue;
}
continue;
}

m_movesVotes.clear();
m_movesVotes.clear();

for (uint8_t i = 0; i < numSearchResults; i++) {
for (uint8_t i = 0; i < numSearchResults; i++) {

const auto& result = searchResults.at(i);
const auto& result = searchResults.at(i);

const Score score = result.score;
const uint8_t depth = result.searchedDepth;
const auto move = result.pvMove;
const Score score = result.score;
const uint8_t depth = result.searchedDepth;
const auto move = result.pvMove;

/* Can be tweaked for optimization */
int64_t voteWeight = (score - s_minScore) * depth;
/* Can be tweaked for optimization */
int64_t voteWeight = (score - s_minScore) * depth;

m_movesVotes.insertOrIncrement(move, voteWeight);
}
m_movesVotes.insertOrIncrement(move, voteWeight);
}

int64_t maxVote = -std::numeric_limits<int64_t>::max();
int64_t maxVote = -std::numeric_limits<int64_t>::max();

for (const auto& [move, vote] : m_movesVotes) {
if (vote > maxVote) {
maxVote = vote;
bestMove = move;
}
for (const auto& [move, vote] : m_movesVotes) {
if (vote > maxVote) {
maxVote = vote;
bestMove = move;
}
}

uint8_t bestWinningDepth = 0;
SearcherResult bestWinningResult;
uint8_t bestWinningDepth = 0;
SearcherResult bestWinningResult;

for (uint8_t i = 0; i < numSearchResults; i++) {
if (searchResults.at(i).pvMove == bestMove) {
if (depth > bestWinningDepth) {
bestWinningResult = searchResults.at(i);
bestWinningDepth = depth;
}
for (uint8_t i = 0; i < numSearchResults; i++) {
if (searchResults.at(i).pvMove == bestMove) {
if (depth > bestWinningDepth) {
bestWinningResult = searchResults.at(i);
bestWinningDepth = depth;
}
}
}

/* prepare window for next iteration */
alpha = bestWinningResult.score - spsa::aspirationWindow;
beta = bestWinningResult.score + spsa::aspirationWindow;
/* prepare window for next iteration */
alpha = bestWinningResult.score - spsa::aspirationWindow;
beta = bestWinningResult.score + spsa::aspirationWindow;

const auto searcher = bestWinningResult.searcher.lock();
if (!searcher) {
/* should not happen during a search - have we been stopped? */
break;
}
const auto searcher = bestWinningResult.searcher.lock();
if (!searcher) {
/* should not happen during a search - have we been stopped? */
break;
}

interface::printSearchInfo(searcher, bestWinningResult.score, d, getNodes(), getTbHits());
m_ponderMove = searcher->getPonderMove();
interface::printSearchInfo(searcher, bestWinningResult.score, d, getNodes(), getTbHits());
m_ponderMove = searcher->getPonderMove();

TimeManager::updateMoveStability(bestMove, bestWinningResult.score, pvMoveNodeFraction(bestMove));
}
TimeManager::updateMoveStability(bestMove, bestWinningResult.score, pvMoveNodeFraction(bestMove));

/* only increment depth, if we didn't fall out of the window */
d++;
}

/* in case we're scanning to a certain depth we need to ensure that we're stopping the time handler */
stop();

return bestMove;
}

Expand Down
2 changes: 2 additions & 0 deletions src/spsa/parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
TUNABLE(seeNoisyMargin, uint8_t, 18, 0, 100, 5) \
TUNABLE(seeDepthLimit, uint8_t, 10, 0, 15, 1) \
TUNABLE(aspirationWindow, uint8_t, 81, 10, 100, 5) \
TUNABLE(aspirationMinDepth, uint8_t, 4, 1, 10, 1) \
TUNABLE(aspirationMaxWindow, uint16_t, 500, 200, 1000, 50) \
TUNABLE(pawnCorrectionWeight, uint16_t, 404, 100, 500, 25) \
TUNABLE(materialCorrectionWeight, uint16_t, 526, 500, 1500, 50) \
TUNABLE(threatCorrectionWeight, uint16_t, 580, 250, 1000, 25) \
Expand Down
Loading