Skip to content

Commit

Permalink
⚡ Refactor Update50movesRule() method (#678)
Browse files Browse the repository at this point in the history
- Move Utils.Update50movesRule to Game, since it updates the 50 moves counter there
- Increase `bench` depth to 8
- Add two more fens to `bench` that involve triple repetition and 50 moves draw
  • Loading branch information
eduherminio committed Feb 24, 2024
1 parent 37b6799 commit e1e2a7b
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/Lynx.Cli/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// Settings that affect the engine behavior
"EngineSettings": {
"MaxDepth": 128,
"BenchDepth": 5,
"BenchDepth": 8,
"TranspositionTableSize": "256",
"TranspositionTableEnabled": true,
"UseOnlineTablebaseInRootPositions": false,
Expand Down
4 changes: 3 additions & 1 deletion src/Lynx/Bench.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ public partial class Engine

"8/8/4k3/3n1n2/5P2/8/3K4/8 b - - 0 12", // NN vs P endgame
"8/5R2/1n2RK2/8/8/7k/4r3/8 b - - 0 1", // RR vs RN endgame, where if black takes, they actually loses
"8/q5rk/8/8/8/8/Q5RK/7N w - - 0 1" // Endgame that can lead to QN vs Q or RN vs R positions
"8/q5rk/8/8/8/8/Q5RK/7N w - - 0 1", // Endgame that can lead to QN vs Q or RN vs R positions
"1kr5/2bp3q/Q7/1K6/6q1/6B1/8/8 w - - 0 1", // Endgame where triple repetition can and needs to be forced by white
"1kr5/2bp3q/R7/1K6/6q1/6B1/8/8 w - - 96 200" // Endgame where 50 moves draw can and needs to be forced by white
];

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Lynx/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public sealed class EngineSettings
/// <summary>
/// Depth for bench command
/// </summary>
public int BenchDepth { get; set; } = 5;
public int BenchDepth { get; set; } = 8;

/// <summary>
/// MB
Expand Down
48 changes: 47 additions & 1 deletion src/Lynx/Model/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,52 @@ public Game(ReadOnlySpan<char> fen, ReadOnlySpan<char> rawMoves, Span<Range> ran
_gameInitialPosition = new Position(CurrentPosition);
}

/// <summary>
/// Updates <paramref name="halfMovesWithoutCaptureOrPawnMove"/>.
/// See also <see cref="Utils.Update50movesRule(int, int)"/>
/// </summary>
/// <param name="moveToPlay"></param>
/// <param name="isCapture"></param>
/// <remarks>
/// Checking halfMovesWithoutCaptureOrPawnMove >= 100 since a capture/pawn move doesn't necessarily 'clear' the variable.
/// i.e. while the engine is searching:
/// At depth 2, 50 rules move applied and eval is 0
/// At depth 3, there's a capture, but the eval should still be 0
/// At depth 4 there's no capture, but the eval should still be 0
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update50movesRule(Move moveToPlay, bool isCapture)
{
if (isCapture)
{
if (HalfMovesWithoutCaptureOrPawnMove < 100)
{
HalfMovesWithoutCaptureOrPawnMove = 0;
}
else
{
++HalfMovesWithoutCaptureOrPawnMove;
}
}
else
{
var pieceToMove = moveToPlay.Piece();

if ((pieceToMove == (int)Piece.P || pieceToMove == (int)Piece.p) && HalfMovesWithoutCaptureOrPawnMove < 100)
{
HalfMovesWithoutCaptureOrPawnMove = 0;
}
else
{
++HalfMovesWithoutCaptureOrPawnMove;
}
}
}

/// <summary>
/// Basic algorithm described in https://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsThreefoldRepetition(Position position) => PositionHashHistory.Contains(position.UniqueIdentifier);

Expand Down Expand Up @@ -114,7 +160,7 @@ public GameState MakeMove(Move moveToPlay)
}

PositionHashHistory.Add(CurrentPosition.UniqueIdentifier);
HalfMovesWithoutCaptureOrPawnMove = Utils.Update50movesRule(moveToPlay, HalfMovesWithoutCaptureOrPawnMove);
Update50movesRule(moveToPlay, moveToPlay.IsCapture());

return gameState;
}
Expand Down
13 changes: 7 additions & 6 deletions src/Lynx/Search/NegaMax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,13 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM

++_nodes;
isAnyMoveValid = true;
var isCapture = move.IsCapture();

PrintPreMove(position, ply, move);

// Before making a move
var oldValue = Game.HalfMovesWithoutCaptureOrPawnMove;
Game.HalfMovesWithoutCaptureOrPawnMove = Utils.Update50movesRule(move, Game.HalfMovesWithoutCaptureOrPawnMove);
var oldHalfMovesWithoutCaptureOrPawnMove = Game.HalfMovesWithoutCaptureOrPawnMove;
Game.Update50movesRule(move, isCapture);
var isThreeFoldRepetition = !Game.PositionHashHistory.Add(position.UniqueIdentifier);

int evaluation;
Expand Down Expand Up @@ -260,7 +261,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
&& moveIndex >= Configuration.EngineSettings.LMP_BaseMovesToTry + (Configuration.EngineSettings.LMP_MovesDepthMultiplier * depth)) // Based on formula suggested by Antares
{
// After making a move
Game.HalfMovesWithoutCaptureOrPawnMove = oldValue;
Game.HalfMovesWithoutCaptureOrPawnMove = oldHalfMovesWithoutCaptureOrPawnMove;
Game.PositionHashHistory.Remove(position.UniqueIdentifier); // We know that there's no triple repetition here
position.UnmakeMove(move, gameState);

Expand All @@ -274,7 +275,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
if (movesSearched >= (pvNode ? Configuration.EngineSettings.LMR_MinFullDepthSearchedMoves : Configuration.EngineSettings.LMR_MinFullDepthSearchedMoves - 1)
&& depth >= Configuration.EngineSettings.LMR_MinDepth
&& !isInCheck
&& !move.IsCapture())
&& !isCapture)
{
reduction = EvaluationConstants.LMRReductions[depth][movesSearched];

Expand Down Expand Up @@ -329,7 +330,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM

// After making a move
// Game.PositionHashHistory is update above
Game.HalfMovesWithoutCaptureOrPawnMove = oldValue;
Game.HalfMovesWithoutCaptureOrPawnMove = oldHalfMovesWithoutCaptureOrPawnMove;
position.UnmakeMove(move, gameState);

PrintMove(ply, move, evaluation);
Expand All @@ -339,7 +340,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
{
PrintMessage($"Pruning: {move} is enough");

if (move.IsCapture())
if (isCapture)
{
var piece = move.Piece();
var targetSquare = move.TargetSquare();
Expand Down
3 changes: 2 additions & 1 deletion src/Lynx/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ public static (int Source, int Target) LongCastleRookSourceAndTargetSquare(int s
}

/// <summary>
/// Updates <paramref name="halfMovesWithoutCaptureOrPawnMove"/>
/// Updates <paramref name="halfMovesWithoutCaptureOrPawnMove"/>.
/// See also <see cref="Game.Update50movesRule(int, bool)"/>
/// </summary>
/// <param name="moveToPlay"></param>
/// <param name="halfMovesWithoutCaptureOrPawnMove"></param>
Expand Down

0 comments on commit e1e2a7b

Please sign in to comment.