diff --git a/src/movegen.c b/src/movegen.c index 47294527..45c58e1d 100644 --- a/src/movegen.c +++ b/src/movegen.c @@ -564,8 +564,9 @@ void GenerateAllMoves(MoveList* moveList, Board* board, SearchData* data) { } // Get the hash move for sorting - TTValue ttValue = TTProbe(board->zobrist); // TODO: Don't I know this already from the search? - Move hashMove = TTMove(ttValue); + int ttHit; + TTEntry* tt = TTProbe(&ttHit, board->zobrist); // TODO: Don't I know this already from the search? + Move hashMove = ttHit ? tt->move : NULL_MOVE; for (int i = 0; i < moveList->count; i++) { Move move = moveList->moves[i]; diff --git a/src/search.c b/src/search.c index d823a9bb..d61afb71 100644 --- a/src/search.c +++ b/src/search.c @@ -212,15 +212,16 @@ int Negamax(int alpha, int beta, int depth, ThreadData* thread, PV* pv) { // check the transposition table for previous info // we ignore the tt on singular extension searches - TTValue ttValue = skipMove ? NO_ENTRY : TTProbe(board->zobrist); + int ttHit = 0; + TTEntry* tt = skipMove ? NULL : TTProbe(&ttHit, board->zobrist); // if the TT has a value that fits our position and has been searched to an equal or greater depth, then we accept // this score and prune - if (!isPV && ttValue && TTDepth(ttValue) >= depth) { - int ttScore = TTScore(ttValue, data->ply); - int flag = TTFlag(ttValue); + if (!isPV && ttHit && tt->depth >= depth) { + int ttScore = TTScore(tt, data->ply); - if (flag == TT_EXACT || (flag == TT_LOWER && ttScore >= beta) || (flag == TT_UPPER && ttScore <= alpha)) + if ((tt->flags & TT_EXACT) || ((tt->flags & TT_LOWER) && ttScore >= beta) || + ((tt->flags & TT_UPPER) && ttScore <= alpha)) return ttScore; } @@ -248,14 +249,14 @@ int Negamax(int alpha, int beta, int depth, ThreadData* thread, PV* pv) { } // if the tablebase gives us what we want, then we accept it's score and return - if (flag == TT_EXACT || (flag == TT_LOWER && score >= beta) || (flag == TT_UPPER && score <= alpha)) { + if ((flag & TT_EXACT) || ((flag & TT_LOWER) && score >= beta) || ((flag & TT_UPPER) && score <= alpha)) { TTPut(board->zobrist, depth, score, flag, 0, data->ply, 0); return score; } // for pv node searches we adjust our a/b search accordingly if (isPV) { - if (flag == TT_LOWER) { + if (flag & TT_LOWER) { bestScore = score; alpha = max(alpha, score); } else @@ -265,7 +266,7 @@ int Negamax(int alpha, int beta, int depth, ThreadData* thread, PV* pv) { } // pull previous static eval from tt - this is depth independent - int eval = data->evals[data->ply] = (ttValue ? TTEval(ttValue) : Evaluate(board, thread)); + int eval = data->evals[data->ply] = (ttHit ? tt->eval : Evaluate(board, thread)); // getting better if eval has gone up int improving = data->ply >= 2 && (data->evals[data->ply] > data->evals[data->ply - 2]); @@ -276,9 +277,9 @@ int Negamax(int alpha, int beta, int depth, ThreadData* thread, PV* pv) { if (!isPV && !board->checkers) { // Our TT might have a more accurate evaluation score, use this - if (ttValue && TTDepth(ttValue) >= depth) { - int ttScore = TTScore(ttValue, data->ply); - if (TTFlag(ttValue) == (ttScore > eval ? TT_LOWER : TT_UPPER)) + if (ttHit && tt->depth >= depth) { + int ttScore = TTScore(tt, data->ply); + if (tt->flags & (ttScore > eval ? TT_LOWER : TT_UPPER)) eval = ttScore; } @@ -311,7 +312,7 @@ int Negamax(int alpha, int beta, int depth, ThreadData* thread, PV* pv) { // less than beta + margin, then we run a shallow search to look int probBeta = beta + 100; if (depth > 4 && abs(beta) < MATE_BOUND && - !(ttValue && TTDepth(ttValue) >= depth - 3 && TTScore(ttValue, data->ply) < probBeta)) { + !(ttHit && tt->depth >= depth - 3 && TTScore(tt, data->ply) < probBeta)) { MoveList moveList; GenerateTacticalMoves(&moveList, board); @@ -339,7 +340,7 @@ int Negamax(int alpha, int beta, int depth, ThreadData* thread, PV* pv) { // IIR by Ed Schroder // http://talkchess.com/forum3/viewtopic.php?f=7&t=74769&sid=64085e3396554f0fba414404445b3120 - if (depth >= 4 && !ttValue && !skipMove) + if (depth >= 4 && !ttHit && !skipMove) depth--; MoveList moveList; @@ -385,9 +386,9 @@ int Negamax(int alpha, int beta, int depth, ThreadData* thread, PV* pv) { // moves at a shallow depth on a nullwindow that is somewhere below the tt evaluation // implemented using "skip move" recursion like in SF (allows for reductions when doing singular search) int extension = 0; - if (depth >= 8 && !skipMove && !isRoot && move == TTMove(ttValue) && TTDepth(ttValue) >= depth - 3 && - abs(TTScore(ttValue, data->ply)) < MATE_BOUND && TTFlag(ttValue) == TT_LOWER) { - int sBeta = max(TTScore(ttValue, data->ply) - depth * 2, -CHECKMATE); + if (depth >= 8 && !skipMove && !isRoot && ttHit && move == tt->move && tt->depth >= depth - 3 && + abs(TTScore(tt, data->ply)) < MATE_BOUND && (tt->flags & TT_LOWER)) { + int sBeta = max(TTScore(tt, data->ply) - depth * 2, -CHECKMATE); int sDepth = depth / 2 - 1; data->skipMove[data->ply] = move; @@ -529,23 +530,23 @@ int Quiesce(int alpha, int beta, ThreadData* thread, PV* pv) { return Evaluate(board, thread); // check the transposition table for previous info - TTValue ttValue = TTProbe(board->zobrist); + int ttHit = 0; + TTEntry* tt = TTProbe(&ttHit, board->zobrist); // TT score pruning - no depth check required since everything in QS is depth 0 - if (ttValue) { - int score = TTScore(ttValue, data->ply); - int flag = TTFlag(ttValue); - - if (flag == TT_EXACT || (flag == TT_LOWER && score >= beta) || (flag == TT_UPPER && score <= alpha)) - return score; + if (ttHit) { + int ttScore = TTScore(tt, data->ply); + if ((tt->flags & TT_EXACT) || ((tt->flags & TT_LOWER) && ttScore >= beta) || + ((tt->flags & TT_UPPER) && ttScore <= alpha)) + return ttScore; } // pull cached eval if it exists - int eval = data->evals[data->ply] = (ttValue ? TTEval(ttValue) : Evaluate(board, thread)); + int eval = data->evals[data->ply] = (ttHit ? tt->eval : Evaluate(board, thread)); // can we use an improved evaluation from the tt? - if (ttValue) { - int ttEval = TTScore(ttValue, data->ply); - if (TTFlag(ttValue) == (ttEval > eval ? TT_LOWER : TT_UPPER)) + if (ttHit) { + int ttEval = TTScore(tt, data->ply); + if (tt->flags & (ttEval > eval ? TT_LOWER : TT_UPPER)) eval = ttEval; } diff --git a/src/transposition.c b/src/transposition.c index 2bd4b295..8feba764 100644 --- a/src/transposition.c +++ b/src/transposition.c @@ -40,7 +40,7 @@ size_t TTInit(int mb) { uint64_t keySize = (uint64_t)log2(mb) + (uint64_t)log2(MEGABYTE / sizeof(TTBucket)); #if defined(__linux__) && !defined(__ANDROID__) - // On Linux systems we align on 2MB boundaries and request Huge Pages + // On Linux systems we align on 2MB boundaries and request Huge Pages TT.buckets = aligned_alloc(2 * MEGABYTE, (1ULL << keySize) * sizeof(TTBucket)); madvise(TT.buckets, (1ULL << keySize) * sizeof(TTBucket), MADV_HUGEPAGE); #else @@ -57,56 +57,56 @@ void TTFree() { free(TT.buckets); } inline void TTClear() { memset(TT.buckets, 0, (TT.mask + 1ULL) * sizeof(TTBucket)); } -inline int TTScore(TTValue value, int ply) { - int score = (int)((int16_t)((value & 0x0000FFFF00000000) >> 32)); - - return score > MATE_BOUND ? score - ply : score < -MATE_BOUND ? score + ply : score; +inline int TTScore(TTEntry* e, int ply) { + return e->score > MATE_BOUND ? e->score - ply : e->score < -MATE_BOUND ? e->score + ply : e->score; } inline void TTPrefetch(uint64_t hash) { __builtin_prefetch(&TT.buckets[TT.mask & hash]); } -inline TTValue TTProbe(uint64_t hash) { -#ifdef TUNE - return NO_ENTRY; -#else - TTBucket bucket = TT.buckets[TT.mask & hash]; +inline TTEntry* TTProbe(int* hit, uint64_t hash) { +#ifndef TUNE + TTBucket* bucket = &TT.buckets[TT.mask & hash]; + uint32_t shortHash = hash >> 32; for (int i = 0; i < BUCKET_SIZE; i++) - if (bucket.entries[i].hash == hash) - return bucket.entries[i].value; - - return NO_ENTRY; + if (bucket->entries[i].hash == shortHash) { + *hit = 1; + return &bucket->entries[i]; + } #endif + + return 0; } -inline TTValue TTPut(uint64_t hash, int depth, int score, int flag, Move move, int ply, int eval) { +inline void TTPut(uint64_t hash, uint8_t depth, int16_t score, uint8_t flag, Move move, int ply, int16_t eval) { #ifdef TUNE - return NO_ENTRY; + return; #else - TTBucket* bucket = &TT.buckets[TT.mask & hash]; + TTBucket bucket = TT.buckets[TT.mask & hash]; + uint32_t shortHash = hash >> 32; + int replacementDepth = INT32_MAX; int replacementIdx = 0; for (int i = 0; i < BUCKET_SIZE; i++) { - TTEntry entry = bucket->entries[i]; + TTEntry entry = bucket.entries[i]; if (!entry.hash) { replacementIdx = i; break; } - int currDepth = TTDepth(entry.value); - if (entry.hash == hash) { - if (currDepth > depth && flag != TT_EXACT) - return entry.value; + if (entry.hash == shortHash) { + if (entry.depth > depth && !(flag & TT_EXACT)) + return; replacementIdx = i; break; } - if (currDepth < replacementDepth) { + if (entry.depth < replacementDepth) { replacementIdx = i; - replacementDepth = currDepth; + replacementDepth = entry.depth; } } @@ -116,10 +116,8 @@ inline TTValue TTPut(uint64_t hash, int depth, int score, int flag, Move move, i else if (score < -MATE_BOUND) adjustedScore -= ply; - bucket->entries[replacementIdx].hash = hash; - TTValue tt = bucket->entries[replacementIdx].value = TTEntry(adjustedScore, flag, depth, move, eval); - - return tt; + TTEntry* entry = &bucket.entries[replacementIdx]; + *entry = (TTEntry){.flags = flag, .depth = depth, .eval = eval, .score = score, .hash = shortHash, .move = move}; #endif } diff --git a/src/transposition.h b/src/transposition.h index 76acc979..a3f8e966 100644 --- a/src/transposition.h +++ b/src/transposition.h @@ -19,17 +19,17 @@ #include "types.h" - - #define NO_ENTRY 0ULL #define MEGABYTE 0x100000ULL #define BUCKET_SIZE 3 -typedef uint64_t TTValue; - typedef struct { - uint64_t hash; - TTValue value; + uint8_t flags; + uint8_t depth; + int16_t eval; + int16_t score; + uint32_t hash; + Move move; } TTEntry; typedef struct { @@ -44,25 +44,17 @@ typedef struct { uint64_t size; } TTTable; -enum { TT_LOWER, TT_UPPER, TT_EXACT }; +enum { TT_LOWER = 1, TT_UPPER = 2, TT_EXACT = 4 }; extern TTTable TT; -#define TTEntry(score, flag, depth, move, eval) \ - ((TTValue)((uint16_t)(eval)) << 48) | ((TTValue)((uint16_t)(score)) << 32) | ((TTValue)(flag) << 30) | \ - ((TTValue)(depth) << 24) | ((TTValue)(move)) -#define TTFlag(value) ((int)((unsigned)((value)&0xC0000000) >> 30)) -#define TTMove(value) ((Move)((value)&0xFFFFFF)) -#define TTDepth(value) ((int)((value)&0x3F000000) >> 24) -#define TTEval(value) ((int)((int16_t)((value) >> 48))) - size_t TTInit(int mb); void TTFree(); void TTClear(); void TTPrefetch(uint64_t hash); -TTValue TTProbe(uint64_t hash); -int TTScore(TTValue value, int ply); -TTValue TTPut(uint64_t hash, int depth, int score, int flag, Move move, int ply, int eval); +TTEntry* TTProbe(int* hit, uint64_t hash); +int TTScore(TTEntry* e, int ply); +void TTPut(uint64_t hash, uint8_t depth, int16_t score, uint8_t flag, Move move, int ply, int16_t eval); int TTFull(); #endif \ No newline at end of file