├── resources
├── icon.ico
└── lynx.png
├── src
├── Lynx
│ ├── Model
│ │ ├── Side.cs
│ │ ├── Piece.cs
│ │ ├── PlyStackEntry.cs
│ │ ├── PawnTableElement.cs
│ │ ├── TranspositionTableFactory.cs
│ │ ├── CastlingRights.cs
│ │ ├── PVTable.cs
│ │ ├── CastlingData.cs
│ │ ├── TTProbeResult.cs
│ │ ├── EvaluationContext.cs
│ │ ├── SearchConstraints.cs
│ │ ├── BoardSquare.cs
│ │ ├── ParseFENResult.cs
│ │ ├── GameState.cs
│ │ ├── TablebaseResult.cs
│ │ ├── SearchResult.cs
│ │ └── TranspositionTableElement.cs
│ ├── UCI
│ │ └── Commands
│ │ │ ├── GUI
│ │ │ ├── QuitCommand.cs
│ │ │ ├── StopCommand.cs
│ │ │ ├── PonderHitCommand.cs
│ │ │ ├── UCICommand.cs
│ │ │ ├── IsReadyCommand.cs
│ │ │ ├── UCINewGameCommand.cs
│ │ │ ├── SetOptionCommand.cs
│ │ │ ├── DebugCommand.cs
│ │ │ ├── PositionCommand.cs
│ │ │ └── RegisterCommand.cs
│ │ │ └── Engine
│ │ │ ├── UciOKCommand.cs
│ │ │ ├── ReadyOKCommand.cs
│ │ │ ├── CopyProtectionCommand.cs
│ │ │ ├── IdCommand.cs
│ │ │ ├── BestMoveCommand.cs
│ │ │ ├── RegistrationCommand.cs
│ │ │ └── InfoCommand.cs
│ ├── LynxException.cs
│ ├── ObjectPools.cs
│ ├── SilentChannelWriter.cs
│ ├── WDL.cs
│ ├── Search
│ │ └── OnlineTablebase.cs
│ └── LynxRandom.cs
├── Lynx.Benchmark
│ ├── BaseBenchmark.cs
│ ├── Program.cs
│ ├── Lynx.Benchmark.csproj
│ ├── UCI_Benchmark.cs
│ ├── ResetLS1B_Benchmark.cs
│ ├── PriorityQueue_EnqueueRange_Benchmark.cs
│ ├── EnumCasting_Benchmark.cs
│ ├── EncodeMove_Benchmark.cs
│ ├── PVTable_SumVsArrayAccess_Benchmark.cs
│ ├── PieceOffset_Boolean_Benchmark.cs
│ ├── PEXT_Benchmark.cs
│ ├── ResetLS1BvsWithoutLS1B_Benchmark.cs
│ ├── ZobristHash_EnPassant_Benchmark.cs
│ └── IsDarkSquare_IsLightSquare_Benchmark.cs
├── Lynx.Dev
│ ├── Properties
│ │ └── launchSettings.json
│ └── Lynx.Dev.csproj
├── Lynx.Cli
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Program.cs
│ ├── Writer.cs
│ ├── Listener.cs
│ ├── appsettings.Development.json
│ ├── Runner.cs
│ └── Lynx.Cli.csproj
└── Lynx.ConstantsGenerator
│ ├── Lynx.ConstantsGenerator.csproj
│ ├── OccupancyGenerator.cs
│ ├── BitBoardsGenerator.cs
│ └── PawnIslandsGenerator.cs
├── tests
├── Lynx.Test
│ ├── Properties
│ │ └── AssembyInfo.cs
│ ├── PerftFRCXFENTestSuite.cs
│ ├── Model
│ │ ├── PVTableTest.cs
│ │ ├── BoardSquareTest.cs
│ │ └── MoveScoreTest.cs
│ ├── BestMove
│ │ ├── SacrificesTest.cs
│ │ ├── IncreaseDepthWhenInCheckTest.cs
│ │ ├── QuiescenceTest.cs
│ │ ├── SingleLegalMoveTest.cs
│ │ ├── MatesInExactlyXTest.cs
│ │ └── MatesTest.cs
│ ├── ConfigurationValuesTest.cs
│ ├── Categories.cs
│ ├── IncrementalEvalTest.cs
│ ├── UCIHandlerTest.cs
│ ├── Lynx.Test.csproj
│ ├── Commands
│ │ ├── StopCommandTest.cs
│ │ ├── GoCommandTest.cs
│ │ └── PositionCommandTest.cs
│ ├── PregeneratedAttacks
│ │ ├── RookOccupancyTest.cs
│ │ ├── BishopOccupancyTest.cs
│ │ ├── SetBishopOrRookOccupancyTest.cs
│ │ ├── KingAttacksTest.cs
│ │ ├── KnightAttacksTest.cs
│ │ ├── PawnAttacksTest.cs
│ │ └── BishopAttacksTest.cs
│ ├── BaseTest.cs
│ ├── MoveGeneration
│ │ ├── RegressionTest.cs
│ │ ├── GeneralMoveGeneratorTest.cs
│ │ └── CastlingMoveTest.cs
│ ├── WDLTest.cs
│ ├── ConfigurationTest.cs
│ ├── ZobristHashGenerationTest.cs
│ ├── PSQTTest.cs
│ └── PawnIslandsTest.cs
└── runsettings.xml
├── global.json
├── .github
├── dependabot.yml
└── workflows
│ ├── on_release.yml
│ ├── bench.yml
│ ├── on-demand-tests.yml
│ ├── benchmarks.yml
│ └── perft.yml
├── .git-blame-ignore-revs
├── Lynx.slnx
├── LICENSE
├── Makefile
├── Directory.Build.props
└── Directory.Packages.props
/resources/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lynx-chess/Lynx/HEAD/resources/icon.ico
--------------------------------------------------------------------------------
/resources/lynx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lynx-chess/Lynx/HEAD/resources/lynx.png
--------------------------------------------------------------------------------
/src/Lynx/Model/Side.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Model;
2 |
3 | public enum Side { Black, White, Both }
4 |
--------------------------------------------------------------------------------
/tests/Lynx.Test/Properties/AssembyInfo.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | [assembly: Parallelizable(ParallelScope.All)]
--------------------------------------------------------------------------------
/tests/Lynx.Test/PerftFRCXFENTestSuite.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lynx-chess/Lynx/HEAD/tests/Lynx.Test/PerftFRCXFENTestSuite.cs
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "10.0.100",
4 | "rollForward": "latestMajor",
5 | "allowPrerelease": false
6 | }
7 | }
--------------------------------------------------------------------------------
/src/Lynx.Benchmark/BaseBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 |
3 | namespace Lynx.Benchmark;
4 |
5 | [MarkdownExporterAttribute.GitHub]
6 | [HtmlExporter]
7 | [MemoryDiagnoser]
8 | //[NativeMemoryProfiler]
9 | public class BaseBenchmark;
10 |
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/GUI/QuitCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.GUI;
2 |
3 | ///
4 | /// quit
5 | /// quit the program as soon as possible
6 | ///
7 | public sealed class QuitCommand
8 | {
9 | public const string Id = "quit";
10 | }
11 |
--------------------------------------------------------------------------------
/src/Lynx.Dev/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Lynx.Dev": {
4 | "commandName": "Project"
5 | },
6 | "WSL 2": {
7 | "commandName": "WSL2",
8 | "environmentVariables": {},
9 | "distributionName": ""
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/GUI/StopCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.GUI;
2 |
3 | ///
4 | /// stop
5 | /// stop calculating as soon as possible,
6 | /// don't forget the "bestmove" and possibly the "ponder" token when finishing the search
7 | ///
8 | public sealed class StopCommand
9 | {
10 | public const string Id = "stop";
11 | }
12 |
--------------------------------------------------------------------------------
/tests/Lynx.Test/Model/PVTableTest.cs:
--------------------------------------------------------------------------------
1 | using Lynx.Model;
2 | using NUnit.Framework;
3 |
4 | namespace Lynx.Test.Model;
5 |
6 | public class PVTableTest
7 | {
8 | [Test]
9 | public void PVTable_Indexes_Support_MaxDepthPlusOne()
10 | {
11 | Assert.DoesNotThrow(() => _ = PVTable.Indexes[Configuration.EngineSettings.MaxDepth]);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/Engine/UciOKCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.Engine;
2 |
3 | ///
4 | /// uciok
5 | /// Must be sent after the id and optional options to tell the GUI that the engine
6 | /// has sent all infos and is ready in uci mode.
7 | ///
8 | public sealed class UciOKCommand
9 | {
10 | public const string Id = "uciok";
11 | }
12 |
--------------------------------------------------------------------------------
/src/Lynx/Model/Piece.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Model;
2 |
3 | #pragma warning disable CA1708 // Identifiers should differ by more than case
4 |
5 | public enum Piece
6 | {
7 | Unknown = -1,
8 | P, N, B, R, Q, K, // White
9 | p, n, b, r, q, k, // Black
10 | None
11 | }
12 |
13 | #pragma warning restore CA1708 // Identifiers should differ by more than case
14 |
--------------------------------------------------------------------------------
/src/Lynx/LynxException.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx;
2 |
3 | public class LynxException : Exception
4 | {
5 | public LynxException()
6 | {
7 | }
8 |
9 | public LynxException(string? message) : base(message)
10 | {
11 | }
12 |
13 | public LynxException(string? message, Exception? innerException) : base(message, innerException)
14 | {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Lynx.Cli/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Lynx.Cli": {
4 | "commandName": "Project",
5 | "environmentVariables": {
6 | "ASPNETCORE_ENVIRONMENT": "Development"
7 | }
8 | },
9 | "WSL 2": {
10 | "commandName": "WSL2",
11 | "environmentVariables": {
12 | "ASPNETCORE_ENVIRONMENT": "Development"
13 | },
14 | "distributionName": ""
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "nuget"
4 | directory: "/"
5 | allow:
6 | - dependency-type: "all"
7 | ignore:
8 | - dependency-name: "Moq"
9 | schedule:
10 | interval: "weekly"
11 | assignees :
12 | - "eduherminio"
13 | - package-ecosystem: "github-actions"
14 | directory: "/"
15 | schedule:
16 | interval: "weekly"
17 | assignees :
18 | - "eduherminio"
19 |
--------------------------------------------------------------------------------
/src/Lynx.Benchmark/Program.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Running;
2 | using System.Reflection;
3 | #if DEBUG
4 | using BenchmarkDotNet.Configs;
5 | #endif
6 |
7 | //Lynx.Benchmark.BitBoard_Struct_ReadonlyStruct_Class_Record.SizeTest();
8 |
9 | #if DEBUG
10 | BenchmarkSwitcher.FromAssembly(Assembly.GetExecutingAssembly()).Run(args, new DebugInProcessConfig());
11 | #else
12 | BenchmarkSwitcher.FromAssembly(Assembly.GetExecutingAssembly()).Run(args);
13 | #endif
14 |
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/GUI/PonderHitCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.GUI;
2 |
3 | ///
4 | /// ponderhit
5 | /// the user has played the expected move. This will be sent if the engine was told to ponder on the same move
6 | /// the user has played. The engine should continue searching but switch from pondering to normal search.
7 | ///
8 | public sealed class PonderHitCommand
9 | {
10 | public const string Id = "ponderhit";
11 | }
12 |
--------------------------------------------------------------------------------
/src/Lynx.ConstantsGenerator/Lynx.ConstantsGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | NonProdExe
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | $(NoWarn),CS8321,S125,S1481,CS0618,CS1591,S3353,S106,S2228
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Lynx/ObjectPools.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.ObjectPool;
2 | using System.Text;
3 |
4 | namespace Lynx;
5 | public static class ObjectPools
6 | {
7 | public static readonly ObjectPool StringBuilderPool =
8 | new DefaultObjectPoolProvider().CreateStringBuilderPool(
9 | initialCapacity: 256,
10 | maximumRetainedCapacity: 4 * 1024); //Default value in StringBuilderPooledObjectPolicy.MaximumRetainedCapacity)
11 | }
12 |
--------------------------------------------------------------------------------
/src/Lynx/Model/PlyStackEntry.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Model;
2 |
3 | public struct PlyStackEntry
4 | {
5 | public int StaticEval { get; set; }
6 |
7 | public int DoubleExtensions { get; set; }
8 |
9 | public Move Move { get; set; }
10 |
11 | public PlyStackEntry()
12 | {
13 | Reset();
14 | }
15 |
16 | public void Reset()
17 | {
18 | StaticEval = int.MaxValue;
19 | DoubleExtensions = 0;
20 | Move = 0;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Lynx/Model/PawnTableElement.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Model;
2 |
3 | #pragma warning disable CA1051 // Do not declare visible instance fields
4 |
5 | public struct PawnTableElement
6 | {
7 | public ulong Key;
8 |
9 | public int PackedScore;
10 |
11 | public void Update(ulong key, int packedScore)
12 | {
13 | Key = key;
14 | PackedScore = packedScore;
15 | }
16 | }
17 |
18 | #pragma warning restore CA1051 // Do not declare visible instance fields
19 |
--------------------------------------------------------------------------------
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # Seal all relevant classes
2 | ade22ec3dd024f16544f34a3f5c27b25c487bc1f
3 |
4 | # Enable implicit usings
5 | d4c05906bb9129bc00c5f8d91d61dd49c446fd7d
6 |
7 | # File scoped namespaces
8 | 403c9ee3ff5c23bb6564b27f9aeaa6960354bc10
9 |
10 | # Benchmark files rename
11 | 0ef149771cad8c69b3024273ec18c33d21fa571e
12 |
13 | # General formatting/cleaning commits
14 | 0c7a8ce29a88c182dd9db95fd6dffbf580a51ba2
15 | d3df78f393b5486954964533bb4ef59743b88447
16 | c5deabcaa157161ef782525dc2e4dbbdc471e3c1
--------------------------------------------------------------------------------
/src/Lynx.Dev/Lynx.Dev.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | NonProdExe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | $(NoWarn),CS8321,S125,S1481,CS0618
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Lynx/Model/TranspositionTableFactory.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Model;
2 |
3 | public static class TranspositionTableFactory
4 | {
5 | public static ITranspositionTable Create()
6 | {
7 | var hashBytes = (ulong)Configuration.EngineSettings.TranspositionTableSize * 1024 * 1024;
8 | var ttEntries = hashBytes / TranspositionTableElement.Size;
9 |
10 | if (ttEntries <= (ulong)Constants.MaxTTArrayLength)
11 | {
12 | return new SingleArrayTranspositionTable();
13 | }
14 |
15 | return new MultiArrayTranspositionTable();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/Engine/ReadyOKCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.Engine;
2 |
3 | ///
4 | /// readyok
5 | /// This must be sent when the engine has received an "isready" command and has
6 | /// processed all input and is ready to accept new commands now.
7 | /// It is usually sent after a command that can take some time to be able to wait for the engine,
8 | /// but it can be used anytime, even when the engine is searching,
9 | /// and must always be answered with "isready".
10 | ///
11 | public sealed class ReadyOKCommand
12 | {
13 | public const string Id = "readyok";
14 | }
15 |
--------------------------------------------------------------------------------
/tests/Lynx.Test/BestMove/SacrificesTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace Lynx.Test.BestMove;
4 |
5 | public class SacrificesTest : BaseTest
6 | {
7 | [Explicit]
8 | [Category(Categories.LongRunning)]
9 | [TestCase("4n3/1p2k2p/p2p2pP/P1bP1pP1/2P2P2/2BB4/3K4/8 w - - 0 43", new[] { "d3f5" },
10 | Description = "Actual Bishop sacrifice - https://lichess.org/VaY6zfHI/white#84")]
11 | public void Sacrifices(string fen, string[]? allowedUCIMoveString, string[]? excludedUCIMoveString = null)
12 | {
13 | TestBestMove(fen, allowedUCIMoveString, excludedUCIMoveString, depth: 25);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Lynx/Model/CastlingRights.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Model;
2 |
3 | ///
4 | /// bin dec
5 | /// 0001 1 White king can O-O
6 | /// 0010 2 White king can O-O-O
7 | /// 0100 4 Black king can O-O
8 | /// 1000 8 Black king can O-O-O
9 | /// Examples:
10 | /// * 1111 Both sides can castle both directions
11 | /// * 1001 Black king => only O-O-O; White king => only O-O
12 | ///
13 | [Flags]
14 | #pragma warning disable S4022 // Enumerations should have "Int32" storage
15 | public enum CastlingRights : byte
16 | #pragma warning restore S4022 // Enumerations should have "Int32" storage
17 | {
18 | None = 0, // RCS1135
19 | WK = 1, WQ = 2, BK = 4, BQ = 8
20 | }
21 |
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/GUI/UCICommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.GUI;
2 |
3 | ///
4 | /// uci
5 | /// tell engine to use the uci (universal chess interface),
6 | /// this will be send once as a first command after program boot
7 | /// to tell the engine to switch to uci mode.
8 | /// After receiving the uci command the engine must identify itself with the "id" command
9 | /// and sent the "option" commands to tell the GUI which engine settings the engine supports if any.
10 | /// After that the engine should sent "uciok" to acknowledge the uci mode.
11 | /// If no uciok is sent within a certain time period, the engine task will be killed by the GUI.
12 | ///
13 | public sealed class UCICommand
14 | {
15 | public const string Id = "uci";
16 | }
17 |
--------------------------------------------------------------------------------
/src/Lynx/Model/PVTable.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 |
3 | namespace Lynx.Model;
4 |
5 | public static class PVTable
6 | {
7 | public static readonly ImmutableArray Indexes = Initialize();
8 |
9 | private static ImmutableArray Initialize()
10 | {
11 | Span indexes = stackalloc int[Configuration.EngineSettings.MaxDepth + Constants.ArrayDepthMargin];
12 | int previousPVIndex = 0;
13 | indexes[0] = previousPVIndex;
14 |
15 | for (int depth = 0; depth < indexes.Length - 1; ++depth)
16 | {
17 | indexes[depth + 1] = previousPVIndex + Configuration.EngineSettings.MaxDepth - depth;
18 | previousPVIndex = indexes[depth + 1];
19 | }
20 |
21 | return [.. indexes];
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.github/workflows/on_release.yml:
--------------------------------------------------------------------------------
1 | name: Notify release
2 |
3 | on:
4 | release:
5 | types: [released]
6 |
7 | jobs:
8 | release-lynx-bot:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: "Extract tag from '${{ github.ref }}'"
14 | shell: pwsh
15 | run: |
16 | $tag = "${{ github.ref }}".Replace("refs/tags/", "")
17 | echo "RELEASE_TAG=$tag" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
18 |
19 | - name: 'Repository Dispatch (tag: ${{ env.RELEASE_TAG }})'
20 | uses: peter-evans/repository-dispatch@v2
21 | with:
22 | token: ${{ secrets.REPO_ACCESS_TOKEN }}
23 | repository: lynx-chess/Lynx_BOT
24 | event-type: lynx-release
25 | client-payload: '{"tag": "${{ env.RELEASE_TAG }}", "sha": "${{ github.sha }}"}'
26 |
--------------------------------------------------------------------------------
/tests/runsettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | cobertura
8 | true
9 | Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | true
18 |
19 |
--------------------------------------------------------------------------------
/src/Lynx.Benchmark/Lynx.Benchmark.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | Benchmark
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | $(NoWarn),CA1822,CA1806,CS0618
21 | true
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/Lynx/Model/CastlingData.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Model;
2 |
3 | #pragma warning disable CA1051 // Do not declare visible instance fields
4 |
5 | public readonly ref struct CastlingData
6 | {
7 | public const int DefaultValues = -1;
8 |
9 | public readonly int WhiteKingsideRook;
10 | public readonly int WhiteQueensideRook;
11 | public readonly int BlackKingsideRook;
12 | public readonly int BlackQueensideRook;
13 |
14 | public CastlingData(
15 | int whiteKingsideRook,
16 | int whiteQueensideRook,
17 | int blackKingsideRook,
18 | int blackQueensideRook)
19 | {
20 | WhiteKingsideRook = whiteKingsideRook;
21 | WhiteQueensideRook = whiteQueensideRook;
22 | BlackKingsideRook = blackKingsideRook;
23 | BlackQueensideRook = blackQueensideRook;
24 | }
25 | }
26 |
27 | #pragma warning restore CA1051 // Do not declare visible instance fields
28 |
--------------------------------------------------------------------------------
/src/Lynx/Model/TTProbeResult.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Model;
2 |
3 | #pragma warning disable CA1051 // Do not declare visible instance fields
4 |
5 | public readonly ref struct TTProbeResult
6 | {
7 | public readonly int Score = EvaluationConstants.NoScore;
8 |
9 | public readonly int StaticEval = EvaluationConstants.NoScore;
10 |
11 | public readonly int Depth;
12 |
13 | public readonly short BestMove;
14 |
15 | public readonly NodeType NodeType;
16 |
17 | public readonly bool WasPv;
18 |
19 | public TTProbeResult(int score, short bestMove, NodeType nodeType, int staticEval, int depth, bool wasPv)
20 | {
21 | Score = score;
22 | BestMove = bestMove;
23 | NodeType = nodeType;
24 | StaticEval = staticEval;
25 | Depth = depth;
26 | WasPv = wasPv;
27 | }
28 | }
29 |
30 | #pragma warning restore CA1051 // Do not declare visible instance fields
31 |
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/GUI/IsReadyCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.GUI;
2 |
3 | ///
4 | /// isready
5 | /// this is used to synchronize the engine with the GUI. When the GUI has sent a command or
6 | /// multiple commands that can take some time to complete,
7 | /// this command can be used to wait for the engine to be ready again or
8 | /// to ping the engine to find out if it is still alive.
9 | /// E.g. this should be sent after setting the path to the tablebases as this can take some time.
10 | /// This command is also required once before the engine is asked to do any search
11 | /// to wait for the engine to finish initializing.
12 | /// This command must always be answered with "readyok" and can be sent also when the engine is calculating
13 | /// in which case the engine should also immediately answer with "readyok" without stopping the search.
14 | ///
15 | public sealed class IsReadyCommand
16 | {
17 | public const string Id = "isready";
18 | }
19 |
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/GUI/UCINewGameCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.GUI;
2 |
3 | ///
4 | /// ucinewgame
5 | /// this is sent to the engine when the next search (started with "position" and "go") will be from
6 | /// a different game. This can be a new game the engine should play or a new game it should analyse but
7 | /// also the next position from a testsuite with positions only.
8 | /// If the GUI hasn't sent a "ucinewgame" before the first "position" command, the engine shouldn't
9 | /// expect any further ucinewgame commands as the GUI is probably not supporting the ucinewgame command.
10 | /// So the engine should not rely on this command even though all new GUIs should support it.
11 | /// As the engine's reaction to "ucinewgame" can take some time the GUI should always send "isready"
12 | /// after "ucinewgame" to wait for the engine to finish its operation.
13 | ///
14 | public sealed class UCINewGameCommand
15 | {
16 | public const string Id = "ucinewgame";
17 | }
18 |
--------------------------------------------------------------------------------
/src/Lynx/Model/EvaluationContext.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Model;
2 |
3 | #pragma warning disable CA1051 // Do not declare visible instance fields
4 |
5 | public ref struct EvaluationContext
6 | {
7 | public Span Attacks;
8 | public Span AttacksBySide;
9 |
10 | public int WhiteKingRingAttacks;
11 | public int BlackKingRingAttacks;
12 |
13 | public EvaluationContext(Span attacks, Span attacksBySide)
14 | {
15 | Attacks = attacks;
16 | AttacksBySide = attacksBySide;
17 |
18 | Attacks.Clear();
19 | AttacksBySide.Clear();
20 | }
21 |
22 | public void IncreaseKingRingAttacks(int side, int count)
23 | {
24 | if (side == (int)Side.White)
25 | {
26 | WhiteKingRingAttacks += count;
27 | }
28 | else
29 | {
30 | BlackKingRingAttacks += count;
31 | }
32 | }
33 | }
34 |
35 | #pragma warning restore CA1051 // Do not declare visible instance fields
36 |
--------------------------------------------------------------------------------
/tests/Lynx.Test/ConfigurationValuesTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using NUnit.Framework;
3 |
4 | namespace Lynx.Test;
5 |
6 | ///
7 | /// Current logic relies on this
8 | ///
9 | [Explicit]
10 | [Category(Categories.Configuration)]
11 | [NonParallelizable]
12 | public class ConfigurationValuesTest
13 | {
14 | [Test]
15 | public void RazoringValues()
16 | {
17 | Assert.Greater(Configuration.EngineSettings.RFP_MaxDepth, Configuration.EngineSettings.Razoring_MaxDepth);
18 |
19 | var config = new ConfigurationBuilder()
20 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
21 | .Build();
22 |
23 | var engineSettingsSection = config.GetRequiredSection(nameof(EngineSettings));
24 | Assert.IsNotNull(engineSettingsSection);
25 | engineSettingsSection.Bind(Configuration.EngineSettings);
26 |
27 | Assert.Greater(Configuration.EngineSettings.RFP_MaxDepth, Configuration.EngineSettings.Razoring_MaxDepth);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/GUI/SetOptionCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.GUI;
2 |
3 | ///
4 | /// setoption name [value]
5 | /// this is sent to the engine when the user wants to change the internal parameters
6 | /// of the engine. For the "button" type no value is needed.
7 | /// One string will be sent for each parameter and this will only be sent when the engine is waiting.
8 | /// The name of the option in should not be case sensitive and can inludes spaces like also the value.
9 | /// The substrings "value" and "name" should be avoided in and to allow unambiguous parsing,
10 | /// for example do not use = "draw value".
11 | /// Here are some strings for the example below:
12 | /// "setoption name Nullmove value true\n"
13 | /// "setoption name Selectivity value 3\n"
14 | /// "setoption name Style value Risky\n"
15 | /// "setoption name Clear Hash\n"
16 | /// "setoption name NalimovPath value c:\chess\tb\4;c:\chess\tb\5\n"
17 | ///
18 | public sealed class SetOptionCommand
19 | {
20 | public const string Id = "setoption";
21 | }
22 |
--------------------------------------------------------------------------------
/src/Lynx/UCI/Commands/Engine/CopyProtectionCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.UCI.Commands.Engine;
2 |
3 | ///
4 | /// copyprotection
5 | /// this is needed for copyprotected engines. After the uciok command the engine can tell the GUI,
6 | /// that it will check the copy protection now. This is done by "copyprotection checking".
7 | /// If the check is ok the engine should sent "copyprotection ok", otherwise "copyprotection error".
8 | /// If there is an error the engine should not function properly but should not quit alone.
9 | /// If the engine reports "copyprotection error" the GUI should not use this engine
10 | /// and display an error message instead!
11 | /// The code in the engine can look like this
12 | /// TellGUI("copyprotection checking\n");
13 | /// ... check the copy protection here ...
14 | /// if (ok)
15 | /// TellGUI("copyprotection ok\n");
16 | /// else
17 | /// TellGUI("copyprotection error\n");
18 | ///
19 | public sealed class CopyProtectionCommand
20 | {
21 | public const string Id = "copyprotection";
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Lynx.Test/Categories.cs:
--------------------------------------------------------------------------------
1 | namespace Lynx.Test;
2 |
3 | public static class Categories
4 | {
5 | public const string Perft = "Perft";
6 |
7 | public const string PerftFRC = "PerftFRC";
8 |
9 | public const string PerftFRCExhaustive = "PerftFRCExhaustive";
10 |
11 | public const string LongRunning = "LongRunning";
12 |
13 | ///
14 | /// Need to run in isolation, since other tests might modify values
15 | ///
16 | public const string Configuration = "Configuration";
17 |
18 | ///
19 | /// Can't be run since it'd take way too long for regular CI
20 | ///
21 | public const string TooLong = "TooLongToBeRun";
22 |
23 | ///
24 | /// Can't be run since no prunning is required
25 | ///
26 | public const string NoPruning = "RequireNoPruning";
27 |
28 | ///
29 | /// Can't be run since our engine is simply not good enough yet
30 | ///
31 | public const string NotGoodEnough = "NotGoodEnough";
32 | }
33 |
--------------------------------------------------------------------------------
/Lynx.slnx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Lynx.Cli/Program.cs:
--------------------------------------------------------------------------------
1 | using Lynx;
2 | using Lynx.Cli;
3 | using Microsoft.Extensions.Configuration;
4 | using NLog;
5 | using NLog.Extensions.Logging;
6 |
7 | #if DEBUG
8 | Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
9 | #endif
10 |
11 | var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
12 |
13 | var config = new ConfigurationBuilder()
14 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
15 | .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: false)
16 | .AddJsonFile("appsettings.tournament.json", optional: true, reloadOnChange: false)
17 | .AddEnvironmentVariables()
18 | .Build();
19 |
20 | config.GetSection(nameof(EngineSettings)).Bind(Configuration.EngineSettings);
21 | config.GetSection(nameof(GeneralSettings)).Bind(Configuration.GeneralSettings);
22 |
23 | if (Configuration.GeneralSettings.EnableLogging)
24 | {
25 | LogManager.Configuration = new NLogLoggingConfiguration(config.GetSection("NLog"));
26 | }
27 |
28 | await Runner.Run(args);
29 |
30 | Thread.Sleep(2_000);
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Eduardo Cáceres
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.github/workflows/bench.yml:
--------------------------------------------------------------------------------
1 | name: Bench
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 |
8 | bench:
9 | runs-on: ${{ matrix.os }}
10 |
11 | strategy:
12 | matrix:
13 | os: [ubuntu-latest, windows-latest, macOS-latest, macOS-15-intel]
14 | fail-fast: false
15 |
16 | env:
17 | DOTNET_VERSION: 10.0.x
18 | DOTNET_CLI_TELEMETRY_OPTOUT: 1
19 |
20 | steps:
21 | - uses: actions/checkout@v6
22 |
23 | - name: Setup .NET
24 | uses: actions/setup-dotnet@v5
25 | with:
26 | dotnet-version: ${{ env.DOTNET_VERSION }}
27 |
28 | - name: Nuget cache
29 | uses: actions/cache@v5
30 | with:
31 | path:
32 | ~/.nuget/packages
33 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
34 | restore-keys: |
35 | ${{ runner.os }}-nuget-
36 |
37 | - name: Build
38 | run: dotnet build -c Release
39 | working-directory: ./src/Lynx.Cli
40 |
41 | - name: Run bench ${{ github.event.inputs.depth }} on ${{ github.event.inputs.fen }}
42 | run: dotnet run -c Release --no-build "bench"
43 | working-directory: ./src/Lynx.Cli
44 |
--------------------------------------------------------------------------------
/src/Lynx.Cli/Writer.cs:
--------------------------------------------------------------------------------
1 | using NLog;
2 | using System.Threading.Channels;
3 |
4 | namespace Lynx.Cli;
5 |
6 | public sealed class Writer
7 | {
8 | private readonly ChannelReader