diff --git a/src/Lynx.Dev/Program.cs b/src/Lynx.Dev/Program.cs
index d825e3ef9..bea4d0971 100644
--- a/src/Lynx.Dev/Program.cs
+++ b/src/Lynx.Dev/Program.cs
@@ -1083,19 +1083,19 @@ static void TesSize(int size)
//var entry = transpositionTable.ProbeHash(position, maxDepth: 5, depth: 3, alpha: 1, beta: 2);
transpositionTable.RecordHash(position, position.StaticEvaluation().Score, depth: 5, ply: 3, score: +19, nodeType: NodeType.Alpha, move: 1234);
- var entry = transpositionTable.ProbeHash(position, depth: 5, ply: 3, alpha: 20, beta: 30);
+ var entry = transpositionTable.ProbeHash(position, ply: 3);
Console.WriteLine(entry); // Expected 20
transpositionTable.RecordHash(position, position.StaticEvaluation().Score, depth: 5, ply: 3, score: +21, nodeType: NodeType.Alpha, move: 1234);
- entry = transpositionTable.ProbeHash(position, depth: 5, ply: 3, alpha: 20, beta: 30);
+ entry = transpositionTable.ProbeHash(position, ply: 3);
Console.WriteLine(entry); // Expected 12_345_678
transpositionTable.RecordHash(position, position.StaticEvaluation().Score, depth: 5, ply: 3, score: +29, nodeType: NodeType.Beta, move: 1234);
- entry = transpositionTable.ProbeHash(position, depth: 5, ply: 3, alpha: 20, beta: 30);
+ entry = transpositionTable.ProbeHash(position, ply: 3);
Console.WriteLine(entry); // Expected 12_345_678
transpositionTable.RecordHash(position, position.StaticEvaluation().Score, depth: 5, ply: 3, score: +31, nodeType: NodeType.Beta, move: 1234);
- entry = transpositionTable.ProbeHash(position, depth: 5, ply: 3, alpha: 20, beta: 30);
+ entry = transpositionTable.ProbeHash(position, ply: 3);
Console.WriteLine(entry); // Expected 30
}
diff --git a/src/Lynx/Model/TranspositionTable.cs b/src/Lynx/Model/TranspositionTable.cs
index b1ab33e9a..5a1d337a6 100644
--- a/src/Lynx/Model/TranspositionTable.cs
+++ b/src/Lynx/Model/TranspositionTable.cs
@@ -59,7 +59,7 @@ public void PrefetchTTEntry(Position position)
///
/// Ply
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public (int Score, ShortMove BestMove, NodeType NodeType, int RawScore, int StaticEval) ProbeHash(Position position, int depth, int ply, int alpha, int beta)
+ public (int Score, ShortMove BestMove, NodeType NodeType, int StaticEval, int Depth) ProbeHash(Position position, int ply)
{
var ttIndex = CalculateTTIndex(position.UniqueIdentifier);
var entry = _tt[ttIndex];
@@ -69,25 +69,11 @@ public void PrefetchTTEntry(Position position)
return (EvaluationConstants.NoHashEntry, default, default, default, default);
}
- var type = entry.Type;
- var rawScore = entry.Score;
- var score = EvaluationConstants.NoHashEntry;
+ // 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 recalculatedScore = RecalculateMateScores(entry.Score, ply);
- if (entry.Depth >= depth)
- {
- var recalculatedScore = RecalculateMateScores(rawScore, ply);
-
- if (type == NodeType.Exact
- || (type == NodeType.Alpha && recalculatedScore <= alpha)
- || (type == NodeType.Beta && recalculatedScore >= beta))
- {
- // 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
- score = recalculatedScore;
- }
- }
-
- return (score, entry.Move, entry.Type, rawScore, entry.StaticEval);
+ return (recalculatedScore, entry.Move, entry.Type, entry.StaticEval, entry.Depth);
}
///
diff --git a/src/Lynx/Model/TranspositionTableElement.cs b/src/Lynx/Model/TranspositionTableElement.cs
index b484eb355..450d37179 100644
--- a/src/Lynx/Model/TranspositionTableElement.cs
+++ b/src/Lynx/Model/TranspositionTableElement.cs
@@ -7,8 +7,17 @@ public enum NodeType : byte
#pragma warning restore S4022 // Enumerations should have "Int32" storage
{
Unknown, // Making it 0 instead of -1 because of default struct initialization
+
Exact,
+
+ ///
+ /// UpperBound
+ ///
Alpha,
+
+ ///
+ /// LowerBound
+ ///
Beta
}
diff --git a/src/Lynx/Search/NegaMax.cs b/src/Lynx/Search/NegaMax.cs
index 0d8d0cd06..31b1c304b 100644
--- a/src/Lynx/Search/NegaMax.cs
+++ b/src/Lynx/Search/NegaMax.cs
@@ -40,17 +40,22 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool cutnode, Cance
ShortMove ttBestMove = default;
NodeType ttElementType = default;
int ttScore = default;
- int ttRawScore = default;
int ttStaticEval = int.MinValue;
+ int ttDepth = default;
Debug.Assert(!pvNode || !cutnode);
if (!isRoot)
{
- (ttScore, ttBestMove, ttElementType, ttRawScore, ttStaticEval) = _tt.ProbeHash(position, depth, ply, alpha, beta);
+ (ttScore, ttBestMove, ttElementType, ttStaticEval, ttDepth) = _tt.ProbeHash(position, ply);
// TT cutoffs
- if (!pvNode && ttScore != EvaluationConstants.NoHashEntry)
+ if (!pvNode
+ && ttScore != EvaluationConstants.NoHashEntry
+ && ttDepth >= depth
+ && (ttElementType == NodeType.Exact
+ || (ttElementType == NodeType.Alpha && ttScore <= alpha)
+ || (ttElementType == NodeType.Beta && ttScore >= beta)))
{
return ttScore;
}
@@ -124,9 +129,9 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool cutnode, Cance
// If the score is outside what the current bounds are, but it did match flag and depth,
// then we can trust that this score is more accurate than the current static evaluation,
// and we can update our static evaluation for better accuracy in pruning
- if (ttElementType != default && ttElementType != (ttRawScore > staticEval ? NodeType.Alpha : NodeType.Beta))
+ if (ttElementType != default && ttElementType != (ttScore > staticEval ? NodeType.Alpha : NodeType.Beta))
{
- staticEval = ttRawScore;
+ staticEval = ttScore;
}
bool isNotGettingCheckmated = staticEval > EvaluationConstants.NegativeCheckmateDetectionLimit;
@@ -190,7 +195,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool cutnode, Cance
&& staticEvalBetaDiff >= 0
&& !parentWasNullMove
&& phase > 2 // Zugzwang risk reduction: pieces other than pawn presents
- && (ttElementType != NodeType.Alpha || ttRawScore >= beta)) // TT suggests NMP will fail: entry must not be a fail-low entry with a score below beta - Stormphrax and Ethereal
+ && (ttElementType != NodeType.Alpha || ttScore >= 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 + Configuration.EngineSettings.NMP_DepthIncrement) / Configuration.EngineSettings.NMP_DepthDivisor) // Clarity
@@ -531,16 +536,27 @@ public int QuiescenceSearch(int ply, int alpha, int beta, CancellationToken canc
var nextPvIndex = PVTable.Indexes[ply + 1];
_pVTable[pvIndex] = _defaultMove; // Nulling the first value before any returns
- var ttProbeResult = _tt.ProbeHash(position, 0, ply, alpha, beta);
- if (ttProbeResult.Score != EvaluationConstants.NoHashEntry)
+ var ttProbeResult = _tt.ProbeHash(position, ply);
+ var ttScore = ttProbeResult.Score;
+ var ttNodeType = ttProbeResult.NodeType;
+ var ttHit = ttNodeType != NodeType.Unknown;
+
+ // QS TT cutoff
+ Debug.Assert(ttProbeResult.Depth >= 0, "Assertion failed", "We would need to add it as a TT cutoff condition");
+
+ if (ttHit
+ && (ttNodeType == NodeType.Exact
+ || (ttNodeType == NodeType.Alpha && ttScore <= alpha)
+ || (ttNodeType == NodeType.Beta && ttScore >= beta)))
{
- return ttProbeResult.Score;
+ return ttScore;
}
+
ShortMove ttBestMove = ttProbeResult.BestMove;
_maxDepthReached[ply] = ply;
- var staticEval = ttProbeResult.NodeType != NodeType.Unknown
+ var staticEval = ttHit // TODO check if static eval
? ttProbeResult.StaticEval
: position.StaticEvaluation(Game.HalfMovesWithoutCaptureOrPawnMove).Score;
diff --git a/tests/Lynx.Test/Model/TranspositionTableTest.cs b/tests/Lynx.Test/Model/TranspositionTableTest.cs
index c3b1bc22c..f09ff4fcd 100644
--- a/tests/Lynx.Test/Model/TranspositionTableTest.cs
+++ b/tests/Lynx.Test/Model/TranspositionTableTest.cs
@@ -24,11 +24,9 @@ public void RecalculateMateScores(int evaluation, int depth, int expectedEvaluat
Assert.AreEqual(expectedEvaluation, TranspositionTable.RecalculateMateScores(evaluation, depth));
}
- [TestCase(+19, NodeType.Alpha, +20, +30, +19)]
- [TestCase(+21, NodeType.Alpha, +20, +30, NoHashEntry)]
- [TestCase(+29, NodeType.Beta, +20, +30, NoHashEntry)]
- [TestCase(+31, NodeType.Beta, +20, +30, +31)]
- public void RecordHash_ProbeHash(int recordedEval, NodeType recordNodeType, int probeAlpha, int probeBeta, int expectedProbeEval)
+ [TestCase(+19, NodeType.Alpha, +19)]
+ [TestCase(+31, NodeType.Beta, +31)]
+ public void RecordHash_ProbeHash(int recordedEval, NodeType recordNodeType, int expectedProbeEval)
{
var position = new Position(Constants.InitialPositionFEN);
var transpositionTable = new TranspositionTable();
@@ -37,7 +35,7 @@ public void RecordHash_ProbeHash(int recordedEval, NodeType recordNodeType, int
transpositionTable.RecordHash(position, staticEval, depth: 5, ply: 3, score: recordedEval, nodeType: recordNodeType, move: 1234);
- var ttEntry = transpositionTable.ProbeHash(position, depth: 5, ply: 3, alpha: probeAlpha, beta: probeBeta);
+ var ttEntry = transpositionTable.ProbeHash(position, ply: 3);
Assert.AreEqual(expectedProbeEval, ttEntry.Score);
Assert.AreEqual(staticEval, ttEntry.StaticEval);
}
@@ -52,7 +50,7 @@ public void RecordHash_ProbeHash_CheckmateSameDepth(int recordedEval)
transpositionTable.RecordHash(position, recordedEval, depth: 10, ply: sharedDepth, score: recordedEval, nodeType: NodeType.Exact, move: 1234);
- var ttEntry = transpositionTable.ProbeHash(position, depth: 7, ply: sharedDepth, alpha: 50, beta: 100);
+ var ttEntry = transpositionTable.ProbeHash(position, ply: sharedDepth);
Assert.AreEqual(recordedEval, ttEntry.Score);
Assert.AreEqual(recordedEval, ttEntry.StaticEval);
}
@@ -68,7 +66,7 @@ public void RecordHash_ProbeHash_CheckmateDifferentDepth(int recordedEval, int r
transpositionTable.RecordHash(position, recordedEval, depth: 10, ply: recordedDeph, score: recordedEval, nodeType: NodeType.Exact, move: 1234);
- var ttEntry = transpositionTable.ProbeHash(position, depth: 7, ply: probeDepth, alpha: 50, beta: 100);
+ var ttEntry = transpositionTable.ProbeHash(position, ply: probeDepth);
Assert.AreEqual(expectedProbeEval, ttEntry.Score);
Assert.AreEqual(recordedEval, ttEntry.StaticEval);
}