Skip to content

Commit

Permalink
Add continuation history
Browse files Browse the repository at this point in the history
  • Loading branch information
eduherminio committed Feb 4, 2024
1 parent 36308f2 commit f794f80
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 18 deletions.
27 changes: 19 additions & 8 deletions src/Lynx/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,29 @@ public Engine(ChannelWriter<string> engineWriter)
_engineWriter = engineWriter;

_quietHistory = new int[12][];
for (int i = 0; i < _quietHistory.Length; ++i)
{
_quietHistory[i] = new int[64];
}

_captureHistory = new int[12][][];
for (int i = 0; i < 12; ++i)
_continuationHistory = new int[12][][][][];

for (int i = 0; i < 12; ++i) // 12
{
_quietHistory[i] = new int[64];
_captureHistory[i] = new int[64][];
for (var j = 0; j < 64; ++j)
_continuationHistory[i] = new int[64][][][];

for (var j = 0; j < 64; ++j) // 64
{
_captureHistory[i][j] = new int[12];
_captureHistory[i][j] = new int[12]; // 12
_continuationHistory[i][j] = new int[2][][];

for (int contPly = 0; contPly < 2; ++contPly) // 2
{
_continuationHistory[i][j][contPly] = new int[12][];

for (int k = 0; k < 12; ++k) // 12
{
_continuationHistory[i][j][contPly][k] = new int[64]; // 64
}
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/Lynx/EvaluationConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,12 @@ static EvaluationConstants()

public const int ThirdKillerMoveValue = 131_072;

public const int CounterMoveValue = 65_536;

// Revisit bad capture pruning in NegaMax.cs if order changes and promos aren't the lowest before bad captures
public const int PromotionMoveScoreValue = 65_536;
public const int PromotionMoveScoreValue = 32_768;

public const int BadCaptureMoveBaseScoreValue = 32_768;
public const int BadCaptureMoveBaseScoreValue = 16_384;

//public const int MaxHistoryMoveValue => Configuration.EngineSettings.MaxHistoryMoveValue;

Expand Down
5 changes: 5 additions & 0 deletions src/Lynx/Model/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ public sealed class Game
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

public List<Move> MoveHistory { get; }

public HashSet<long> PositionHashHistory { get; }

public Move[] MoveStack { get; }

public int HalfMovesWithoutCaptureOrPawnMove { get; set; }

public Position CurrentPosition { get; private set; }
Expand All @@ -34,6 +37,7 @@ public Game(ReadOnlySpan<char> fen)
PositionHashHistory = new(1024) { CurrentPosition.UniqueIdentifier };

HalfMovesWithoutCaptureOrPawnMove = parsedFen.HalfMoveClock;
MoveStack = new Move[1024];
}

/// <summary>
Expand All @@ -47,6 +51,7 @@ internal Game(Position position)

MoveHistory = new(1024);
PositionHashHistory = new(1024) { position.UniqueIdentifier };
MoveStack = new Move[1024];
}

[Obsolete("Just intended for testing purposes")]
Expand Down
38 changes: 31 additions & 7 deletions src/Lynx/Search/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ public sealed partial class Engine
/// Returns the score evaluation of a move taking into account <see cref="_isScoringPV"/>, <paramref name="bestMoveTTCandidate"/>, <see cref="EvaluationConstants.MostValueableVictimLeastValuableAttacker"/>, <see cref="_killerMoves"/> and <see cref="_quietHistory"/>
/// </summary>
/// <param name="move"></param>
/// <param name="depth"></param>
/// <param name="ply"></param>
/// <param name="isNotQSearch"></param>
/// <param name="bestMoveTTCandidate"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int ScoreMove(Move move, int depth, bool isNotQSearch, ShortMove bestMoveTTCandidate = default)
internal int ScoreMove(Move move, int ply, bool isNotQSearch, ShortMove bestMoveTTCandidate = default)
{
if (_isScoringPV && move == _pVTable[depth])
if (_isScoringPV && move == _pVTable[ply])
{
_isScoringPV = false;

Expand Down Expand Up @@ -122,23 +122,47 @@ internal int ScoreMove(Move move, int depth, bool isNotQSearch, ShortMove bestMo
if (isNotQSearch)
{
// 1st killer move
if (_killerMoves[0][depth] == move)
if (_killerMoves[0][ply] == move)
{
return EvaluationConstants.FirstKillerMoveValue;
}

if (_killerMoves[1][depth] == move)
// 2nd killer move
if (_killerMoves[1][ply] == move)
{
return EvaluationConstants.SecondKillerMoveValue;
}

if (_killerMoves[2][depth] == move)
// 3rd killer move
if (_killerMoves[2][ply] == move)
{
return EvaluationConstants.ThirdKillerMoveValue;
}

// Counter move history
if (ply >= 1)
{
var previousMove = Game.MoveStack[ply - 1];

// Counter move and follow up history
if (ply >= 2)
{
var previousPreviousMove = Game.MoveStack[ply - 2];

return EvaluationConstants.BaseMoveScore
+ _quietHistory[move.Piece()][move.TargetSquare()]
+ _continuationHistory[move.Piece()][move.TargetSquare()][0][previousMove.Piece()][previousMove.TargetSquare()]
+ _continuationHistory[move.Piece()][move.TargetSquare()][1][previousPreviousMove.Piece()][previousPreviousMove.TargetSquare()];
}

return EvaluationConstants.CounterMoveValue
+ _quietHistory[move.Piece()][move.TargetSquare()]
+ _continuationHistory[move.Piece()][move.TargetSquare()][0][previousMove.Piece()][previousMove.TargetSquare()];
}

// History move or 0 if not found
return EvaluationConstants.BaseMoveScore + _quietHistory[move.Piece()][move.TargetSquare()];
return EvaluationConstants.BaseMoveScore
+ _quietHistory[move.Piece()][move.TargetSquare()];
}

return EvaluationConstants.BaseMoveScore;
Expand Down
19 changes: 18 additions & 1 deletion src/Lynx/Search/IDDFS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,31 @@ public sealed partial class Engine

/// <summary>
/// 12x64
/// piece x target square
/// </summary>
private readonly int[][] _quietHistory;

/// <summary>
/// 12x64x12
/// 12x64x12,
/// piece x target square x captured piece
/// </summary>
private readonly int[][][] _captureHistory;

/// <summary>
/// 12x64x2x12x64,
/// piece x target square x 2 ply x last piece x last target square
/// ply 0 -> Continuation move history
/// ply 1 -> Followup move history
/// </summary>
private readonly int[][][][][] _continuationHistory;

/// <summary>
/// <see cref="Configuration.EngineSettings.MaxDepth"/>x12x64,
/// depth x piece x target square
/// https://discord.com/channels/1132289356011405342/1134900681401176064/1152037164415205447
/// </summary>
//private readonly int[][][] _continuationHistory;

private readonly int[] _maxDepthReached = new int[Constants.AbsoluteMaxDepth];
private TranspositionTable _tt = [];
private int _ttMask;
Expand Down
30 changes: 30 additions & 0 deletions src/Lynx/Search/NegaMax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
var oldValue = Game.HalfMovesWithoutCaptureOrPawnMove;
Game.HalfMovesWithoutCaptureOrPawnMove = Utils.Update50movesRule(move, Game.HalfMovesWithoutCaptureOrPawnMove);
var isThreeFoldRepetition = !Game.PositionHashHistory.Add(position.UniqueIdentifier);
Game.MoveStack[ply] = move;

int evaluation;
if (isThreeFoldRepetition)
Expand Down Expand Up @@ -262,6 +263,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
Game.HalfMovesWithoutCaptureOrPawnMove = oldValue;
Game.PositionHashHistory.Remove(position.UniqueIdentifier); // We know that there's no triple repetition here
position.UnmakeMove(move, gameState);
Game.MoveStack[ply] = 0;

break;
}
Expand Down Expand Up @@ -330,6 +332,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
// Game.PositionHashHistory is update above
Game.HalfMovesWithoutCaptureOrPawnMove = oldValue;
position.UnmakeMove(move, gameState);
Game.MoveStack[ply] = 0;

PrintMove(ply, move, evaluation);

Expand Down Expand Up @@ -394,6 +397,31 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
}
}

// 🔍 Continuation history
// - Counter move history (continuation history, ply - 1)
if (ply >= 1)
{
var previousMove = Game.MoveStack[ply - 1];
var previousMovePiece = previousMove.Piece();
var previousTargetSquare = previousMove.TargetSquare();

_continuationHistory[piece][targetSquare][0][previousMovePiece][previousTargetSquare] = ScoreHistoryMove(
_continuationHistory[piece][targetSquare][0][previousMovePiece][previousTargetSquare],
EvaluationConstants.HistoryBonus[depth]);

// - Followup move history (continuation history, ply - 2)
if (ply >= 2)
{
var previousPreviousMove = Game.MoveStack[ply - 2];
var previousPreviousMovePiece = previousPreviousMove.Piece();
var previousPreviousMoveTargetSquare = previousPreviousMove.TargetSquare();

_continuationHistory[piece][targetSquare][1][previousPreviousMovePiece][previousPreviousMoveTargetSquare] = ScoreHistoryMove(
_continuationHistory[piece][targetSquare][1][previousPreviousMovePiece][previousPreviousMoveTargetSquare],
EvaluationConstants.HistoryBonus[depth]);
}
}

// 🔍 Killer moves
if (move.PromotedPiece() == default && move != _killerMoves[0][ply])
{
Expand Down Expand Up @@ -549,6 +577,7 @@ public int QuiescenceSearch(int ply, int alpha, int beta)
var oldValue = Game.HalfMovesWithoutCaptureOrPawnMove;
Game.HalfMovesWithoutCaptureOrPawnMove = Utils.Update50movesRule(move, Game.HalfMovesWithoutCaptureOrPawnMove);
var isThreeFoldRepetition = !Game.PositionHashHistory.Add(position.UniqueIdentifier);
Game.MoveStack[ply] = move;

int evaluation;
if (isThreeFoldRepetition || Game.Is50MovesRepetition())
Expand All @@ -572,6 +601,7 @@ public int QuiescenceSearch(int ply, int alpha, int beta)
Game.PositionHashHistory.Remove(position.UniqueIdentifier);
}
position.UnmakeMove(move, gameState);
Game.MoveStack[ply] = 0;

PrintMove(ply, move, evaluation);

Expand Down

0 comments on commit f794f80

Please sign in to comment.