Skip to content

Commit

Permalink
Merge branch 'main' into tt-quiescence
Browse files Browse the repository at this point in the history
  • Loading branch information
eduherminio committed Aug 13, 2023
2 parents cc520b9 + 519d693 commit 31c16f1
Show file tree
Hide file tree
Showing 18 changed files with 89 additions and 56 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,28 @@ If you're a Linux user and are new to .NET ecosystem, the conversation in [this

- Aspiration Windows [[1](https://web.archive.org/web/20071031095918/http://www.brucemo.com/compchess/programming/aspiration.htm)] [[2](https://www.chessprogramming.org/Aspiration_Windows)]

- Transposition Table [[1](https://web.archive.org/web/20071031100051/http://www.brucemo.com/compchess/programming/hashing.htm)]

- Check extensions [[1](https://www.chessprogramming.org/Check_Extensions)]

## Credits

Lynx development wouldn't have been possible without:

- [`BitBoard Chess Engine in C` YouTube playlist](https://www.youtube.com/playlist?list=PLmN0neTso3Jxh8ZIylk74JpwfiWNI76Cs), where [@maksimKorzh](https://github.com/maksimKorzh) explains how he developed his [BBC](https://github.com/maksimKorzh/bbc) engine

- [Chess Programming Wiki](https://www.chessprogramming.org/)

- [`BitBoard Chess Engine in C` YouTube playlist](https://www.youtube.com/playlist?list=PLmN0neTso3Jxh8ZIylk74JpwfiWNI76Cs), where [@maksimKorzh](https://github.com/maksimKorzh) explains how he developed his [BBC](https://github.com/maksimKorzh/bbc) engine
- Engine Programming discord group, especially Jamie Whiting
([Akimbo](https://github.com/JacquesRW/akimbo)), Antares ([Altair](https://github.com/Alex2262/AltairChessEngine)), Ciekce ([Stormphrax](https://github.com/Ciekce/Stormphrax/)), Rak ([Mess](https://github.com/raklaptudirm/mess)), etc.

- The community Discord around [SebLague/Chess-Challenge](https://github.com/SebLague/Chess-Challenge/), which allowed me to discover EP discord and to revisit the basics, this time explained by very knowledgeable developers (such as the ones above) to people without any previous chess engine programming knowledge

- [Marcel Vanthoor's blog](https://rustic-chess.org/) about creating his engine Rustic

- Countless other developers and online resources, who/which I should probably remember, but don't come to my mind right now

Thanks also to all the testers that invest their time in computer chess, especially those ones that test lower rated engines (as opposed to only top ones).

[buildlink]: https://github.com/lynx-chess/Lynx/actions/workflows/ci.yml
[buildlogo]: https://github.com/lynx-chess/Lynx/actions/workflows/ci.yml/badge.svg
Expand Down
2 changes: 1 addition & 1 deletion src/Lynx.Benchmark/FENGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2033,7 +2033,7 @@ public RecordStructCustomPosition(RecordStructCustomPosition position, Move move
_fen = FENHelpers.UpdateSecondPartOfFEN(fenSb, Side, Castle, EnPassant);
}

private string CalculateFEN()
private readonly string CalculateFEN()
{
var sb = new StringBuilder(100);

Expand Down
14 changes: 8 additions & 6 deletions src/Lynx.Benchmark/MakeUnmakeMove_implementation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
* | MakeUnmakeMove_WithZobristKey | (rnbq(...)1, 4) [61] | 29.83 ms | 0.586 ms | 0.651 ms | 0.78 | 0.03 | 4781.2500 | 9.54 MB | 0.22 |
*/

#pragma warning disable S101, S1854 // Types should be named in PascalCase

using BenchmarkDotNet.Attributes;
using Lynx.Model;
using NLog;
Expand Down Expand Up @@ -777,7 +779,7 @@ public void UnMakeNullMove(MakeMoveGameState gameState)
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool IsValid()
internal readonly bool IsValid()
{
var kingBitBoard = PieceBitBoards[(int)Piece.K + Utils.PieceOffset(Side)];
var kingSquare = kingBitBoard == default ? -1 : kingBitBoard.GetLS1BIndex();
Expand All @@ -796,7 +798,7 @@ internal bool IsValid()
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool WasProduceByAValidMove()
public readonly bool WasProduceByAValidMove()
{
var oppositeKingBitBoard = PieceBitBoards[(int)Piece.K + Utils.PieceOffset((Side)Utils.OppositeSide(Side))];
var oppositeKingSquare = oppositeKingBitBoard == default ? -1 : oppositeKingBitBoard.GetLS1BIndex();
Expand All @@ -805,7 +807,7 @@ public bool WasProduceByAValidMove()
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IEnumerable<Move> AllPossibleMoves(Move[]? movePool = null) => MakeMoveMoveGenerator.GenerateAllMoves(this, movePool);
public readonly IEnumerable<Move> AllPossibleMoves(Move[]? movePool = null) => MakeMoveMoveGenerator.GenerateAllMoves(this, movePool);
}

public readonly struct MakeMoveGameState
Expand Down Expand Up @@ -990,15 +992,13 @@ public static class MakeMoveMoveGenerator
(int origin, BitBoard _) => MakeMoveAttacks.KnightAttacks[origin],
(int origin, BitBoard occupancy) => MakeMoveAttacks.BishopAttacks(origin, occupancy),
(int origin, BitBoard occupancy) => MakeMoveAttacks.RookAttacks(origin, occupancy),
// TODO try to improve performance by re-using bishop and rook attacks
(int origin, BitBoard occupancy) => MakeMoveAttacks.QueenAttacks(origin, occupancy),
(int origin, BitBoard _) => MakeMoveAttacks.KingAttacks[origin],

(int origin, BitBoard _) => MakeMoveAttacks.PawnAttacks[(int)Side.Black, origin],
(int origin, BitBoard _) => MakeMoveAttacks.KnightAttacks[origin],
(int origin, BitBoard occupancy) => MakeMoveAttacks.BishopAttacks(origin, occupancy),
(int origin, BitBoard occupancy) => MakeMoveAttacks.RookAttacks(origin, occupancy),
// TODO try to improve performance by re-using bishop and rook attacks
(int origin, BitBoard occupancy) => MakeMoveAttacks.QueenAttacks(origin, occupancy),
(int origin, BitBoard _) => MakeMoveAttacks.KingAttacks[origin],
};
Expand Down Expand Up @@ -1404,4 +1404,6 @@ private static bool IsSquareAttackedByQueens(int offset, BitBoard bishopAttacks,
}

#endregion
}
}

#pragma warning restore S101, S1854 // Types should be named in PascalCase
21 changes: 10 additions & 11 deletions src/Lynx.Benchmark/MakeUnmakeMove_integration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
* | MakeUnmakeMove_PassRef | (rnbq(...)1, 4) [61] | 27.016 ms | 0.5351 ms | 0.8940 ms | 26.693 ms | 0.97 | 0.05 | 375.0000 | - | 9.54 MB | 0.22 |
*/

#pragma warning disable S101, S1854 // Types should be named in PascalCase
using BenchmarkDotNet.Attributes;
using Lynx.Model;
using NLog;
Expand All @@ -156,7 +157,7 @@ public class MakeUnmakeMove_integration : BaseBenchmark
("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 4),
("3K4/8/8/8/8/8/4p3/2k2R2 b - - 0 1", 6),
("2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1", 6),
("8/p7/8/1P6/K1k3p1/6P1/7P/8 w - -", 6),
("8/p7/8/1P6/K1k3p1/6P1/7P/8 w - -", 6)
};

[Benchmark(Baseline = true)]
Expand Down Expand Up @@ -1178,7 +1179,7 @@ public void UnMakeNullMove(MakeMoveGameState gameState)
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool IsValid()
internal readonly bool IsValid()
{
var kingBitBoard = PieceBitBoards[(int)Piece.K + Utils.PieceOffset(Side)];
var kingSquare = kingBitBoard == default ? -1 : kingBitBoard.GetLS1BIndex();
Expand All @@ -1197,7 +1198,7 @@ internal bool IsValid()
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool WasProduceByAValidMove()
public readonly bool WasProduceByAValidMove()
{
var oppositeKingBitBoard = PieceBitBoards[(int)Piece.K + Utils.PieceOffset((Side)Utils.OppositeSide(Side))];
var oppositeKingSquare = oppositeKingBitBoard == default ? -1 : oppositeKingBitBoard.GetLS1BIndex();
Expand All @@ -1206,7 +1207,7 @@ public bool WasProduceByAValidMove()
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IEnumerable<Move> AllPossibleMoves(Move[]? movePool = null) => MakeMoveMoveGenerator.GenerateAllMoves(this, movePool);
public readonly IEnumerable<Move> AllPossibleMoves(Move[]? movePool = null) => MakeMoveMoveGenerator.GenerateAllMoves(this, movePool);
}

public readonly struct MakeMoveGameState
Expand Down Expand Up @@ -1245,19 +1246,17 @@ public MakeMoveGameState_PassOut(int capturedPiece, int castle, BoardSquare enpa
}
}


public struct MakeMoveGameState_PassRef
{
#pragma warning disable S1104 // Fields should not have public accessibility
public int CapturedPiece;

public int Castle;

public BoardSquare EnPassant;

// TODO: save full Zobrist key?
#pragma warning restore S1104 // Fields should not have public accessibility
}


#region ;(

public static class MakeMoveZobristTable
Expand Down Expand Up @@ -1403,15 +1402,13 @@ public static class MakeMoveMoveGenerator
(int origin, BitBoard _) => MakeMoveAttacks.KnightAttacks[origin],
(int origin, BitBoard occupancy) => MakeMoveAttacks.BishopAttacks(origin, occupancy),
(int origin, BitBoard occupancy) => MakeMoveAttacks.RookAttacks(origin, occupancy),
// TODO try to improve performance by re-using bishop and rook attacks
(int origin, BitBoard occupancy) => MakeMoveAttacks.QueenAttacks(origin, occupancy),
(int origin, BitBoard _) => MakeMoveAttacks.KingAttacks[origin],

(int origin, BitBoard _) => MakeMoveAttacks.PawnAttacks[(int)Side.Black, origin],
(int origin, BitBoard _) => MakeMoveAttacks.KnightAttacks[origin],
(int origin, BitBoard occupancy) => MakeMoveAttacks.BishopAttacks(origin, occupancy),
(int origin, BitBoard occupancy) => MakeMoveAttacks.RookAttacks(origin, occupancy),
// TODO try to improve performance by re-using bishop and rook attacks
(int origin, BitBoard occupancy) => MakeMoveAttacks.QueenAttacks(origin, occupancy),
(int origin, BitBoard _) => MakeMoveAttacks.KingAttacks[origin],
};
Expand Down Expand Up @@ -1817,4 +1814,6 @@ private static bool IsSquareAttackedByQueens(int offset, BitBoard bishopAttacks,
}

#endregion
}
}

#pragma warning restore S101, S1854 // Types should be named in PascalCase
1 change: 1 addition & 0 deletions src/Lynx.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
engineChannel.Writer.TryComplete();
uciChannel.Writer.TryComplete();
//source.Cancel();
NLog.LogManager.Shutdown(); // Flush and close down internal threads and timers
}

Thread.Sleep(2_000);
2 changes: 2 additions & 0 deletions src/Lynx.Cli/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"UseOnlineTablebaseInSearch": false
},
"NLog": {
"internalLogLevel": "Warn",
"throwExceptions": true,
"targets": {
"console": {
"type": "ColoredConsole",
Expand Down
7 changes: 4 additions & 3 deletions src/Lynx.Cli/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,16 @@

// Logging settings
"NLog": {
"autoreload": true,
"autoreload": false,
"internalLogLevel": "Error",
"internalLogFile": "${basedir}/logs/internal-nlog.txt",
"throwExceptions": true,
"throwExceptions": false,
"variables": {
"logDirectory": "${basedir}/logs",
"archiveLogDirectory": "${basedir}/logs/archives"
},
"targets": {
"async": true,
"errors": {
"type": "File",
"layout": "${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${processid}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}",
Expand Down Expand Up @@ -186,4 +187,4 @@
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/Lynx/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ public static class Constants
/// Black kingside Rook moved 1111 & 1011 = 1011 11
/// Black queenside Rook moved 1111 & 0111 = 0111 7
/// </summary>
public static readonly int[] CastlingRightsUpdateConstants = new int[64]
public static readonly byte[] CastlingRightsUpdateConstants = new byte[64]
{
7, 15, 15, 15, 3, 15, 15, 11,
15, 15, 15, 15, 15, 15, 15, 15,
Expand Down
1 change: 0 additions & 1 deletion src/Lynx/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ private SearchResult SearchBestMove(int minDepth, int? maxDepth, int? decisionTi
// Local copy of positionHashHistory and HalfMovesWithoutCaptureOrPawnMove so that it doesn't interfere with regular search
var currentHalfMovesWithoutCaptureOrPawnMove = Game.HalfMovesWithoutCaptureOrPawnMove;


var tasks = new Task<SearchResult?>[] {
ProbeOnlineTablebase(Game.CurrentPosition, new(Game.PositionHashHistory), currentHalfMovesWithoutCaptureOrPawnMove), // Other copies of positionHashHistory and HalfMovesWithoutCaptureOrPawnMove (same reason)
IDDFS(minDepth, maxDepth, decisionTime),
Expand Down
16 changes: 8 additions & 8 deletions src/Lynx/FENParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static partial class FENParser

private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

public static (bool Success, BitBoard[] PieceBitBoards, BitBoard[] OccupancyBitBoards, Side Side, int Castle, BoardSquare EnPassant,
public static (bool Success, BitBoard[] PieceBitBoards, BitBoard[] OccupancyBitBoards, Side Side, byte Castle, BoardSquare EnPassant,
int HalfMoveClock, int FullMoveCounter) ParseFEN(string fen)
{
fen = fen.Trim();
Expand All @@ -27,7 +27,7 @@ public static partial class FENParser

bool success;
Side side = Side.Both;
int castle = 0;
byte castle = 0;
int halfMoveClock = 0, fullMoveCounter = 1;
BoardSquare enPassant = BoardSquare.noSquare;

Expand Down Expand Up @@ -140,18 +140,18 @@ private static Side ParseSide(string sideString)
#pragma warning restore S3358 // Ternary operators should not be nested
}

private static int ParseCastlingRights(string castleString)
private static byte ParseCastlingRights(string castleString)
{
int castle = 0;
byte castle = 0;

foreach (var ch in castleString)
{
castle |= ch switch
{
'K' => (int)CastlingRights.WK,
'Q' => (int)CastlingRights.WQ,
'k' => (int)CastlingRights.BK,
'q' => (int)CastlingRights.BQ,
'K' => (byte)CastlingRights.WK,
'Q' => (byte)CastlingRights.WQ,
'k' => (byte)CastlingRights.BK,
'q' => (byte)CastlingRights.BQ,
'-' => castle,
_ => throw new($"Unrecognized castling char: {ch}")
};
Expand Down
2 changes: 1 addition & 1 deletion src/Lynx/LinxDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ private void HandleDivide(string rawCommand)

if (items.Length >= 2 && int.TryParse(items[1], out int depth) && depth >= 1)
{
Perft.Results(_engine.Game.CurrentPosition, depth);
Perft.Divide(_engine.Game.CurrentPosition, depth);
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/Lynx/Model/BitBoard.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics.X86;

#pragma warning disable S4136

Expand Down Expand Up @@ -121,6 +122,11 @@ public static int GetLS1BIndex(this BitBoard board)
{
Utils.Assert(board != default);

if (Bmi1.X64.IsSupported)
{
return (int)Bmi1.X64.TrailingZeroCount(board);
}

return BitOperations.TrailingZeroCount(board);
}

Expand Down Expand Up @@ -149,6 +155,11 @@ public static void ResetLS1B(this ref BitBoard board)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CountBits(this BitBoard board)
{
if (Popcnt.X64.IsSupported)
{
return (int)Popcnt.X64.PopCount(board);
}

return BitOperations.PopCount(board);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Lynx/Model/BoardSquare.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
/// SW S SE
///
/// </summary>
public enum BoardSquare
public enum BoardSquare : sbyte
{
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
Expand Down
2 changes: 1 addition & 1 deletion src/Lynx/Model/CastlingRights.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/// * 1111 Both sides can castle both directions
/// * 1001 Black king => only O-O-O; White king => only O-O
/// </summary>
public enum CastlingRights
public enum CastlingRights : byte
{
WK = 1, WQ = 2, BK = 4, BQ = 8
}
6 changes: 3 additions & 3 deletions src/Lynx/Model/GameState.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
namespace Lynx.Model;
public readonly struct GameState
{
public readonly int CapturedPiece;
public readonly sbyte CapturedPiece;

public readonly int Castle;
public readonly byte Castle;

public readonly BoardSquare EnPassant;

public readonly long ZobristKey;

// TODO: save full Zobrist key?

public GameState(int capturedPiece, int castle, BoardSquare enpassant, long zobristKey)
public GameState(sbyte capturedPiece, byte castle, BoardSquare enpassant, long zobristKey)
{
CapturedPiece = capturedPiece;
Castle = castle;
Expand Down
2 changes: 1 addition & 1 deletion src/Lynx/Model/Piece.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Lynx.Model;

public enum Piece
public enum Piece : sbyte
{
Unknown = -1,
P, N, B, R, Q, K, // White
Expand Down

0 comments on commit 31c16f1

Please sign in to comment.