diff --git a/src/Lynx.Dev/Program.cs b/src/Lynx.Dev/Program.cs
index f3c4f8e01..a7f58c9c7 100644
--- a/src/Lynx.Dev/Program.cs
+++ b/src/Lynx.Dev/Program.cs
@@ -1084,21 +1084,23 @@ static void TesSize(int size)
//transpositionTable.RecordHash(position, depth: 3, maxDepth: 5, move: 1234, eval: +5, nodeType: NodeType.Alpha);
//var entry = transpositionTable.ProbeHash(position, maxDepth: 5, depth: 3, alpha: 1, beta: 2);
- transpositionTable.RecordHash(mask, position, depth: 5, ply: 3, eval: +19, nodeType: NodeType.Alpha, move: 1234);
- var entry = transpositionTable.ProbeHash(mask, position, depth: 5, ply: 3, alpha: 20, beta: 30);
- Console.WriteLine(entry); // Expected 20
+ TranspositionTableElement ttEntry = default;
- transpositionTable.RecordHash(mask, position, depth: 5, ply: 3, eval: +21, nodeType: NodeType.Alpha, move: 1234);
- entry = transpositionTable.ProbeHash(mask, position, depth: 5, ply: 3, alpha: 20, beta: 30);
- Console.WriteLine(entry); // Expected 12_345_678
+ transpositionTable.Save(mask, position, depth: 5, ply: 3, eval: +19, nodeType: NodeType.Alpha, move: 1234);
+ var eval = transpositionTable.Read(mask, position, depth: 5, ply: 3, alpha: 20, beta: 30, ref ttEntry);
+ Console.WriteLine(eval); // Expected 20
- transpositionTable.RecordHash(mask, position, depth: 5, ply: 3, eval: +29, nodeType: NodeType.Beta, move: 1234);
- entry = transpositionTable.ProbeHash(mask, position, depth: 5, ply: 3, alpha: 20, beta: 30);
- Console.WriteLine(entry); // Expected 12_345_678
+ transpositionTable.Save(mask, position, depth: 5, ply: 3, eval: +21, nodeType: NodeType.Alpha, move: 1234);
+ eval = transpositionTable.Read(mask, position, depth: 5, ply: 3, alpha: 20, beta: 30, ref ttEntry);
+ Console.WriteLine(eval); // Expected 12_345_678
- transpositionTable.RecordHash(mask, position, depth: 5, ply: 3, eval: +31, nodeType: NodeType.Beta, move: 1234);
- entry = transpositionTable.ProbeHash(mask, position, depth: 5, ply: 3, alpha: 20, beta: 30);
- Console.WriteLine(entry); // Expected 30
+ transpositionTable.Save(mask, position, depth: 5, ply: 3, eval: +29, nodeType: NodeType.Beta, move: 1234);
+ eval = transpositionTable.Read(mask, position, depth: 5, ply: 3, alpha: 20, beta: 30, ref ttEntry);
+ Console.WriteLine(eval); // Expected 12_345_678
+
+ transpositionTable.Save(mask, position, depth: 5, ply: 3, eval: +31, nodeType: NodeType.Beta, move: 1234);
+ eval = transpositionTable.Read(mask, position, depth: 5, ply: 3, alpha: 20, beta: 30, ref ttEntry);
+ Console.WriteLine(eval); // Expected 30
}
static void UnmakeMove()
diff --git a/src/Lynx/Model/TranspositionTable.cs b/src/Lynx/Model/TranspositionTable.cs
index b97336779..5d76c9016 100644
--- a/src/Lynx/Model/TranspositionTable.cs
+++ b/src/Lynx/Model/TranspositionTable.cs
@@ -105,31 +105,32 @@ public static void ClearTranspositionTable(this TranspositionTable transposition
/// Ply
///
///
+ ///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static (int Evaluation, ShortMove BestMove, NodeType NodeType) ProbeHash(this TranspositionTable tt, int ttMask, Position position, int depth, int ply, int alpha, int beta)
+ public static int Read(this TranspositionTable tt, int ttMask, Position position, int depth, int ply, int alpha, int beta, ref TranspositionTableElement entry)
{
if (!Configuration.EngineSettings.TranspositionTableEnabled)
{
- return (EvaluationConstants.NoHashEntry, default, default);
+ return EvaluationConstants.NoHashEntry;
}
- ref var entry = ref tt[position.UniqueIdentifier & ttMask];
+ ref TranspositionTableElement localEntry = ref tt[position.UniqueIdentifier & ttMask];
- if ((position.UniqueIdentifier >> 48) != entry.Key)
+ if ((position.UniqueIdentifier >> 48) != localEntry.Key)
{
- return (EvaluationConstants.NoHashEntry, default, default);
+ return EvaluationConstants.NoHashEntry;
}
var eval = EvaluationConstants.NoHashEntry;
- if (entry.Depth >= depth)
+ if (localEntry.Depth >= depth)
{
// We want to translate the checkmate position relative to the saved node to our root position from which we're searching
// If the recorded score is a checkmate in 3 and we are at depth 5, we want to read checkmate in 8
- var score = RecalculateMateScores(entry.Score, ply);
+ var score = RecalculateMateScores(localEntry.Score, ply);
- eval = entry.Type switch
+ eval = localEntry.Type switch
{
NodeType.Exact => score,
NodeType.Alpha when score <= alpha => alpha,
@@ -138,7 +139,9 @@ public static (int Evaluation, ShortMove BestMove, NodeType NodeType) ProbeHash(
};
}
- return (eval, entry.Move, entry.Type);
+ entry = localEntry;
+
+ return eval;
}
///
@@ -153,7 +156,7 @@ public static (int Evaluation, ShortMove BestMove, NodeType NodeType) ProbeHash(
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void RecordHash(this TranspositionTable tt, int ttMask, Position position, int depth, int ply, int eval, NodeType nodeType, Move? move = null)
+ public static void Save(this TranspositionTable tt, int ttMask, Position position, int depth, int ply, int eval, NodeType nodeType, Move? move = null)
{
if (!Configuration.EngineSettings.TranspositionTableEnabled)
{
diff --git a/src/Lynx/Search/NegaMax.cs b/src/Lynx/Search/NegaMax.cs
index 967fab7f8..a6926136e 100644
--- a/src/Lynx/Search/NegaMax.cs
+++ b/src/Lynx/Search/NegaMax.cs
@@ -38,13 +38,12 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
bool isRoot = ply == 0;
bool pvNode = beta - alpha > 1;
- ShortMove ttBestMove = default;
- NodeType ttElementType = default;
+ TranspositionTableElement ttEntry = default;
int ttEvaluation = default;
if (!isRoot)
{
- (ttEvaluation, ttBestMove, ttElementType) = _tt.ProbeHash(_ttMask, position, depth, ply, alpha, beta);
+ ttEvaluation = _tt.Read(_ttMask, position, depth, ply, alpha, beta, ref ttEntry);
if (ttEvaluation != EvaluationConstants.NoHashEntry)
{
return ttEvaluation;
@@ -55,7 +54,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
// so the search will be potentially expensive.
// Therefore, we search with reduced depth for now, expecting to record a TT move
// which we'll be able to use later for the full depth search
- if (ttElementType == default && depth >= Configuration.EngineSettings.IIR_MinDepth)
+ if (ttEntry.Type == default && depth >= Configuration.EngineSettings.IIR_MinDepth)
{
--depth;
}
@@ -78,7 +77,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
}
var finalPositionEvaluation = Position.EvaluateFinalPosition(ply, isInCheck);
- _tt.RecordHash(_ttMask, position, depth, ply, finalPositionEvaluation, NodeType.Exact);
+ _tt.Save(_ttMask, position, depth, ply, finalPositionEvaluation, NodeType.Exact);
return finalPositionEvaluation;
}
@@ -91,7 +90,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
&& staticEval >= beta
&& !parentWasNullMove
&& phase > 2 // Zugzwang risk reduction: pieces other than pawn presents
- && (ttElementType != NodeType.Alpha || ttEvaluation >= beta)) // TT suggests NMP will fail: entry must not be a fail-low entry with a score below beta - Stormphrax and Ethereal
+ && (ttEntry.Type != NodeType.Alpha || ttEvaluation >= beta)) // TT suggests NMP will fail: entry must not be a fail-low entry with a score below beta - Stormphrax and Ethereal
{
var nmpReduction = Configuration.EngineSettings.NMP_BaseDepthReduction + ((depth + 1) / 3); // Clarity
@@ -165,7 +164,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
_isFollowingPV = false;
for (int i = 0; i < pseudoLegalMoves.Length; ++i)
{
- scores[i] = ScoreMove(pseudoLegalMoves[i], ply, isNotQSearch: true, ttBestMove);
+ scores[i] = ScoreMove(pseudoLegalMoves[i], ply, isNotQSearch: true, ttEntry.Move);
if (pseudoLegalMoves[i] == _pVTable[depth])
{
@@ -178,7 +177,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
{
for (int i = 0; i < pseudoLegalMoves.Length; ++i)
{
- scores[i] = ScoreMove(pseudoLegalMoves[i], ply, isNotQSearch: true, ttBestMove);
+ scores[i] = ScoreMove(pseudoLegalMoves[i], ply, isNotQSearch: true, ttEntry.Move);
}
}
@@ -378,7 +377,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
}
}
- _tt.RecordHash(_ttMask, position, depth, ply, beta, NodeType.Beta, bestMove);
+ _tt.Save(_ttMask, position, depth, ply, beta, NodeType.Beta, bestMove);
return beta; // TODO return evaluation?
}
@@ -401,11 +400,11 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
{
var eval = Position.EvaluateFinalPosition(ply, isInCheck);
- _tt.RecordHash(_ttMask, position, depth, ply, eval, NodeType.Exact);
+ _tt.Save(_ttMask, position, depth, ply, eval, NodeType.Exact);
return eval;
}
- _tt.RecordHash(_ttMask, position, depth, ply, alpha, nodeType, bestMove);
+ _tt.Save(_ttMask, position, depth, ply, alpha, nodeType, bestMove);
// Node fails low
return alpha;
@@ -441,12 +440,13 @@ public int QuiescenceSearch(int ply, int alpha, int beta)
var nextPvIndex = PVTable.Indexes[ply + 1];
_pVTable[pvIndex] = _defaultMove; // Nulling the first value before any returns
- var ttProbeResult = _tt.ProbeHash(_ttMask, position, 0, ply, alpha, beta);
- if (ttProbeResult.Evaluation != EvaluationConstants.NoHashEntry)
+ TranspositionTableElement ttEntry = default;
+ var ttProbeEvaluation = _tt.Read(_ttMask, position, 0, ply, alpha, beta, ref ttEntry);
+ if (ttProbeEvaluation != EvaluationConstants.NoHashEntry)
{
- return ttProbeResult.Evaluation;
+ return ttProbeEvaluation;
}
- ShortMove ttBestMove = ttProbeResult.BestMove;
+ ShortMove ttBestMove = ttEntry.Move;
_maxDepthReached[ply] = ply;
@@ -551,7 +551,7 @@ public int QuiescenceSearch(int ply, int alpha, int beta)
{
PrintMessage($"Pruning: {move} is enough to discard this line");
- _tt.RecordHash(_ttMask, position, 0, ply, beta, NodeType.Beta, bestMove);
+ _tt.Save(_ttMask, position, 0, ply, beta, NodeType.Beta, bestMove);
return evaluation; // The refutation doesn't matter, since it'll be pruned
}
@@ -573,12 +573,12 @@ public int QuiescenceSearch(int ply, int alpha, int beta)
&& !MoveGenerator.CanGenerateAtLeastAValidMove(position))
{
var finalEval = Position.EvaluateFinalPosition(ply, position.IsInCheck());
- _tt.RecordHash(_ttMask, position, 0, ply, finalEval, NodeType.Exact);
+ _tt.Save(_ttMask, position, 0, ply, finalEval, NodeType.Exact);
return finalEval;
}
- _tt.RecordHash(_ttMask, position, 0, ply, alpha, nodeType, bestMove);
+ _tt.Save(_ttMask, position, 0, ply, alpha, nodeType, bestMove);
return alpha;
}
diff --git a/tests/Lynx.Test/Model/TranspositionTableTest.cs b/tests/Lynx.Test/Model/TranspositionTableTest.cs
index 236ea8632..f07513b17 100644
--- a/tests/Lynx.Test/Model/TranspositionTableTest.cs
+++ b/tests/Lynx.Test/Model/TranspositionTableTest.cs
@@ -84,9 +84,10 @@ public void RecordHash_ProbeHash(int recordedEval, NodeType recordNodeType, int
var (mask, length) = TranspositionTableExtensions.CalculateLength(Configuration.EngineSettings.TranspositionTableSize);
var transpositionTable = new TranspositionTableElement[length];
- transpositionTable.RecordHash(mask, position, depth: 5, ply: 3, eval: recordedEval, nodeType: recordNodeType, move: 1234);
+ transpositionTable.Save(mask, position, depth: 5, ply: 3, eval: recordedEval, nodeType: recordNodeType, move: 1234);
- Assert.AreEqual(expectedProbeEval, transpositionTable.ProbeHash(mask, position, depth: 5, ply: 3, alpha: probeAlpha, beta: probeBeta).Evaluation);
+ TranspositionTableElement ttEntry = default;
+ Assert.AreEqual(expectedProbeEval, transpositionTable.Read(mask, position, depth: 5, ply: 3, alpha: probeAlpha, beta: probeBeta, ref ttEntry));
}
[TestCase(CheckMateBaseEvaluation - 8 * CheckmateDepthFactor)]
@@ -98,9 +99,10 @@ public void RecordHash_ProbeHash_CheckmateSameDepth(int recordedEval)
var (mask, length) = TranspositionTableExtensions.CalculateLength(Configuration.EngineSettings.TranspositionTableSize);
var transpositionTable = new TranspositionTableElement[length];
- transpositionTable.RecordHash(mask, position, depth: 10, ply: sharedDepth, eval: recordedEval, nodeType: NodeType.Exact, move: 1234);
+ transpositionTable.Save(mask, position, depth: 10, ply: sharedDepth, eval: recordedEval, nodeType: NodeType.Exact, move: 1234);
- Assert.AreEqual(recordedEval, transpositionTable.ProbeHash(mask, position, depth: 7, ply: sharedDepth, alpha: 50, beta: 100).Evaluation);
+ TranspositionTableElement ttEntry = default;
+ Assert.AreEqual(recordedEval, transpositionTable.Read(mask, position, depth: 7, ply: sharedDepth, alpha: 50, beta: 100, ref ttEntry));
}
[TestCase(CheckMateBaseEvaluation - 8 * CheckmateDepthFactor, 5, 4, CheckMateBaseEvaluation - 7 * CheckmateDepthFactor)]
@@ -113,8 +115,9 @@ public void RecordHash_ProbeHash_CheckmateDifferentDepth(int recordedEval, int r
var (mask, length) = TranspositionTableExtensions.CalculateLength(Configuration.EngineSettings.TranspositionTableSize);
var transpositionTable = new TranspositionTableElement[length];
- transpositionTable.RecordHash(mask, position, depth: 10, ply: recordedDeph, eval: recordedEval, nodeType: NodeType.Exact, move: 1234);
+ transpositionTable.Save(mask, position, depth: 10, ply: recordedDeph, eval: recordedEval, nodeType: NodeType.Exact, move: 1234);
- Assert.AreEqual(expectedProbeEval, transpositionTable.ProbeHash(mask, position, depth: 7, ply: probeDepth, alpha: 50, beta: 100).Evaluation);
+ TranspositionTableElement ttEntry = default;
+ Assert.AreEqual(expectedProbeEval, transpositionTable.Read(mask, position, depth: 7, ply: probeDepth, alpha: 50, beta: 100, ref ttEntry));
}
}
\ No newline at end of file