Skip to content

Commit

Permalink
clean up LMR, store more things in TT, less template to get a smaller…
Browse files Browse the repository at this point in the history
… executable
  • Loading branch information
tryingsomestuff committed Jul 11, 2020
1 parent d0744cd commit 58ab7c4
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 203 deletions.
2 changes: 1 addition & 1 deletion Fathom
Submodule Fathom updated 1 files
+1 −1 src/tbprobe.c
2 changes: 1 addition & 1 deletion Source/definition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ typedef uint64_t u_int64_t;
#include <unistd.h>
#endif

const std::string MinicVersion = "2.38";
const std::string MinicVersion = "2.40";

// *** options
#define WITH_UCI
Expand Down
4 changes: 2 additions & 2 deletions Source/searcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ struct Searcher{

ScoreType drawScore();

template <bool pvnode, bool canPrune> ScoreType pvs(ScoreType alpha, ScoreType beta, const Position & p, DepthType depth, unsigned int ply, PVList & pv, DepthType & seldepth, bool isInCheck, bool cutNode, const std::vector<MiniMove> * skipMoves = nullptr);
template <bool qRoot, bool pvnode> ScoreType qsearch(ScoreType alpha, ScoreType beta, const Position & p, unsigned int ply, DepthType & seldepth, unsigned int qply);
template <bool pvnode> ScoreType pvs(ScoreType alpha, ScoreType beta, const Position & p, DepthType depth, unsigned int ply, PVList & pv, DepthType & seldepth, bool isInCheck, bool cutNode, bool canPrune, const std::vector<MiniMove> * skipMoves = nullptr);
ScoreType qsearch(ScoreType alpha, ScoreType beta, const Position & p, unsigned int ply, DepthType & seldepth, unsigned int qply, bool qRoot, bool pvnode, char isInCheckHint = -1);
ScoreType qsearchNoPruning(ScoreType alpha, ScoreType beta, const Position & p, unsigned int ply, DepthType & seldepth);
bool SEE_GE(const Position & p, const Move & m, ScoreType threshold)const;
ScoreType SEE(const Position & p, const Move & m)const;
Expand Down
4 changes: 2 additions & 2 deletions Source/searcherDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ PVList Searcher::search(const Position & p, Move & m, DepthType & d, ScoreType &
&& currentMoveMs < INFINITETIME && currentMoveMs > 800 && TimeMan::msecUntilNextTC > 0){
// easy move detection (small open window search)
rootScores.clear();
ScoreType easyScore = pvs<true,false>(-MATE, MATE, p, easyMoveDetectionDepth, 0, pvOut, seldepth, isInCheck,false);
ScoreType easyScore = pvs<true>(-MATE, MATE, p, easyMoveDetectionDepth, 0, pvOut, seldepth, isInCheck,false,false);
std::sort(rootScores.begin(), rootScores.end(), [](const RootScores& r1, const RootScores & r2) {return r1.s > r2.s; });
if (stopFlag) { // no more time, this is strange ...
bestScore = easyScore;
Expand Down Expand Up @@ -166,7 +166,7 @@ PVList Searcher::search(const Position & p, Move & m, DepthType & d, ScoreType &

pvLoc.clear();
stack[p.halfmoves].h = p.h;
score = pvs<true,false>(alpha,beta,p,windowDepth,0,pvLoc,seldepth,isInCheck,false,skipMoves.empty()?nullptr:&skipMoves);
score = pvs<true>(alpha,beta,p,windowDepth,0,pvLoc,seldepth,isInCheck,false,false,skipMoves.empty()?nullptr:&skipMoves);
if ( stopFlag ) break;
delta += 2 + delta/2; // from xiphos ...
if (alpha > -MATE && score <= alpha) {
Expand Down
65 changes: 35 additions & 30 deletions Source/searcherPVS.hpp

Large diffs are not rendered by default.

162 changes: 162 additions & 0 deletions Source/searcherQSearch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,165 @@ ScoreType Searcher::qsearchNoPruning(ScoreType alpha, ScoreType beta, const Posi
}
return bestScore;
}

inline ScoreType qDeltaMargin(const Position & p) {
ScoreType delta = (p.pieces_const(p.c,P_wp) & seventhRank[p.c]) ? Values[P_wq+PieceShift] : Values[P_wp+PieceShift];
return delta + Values[P_wq+PieceShift];
}

ScoreType Searcher::qsearch(ScoreType alpha, ScoreType beta, const Position & p, unsigned int ply, DepthType & seldepth, unsigned int qply, bool qRoot, bool pvnode, char isInCheckHint){
if (stopFlag) return STOPSCORE; // no time verification in qsearch, too slow
++stats.counters[Stats::sid_qnodes];

alpha = std::max(alpha, (ScoreType)(-MATE + ply));
beta = std::min(beta , (ScoreType)( MATE - ply + 1));
if (alpha >= beta) return alpha;

if ((int)ply > seldepth) seldepth = ply;

EvalData data;
if (ply >= MAX_DEPTH - 1) return eval(p, data, *this);
Move bestMove = INVALIDMOVE;

debug_king_cap(p);

// probe TT
TT::Entry e;
const Hash pHash = computeHash(p);
const bool ttDepthOk = TT::getEntry(*this, p, pHash, 0, e);
const TT::Bound bound = TT::Bound(e.b & ~TT::B_allFlags);
const bool ttHit = e.h != nullHash;
const bool validTTmove = ttHit && e.m != INVALIDMINIMOVE;
const bool ttPV = pvnode || (validTTmove && (e.b&TT::B_ttFlag));
const bool ttIsInCheck = validTTmove && (e.b&TT::B_isInCheckFlag);
const bool isInCheck = isInCheckHint!=-1 ? isInCheckHint : ttIsInCheck || isAttacked(p, kingSquare(p));
const bool specialQSearch = isInCheck || qRoot;
const DepthType hashDepth = specialQSearch ? 0 : -1;
if (ttDepthOk && e.d >= hashDepth) { // if depth of TT entry is enough
if (!pvnode && ((bound == TT::B_alpha && e.s <= alpha) || (bound == TT::B_beta && e.s >= beta) || (bound == TT::B_exact))) {
return adjustHashScore(e.s, ply);
}
}
if ( qRoot && interiorNodeRecognizer<true,false,true>(p) == MaterialHash::Ter_Draw) return drawScore(); ///@todo is that gain elo ???

if ( validTTmove && (isInCheck || isCapture(e.m)) ) bestMove = e.m;

// get a static score for the position.
ScoreType evalScore;
if (isInCheck) evalScore = -MATE + ply;
else if ( p.lastMove == NULLMOVE && ply > 0 ) evalScore = 2*ScaleScore(EvalConfig::tempo,stack[p.halfmoves-1].data.gp) - stack[p.halfmoves-1].eval; // skip eval if nullmove just applied ///@todo wrong ! gp is 0 here so tempoMG must be == tempoEG
else{
if (ttHit){ // if we had a TT hit (with or without associated move), wa can use its eval instead of calling eval()
++stats.counters[Stats::sid_ttschits];
evalScore = e.e;
/*
const Hash matHash = MaterialHash::getMaterialHash(p.mat);
if ( matHash ){
++stats.counters[Stats::sid_materialTableHits];
const MaterialHash::MaterialHashEntry & MEntry = MaterialHash::materialHashTable[matHash];
data.gp = MEntry.gp;
}
else{
ScoreType matScoreW = 0;
ScoreType matScoreB = 0;
data.gp = gamePhase(p,matScoreW,matScoreB);
++stats.counters[Stats::sid_materialTableMiss];
}
*/
data.gp = 0.5; // force mid game value in sorting ... affect only quiet move, so here check evasion ...
}
else {
++stats.counters[Stats::sid_ttscmiss];
evalScore = eval(p, data, *this);
}
}
const ScoreType staticScore = evalScore;
bool evalScoreIsHashScore = false;
// use tt score if possible and not in check
if ( !isInCheck){
if ( ttHit && ((bound == TT::B_alpha && e.s <= evalScore) || (bound == TT::B_beta && e.s >= evalScore) || (bound == TT::B_exact)) ) evalScore = e.s, evalScoreIsHashScore = true;
if ( !ttHit ) TT::setEntry(*this,pHash,INVALIDMOVE,createHashScore(evalScore,ply),createHashScore(evalScore,ply),TT::B_none,-2); // already insert an eval here in case of pruning ...
}

// early cut-off based on eval score (static or from TT score)
if ( evalScore >= beta ){
//if ( !ttHit ) TT::setEntry(*this,pHash,INVALIDMOVE,createHashScore(evalScore,ply),createHashScore(evalScore,ply),TT::B_none,-2);
return evalScore;
}
if ( !isInCheck && SearchConfig::doQDeltaPruning && staticScore + qDeltaMargin(p) < alpha ) return ++stats.counters[Stats::sid_delta],alpha;
if ( evalScore > alpha) alpha = evalScore;

TT::Bound b = TT::B_alpha;
ScoreType bestScore = evalScore;
const ScoreType alphaInit = alpha;
int validMoveCount = 0;

// try the tt move before move generation
if ( validTTmove && (isInCheck || isCapture(e.m)) ){
Position p2 = p;
if ( apply(p2,e.m) ){;
++validMoveCount;
TT::prefetch(computeHash(p2));
const ScoreType score = -qsearch(-beta,-alpha,p2,ply+1,seldepth,isInCheck?0:qply+1,false,false);
if ( score > bestScore){
bestMove = e.m;
bestScore = score;
if ( score > alpha ){
if (score >= beta) {
b = TT::B_beta;
TT::setEntry(*this,pHash,bestMove,createHashScore(bestScore,ply),createHashScore(evalScore,ply),TT::Bound(b|(ttPV?TT::B_ttFlag:TT::B_none)|(isInCheck?TT::B_isInCheckFlag:TT::B_none)),hashDepth);
return bestScore;
}
b = TT::B_exact;
alpha = score;
}
}
}
}

MoveList moves;
if ( isInCheck ) MoveGen::generate<MoveGen::GP_all>(p,moves); ///@todo generate only evasion !
else MoveGen::generate<MoveGen::GP_cap>(p,moves);
CMHPtrArray cmhPtr;
getCMHPtr(p.halfmoves,cmhPtr);

const Square recapture = VALIDMOVE(p.lastMove) ? Move2To(p.lastMove) : INVALIDSQUARE;
const bool onlyRecapture = qply > 5 && isCapture(p.lastMove) && recapture != INVALIDSQUARE;

#ifdef USE_PARTIAL_SORT
MoveSorter::score(*this,moves,p,data.gp,ply,cmhPtr,false,isInCheck,validTTmove?&e:NULL); ///@todo warning gp is often = 0.5 here !
size_t offset = 0;
const Move * it = nullptr;
while( (it = MoveSorter::pickNext(moves,offset))){
#else
MoveSorter::scoreAndSort(*this,moves,p,data.gp,ply,cmhPtr,false,isInCheck,validTTmove?&e:NULL); ///@todo warning gp is often = 0.5 here !
for(auto it = moves.begin() ; it != moves.end() ; ++it){
#endif
if (validTTmove && sameMove(e.m, *it)) continue; // already tried
if (!isInCheck) {
if (onlyRecapture && Move2To(*it) != recapture ) continue; // only recapture now ...
if (!SEE_GE(p,*it,0)) {++stats.counters[Stats::sid_qsee];continue;}
if (SearchConfig::doQFutility && staticScore + SearchConfig::qfutilityMargin[evalScoreIsHashScore] + (isPromotionCap(*it) ? (Values[P_wq+PieceShift]-Values[P_wp+PieceShift]) : 0 ) + (Move2Type(*it)==T_ep ? Values[P_wp+PieceShift] : PieceTools::getAbsValue(p, Move2To(*it))) <= alphaInit) {++stats.counters[Stats::sid_qfutility];continue;}
}
Position p2 = p;
if ( ! apply(p2,*it) ) continue;
++validMoveCount;
TT::prefetch(computeHash(p2));
const ScoreType score = -qsearch(-beta,-alpha,p2,ply+1,seldepth,isInCheck?0:qply+1,false,false);
if ( score > bestScore){
bestMove = *it;
bestScore = score;
if ( score > alpha ){
if (score >= beta) {
b = TT::B_beta;
break;
}
b = TT::B_exact;
alpha = score;
}
}
}
if ( validMoveCount==0 && isInCheck) bestScore = -MATE + ply;
TT::setEntry(*this,pHash,bestMove,createHashScore(bestScore,ply),createHashScore(evalScore,ply),TT::Bound(b|(ttPV?TT::B_ttFlag:TT::B_none)|(isInCheck?TT::B_isInCheckFlag:TT::B_none)),hashDepth);
return bestScore;
}
165 changes: 0 additions & 165 deletions Source/searcherQSearch.hpp
Original file line number Diff line number Diff line change
@@ -1,166 +1 @@
#pragma once

inline ScoreType qDeltaMargin(const Position & p) {
ScoreType delta = (p.pieces_const(p.c,P_wp) & seventhRank[p.c]) ? Values[P_wq+PieceShift] : Values[P_wp+PieceShift];
return delta + Values[P_wq+PieceShift];
}

///@todo only recapture after a while

template < bool qRoot, bool pvnode >
ScoreType Searcher::qsearch(ScoreType alpha, ScoreType beta, const Position & p, unsigned int ply, DepthType & seldepth, unsigned int qply){
if (stopFlag) return STOPSCORE; // no time verification in qsearch, too slow
++stats.counters[Stats::sid_qnodes];

alpha = std::max(alpha, (ScoreType)(-MATE + ply));
beta = std::min(beta , (ScoreType)( MATE - ply + 1));
if (alpha >= beta) return alpha;

if ((int)ply > seldepth) seldepth = ply;

EvalData data;
if (ply >= MAX_DEPTH - 1) return eval(p, data, *this);
Move bestMove = INVALIDMOVE;

const bool isInCheck = isAttacked(p, kingSquare(p));
const bool specialQSearch = isInCheck || qRoot;
DepthType hashDepth = specialQSearch ? 0 : -1;

debug_king_cap(p);

// probe TT
TT::Entry e;
const Hash pHash = computeHash(p);
const bool ttDepthOk = TT::getEntry(*this, p, pHash, hashDepth, e);
TT::Bound bound = TT::Bound(e.b & ~TT::B_ttFlag);
if (ttDepthOk) { // if depth of TT entry is enough
if (!pvnode && ((bound == TT::B_alpha && e.s <= alpha) || (bound == TT::B_beta && e.s >= beta) || (bound == TT::B_exact))) {
return adjustHashScore(e.s, ply);
}
}
if ( qRoot && interiorNodeRecognizer<true,false,true>(p) == MaterialHash::Ter_Draw) return drawScore(); ///@todo is that gain elo ???

const bool ttHit = e.h != nullHash;
const bool validTTmove = ttHit && e.m != INVALIDMINIMOVE;
const bool ttPV = pvnode || (validTTmove && (e.b&TT::B_ttFlag));
if ( validTTmove && (isInCheck || isCapture(e.m)) ) bestMove = e.m;

// get a static score for the position.
ScoreType evalScore;
if (isInCheck) evalScore = -MATE + ply;
else if ( p.lastMove == NULLMOVE && ply > 0 ) evalScore = 2*ScaleScore(EvalConfig::tempo,stack[p.halfmoves-1].data.gp) - stack[p.halfmoves-1].eval; // skip eval if nullmove just applied ///@todo wrong ! gp is 0 here so tempoMG must be == tempoEG
else{
if (ttHit){ // if we had a TT hit (with or without associated move), wa can use its eval instead of calling eval()
++stats.counters[Stats::sid_ttschits];
evalScore = e.e;
/*
const Hash matHash = MaterialHash::getMaterialHash(p.mat);
if ( matHash ){
++stats.counters[Stats::sid_materialTableHits];
const MaterialHash::MaterialHashEntry & MEntry = MaterialHash::materialHashTable[matHash];
data.gp = MEntry.gp;
}
else{
ScoreType matScoreW = 0;
ScoreType matScoreB = 0;
data.gp = gamePhase(p,matScoreW,matScoreB);
++stats.counters[Stats::sid_materialTableMiss];
}
*/
data.gp = 0.5; // force mid game value in sorting ... affect only quiet move, so here check evasion ...
}
else {
++stats.counters[Stats::sid_ttscmiss];
evalScore = eval(p, data, *this);
}
}
const ScoreType staticScore = evalScore;
bool evalScoreIsHashScore = false;
// use tt score if possible and not in check
if ( !isInCheck){
if ( ttHit && ((bound == TT::B_alpha && e.s <= evalScore) || (bound == TT::B_beta && e.s >= evalScore) || (bound == TT::B_exact)) ) evalScore = e.s, evalScoreIsHashScore = true;
if ( !ttHit ) TT::setEntry(*this,pHash,INVALIDMOVE,createHashScore(evalScore,ply),createHashScore(evalScore,ply),TT::B_none,-2); // already insert an eval here in case of pruning ...
}

// early cut-off based on eval score (static or from TT score)
if ( evalScore >= beta ){
//if ( !ttHit ) TT::setEntry(*this,pHash,INVALIDMOVE,createHashScore(evalScore,ply),createHashScore(evalScore,ply),TT::B_none,-2);
return evalScore;
}
if ( !isInCheck && SearchConfig::doQDeltaPruning && staticScore + qDeltaMargin(p) < alpha ) return ++stats.counters[Stats::sid_delta],alpha;
if ( evalScore > alpha) alpha = evalScore;

TT::Bound b = TT::B_alpha;
ScoreType bestScore = evalScore;
const ScoreType alphaInit = alpha;
int validMoveCount = 0;

// try the tt move before move generation
if ( validTTmove && (isInCheck || isCapture(e.m)) ){
Position p2 = p;
if ( apply(p2,e.m) ){;
++validMoveCount;
TT::prefetch(computeHash(p2));
const ScoreType score = -qsearch<false,false>(-beta,-alpha,p2,ply+1,seldepth,isInCheck?0:qply+1);
if ( score > bestScore){
bestMove = e.m;
bestScore = score;
if ( score > alpha ){
if (score >= beta) {
b = TT::B_beta;
TT::setEntry(*this,pHash,bestMove,createHashScore(bestScore,ply),createHashScore(evalScore,ply),TT::Bound(b|(ttPV?TT::B_ttFlag:TT::B_none)),hashDepth);
return bestScore;
}
b = TT::B_exact;
alpha = score;
}
}
}
}

MoveList moves;
if ( isInCheck ) MoveGen::generate<MoveGen::GP_all>(p,moves); ///@todo generate only evasion !
else MoveGen::generate<MoveGen::GP_cap>(p,moves);
CMHPtrArray cmhPtr;
getCMHPtr(p.halfmoves,cmhPtr);

const Square recapture = VALIDMOVE(p.lastMove) ? Move2To(p.lastMove) : INVALIDSQUARE;
const bool onlyRecapture = qply > 5 && isCapture(p.lastMove) && recapture != INVALIDSQUARE;

#ifdef USE_PARTIAL_SORT
MoveSorter::score(*this,moves,p,data.gp,ply,cmhPtr,false,isInCheck,validTTmove?&e:NULL); ///@todo warning gp is often = 0.5 here !
size_t offset = 0;
const Move * it = nullptr;
while( (it = MoveSorter::pickNext(moves,offset))){
#else
MoveSorter::scoreAndSort(*this,moves,p,data.gp,ply,cmhPtr,false,isInCheck,validTTmove?&e:NULL); ///@todo warning gp is often = 0.5 here !
for(auto it = moves.begin() ; it != moves.end() ; ++it){
#endif
if (validTTmove && sameMove(e.m, *it)) continue; // already tried
if (!isInCheck) {
if (onlyRecapture && Move2To(*it) != recapture ) continue; // only recapture now ...
if (!SEE_GE(p,*it,0)) {++stats.counters[Stats::sid_qsee];continue;}
if (SearchConfig::doQFutility && staticScore + SearchConfig::qfutilityMargin[evalScoreIsHashScore] + (isPromotionCap(*it) ? (Values[P_wq+PieceShift]-Values[P_wp+PieceShift]) : 0 ) + (Move2Type(*it)==T_ep ? Values[P_wp+PieceShift] : PieceTools::getAbsValue(p, Move2To(*it))) <= alphaInit) {++stats.counters[Stats::sid_qfutility];continue;}
}
Position p2 = p;
if ( ! apply(p2,*it) ) continue;
++validMoveCount;
TT::prefetch(computeHash(p2));
const ScoreType score = -qsearch<false,false>(-beta,-alpha,p2,ply+1,seldepth,isInCheck?0:qply+1);
if ( score > bestScore){
bestMove = *it;
bestScore = score;
if ( score > alpha ){
if (score >= beta) {
b = TT::B_beta;
break;
}
b = TT::B_exact;
alpha = score;
}
}
}
if ( validMoveCount==0 && isInCheck) bestScore = -MATE + ply;
TT::setEntry(*this,pHash,bestMove,createHashScore(bestScore,ply),createHashScore(evalScore,ply),TT::Bound(b|(ttPV?TT::B_ttFlag:TT::B_none)),hashDepth);
return bestScore;
}
4 changes: 2 additions & 2 deletions Source/transposition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct Searcher;
namespace TT{

extern GenerationType curGen;
enum Bound : unsigned char{ B_none = 0, B_alpha = 1, B_beta = 2, B_exact = 3, B_ttFlag = 4};
enum Bound : unsigned char{ B_none = 0, B_alpha = 1, B_beta = 2, B_exact = 3, B_ttFlag = 4, B_isCheckFlag = 8, B_isInCheckFlag = 16, B_allFlags = B_ttFlag|B_isCheckFlag|B_isInCheckFlag};
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
Expand All @@ -28,7 +28,7 @@ enum Bound : unsigned char{ B_none = 0, B_alpha = 1, B_beta = 2, B_exact = 3, B_
#endif // defined(__clang__)
#pragma pack(push, 1)
struct Entry{
Entry():m(INVALIDMINIMOVE),h(0),s(0),e(0),b(B_none),d(-1)/*,generation(curGen)*/{}
Entry():m(INVALIDMINIMOVE),h(0),s(0),e(0),b(B_none),d(-2)/*,generation(curGen)*/{}
Entry(Hash _h, Move _m, ScoreType _s, ScoreType _e, Bound _b, DepthType _d) : h(Hash64to32(_h)), m(Move2MiniMove(_m)), s(_s), e(_e), /*generation(curGen),*/ b(_b), d(_d){}
MiniHash h; //32
ScoreType s, e; //16 + 16
Expand Down

0 comments on commit 58ab7c4

Please sign in to comment.