From bd2a7c04865543233d081c578ff7c7d2880ac775 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Wed, 30 Nov 2022 20:11:17 +0100
Subject: [PATCH 001/119] .net 7 update

- Better use of MemoryMarshal
- Replaces some home-brew functionality with new dotnet 7 equivalent
- Fixed a few sonar warnings
- Added [SkipLocalsInit] where appropriate
- Updated dependencies:
 - Microsoft.Extensions.ObjectPool 6.0.10 -> 7.0.0
 - ZString 2.4.4 -> 2.5.0
 - DryIoc 5.2.2 -> 5.3.1
 - Microsoft.Extensions.Caching.Memory 6.0.1 -> 7.0.0
 - Microsoft.Extensions.Configuration 6.0.1 -> 7.0.0
 - Microsoft.Extensions.Configuration.EnvironmentVariables 6.0.1 -> 7.0.0
 - Microsoft.Extensions.Configuration.Json 6.0.0 -> 7.0.0
---
 .../InBetweenBenchmark.cs                     | 11 ++++-
 .../IsNumericBenchmark.cs                     | 47 +++++++++++++++++++
 .../IterateBenchmark.cs                       |  1 -
 .../Rudzoft.ChessLib.Benchmark.csproj         |  2 +-
 .../Rudzoft.ChessLib.Perft.Interfaces.csproj  |  2 +-
 .../Rudzoft.ChessLib.Perft.csproj             |  2 +-
 .../BitboardTests/BitboardTests.cs            |  2 +
 .../FENTests/FenTests.cs                      | 14 +++++-
 .../RankTests/RankEdgeDistanceTests.cs        |  4 +-
 .../Rudzoft.ChessLib.Test.csproj              |  6 +--
 .../Rudzoft.ChessLib.WebApi.csproj            |  4 +-
 src/Rudzoft.ChessLib/Blockage.cs              |  8 +++-
 src/Rudzoft.ChessLib/Cuckoo.cs                | 15 +++---
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs | 28 +++++++----
 src/Rudzoft.ChessLib/Extensions/Maths.cs      |  6 +--
 src/Rudzoft.ChessLib/Factories/GameFactory.cs |  4 +-
 src/Rudzoft.ChessLib/Fen/Fen.cs               | 32 ++++++++++---
 src/Rudzoft.ChessLib/Fen/FenData.cs           | 21 ++++++---
 src/Rudzoft.ChessLib/Game.cs                  | 17 +++++--
 src/Rudzoft.ChessLib/GlobalSuppressions.cs    |  1 +
 src/Rudzoft.ChessLib/Hash/RKiss.cs            |  8 +++-
 .../Transposition/TranspositionTable.cs       | 38 ++++++++-------
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          |  4 +-
 .../MoveGeneration/MoveGenerator.cs           |  6 ++-
 .../MoveGeneration/MoveList.cs                |  1 +
 .../Notation/Notations/CoordinateNotation.cs  |  1 +
 .../Notation/Notations/FanNotation.cs         |  1 +
 .../Notation/Notations/IccfNotation.cs        |  1 +
 .../Notation/Notations/LanNotation.cs         |  1 +
 .../Notation/Notations/RanNotation.cs         |  1 +
 .../Notation/Notations/SanNotation.cs         |  1 +
 .../Notation/Notations/SmithNotation.cs       |  1 +
 src/Rudzoft.ChessLib/Position.cs              | 26 ++++++++--
 .../Protocol/UCI/SearchParameters.cs          |  2 +
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      | 17 ++++++-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  6 +--
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |  6 ++-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 19 ++++++--
 src/Rudzoft.ChessLib/Types/MagicBB.cs         |  2 +
 src/Rudzoft.ChessLib/Types/Move.cs            |  1 +
 src/Rudzoft.ChessLib/Types/Score.cs           |  2 +
 .../Rudzoft.Perft.Framework.csproj            | 10 ++--
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |  6 +--
 43 files changed, 292 insertions(+), 96 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs
index 5b2ebbae..a29bc9c1 100644
--- a/src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs
@@ -1,5 +1,4 @@
 using System.Runtime.CompilerServices;
-using BenchmarkDotNet.Attributes;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
@@ -38,6 +37,16 @@ public void UnsignedCompareInt()
         }
     }
 
+    [Benchmark(Description = ".NET 7 IsAsciiDigit")]
+    public void IsAsciiDigit()
+    {
+        var half = N / 2;
+        for (var i = 0; i < N; ++i)
+        {
+            var inBetween = InBetween(i, 0, half);
+        }
+    }
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static bool InBetweenOr(int v, int min, int max) => ((v - min) | (max - v)) >= 0;
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
new file mode 100644
index 00000000..22453fc5
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
@@ -0,0 +1,47 @@
+using Rudzoft.ChessLib.Extensions;
+using System;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Rudzoft.ChessLib.Benchmark;
+
+public class IsNumericBenchmark
+{
+    [Params(100, 500)]
+    public int N;
+
+    private string _s;
+
+    [GlobalSetup]
+    public void Setup()
+    {
+        var r = new Random(0);
+        _s = RandomString(r, N);
+    }
+
+    [Benchmark(Baseline = true)]
+    public void MathExtInBetween()
+    {
+        foreach (var c in _s.AsSpan())
+        {
+            var b = MathExtensions.InBetween(c, '0', '9');
+        }
+    }
+
+    [Benchmark]
+    public void IsAsciiDigit()
+    {
+        foreach (var c in _s.AsSpan())
+        {
+            var b = char.IsAsciiDigit(c);
+        }
+    }
+
+    private static string RandomString(Random random, int length)
+    {
+        const string chars = "ABCDE0123456789ABCDE";
+        return new string(Enumerable.Repeat(chars, length)
+            .Select(s => s[random.Next(s.Length)]).ToArray());
+    }
+}
diff --git a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
index 3542b477..8da1f5a6 100644
--- a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
@@ -1,5 +1,4 @@
 using System;
-using BenchmarkDotNet.Attributes;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 4750ed25..6f8bb7ae 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net7.0</TargetFramework>
     <Platforms>AnyCPU;x64</Platforms>
     <LangVersion>default</LangVersion>
   </PropertyGroup>
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
index a35ffe2d..b866c2d3 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net7.0</TargetFramework>
     <LangVersion>default</LangVersion>
   </PropertyGroup>
 
diff --git a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
index 15278006..1bfd5451 100644
--- a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
+++ b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net7.0</TargetFramework>
     <LangVersion>default</LangVersion>
   </PropertyGroup>
 
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
index 2b456fb8..165256f7 100644
--- a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.BitboardTests;
@@ -46,6 +47,7 @@ public void MakeBitBoard()
     }
 
     [Fact]
+    [SkipLocalsInit]
     public void BitBoardOrAll()
     {
         Span<Square> baseSquares = stackalloc Square[8]
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index af9b659e..a9cb41f4 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -24,9 +24,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Factories;
-using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.FENTests;
 
@@ -46,4 +45,15 @@ public void GetFen(string fen)
         var actualFen = g.GetFen().ToString();
         Assert.Equal(fen, actualFen);
     }
+
+    [Theory]
+    [InlineData("z3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1")]
+    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/ip2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1")]
+    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/lPPBBPPP/R3K2R w KQkq - 0 1")]
+    public void Validate(string fen)
+    {
+        var exception = Assert.Throws<InvalidFen>(() => GameFactory.Create(fen, true));
+        Assert.NotNull(exception.Message);
+        Assert.StartsWith("Invalid char detected", exception.Message);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs b/src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs
index 92a908f1..e97107e5 100644
--- a/src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs
@@ -41,8 +41,8 @@ public sealed class RankEdgeDistanceTests
     [InlineData(Ranks.Rank8, 0)]
     public void RankEdgeDistanceFolding(Ranks rs, int expected)
     {
-        var f = new Rank(rs);
-        var actual = f.EdgeDistance();
+        var r = new Rank(rs);
+        var actual = r.EdgeDistance();
         Assert.Equal(expected, actual);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 5dbfaaf6..221796b6 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net7.0</TargetFramework>
     <IsPackable>false</IsPackable>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <LangVersion>default</LangVersion>
@@ -26,8 +26,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="FluentAssertions" Version="6.7.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
+    <PackageReference Include="FluentAssertions" Version="6.8.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.analyzers" Version="1.0.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index db511fd6..078997cf 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -1,14 +1,14 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
     <PropertyGroup>
-        <TargetFramework>net6.0</TargetFramework>
+        <TargetFramework>net7.0</TargetFramework>
         <Nullable>enable</Nullable>
         <ImplicitUsings>enable</ImplicitUsings>
         <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
+        <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
         <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
     </ItemGroup>
 
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 7c12b6e2..04b925e4 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -310,8 +310,10 @@ private bool FormsFence(Square sq)
         Span<Direction> directions = stackalloc Direction[]
             { _us.PawnPushDistance(), Directions.East, _them.PawnPushDistance() };
 
-        foreach (var direction in directions)
+        ref var directionSpace = ref MemoryMarshal.GetReference(directions);
+        for (var i = 0; i < directions.Length; ++i)
         {
+            var direction = Unsafe.Add(ref directionSpace, i);
             var s = sq + direction;
             if ((_marked & s).IsEmpty || !(_processed & s).IsEmpty || !FormsFence(s))
                 continue;
@@ -347,8 +349,10 @@ private BitBoard ComputeDynamicFencedPawns()
 
         var result = BitBoard.Empty;
 
-        foreach (var f in File.AllFiles)
+        ref var fileSpace = ref MemoryMarshal.GetArrayDataReference(File.AllFiles);
+        for (var i = 0; i < File.AllFiles.Length; ++i)
         {
+            var f = Unsafe.Add(ref fileSpace, i);
             var sq = NextFenceRankSquare(f, _them);
             var b = sq.ForwardFile(_them) & _theirPawns;
             while (b)
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 7498d34b..92500360 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -26,6 +26,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Types;
 
@@ -45,8 +47,9 @@ public static class Cuckoo
     static Cuckoo()
     {
         var count = 0;
-        foreach (var pc in Piece.AllPieces)
-        {
+        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.AllPieces);
+        for (var i = 0; i < Piece.AllPieces.Length; i++) {
+            var pc = Unsafe.Add(ref piecesSpace, i);
             var bb = BitBoards.AllSquares;
             while (bb)
             {
@@ -58,18 +61,18 @@ static Cuckoo()
 
                     var move = Move.Create(sq1, sq2);
                     var key = pc.GetZobristPst(sq1) ^ pc.GetZobristPst(sq2) ^ Zobrist.GetZobristSide();
-                    var i = CuckooHashOne(in key);
+                    var j = CuckooHashOne(in key);
                     do
                     {
-                        (CuckooKeys[i], key) = (key, CuckooKeys[i]);
-                        (CuckooMoves[i], move) = (move, CuckooMoves[i]);
+                        (CuckooKeys[j], key) = (key, CuckooKeys[j]);
+                        (CuckooMoves[j], move) = (move, CuckooMoves[j]);
 
                         // check for empty slot
                         if (move.IsNullMove())
                             break;
 
                         // Push victim to alternative slot
-                        i = i == CuckooHashOne(in key)
+                        j = j == CuckooHashOne(in key)
                             ? CuckooHashTwo(in key)
                             : CuckooHashOne(in key);
                     } while (true);
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index 0a6cefd8..a9585f4e 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -28,6 +28,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
@@ -48,32 +50,38 @@ public static class KpkBitBase
 
     static KpkBitBase()
     {
-        List<KpkPosition> db = new(MaxIndex);
-        int idx;
-
         // Initialize db with known win / draw positions
-        for (idx = 0; idx < MaxIndex; ++idx)
-            db.Add(KpkPosition.Create(idx));
+        var db = Enumerable
+            .Range(0, MaxIndex)
+            .Select(KpkPosition.Create)
+            .ToArray()
+            .AsSpan();
 
         // Iterate through the positions until none of the unknown positions can be
         // changed to either wins or draws (15 cycles needed).
         var repeat = 1;
 
-        var dbs = CollectionsMarshal.AsSpan(db);
         while (repeat != 0)
         {
             repeat = 1;
-            foreach (var kpkPosition in dbs)
+            ref var dbSpace = ref MemoryMarshal.GetReference(db);
+            for (var i = 0; i < db.Length; ++i)
+            {
+                var kpkPosition = Unsafe.Add(ref dbSpace, i);
                 repeat = (kpkPosition.Result == Result.Unknown && kpkPosition.Classify(db) != Result.Unknown).AsByte();
+            }
         }
 
         // Fill the bitbase with the decisive results
-        idx = 0;
-        foreach (var kpkPosition in dbs)
+        var idx = 0;
+        ref var setDbSpace = ref MemoryMarshal.GetReference(db);
+        for (var i = 0; i < db.Length; ++i)
         {
+            var kpkPosition = Unsafe.Add(ref setDbSpace, i);
             if (kpkPosition.Result == Result.Win)
                 KpKbb.Set(idx, true);
             idx++;
+
         }
     }
 
@@ -185,7 +193,7 @@ public static KpkPosition Create(int idx)
         /// </summary>
         /// <param name="db">Current KpkPositions as list</param>
         /// <returns>Result after classification</returns>
-        public Result Classify(IList<KpkPosition> db)
+        public Result Classify(ReadOnlySpan<KpkPosition> db)
         {
             var (good, bad) = Stm.IsWhite
                 ? (Result.Win, Result.Draw)
diff --git a/src/Rudzoft.ChessLib/Extensions/Maths.cs b/src/Rudzoft.ChessLib/Extensions/Maths.cs
index 18a68c12..6430f37d 100644
--- a/src/Rudzoft.ChessLib/Extensions/Maths.cs
+++ b/src/Rudzoft.ChessLib/Extensions/Maths.cs
@@ -53,7 +53,7 @@ public static int ToIntegral(ReadOnlySpan<char> str)
             pos++;
         }
 
-        while (pos <= max && str[pos].InBetween('0', '9'))
+        while (pos <= max && char.IsAsciiDigit(str[pos]))
         {
             x = x * 10 + (str[pos] - '0');
             pos++;
@@ -74,7 +74,7 @@ public static bool ToIntegral(this ReadOnlySpan<char> str, out ulong result)
         var x = ulong.MinValue;
         var pos = 0;
         var max = str.Length - 1;
-        while (pos <= max && str[pos].InBetween('0', '9'))
+        while (pos <= max && char.IsAsciiDigit(str[pos]))
         {
             x = x * 10 + (ulong)(str[pos] - '0');
             pos++;
@@ -96,7 +96,7 @@ public static bool ToIntegral(ReadOnlySpan<char> str, out int result)
         var x = 0;
         var pos = 0;
         var max = str.Length - 1;
-        while (pos <= max && str[pos].InBetween('0', '9'))
+        while (pos <= max && char.IsAsciiDigit(str[pos]))
         {
             x = x * 10 + (str[pos] - '0');
             pos++;
diff --git a/src/Rudzoft.ChessLib/Factories/GameFactory.cs b/src/Rudzoft.ChessLib/Factories/GameFactory.cs
index 77768e01..dbcb2ee3 100644
--- a/src/Rudzoft.ChessLib/Factories/GameFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/GameFactory.cs
@@ -33,12 +33,12 @@ public static class GameFactory
 {
     public static IGame Create(IPosition position) => new Game(position);
 
-    public static IGame Create(string fen)
+    public static IGame Create(string fen, bool validate = false)
     {
         var g = Create();
         var fenData = new FenData(fen);
         var state = new State();
-        g.Pos.Set(in fenData, Enums.ChessMode.Normal, state);
+        g.Pos.Set(in fenData, Enums.ChessMode.Normal, state, validate);
         return g;
     }
 
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index e926fa33..051f9436 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Text.RegularExpressions;
 using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Extensions;
@@ -41,6 +42,8 @@ public static class Fen
 
     private const string FenRankRegexSnippet = @"[1-8KkQqRrBbNnPp]{1,8}";
 
+    private const string ValidChars = @"012345678pPnNbBrRqQkK/ w-abcdefgh";
+
     private const char Space = ' ';
 
     private const int SpaceCount = 3;
@@ -51,7 +54,7 @@ public static class Fen
 
     private static readonly Lazy<Regex> ValidFenRegex = new(static () => new Regex(
        string.Format(@"^ \s* {0}/{0}/{0}/{0}/{0}/{0}/{0}/{0} \s+ (?:w|b) \s+ (?:[KkQq]+|\-) \s+ (?:[a-h][1-8]|\-) \s+ \d+ \s+ \d+ \s* $", FenRankRegexSnippet),
-       RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline));
+       RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.NonBacktracking));
 
     /// <summary>
     /// Performs basic validation of FEN string.
@@ -65,7 +68,12 @@ public static class Fen
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static void Validate(string fen)
     {
-        var f = fen.Trim().AsSpan();
+        var f = fen.AsSpan().Trim();
+
+        var invalidCharIndex = f.IndexOfAnyExcept(ValidChars.AsSpan());
+
+        if (invalidCharIndex > -1)
+            throw new InvalidFen($"Invalid char detected in fen. fen={f}, pos={invalidCharIndex}");
 
         if (f.Length >= MaxFenLen)
             throw new InvalidFen($"Invalid length for fen {fen}.");
@@ -81,6 +89,7 @@ public static void Validate(string fen)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool IsDelimiter(char c) => c == Space;
 
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static bool CountPieceValidity(ReadOnlySpan<char> s)
     {
@@ -95,8 +104,11 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
         // piece count storage, using index 0 = '/' count
         Span<int> pieceCount = stackalloc int[Pieces.PieceNb.AsInt()];
 
-        foreach (var t in mainSection)
+        ref var mainSectionSpace = ref MemoryMarshal.GetReference(mainSection);
+
+        for (var i = 0; i < mainSection.Length; ++i)
         {
+            var t = Unsafe.Add(ref mainSectionSpace, i);
             if (t == '/')
             {
                 if (++pieceCount[0] > SeparatorCount)
@@ -106,7 +118,7 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
 
             if (char.IsNumber(t))
             {
-                if (!t.InBetween('1', '8'))
+                if (!char.IsBetween(t, '1', '8'))
                     throw new InvalidFen($"Invalid fen (not a valid square jump) {s.ToString()}");
                 continue;
             }
@@ -131,8 +143,10 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
         static bool GetSpanSum(ReadOnlySpan<int> span, int limit)
         {
             var sum = 0;
-            foreach (var v in span)
-                sum += v;
+
+            ref var spanSpace = ref MemoryMarshal.GetReference(span);
+            for (var i = 0; i < span.Length; ++i)
+                sum += Unsafe.Add(ref spanSpace, i);
 
             return sum <= limit;
         }
@@ -186,7 +200,10 @@ private static (int, int) CountSpacesAndSeparators(ReadOnlySpan<char> str)
     {
         var result = (spaceCount: 0, separatorCount: 0);
 
-        foreach (var c in str)
+        ref var strSpace = ref MemoryMarshal.GetReference(str);
+        for (var i = 0; i < str.Length; ++i)
+        {
+            var c = Unsafe.Add(ref strSpace, i);
             switch (c)
             {
                 case Separator:
@@ -197,6 +214,7 @@ private static (int, int) CountSpacesAndSeparators(ReadOnlySpan<char> str)
                     result.spaceCount++;
                     break;
             }
+        }
 
         return result;
     }
diff --git a/src/Rudzoft.ChessLib/Fen/FenData.cs b/src/Rudzoft.ChessLib/Fen/FenData.cs
index 8f22c878..33116f5e 100644
--- a/src/Rudzoft.ChessLib/Fen/FenData.cs
+++ b/src/Rudzoft.ChessLib/Fen/FenData.cs
@@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Text;
 
 namespace Rudzoft.ChessLib.Fen;
@@ -40,18 +41,24 @@ public sealed class FenData : EventArgs, IFenData
 {
     private readonly Queue<(int, int)> _splitPoints;
 
-    public FenData(ReadOnlyMemory<char> fen)
+    private FenData()
     {
         _splitPoints = new Queue<(int, int)>(6);
+    }
+
+    public FenData(ReadOnlyMemory<char> fen) : this()
+    {
         Fen = fen;
         var start = 0;
 
         var s = fen.Span;
 
         // determine split points
+        ref var splitPointsSpace = ref MemoryMarshal.GetReference(s);
         for (var i = 0; i < s.Length; i++)
         {
-            if (s[i] != ' ')
+            var splitPoint = Unsafe.Add(ref splitPointsSpace, i);
+            if (splitPoint != ' ')
                 continue;
 
             _splitPoints.Enqueue((start, i));
@@ -62,14 +69,14 @@ public FenData(ReadOnlyMemory<char> fen)
         _splitPoints.Enqueue((start, s.Length));
     }
 
-    public FenData(ReadOnlySpan<string> fen)
+    public FenData(ReadOnlySpan<string> fen) : this()
     {
-        _splitPoints = new Queue<(int, int)>(6);
         var sb = new StringBuilder(128);
 
+        ref var fenSpace = ref MemoryMarshal.GetReference(fen);
         for (var i = 0; i < fen.Length; ++i)
         {
-            sb.Append(fen[i]);
+            sb.Append(Unsafe.Add(ref fenSpace, i));
             if (i < fen.Length - 1)
                 sb.Append(' ');
         }
@@ -80,9 +87,11 @@ public FenData(ReadOnlySpan<string> fen)
         var s = Fen.Span;
 
         // determine split points
+        ref var splitPointsSpace = ref MemoryMarshal.GetReference(s);
         for (var i = 0; i < s.Length; i++)
         {
-            if (s[i] != ' ')
+            var splitPoint = Unsafe.Add(ref splitPointsSpace, i);
+            if (splitPoint != ' ')
                 continue;
 
             _splitPoints.Enqueue((start, i));
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 364b7db6..eba7df22 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -28,6 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Collections;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
@@ -96,13 +97,18 @@ public void UpdateDrawTypes()
 
         var moveList = _moveLists.Get();
         moveList.Generate(in _pos);
-        // ReSharper disable once LoopCanBeConvertedToQuery
-        foreach (var em in moveList.Get())
+
+        var moves = moveList.Get();
+        ref var movesSpace = ref MemoryMarshal.GetReference(moves);
+        for (var i = 0; i < moves.Length; ++i)
+        {
+            var em = Unsafe.Add(ref movesSpace, i);
             if (!Pos.IsLegal(em.Move))
             {
                 gameEndType |= GameEndTypes.Pat;
                 break;
             }
+        }
 
         GameEndType = gameEndType;
     }
@@ -131,7 +137,11 @@ public ulong Perft(int depth, bool root = true)
         ml.Generate(in _pos);
         var state = new State();
 
-        foreach (var em in ml.Get())
+        var moves = ml.Get();
+        ref var movesSpace = ref MemoryMarshal.GetReference(moves);
+        for (var i = 0; i < moves.Length; ++i)
+        {
+            var em = Unsafe.Add(ref movesSpace, i);
             if (root && depth <= 1)
                 tot++;
             else
@@ -151,6 +161,7 @@ public ulong Perft(int depth, bool root = true)
 
                 _pos.TakeMove(m);
             }
+        }
 
         _moveLists.Return(ml);
 
diff --git a/src/Rudzoft.ChessLib/GlobalSuppressions.cs b/src/Rudzoft.ChessLib/GlobalSuppressions.cs
index d944bb4b..d7d918c2 100644
--- a/src/Rudzoft.ChessLib/GlobalSuppressions.cs
+++ b/src/Rudzoft.ChessLib/GlobalSuppressions.cs
@@ -6,3 +6,4 @@
 using System.Diagnostics.CodeAnalysis;
 
 [assembly: SuppressMessage("Minor Code Smell", "S3963:\"static\" fields should be initialized inline", Justification = "Needed for some functionality", Scope = "member", Target = "~M:Rudzoft.ChessLib.Evaluation.KpkBitBase.#cctor")]
+[assembly: SuppressMessage("Critical Bug", "S2222:Locks should be released", Justification = "Should be alright", Scope = "member", Target = "~M:Rudzoft.ChessLib.Protocol.UCI.InputOutput.Write(System.String,Rudzoft.ChessLib.Protocol.UCI.InputOutputMutex)")]
diff --git a/src/Rudzoft.ChessLib/Hash/RKiss.cs b/src/Rudzoft.ChessLib/Hash/RKiss.cs
index bbce628f..7d6b90b0 100644
--- a/src/Rudzoft.ChessLib/Hash/RKiss.cs
+++ b/src/Rudzoft.ChessLib/Hash/RKiss.cs
@@ -27,6 +27,7 @@
 
  */
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
@@ -55,7 +56,12 @@ public IEnumerable<ulong> Get(int count)
     /// Special generator used to fast init magic numbers.
     /// Output values only have 1/8th of their bits set on average.
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ulong Sparse() => Rand64() & Rand64() & Rand64();
+    public ulong Sparse()
+    {
+        var h = Rand64();
+        h &= Rand64();
+        return h & Rand64();
+    }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private ulong Rand64()
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index 93314c01..8473215a 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Types;
 
@@ -133,13 +134,15 @@ public bool Probe(in HashKey key, ref TranspositionTableEntry e)
         // probing retrieves an element.
 
         var set = false;
+        ref var clusterSpace = ref MemoryMarshal.GetArrayDataReference(ttc.Cluster);
         for (var i = 0; i < ttc.Cluster.Length; ++i)
         {
-            if (ttc.Cluster[i].Key32 != uint.MinValue && ttc.Cluster[i].Key32 != key.UpperKey)
+            var entry = Unsafe.Add(ref clusterSpace, i);
+            if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
                 continue;
 
-            ttc.Cluster[i].Generation = g;
-            e = ttc.Cluster[i];
+            entry.Generation = g;
+            e = entry;
             set = true;
             Hits++;
             break;
@@ -156,9 +159,11 @@ public void Save(in HashKey key, in TranspositionTableEntry e)
         var ttc = FindCluster(in key);
         var entryIndex = 0;
 
+        ref var clusterSpace = ref MemoryMarshal.GetArrayDataReference(ttc.Cluster);
         for (var i = 0; i < ttc.Cluster.Length; ++i)
         {
-            if (ttc.Cluster[i].Key32 != uint.MinValue && ttc.Cluster[i].Key32 != key.UpperKey)
+            var entry = Unsafe.Add(ref clusterSpace, i);
+            if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
                 continue;
 
             entryIndex = i;
@@ -194,9 +199,11 @@ public void Store(in HashKey key, int value, Bound type, sbyte depth, Move move,
         var clusterIndex = 0;
         var found = false;
 
+        ref var clusterSpace = ref MemoryMarshal.GetArrayDataReference(ttc.Cluster);
         for (var i = 0; i < ttc.Cluster.Length; ++i)
         {
-            if (ttc.Cluster[i].Key32 != uint.MinValue && ttc.Cluster[i].Key32 != key.UpperKey)
+            var entry = Unsafe.Add(ref clusterSpace, i);
+            if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
                 continue;
 
             clusterIndex = i;
@@ -209,19 +216,16 @@ public void Store(in HashKey key, int value, Bound type, sbyte depth, Move move,
             var index = 0;
             var candidate = ttc.Cluster[index];
             var g = _generation;
-            var i = 0;
-            foreach (var ttEntry in ttc.Cluster)
-            {
-                var (cc1, cc2, cc3, cc4) =
-                    (candidate.Generation == g,
-                        ttEntry.Generation == g,
-                        ttEntry.Type == Bound.Exact,
-                        ttEntry.Depth <= candidate.Depth);
 
+            ref var entrySpace = ref MemoryMarshal.GetArrayDataReference(ttc.Cluster);
+            for (var i = 0; i < ttc.Cluster.Length; ++i)
+            {
+                var entry = Unsafe.Add(ref entrySpace, i);
+                var (cc1, cc2, cc3, cc4) = (candidate.Generation == g, entry.Generation == g, entry.Type == Bound.Exact, entry.Depth <= candidate.Depth);
                 if ((cc1 && cc4) || (!(cc2 || cc3) && (cc4 || cc1)))
                 {
                     index = i;
-                    candidate = ttEntry;
+                    candidate = entry;
                 }
 
                 i++;
@@ -245,6 +249,7 @@ public int Fullness()
             return 0;
         var gen = _generation;
         var sum = 0;
+
         for (var i = 0; i < _fullnessElements; ++i)
             sum += _table[i].Cluster.Count(x => x.Generation == gen);
 
@@ -257,8 +262,9 @@ public int Fullness()
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Clear()
     {
-        foreach (var t in _table)
-            t.Reset();
+        ref var tableSpace = ref MemoryMarshal.GetArrayDataReference(_table);
+        for (var i = 0; i < _table.Length; ++i)
+            Unsafe.Add(ref tableSpace, i).Reset();
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index 458f47f4..c37c895b 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -97,8 +97,10 @@ private static void InitializePst(in IRKiss rnd)
         for (var i = 0; i < ZobristPst.Length; i++)
             ZobristPst[i] = new HashKey[Square.Count];
 
-        foreach (var pc in Piece.AllPieces)
+        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.AllPieces);
+        for (var i = 0; i < Piece.AllPieces.Length; ++i)
         {
+            var pc = Unsafe.Add(ref piecesSpace, i);
             var bb = BitBoards.AllSquares;
             while (bb)
             {
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index c89245ca..e2648e5d 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -26,6 +26,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Types;
 
@@ -239,8 +241,10 @@ private static int GenerateMoves(
 
         var squares = pos.Squares(pt, us);
 
-        foreach (var from in squares)
+        ref var squaresSpace = ref MemoryMarshal.GetReference(squares);
+        for (var i = 0; i < squares.Length; ++i)
         {
+            var from = Unsafe.Add(ref squaresSpace, i);
             if (checks
                 && (!(pos.KingBlockers(~us) & from).IsEmpty ||
                     !(pt.PseudoAttacks(from) & target & pos.CheckedSquares(pt))))
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index 1796e06e..470f9204 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -112,6 +112,7 @@ public void Generate(in IPosition pos, MoveGenerationType type = MoveGenerationT
         _moves[Length] = ExtMove.Empty;
     }
 
+    [SkipLocalsInit]
     public static int GenerateMoveCount(in IPosition pos, MoveGenerationType type = MoveGenerationType.Legal)
     {
         Span<ExtMove> moves = stackalloc ExtMove[218];
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
index 143ffb36..956d8db7 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
@@ -37,6 +37,7 @@ public CoordinateNotation(IPosition pos) : base(pos)
     {
     }
 
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string Convert(Move move)
     {
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
index 97dcbbdf..9c0089a1 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
@@ -42,6 +42,7 @@ public FanNotation(IPosition pos) : base(pos)
     /// </summary>
     /// <param name="move">The move to convert</param>
     /// <returns>FAN move string</returns>
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string Convert(Move move)
     {
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index d1297cf1..4c727b9f 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -36,6 +36,7 @@ public IccfNotation(IPosition pos) : base(pos)
     {
     }
 
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string Convert(Move move)
     {
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
index 3c11da50..8d3361bc 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
@@ -42,6 +42,7 @@ public LanNotation(IPosition pos) : base(pos)
     /// </summary>
     /// <param name="move">The move to convert</param>
     /// <returns>LAN move string</returns>
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string Convert(Move move)
     {
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
index d1562981..930be6a2 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
@@ -42,6 +42,7 @@ public RanNotation(IPosition pos) : base(pos)
     /// </summary>
     /// <param name="move">The move to convert</param>
     /// <returns>RAN move string</returns>
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string Convert(Move move)
     {
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
index 410497ab..d1cfe6d8 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
@@ -43,6 +43,7 @@ public SanNotation(IPosition pos) : base(pos)
     /// </summary>
     /// <param name="move">The move to convert</param>
     /// <returns>SAN move string</returns>
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string Convert(Move move)
     {
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
index 96940189..90b2f56d 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
@@ -37,6 +37,7 @@ public SmithNotation(IPosition pos) : base(pos)
     {
     }
 
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string Convert(Move move)
     {
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 44896084..03437454 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Text;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
@@ -276,6 +277,7 @@ public void Clear()
     /// Parses the board layout to a FEN representation.. Beware, goblins are a foot.
     /// </summary>
     /// <returns>The FenData which contains the fen string that was generated.</returns>
+    [SkipLocalsInit]
     public FenData GenerateFen()
     {
         const char space = ' ';
@@ -867,6 +869,7 @@ public Piece MovedPiece(Move m)
     /// </summary>
     /// <param name="m">The move to convert</param>
     /// <param name="output">The string builder used to generate the string with</param>
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void MoveToString(Move m, in StringBuilder output)
     {
@@ -1087,7 +1090,10 @@ private void SetupPieces(ReadOnlySpan<char> fenChunk)
         var f = 1; // file (column)
         var r = 8; // rank (row)
 
-        foreach (var c in fenChunk)
+        ref var fenChunkSpace = ref MemoryMarshal.GetReference(fenChunk);
+        for (var i = 0; i < fenChunk.Length; i++)
+        {
+            var c = Unsafe.Add(ref fenChunkSpace, i);
             if (char.IsNumber(c))
                 f += c - '0';
             else if (c == '/')
@@ -1108,6 +1114,7 @@ private void SetupPieces(ReadOnlySpan<char> fenChunk)
 
                 f++;
             }
+        }
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -1118,7 +1125,7 @@ private void SetupEnPassant(ReadOnlySpan<char> fenChunk)
     {
         var enPassant = fenChunk.Length == 2
                         && fenChunk[0] != '-'
-                        && fenChunk[0].InBetween('a', 'h')
+                        && char.IsBetween(fenChunk[0], 'a', 'h')
                         && fenChunk[1] == (_sideToMove.IsWhite ? '6' : '3');
 
         if (enPassant)
@@ -1267,6 +1274,7 @@ public void TakeNullMove()
         _sideToMove = ~_sideToMove;
     }
 
+    [SkipLocalsInit]
     public override string ToString()
     {
         const string separator = "\n  +---+---+---+---+---+---+---+---+\n";
@@ -1432,9 +1440,13 @@ private void SetState()
         key ^= State.CastlelingRights.Key();
 
         var materialKey = HashKey.Empty;
-        foreach (var pc in Piece.AllPieces)
+        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.AllPieces);
+        for (int i = 0; i < Piece.AllPieces.Length; i++)
+        {
+            var pc = Unsafe.Add(ref piecesSpace, i);
             for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
                 materialKey ^= pc.GetZobristPst(cnt);
+        }
 
         State.Key = key;
         State.PawnStructureKey = pawnKey;
@@ -1453,8 +1465,11 @@ Square RookSquare(Square startSq, Piece rook)
             return targetSq;
         }
 
-        foreach (var ca in castleling)
+        ref var castleSpace = ref MemoryMarshal.GetReference(castleling);
+
+        for (var i = 0; i < castleling.Length; ++i)
         {
+            var ca = Unsafe.Add(ref castleSpace, i);
             Player c = char.IsLower(ca);
             var rook = PieceTypes.Rook.MakePiece(c);
             var token = char.ToUpper(ca);
@@ -1463,11 +1478,12 @@ Square RookSquare(Square startSq, Piece rook)
             {
                 'K' => RookSquare(Square.H1.Relative(c), rook),
                 'Q' => RookSquare(Square.A1.Relative(c), rook),
-                _ => token.InBetween('A', 'H') ? new Square(Rank.Rank1.Relative(c), new File(token - 'A')) : Square.None
+                _ => char.IsBetween(token, 'A', 'H') ? new Square(Rank.Rank1.Relative(c), new File(token - 'A')) : Square.None
             };
 
             if (rsq != Square.None)
                 SetCastlingRight(c, rsq);
+
         }
     }
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index d4cb1981..016f8d94 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -141,6 +141,7 @@ public void Clear()
         Infinite = false;
     }
 
+    [SkipLocalsInit]
     public override string ToString()
     {
         Span<char> s = stackalloc char[128];
@@ -153,6 +154,7 @@ public override string ToString()
 
     public void AddSearchMove(Move move) => SearchMoves.Add(move);
 
+    [SkipLocalsInit]
     private static int ParseValue(int index, in ulong value, Span<char> target)
     {
         Span<char> number = stackalloc char[32];
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index 0b6703ac..0b775721 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -34,6 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using System.Linq;
+using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
@@ -92,9 +93,16 @@ public ulong Nps(in ulong nodes, in TimeSpan time)
     public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
     {
         var moveList = pos.GenerateMoves();
-        foreach (var move in moveList.Get())
+
+        var moves = moveList.Get();
+        ref var movesSpace = ref MemoryMarshal.GetReference(moves);
+
+        for (var i = 0; i < moves.Length; ++i)
+        {
+            var move = Unsafe.Add(ref movesSpace, i);
             if (uciMove.Equals(move.Move.ToString(), StringComparison.InvariantCultureIgnoreCase))
                 return move.Move;
+        }
 
         return Move.EmptyMove;
     }
@@ -172,6 +180,7 @@ public string Fullness(in ulong tbHits, in ulong nodes, in TimeSpan time)
         =>
             $"info hashfull {Game.Table.Fullness()} tbhits {tbHits} nodes {nodes} time {time.Milliseconds} nps {Nps(in nodes, in time)}";
 
+    [SkipLocalsInit]
     public string MoveToString(Move m, ChessMode chessMode = ChessMode.Normal)
     {
         if (m.IsNullMove())
@@ -207,8 +216,12 @@ public string MoveToString(Move m, ChessMode chessMode = ChessMode.Normal)
         list.Sort(OptionComparer);
         var sb = _pvPool.Get();
 
-        foreach (var opt in CollectionsMarshal.AsSpan(list))
+        var listSpan = CollectionsMarshal.AsSpan(list);
+        ref var listSpace = ref MemoryMarshal.GetReference(listSpan);
+
+        for (var i = 0; i < list.Count; ++i)
         {
+            var opt = Unsafe.Add(ref listSpace, i);
             sb.AppendLine();
             sb.Append("option name ").Append(opt.Name).Append(" type ").Append(OptionTypeStrings[(int)opt.Type]);
             if (opt.Type != UciOptionType.Button)
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 812a1927..696d44e7 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -7,7 +7,7 @@
     <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
     <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
     <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net7.0</TargetFramework>
     <PackageVersion>0.0.3</PackageVersion>
     <Authors>Rudy Alex Kohn</Authors>
     <Company>None</Company>
@@ -54,8 +54,8 @@
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.10" />
-    <PackageReference Include="ZString" Version="2.4.4" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.0" />
+    <PackageReference Include="ZString" Version="2.5.0" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="Protocol\" />
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index 29043a6e..8edc8ee0 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -30,6 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.IO;
 using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 
 // ReSharper disable MemberCanBeInternal
 namespace Rudzoft.ChessLib.Types;
@@ -220,8 +221,9 @@ public BitBoard OrAll(IEnumerable<BitBoard> bbs)
     public BitBoard OrAll(ReadOnlySpan<Square> sqs)
     {
         var b = this;
-        foreach (var sq in sqs)
-            b |= sq;
+        ref var squaresSpace = ref MemoryMarshal.GetReference(sqs);
+        for (var i = 0; i < sqs.Length; ++i)
+            b |= Unsafe.Add(ref squaresSpace, i);
 
         return b;
     }
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 61b254e9..2b2e31e8 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -34,6 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Linq;
 using System.Numerics;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Extensions;
 
 namespace Rudzoft.ChessLib.Types;
@@ -232,16 +233,20 @@ static BitBoards()
         static int distanceRank(Square x, Square y) => distance(x.Rank.AsInt(), y.Rank.AsInt());
 
         // ForwardRanksBB population loop idea from sf
-        foreach (var r in Rank.All)
+        ref var rankSpace = ref MemoryMarshal.GetArrayDataReference(Rank.All);
+        for (var i = 0; i < Rank.All.Length; i++)
         {
+            var r = Unsafe.Add(ref rankSpace, i);
             var rank = r.AsInt();
             ForwardRanksBB[0][rank] = ~(ForwardRanksBB[1][rank + 1] = ForwardRanksBB[1][rank] | r.BitBoardRank());
         }
 
         BitBoard bb;
 
-        foreach (var player in Player.AllPlayers)
+        ref var playerSpace = ref MemoryMarshal.GetArrayDataReference(Player.AllPlayers);
+        for (var i = 0; i < Player.AllPlayers.Length; ++i)
         {
+            var player = Unsafe.Add(ref playerSpace, i);
             bb = AllSquares;
             while (bb)
             {
@@ -314,8 +319,10 @@ static BitBoard ComputeKnightAttack(in BitBoard bb)
                                     | b.NorthEastOne() | b.NorthWestOne() | b.SouthEastOne() | b.SouthWestOne();
 
             // Compute lines and betweens
-            foreach (var validMagicPiece in validMagicPieces)
+            ref var magicPiecesSpace = ref MemoryMarshal.GetReference(validMagicPieces);
+            for (var i = 0; i < validMagicPieces.Length; i++)
             {
+                var validMagicPiece = Unsafe.Add(ref magicPiecesSpace, i);
                 pt = validMagicPiece.AsInt();
                 var bb3 = AllSquares;
                 while (bb3)
@@ -339,8 +346,11 @@ static BitBoard ComputeKnightAttack(in BitBoard bb)
     private static void InitializeKingRing(Square s1, int sq, File file)
     {
         const int pt = (int)PieceTypes.King;
-        foreach (var player in Player.AllPlayers)
+
+        ref var playerSpace = ref MemoryMarshal.GetArrayDataReference(Player.AllPlayers);
+        for (var i = 0; i < Player.AllPlayers.Length; ++i)
         {
+            var player = Unsafe.Add(ref playerSpace, i);
             KingRingBB[player.Side][sq] = PseudoAttacksBB[pt][sq];
             if (s1.RelativeRank(player) == Ranks.Rank1)
                 KingRingBB[player.Side][sq] |= KingRingBB[player.Side][sq].Shift(PawnPushDirections[player.Side]);
@@ -531,6 +541,7 @@ public static BitBoard DistanceRing(this Square sq, int length)
     public static BitBoard PromotionRank(this Player p)
         => PromotionRanks[p.Side];
 
+    [SkipLocalsInit]
     public static string PrintBitBoard(in BitBoard bb, string title = "")
     {
         const string line = "+---+---+---+---+---+---+---+---+---+";
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index 4c9e51ca..dd60f11f 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -30,6 +30,7 @@
 */
 
 using System;
+using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Types;
 
@@ -128,6 +129,7 @@ public static class MagicBB
         0x6E10101010101000UL, 0x5E20202020202000UL, 0x3E40404040404000UL, 0x7E80808080808000UL
     };
 
+    [SkipLocalsInit]
     static MagicBB()
     {
         for (var i = 0; i < MagicBishopDb.Length; i++)
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index ab54d66f..b26530ad 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -163,6 +163,7 @@ public bool Equals(Move other)
     public override int GetHashCode()
         => Data;
 
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string ToString()
     {
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index 67933981..b498f282 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -128,7 +128,9 @@ public bool Equals(Score other)
     public override bool Equals(object obj)
         => obj is Score other && Equals(other);
 
+#pragma warning disable S2328 // "GetHashCode" should not reference mutable fields
     public override int GetHashCode()
+#pragma warning restore S2328 // "GetHashCode" should not reference mutable fields
         => _data.GetHashCode();
 
     public static bool operator ==(Score left, Score right)
diff --git a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
index fc13d981..739e8757 100644
--- a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
+++ b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net7.0</TargetFramework>
     <RootNamespace>Perft</RootNamespace>
     <LangVersion>default</LangVersion>
   </PropertyGroup>
@@ -13,10 +13,10 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="DryIoc" Version="5.2.2" />
-    <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
+    <PackageReference Include="DryIoc" Version="5.3.1" />
+    <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
     <PackageReference Include="Serilog" Version="2.12.0" />
     <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
     <PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index adf1238b..c1c09368 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net7.0</TargetFramework>
     <Platforms>AnyCPU;x64</Platforms>
     <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
     <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
@@ -36,14 +36,14 @@
   <ItemGroup>
     <PackageReference Include="CommandLineParser" Version="2.9.1" />
     <PackageReference Include="ConsoleGUI" Version="1.4.1" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.10" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.0" />
     <PackageReference Include="Serilog" Version="2.12.0" />
     <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
     <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />
     <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
     <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
     <PackageReference Include="System.Reactive" Version="5.0.0" />
-    <PackageReference Include="System.Threading.Tasks.Dataflow" Version="6.0.0" />
+    <PackageReference Include="System.Threading.Tasks.Dataflow" Version="7.0.0" />
     <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
     <PackageReference Include="Timestamp" Version="1.0.2" />
   </ItemGroup>

From e61fb7a4767dc6520cf4674bd3f003074a4ba8f3 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 22 Dec 2022 08:38:23 +0100
Subject: [PATCH 002/119] Fixed minor formatting etc.

---
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs       |  6 +-----
 src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs   |  1 -
 .../PerftPosition.cs                               |  2 +-
 .../Rudzoft.ChessLib.Test.csproj                   |  4 ++--
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs      |  7 +++----
 src/Rudzoft.ChessLib/Extensions/MathExtensions.cs  |  1 -
 src/Rudzoft.ChessLib/Hash/RKiss.cs                 |  1 -
 src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs         |  4 +---
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs           |  2 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj       |  2 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs             | 14 +++++++++++---
 src/Rudzoft.ChessLib/Types/BitBoards.cs            |  4 ++--
 src/Rudzoft.ChessLib/Types/Rank.cs                 |  2 +-
 src/Rudzoft.ChessLib/Types/Score.cs                |  3 +--
 src/Rudzoft.ChessLib/Types/Square.cs               |  6 +++---
 src/Rudzoft.ChessLib/Types/StateStack.cs           |  1 -
 src/Rudzoft.Perft/Rudzoft.Perft.csproj             |  2 +-
 17 files changed, 29 insertions(+), 33 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index ba32b529..9bb50157 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -27,13 +27,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Collections.Generic;
 using System.Threading.Tasks;
-using BenchmarkDotNet.Attributes;
 using Rudzoft.ChessLib.Perft;
 using Rudzoft.ChessLib.Perft.Interfaces;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
 [MemoryDiagnoser]
+// ReSharper disable once ClassCanBeSealed.Global
 public class PerftBench
 {
     private IPerft _perft;
@@ -71,10 +71,8 @@ public async Task<ulong> PerftIAsync()
     {
         var total = ulong.MinValue;
         for (var i = 0; i < N; i++)
-        {
             await foreach (var res in _perft.DoPerft(N).ConfigureAwait(false))
                 total += res;
-        }
         return total;
     }
 
@@ -83,9 +81,7 @@ public async Task<ulong> Perft()
     {
         var total = ulong.MinValue;
         for (var i = 0; i < N; ++i)
-        {
             total += await _perft.DoPerftAsync(N);
-        }
 
         return total;
     }
diff --git a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
index 3675be5f..7fa89b0f 100644
--- a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using BenchmarkDotNet.Attributes;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs b/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
index 45c753a7..385c1be8 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
@@ -30,4 +30,4 @@ namespace Rudzoft.ChessLib.Perft.Interfaces;
 
 public record struct PerftPositionValue(int Depth, ulong MoveCount);
 
-public record PerftPosition(string Id, string Fen, IList<PerftPositionValue> Value);
+public sealed record PerftPosition(string Id, string Fen, IList<PerftPositionValue> Value);
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 221796b6..80879300 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -27,9 +27,9 @@
 
   <ItemGroup>
     <PackageReference Include="FluentAssertions" Version="6.8.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
     <PackageReference Include="xunit" Version="2.4.2" />
-    <PackageReference Include="xunit.analyzers" Version="1.0.0" />
+    <PackageReference Include="xunit.analyzers" Version="1.1.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index a9585f4e..ab78f714 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using System.Collections;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
 using System.Runtime.CompilerServices;
@@ -72,6 +71,7 @@ static KpkBitBase()
             }
         }
 
+
         // Fill the bitbase with the decisive results
         var idx = 0;
         ref var setDbSpace = ref MemoryMarshal.GetReference(db);
@@ -81,12 +81,11 @@ static KpkBitBase()
             if (kpkPosition.Result == Result.Win)
                 KpKbb.Set(idx, true);
             idx++;
-
         }
     }
 
     /// <summary>
-    /// A KPK bitbase index is an integer in [0, IndexMax] range
+    /// A KPK bitbase index is an integer in [0,IndexMax] range
     ///
     /// Information is mapped in a way that minimizes the number of iterations:
     ///
@@ -175,7 +174,7 @@ public static KpkPosition Create(int idx)
                 psq);
         }
 
-        public Result Result { get; private set; }
+        public Result Result { get; }
         private Player Stm { get; }
         private Square[] KingSquares { get; }
         private Square PawnSquare { get; }
diff --git a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
index 74efb4e4..2ae4d36f 100644
--- a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Types;
 // ReSharper disable MemberCanBeInternal
 // ReSharper disable RedundantCast
diff --git a/src/Rudzoft.ChessLib/Hash/RKiss.cs b/src/Rudzoft.ChessLib/Hash/RKiss.cs
index 7d6b90b0..838d978f 100644
--- a/src/Rudzoft.ChessLib/Hash/RKiss.cs
+++ b/src/Rudzoft.ChessLib/Hash/RKiss.cs
@@ -27,7 +27,6 @@
 
  */
 
-using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
index 94dc8d32..025d96f0 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
@@ -26,6 +26,4 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
-public record struct Clock(ulong[] Inc, ulong[] Time)
-{
-}
\ No newline at end of file
+public record struct Clock(ulong[] Inc, ulong[] Time);
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index 0b775721..3be7a3bd 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -111,7 +111,7 @@ public IEnumerable<Move> MovesFromUci(IPosition pos, Stack<State> states, IEnume
     {
         var uciMoves = moves
             .Select(x => MoveFromUci(pos, x))
-            .Where(x => !x.IsNullMove());
+            .Where(static x => !x.IsNullMove());
 
         foreach (var move in uciMoves)
         {
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 696d44e7..1208bc73 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -54,7 +54,7 @@
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.1" />
     <PackageReference Include="ZString" Version="2.5.0" />
   </ItemGroup>
   <ItemGroup>
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index 8edc8ee0..f44cd70c 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
@@ -41,7 +42,7 @@ namespace Rudzoft.ChessLib.Types;
 /// Enumeration will yield each set bit as a Square struct.
 /// <para>For more information - please see https://github.com/rudzen/ChessLib/wiki/BitBoard</para>
 /// </summary>
-public readonly record struct BitBoard(ulong Value) : IEnumerable<Square>
+public readonly record struct BitBoard(ulong Value) : IEnumerable<Square>, IMinMaxValue<BitBoard>
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard(BitBoard value)
@@ -61,6 +62,10 @@ private BitBoard(int value)
 
     public static readonly BitBoard Empty = BitBoards.EmptyBitBoard;
 
+    public static BitBoard MaxValue { get; } = BitBoards.EmptyBitBoard;
+
+    public static BitBoard MinValue { get; } = BitBoards.AllSquares;
+
     public string String => Convert.ToString((long)Value, 2).PadLeft(64, '0');
 
     /// <summary>
@@ -230,7 +235,10 @@ public BitBoard OrAll(ReadOnlySpan<Square> sqs)
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool MoreThanOne()
-        => (Value & (Value - 1)) > 0;
+    {
+        var v = Value;
+        return (v & (v - 1)) > 0;
+    }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public IEnumerator<Square> GetEnumerator()
@@ -249,7 +257,7 @@ IEnumerator IEnumerable.GetEnumerator()
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string ToString()
-        => BitBoards.PrintBitBoard(this, Value.ToString());
+        => BitBoards.PrintBitBoard(in this, Value.ToString());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void ToString(TextWriter textWriter)
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 2b2e31e8..00f9a973 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -564,7 +564,7 @@ public static string PrintBitBoard(in BitBoard bb, string title = "")
             {
                 span[idx++] = '|';
                 span[idx++] = ' ';
-                span[idx++] = (bb & new Square(r, f)).IsEmpty ? ' ' : 'X';;
+                span[idx++] = (bb & new Square(r, f)).IsEmpty ? ' ' : 'X';
                 span[idx++] = ' ';
             }
 
@@ -814,7 +814,7 @@ private static IDictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
         };
 
     private static Func<BitBoard, BitBoard>[] MakeFillFuncs()
-        => new Func<BitBoard, BitBoard>[] { NorthFill, SouthFill };
+        => new[] { NorthFill, SouthFill };
 
     private static BitBoard GetAttacks(this in Square sq, PieceTypes pt, in BitBoard occ = default)
     {
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index 1d55ff50..f699400d 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -229,7 +229,7 @@ public override int GetHashCode()
         => AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank Relative(Player p) 
+    public Rank Relative(Player p)
         => new(AsInt() ^ (p.Side * 7));
 
     /// <summary>
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index b498f282..16a18aa8 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -27,7 +27,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Numerics;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 
 namespace Rudzoft.ChessLib.Types;
 
@@ -129,7 +128,7 @@ public override bool Equals(object obj)
         => obj is Score other && Equals(other);
 
 #pragma warning disable S2328 // "GetHashCode" should not reference mutable fields
-    public override int GetHashCode()
+    public readonly override int GetHashCode()
 #pragma warning restore S2328 // "GetHashCode" should not reference mutable fields
         => _data.GetHashCode();
 
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index cf30b3d5..cd582639 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -385,16 +385,16 @@ public int CompareTo(Square other)
     }
 
     /// <summary>
-    /// Swap A1 <-> H1
+    /// Swap A1 &lt;-&gt; H1
     /// </summary>
     /// <returns>Flipped square by File</returns>
     public Square FlipFile()
         => AsInt() ^ Squares.h1.AsInt();
 
     /// <summary>
-    /// Swap A1 <-> A8
+    /// Swap A1 &lt;-&gt; A8
     /// </summary>
-    /// <returns>Flipped square by Fank</returns>
+    /// <returns>Flipped square by Rank</returns>
     public Square FlipRank()
         => AsInt() ^ Squares.a8.AsInt();
 
diff --git a/src/Rudzoft.ChessLib/Types/StateStack.cs b/src/Rudzoft.ChessLib/Types/StateStack.cs
index 36559c7d..e8f8f041 100644
--- a/src/Rudzoft.ChessLib/Types/StateStack.cs
+++ b/src/Rudzoft.ChessLib/Types/StateStack.cs
@@ -29,7 +29,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
 
 namespace Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index c1c09368..a20507df 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -36,7 +36,7 @@
   <ItemGroup>
     <PackageReference Include="CommandLineParser" Version="2.9.1" />
     <PackageReference Include="ConsoleGUI" Version="1.4.1" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.1" />
     <PackageReference Include="Serilog" Version="2.12.0" />
     <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
     <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />

From 01536f4d9bb8d9c06ca43f3c3f5224b9f8f67565 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 12 Feb 2023 09:15:01 +0100
Subject: [PATCH 003/119] Various minor additions

- Added some more helper functions to IPosition
- Fixed a UCI bug with options in regards to bool values
- Updated dependencies
- Added some static Create() factory methods to Score
- Updated SearchParameters slightly
- Minor update to HashTable<T>
- Clarified some methods in KpkBitBase
---
 .github/workflows/publish.yml                 |   2 +-
 .github/workflows/test.yml                    |   2 +-
 Rudzoft.ChessLib.sln.DotSettings              |   2 +
 .../Rudzoft.ChessLib.Benchmark.csproj         |   2 +-
 .../BoardTests/BoardTests.cs                  |  71 +++++++++++
 .../OptionsTests.cs}                          | 115 +++++++++---------
 .../Rudzoft.ChessLib.Test.csproj              |   2 +-
 src/Rudzoft.ChessLib/Board.cs                 |  10 +-
 src/Rudzoft.ChessLib/Cuckoo.cs                |   1 -
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |  41 ++++---
 src/Rudzoft.ChessLib/Game.cs                  |   9 +-
 src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs |   7 +-
 .../Hash/Tables/IHashTable.cs                 |   7 +-
 .../Hash/Tables/ITableEntry.cs                |  12 ++
 src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs |  21 ++--
 src/Rudzoft.ChessLib/IPosition.cs             |  14 ++-
 .../Notation/Notations/SanNotation.cs         |   9 +-
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |  10 +-
 .../Polyglot/PolyglotBookZobrist.cs           |  16 +--
 src/Rudzoft.ChessLib/Position.cs              |  22 +++-
 src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs    |   2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Option.cs   |  10 +-
 .../Protocol/UCI/SearchParameters.cs          |  49 +++++---
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  12 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |   1 -
 src/Rudzoft.ChessLib/Types/Piece.cs           |   6 +-
 src/Rudzoft.ChessLib/Types/PieceValue.cs      |  23 ++--
 src/Rudzoft.ChessLib/Types/Score.cs           |   6 +
 .../Rudzoft.Perft.Framework.csproj            |   2 +-
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |   2 +-
 30 files changed, 315 insertions(+), 173 deletions(-)
 create mode 100644 Rudzoft.ChessLib.sln.DotSettings
 create mode 100644 src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
 rename src/Rudzoft.ChessLib.Test/{HashTableTests/PawnHashTableTest.cs => ProtocolTests/OptionsTests.cs} (52%)
 create mode 100644 src/Rudzoft.ChessLib/Hash/Tables/ITableEntry.cs

diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 7d10cbc8..e26859e6 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -14,7 +14,7 @@ jobs:
     - name: Setup .NET
       uses: actions/setup-dotnet@v2
       with:
-        dotnet-version: 6.0.x
+        dotnet-version: 7.0.x
     - name: Restore dependencies
       run: dotnet restore
     - name: Build
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index dbde803b..bce1b744 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -14,7 +14,7 @@ jobs:
     - name: Setup .NET
       uses: actions/setup-dotnet@v2
       with:
-        dotnet-version: 6.0.x
+        dotnet-version: 7.0.x
     - name: Restore dependencies
       run: dotnet restore
     - name: Build
diff --git a/Rudzoft.ChessLib.sln.DotSettings b/Rudzoft.ChessLib.sln.DotSettings
new file mode 100644
index 00000000..85dd9501
--- /dev/null
+++ b/Rudzoft.ChessLib.sln.DotSettings
@@ -0,0 +1,2 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=Perft/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 6f8bb7ae..564edc4c 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -12,7 +12,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.13.4" />
     <PackageReference Include="System.Runtime" Version="4.3.1" />
   </ItemGroup>
 
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
new file mode 100644
index 00000000..3c25dffb
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -0,0 +1,71 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Test.BoardTests;
+
+public sealed class BoardTests
+{
+    [Theory]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Pawn, Players.White, 8)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Pawn, Players.Black, 8)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Knight, Players.White, 2)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Knight, Players.Black, 2)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Bishop, Players.White, 2)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Bishop, Players.Black, 2)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Rook, Players.White, 2)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Rook, Players.Black, 2)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Queen, Players.White, 1)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Queen, Players.Black, 1)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.King, Players.White, 1)]
+    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.King, Players.Black, 1)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Pawn, Players.White, 4)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Pawn, Players.Black, 3)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Knight, Players.White, 1)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Knight, Players.Black, 1)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Bishop, Players.White, 0)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Bishop, Players.Black, 0)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Rook, Players.White, 2)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Rook, Players.Black, 2)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Queen, Players.White, 0)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Queen, Players.Black, 0)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.King, Players.White, 1)]
+    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.King, Players.Black, 1)]
+    public void BoardPieceCount(string fen, PieceTypes pt, Player p, int expected)
+    {
+        var game = GameFactory.Create(fen);
+        var pos = game.Pos;
+        var board = pos.Board;
+
+        var posCount = pos.Pieces(pt, p).Count;
+        var boardCount = board.PieceCount(pt, p);
+        
+        Assert.Equal(posCount, boardCount);
+        Assert.Equal(expected, boardCount);        
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/HashTableTests/PawnHashTableTest.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
similarity index 52%
rename from src/Rudzoft.ChessLib.Test/HashTableTests/PawnHashTableTest.cs
rename to src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
index a832f89b..622f2f95 100644
--- a/src/Rudzoft.ChessLib.Test/HashTableTests/PawnHashTableTest.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
@@ -1,58 +1,59 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2022 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using System;
-using System.Runtime.InteropServices;
-using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.Hash.Tables;
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Test.HashTableTests;
-
-public sealed class PawnHashTableTest
-{
-    [Fact (Skip = "Weird System.ArgumentException")]
-    public void BaseInitialize()
-    {
-        const int tableSizeMb = 8;
-
-        var t = new PawnTable();
-
-        var key = new RKiss(1234567).Rand();
-        var size = Marshal.SizeOf(typeof(PawnTableEntry));
-        var initEntry = new Func<PawnTableEntry>(static () => new PawnTableEntry(0));
-
-        t.Initialize(size, tableSizeMb, initEntry);
-
-        var fromTable = t[key];
-
-        fromTable.PassedPawns[0] = BitBoards.Center;
-
-        var reRead = t[key];
-
-        Assert.Equal(reRead.PassedPawns[0], BitBoards.Center);
-    }
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Rudzoft.ChessLib.Protocol.UCI;
+
+namespace Rudzoft.ChessLib.Test.ProtocolTests;
+
+public class OptionsTests
+{
+    [Theory]
+    [InlineData("Boolean Test", true, "true", true, "option name Boolean Test type Check default true")]
+    [InlineData("Boolean Test", false, "false", false, "option name Boolean Test type Check default false")]
+    public void Boolean(string name, bool value, string expectedText, bool expected, string uciString)
+    {
+        IUci uci = new Uci();
+        
+        uci.Initialize();
+        
+        var option = new Option(name, 0, value);
+        uci.AddOption(name, option);
+
+        var actualText = option.GetText();
+        
+        Assert.Equal(expectedText, actualText);
+
+        var actual = option.GetBool();
+        
+        Assert.Equal(expected, actual);
+        Assert.Equal(expected, option);
+        
+        var t = uci.ToString();
+        
+        Assert.Contains(uciString, t);
+    }
+    
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 80879300..19e477ca 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -26,7 +26,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="FluentAssertions" Version="6.8.0" />
+    <PackageReference Include="FluentAssertions" Version="6.9.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.analyzers" Version="1.1.0" />
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index a943012d..6549cb6f 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -28,6 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Linq;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
 
@@ -176,14 +177,7 @@ public int PieceCount()
         => PieceCount(PieceTypes.AllPieces);
 
     public IEnumerator<Piece> GetEnumerator()
-    {
-        for (var i = 0; i < _pieces.Length; ++i)
-        {
-            var pc = _pieces[i];
-            if (pc != Piece.EmptyPiece)
-                yield return pc;
-        }
-    }
+        => _pieces.Where(static pc => pc != Piece.EmptyPiece).GetEnumerator();
 
     IEnumerator IEnumerable.GetEnumerator()
         => GetEnumerator();
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 92500360..c21ac4fe 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index ab78f714..41558198 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -96,17 +96,17 @@ static KpkBitBase()
     /// bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
     /// </summary>
     /// <param name="stm"></param>
-    /// <param name="blackKsq"></param>
-    /// <param name="whiteKsq"></param>
-    /// <param name="pawnSq"></param>
+    /// <param name="weakKingSq"></param>
+    /// <param name="strongKsq"></param>
+    /// <param name="strongPawnSq"></param>
     /// <returns></returns>
-    private static int Index(Player stm, Square blackKsq, Square whiteKsq, Square pawnSq)
+    private static int Index(Player stm, Square weakKingSq, Square strongKsq, Square strongPawnSq)
     {
-        return whiteKsq.AsInt()
-               | (blackKsq.AsInt() << 6)
+        return strongKsq.AsInt()
+               | (weakKingSq.AsInt() << 6)
                | (stm << 12)
-               | (pawnSq.File.AsInt() << 13)
-               | ((Ranks.Rank7.AsInt() - pawnSq.Rank.AsInt()) << 15);
+               | (strongPawnSq.File.AsInt() << 13)
+               | ((Ranks.Rank7.AsInt() - strongPawnSq.Rank.AsInt()) << 15);
     }
 
     [Flags]
@@ -256,18 +256,29 @@ public static Square Normalize(IPosition pos, Player strongSide, Square sq)
     /// <summary>
     /// Probe with normalized squares and strong player
     /// </summary>
-    /// <param name="whiteKsq">"Strong" side king square</param>
-    /// <param name="pawnSq">"Strong" side pawn square</param>
-    /// <param name="blackKsq">"Weak" side king square</param>
+    /// <param name="strongKsq">"Strong" side king square</param>
+    /// <param name="strongPawnSq">"Strong" side pawn square</param>
+    /// <param name="weakKsq">"Weak" side king square</param>
     /// <param name="stm">strong side. fx strongSide == pos.SideToMove ? Player.White : Player.Black</param>
     /// <returns>true if strong side "won"</returns>
-    public static bool Probe(Square whiteKsq, Square pawnSq, Square blackKsq, Player stm)
+    public static bool Probe(Square strongKsq, Square strongPawnSq, Square weakKsq, Player stm)
     {
-        Debug.Assert(pawnSq.File <= File.FileD);
-
-        return KpKbb[Index(stm, blackKsq, whiteKsq, pawnSq)];
+        Debug.Assert(strongPawnSq.File <= File.FileD);
+        return KpKbb[Index(stm, weakKsq, strongKsq, strongPawnSq)];
     }
 
+    /// <summary>
+    /// </summary>
+    /// <param name="stngActive"></param>
+    /// <param name="skSq">White King</param>
+    /// <param name="wkSq">Black King</param>
+    /// <param name="spSq">White Pawn</param>
+    /// <returns></returns>
+    public static bool Probe(bool stngActive, Square skSq, Square wkSq, Square spSq)
+    {
+        return KpKbb[Index(stngActive, skSq, wkSq, spSq)];
+    }
+    
     public static bool IsDraw(IPosition pos)
     {
         return !Probe(
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index eba7df22..356955df 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -103,11 +103,10 @@ public void UpdateDrawTypes()
         for (var i = 0; i < moves.Length; ++i)
         {
             var em = Unsafe.Add(ref movesSpace, i);
-            if (!Pos.IsLegal(em.Move))
-            {
-                gameEndType |= GameEndTypes.Pat;
-                break;
-            }
+            if (Pos.IsLegal(em.Move))
+                continue;
+            gameEndType |= GameEndTypes.Pat;
+            break;
         }
 
         GameEndType = gameEndType;
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs
index 88db3649..fef44434 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -25,16 +25,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Hash.Tables;
 
-public abstract class HashTable<T> : IHashTable<T> where T : new()
+public abstract class HashTable<T> : IHashTable<T> where T : ITableEntry
 {
     private T[] _table;
 
     public int Count => _table.Length;
 
-    public ref T this[ulong key] => ref _table[(uint)key & (_table.Length - 1)];
+    public ref T this[HashKey key] => ref _table[key.LowerKey & (_table.Length - 1)];
 
     /// <summary>
     /// Initialized the table array. In case the table array is initialized with a different
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/IHashTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/IHashTable.cs
index 79ed57ed..655f045a 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/IHashTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/IHashTable.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -25,14 +25,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Hash.Tables;
 
-public interface IHashTable<T> where T : new()
+public interface IHashTable<T> where T : ITableEntry
 {
     int Count { get; }
 
-    ref T this[ulong key] { get; }
+    ref T this[HashKey key] { get; }
 
     void Initialize(int elementSize, int tableSizeMb, Func<T> initializer);
 }
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/ITableEntry.cs b/src/Rudzoft.ChessLib/Hash/Tables/ITableEntry.cs
new file mode 100644
index 00000000..b25c6ef8
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Hash/Tables/ITableEntry.cs
@@ -0,0 +1,12 @@
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Hash.Tables;
+
+public interface ITableEntry
+{
+    HashKey Key { get; set; }
+
+    Score Evaluate(IPosition pos);
+    
+    Score Evaluate(IPosition pos, in BitBoard attacks, Player own);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs
index efd7b377..c75d29de 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -28,9 +28,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Hash.Tables;
 
-public struct PawnTableEntry
+public class PawnTableEntry : ITableEntry
 {
-    public ulong Key;
     public Score Score;
     public BitBoard[] PassedPawns;
 
@@ -40,8 +39,16 @@ public PawnTableEntry(ulong key)
         Score = Score.Zero;
         PassedPawns = new BitBoard[Player.Count];
     }
-}
 
-public sealed class PawnTable : HashTable<PawnTableEntry>
-{
-}
\ No newline at end of file
+    public HashKey Key { get; set; }
+    
+    public Score Evaluate(IPosition pos)
+    {
+        throw new System.NotImplementedException();
+    }
+
+    public Score Evaluate(IPosition pos, in BitBoard attacks, Player own)
+    {
+        throw new System.NotImplementedException();
+    }
+}
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index de7cf116..fdfae929 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -117,6 +117,14 @@ public interface IPosition : IEnumerable<Piece>
     BitBoard Pieces(PieceTypes pt, Player p);
 
     BitBoard Pieces(PieceTypes pt1, PieceTypes pt2, Player p);
+    
+    int PieceCount();
+
+    int PieceCount(Piece pc);
+
+    int PieceCount(PieceTypes pt);
+
+    int PieceCount(PieceTypes pt, Player p);
 
     BitBoard PawnsOnColor(Player p, Square sq);
 
@@ -180,7 +188,7 @@ public interface IPosition : IEnumerable<Piece>
 
     FenData GenerateFen();
 
-    void Set(in FenData fenData, ChessMode chessMode, State state, bool validate = false, int searcher = 0);
+    IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool validate = false, int searcher = 0);
 
     HashKey GetPiecesKey();
 
@@ -200,5 +208,9 @@ public interface IPosition : IEnumerable<Piece>
 
     bool SeeGe(Move m, Value threshold);
 
+    Value NonPawnMaterial(Player p);
+    
+    Value NonPawnMaterial();
+    
     IPositionValidator Validate(PositionValidationTypes type = PositionValidationTypes.Basic);
 }
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
index d1cfe6d8..b5768a6d 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
@@ -87,13 +87,10 @@ public override string Convert(Move move)
         {
             re[i++] = '=';
             re[i++] = move.PromotedPieceType().MakePiece(Pos.SideToMove).GetPgnChar();
-        }
+        } else if (Pos.InCheck && Pos.GenerateMoves().Get().IsEmpty)
+            re[i++] = '#';
 
-        if (Pos.InCheck)
-        {
-            if (Pos.GenerateMoves().Get().IsEmpty)
-                re[i++] = '#';
-        } else if (GivesCheck(move))
+        if (GivesCheck(move))
             re[i++] = '+';
 
         return new string(re[..i]);
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index 115a6a5b..a16f7fd7 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -127,9 +127,6 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
         // bit 0- 5: destination square (from 0 to 63)
         // bit 6-11: origin square (from 0 to 63)
         // bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
-        //
-        // In case book move is a non-normal move, the move have to be converted. Castleling moves
-        // are especially converted to reflect castleling move format.
 
         Move move = polyMove;
 
@@ -153,10 +150,9 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
             .Where(m =>
             {
                 var type = move.MoveType();
-                if (m.IsPromotionMove())
-                    return type == MoveTypes.Promotion;
-                else
-                    return type != MoveTypes.Promotion;
+                return m.IsPromotionMove()
+                    ? type == MoveTypes.Promotion
+                    : type != MoveTypes.Promotion;
             })
             .Where(m => !IsInCheck(pos, m));
 
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 8607d475..36007b0f 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -30,7 +30,7 @@ namespace Rudzoft.ChessLib.Polyglot;
 
 internal static class PolyglotBookZobrist
 {
-    private static readonly ulong[,] psq =
+    private static readonly ulong[,] PsqKeys =
     {
         {
             0x9D39247E33776D41UL, 0x2AF7398005AAA5C7UL, 0x44DB015024623547UL,
@@ -322,7 +322,7 @@ internal static class PolyglotBookZobrist
         }
     };
 
-    private static readonly ulong[] castle =
+    private static readonly ulong[] CastleKeys =
     {
         0UL,
         0x31D71DCE64B2C310UL, // white short
@@ -335,23 +335,23 @@ internal static class PolyglotBookZobrist
         0x1EF6E6DBB1961EC9UL // black long
     };
 
-    private static readonly ulong[] enpassant =
+    private static readonly ulong[] EnPassantKeys =
     {
         0x70CC73D90BC26E24UL, 0xE21A6B35DF0C3AD7UL, 0x003A93D8B2806962UL, 0x1C99DED33CB890A1UL,
         0xCF3145DE0ADD4289UL, 0xD0E4427A5514FB72UL, 0x77C621CC9FB3A483UL, 0x67A34DAC4356550BUL
     };
 
-    private const ulong turn = 0xF8D626AAAF278509UL;
+    private const ulong TurnKey = 0xF8D626AAAF278509UL;
 
     internal static ulong Psq(int piece, Square sq)
-        => psq[piece, sq.AsInt()];
+        => PsqKeys[piece, sq.AsInt()];
 
     internal static ulong Castle(CastleRight rights)
-        => castle[rights.AsInt()];
+        => CastleKeys[rights.AsInt()];
 
     internal static ulong EnPassant(File f)
-        => enpassant[f.AsInt()];
+        => EnPassantKeys[f.AsInt()];
 
     internal static ulong Turn()
-        => turn;
+        => TurnKey;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 03437454..f8694a8e 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -953,6 +953,18 @@ public BitBoard Pieces(PieceTypes pt, Player p)
     public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2, Player p)
         => Board.Pieces(p, pt1, pt2);
 
+    public int PieceCount()
+        => Board.PieceCount();
+
+    public int PieceCount(Piece pc)
+        => Board.PieceCount(pc.Type(), pc.ColorOf());
+
+    public int PieceCount(PieceTypes pt)
+        => Board.PieceCount(pt);
+
+    public int PieceCount(PieceTypes pt, Player p)
+        => Board.PieceCount(pt, p);
+
     public BitBoard PawnsOnColor(Player p, Square sq)
         => Pieces(PieceTypes.Pawn, p) & sq.Color().ColorBB();
 
@@ -1085,6 +1097,10 @@ public bool SeeGe(Move m, Value threshold)
         return res > 0;
     }
 
+    public Value NonPawnMaterial(Player p) => State.NonPawnMaterial[p.Side];
+
+    public Value NonPawnMaterial() => State.NonPawnMaterial[0] - State.NonPawnMaterial[1];
+
     private void SetupPieces(ReadOnlySpan<char> fenChunk)
     {
         var f = 1; // file (column)
@@ -1177,7 +1193,7 @@ private void SetupMoveNumber(IFenData fenData)
     /// <param name="state">State reference to use. Allows to keep track of states if pre-created (i.e. in a stack) before engine search start</param>
     /// <param name="validate">If true, the fen should be validated, otherwise not</param>
     /// <param name="searcher">Searcher index, to help point to a specific search index in thread-based search array</param>
-    public void Set(in FenData fenData, ChessMode chessMode, State state, bool validate = false, int searcher = 0)
+    public IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool validate = false, int searcher = 0)
     {
         if (validate)
             Fen.Fen.Validate(fenData.Fen.ToString());
@@ -1197,6 +1213,8 @@ public void Set(in FenData fenData, ChessMode chessMode, State state, bool valid
         ChessMode = chessMode;
 
         SetState();
+
+        return this;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -1229,7 +1247,7 @@ public void TakeMove(Move m)
         }
 
         if (type == MoveTypes.Castling)
-            var (_, _) = DoCastleling(us, from, ref to, CastlePerform.Undo);
+            DoCastleling(us, from, ref to, CastlePerform.Undo);
         else
         {
             // Note: The parameters are reversed, since we move the piece "back"
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
index 025d96f0..2b3b3243 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
@@ -26,4 +26,4 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
-public record struct Clock(ulong[] Inc, ulong[] Time);
+public record struct Clock(ulong Inc, ulong Time);
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
index ae567c9e..a8a9cacd 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
@@ -32,6 +32,8 @@ namespace Rudzoft.ChessLib.Protocol.UCI;
 
 public sealed class Option : IOption
 {
+    private static readonly string[] BoolStrings = { "false", "true" };
+    
     private string _currentValue;
 
     public Option()
@@ -56,7 +58,7 @@ public Option(string name, int indices, bool v, Action<IOption> func = null)
         Min = Max = 0;
         Idx = indices;
         OnChange = func;
-        DefaultValue = _currentValue = v.ToString();
+        DefaultValue = _currentValue = BoolStrings[v.AsByte()];
     }
 
     public Option(string name, int indices, string v, Action<IOption> func = null)
@@ -82,9 +84,7 @@ public Option(string name, int indices, int v, int minValue, int maxValue, Actio
 
     public static implicit operator bool(Option o)
     {
-        if (o.Type == UciOptionType.Check)
-            return bool.Parse(o._currentValue);
-        return false;
+        return o.Type == UciOptionType.Check && o.GetBool();
     }
 
     public string Name { get; set; }
@@ -121,7 +121,7 @@ public string GetText()
     public bool GetBool()
     {
         var b = bool.TryParse(_currentValue, out var r);
-        return b ? r : b;
+        return b && r;
     }
 
     /// <summary>
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index 016f8d94..a38801c9 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -37,14 +37,14 @@ namespace Rudzoft.ChessLib.Protocol.UCI;
 /// </summary>
 public sealed class SearchParameters : ISearchParameters, ISpanFormattable
 {
-    private readonly Clock _clock;
+    private readonly Clock[] _clock;
     private ulong _movesToGo;
     private ulong _moveTime;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public SearchParameters()
     {
-        _clock = new Clock(new ulong[Player.Count], new ulong[Player.Count]);
+        _clock = new Clock[Player.Count];
         SearchMoves = new List<Move>();
     }
 
@@ -104,39 +104,52 @@ public ulong MovesToGo
 
     public ulong WhiteTimeMilliseconds
     {
-        get => _clock.Time[Player.White.Side];
-        set => _clock.Time[Player.White.Side] = value;
+        get => _clock[Player.White.Side].Time;
+        set => _clock[Player.White.Side].Time = value;
     }
 
     public ulong BlackTimeMilliseconds
     {
-        get => _clock.Time[Player.Black.Side];
-        set => _clock.Time[Player.Black.Side] = value;
+        get => _clock[Player.Black.Side].Time;
+        set => _clock[Player.Black.Side].Time = value;
     }
 
     public ulong WhiteIncrementTimeMilliseconds
     {
-        get => _clock.Inc[Player.White.Side];
-        set => _clock.Inc[Player.White.Side] = value;
+        get => _clock[Player.White.Side].Inc;
+        set => _clock[Player.White.Side].Inc = value;
     }
 
     public ulong BlackIncrementTimeMilliseconds
     {
-        get => _clock.Inc[Player.Black.Side];
-        set => _clock.Inc[Player.Black.Side] = value;
+        get => _clock[Player.Black.Side].Inc;
+        set => _clock[Player.Black.Side].Inc = value;
     }
 
+    public ref Clock Clock(Player p)
+    {
+        return ref _clock[p.Side];
+    }
+
+    public bool UseTimeManagement()
+    {
+        return _clock[Player.White.Side].Time != 0 && _clock[Player.Black.Side].Time != 0;
+    }
+    
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ulong Time(Player p) => _clock.Time[p.Side];
+    public ulong Time(Player p) => _clock[p.Side].Time;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ulong Inc(Player p) => _clock.Inc[p.Side];
+    public ulong Inc(Player p) => _clock[p.Side].Inc;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Clear()
     {
-        Array.Clear(_clock.Time);
-        Array.Clear(_clock.Inc);
+        _clock[Player.White.Side].Time = ulong.MinValue;
+        _clock[Player.White.Side].Time = ulong.MinValue;
+        _clock[Player.Black.Side].Inc = ulong.MinValue;
+        _clock[Player.Black.Side].Inc = ulong.MinValue;
+        
         MoveTime = 0;
         Infinite = false;
     }
@@ -195,7 +208,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
         destination[index++] = 'm';
         destination[index++] = 'e';
         destination[index++] = ' ';
-        index = ParseValue(index, in _clock.Time[Player.White.Side], destination);
+        index = ParseValue(index, _clock[Player.White.Side].Time, destination);
 
         destination[index++] = ' ';
         destination[index++] = 'b';
@@ -204,7 +217,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
         destination[index++] = 'm';
         destination[index++] = 'e';
         destination[index++] = ' ';
-        index = ParseValue(index, in _clock.Time[Player.Black.Side], destination);
+        index = ParseValue(index, _clock[Player.Black.Side].Time, destination);
 
         if (MoveTime > ulong.MinValue)
         {
@@ -227,7 +240,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
         destination[index++] = 'n';
         destination[index++] = 'c';
         destination[index++] = ' ';
-        index = ParseValue(index, in _clock.Inc[Player.White.Side], destination);
+        index = ParseValue(index, _clock[Player.White.Side].Inc, destination);
 
         destination[index++] = ' ';
         destination[index++] = 'b';
@@ -236,7 +249,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
         destination[index++] = 'c';
         destination[index++] = ' ';
 
-        index = ParseValue(index, in _clock.Inc[Player.Black.Side], destination);
+        index = ParseValue(index, _clock[Player.Black.Side].Inc, destination);
 
         if (_movesToGo == ulong.MinValue)
         {
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 1208bc73..87cf51b3 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -8,18 +8,18 @@
     <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
     <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
     <TargetFramework>net7.0</TargetFramework>
-    <PackageVersion>0.0.3</PackageVersion>
+    <PackageVersion>0.0.4</PackageVersion>
     <Authors>Rudy Alex Kohn</Authors>
     <Company>None</Company>
-    <Version>0.0.3</Version>
+    <Version>0.0.4</Version>
     <Description>Chess library with data structures and move generation.</Description>
     <Copyright>(C) 2017-2022 Rudy Alex Kohn</Copyright>
-    <AssemblyVersion>0.0.3</AssemblyVersion>
-    <FileVersion>0.0.3</FileVersion>
+    <AssemblyVersion>0.0.4</AssemblyVersion>
+    <FileVersion>0.0.4</FileVersion>
     <PackageProjectUrl>https://github.com/rudzen/ChessLib</PackageProjectUrl>
     <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
     <PackageLicenseFile>LICENSE</PackageLicenseFile>
-    <Nullable>warnings</Nullable>
+    <Nullable>disable</Nullable>
     <EnforceCodeStyleInBuild>False</EnforceCodeStyleInBuild>
     <Title>Rudzoft.ChessLib</Title>
     <PackageReadmeFile>README.md</PackageReadmeFile>
@@ -54,7 +54,7 @@
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.1" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.2" />
     <PackageReference Include="ZString" Version="2.5.0" />
   </ItemGroup>
   <ItemGroup>
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index f44cd70c..0fa57475 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -36,7 +36,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // ReSharper disable MemberCanBeInternal
 namespace Rudzoft.ChessLib.Types;
 
-/// <inheritdoc />
 /// <summary>
 /// Bitboard struct, wraps an unsigned long with some nifty helper functionality and operators.
 /// Enumeration will yield each set bit as a Square struct.
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 4cda63cf..db544c03 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -24,11 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 
 namespace Rudzoft.ChessLib.Types;
@@ -61,7 +57,7 @@ public enum PieceTypes
     Queen = 5,
     King = 6,
     PieceTypeNb = 7,
-    AllPieces = 0
+    AllPieces = NoPieceType
 }
 
 public static class PieceTypesExtensions
diff --git a/src/Rudzoft.ChessLib/Types/PieceValue.cs b/src/Rudzoft.ChessLib/Types/PieceValue.cs
index c3fb3246..5bdd4b3a 100644
--- a/src/Rudzoft.ChessLib/Types/PieceValue.cs
+++ b/src/Rudzoft.ChessLib/Types/PieceValue.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Runtime.CompilerServices;
 using System.Text.Json.Serialization;
 using Rudzoft.ChessLib.Enums;
 
@@ -58,6 +59,12 @@ public enum PieceValues
     EndgameLimit = 3915
 }
 
+public static class PieceValuesExtensions
+{
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static int AsInt(this PieceValues @this) => (int)@this;
+}
+
 public sealed class PieceValue : IPieceValue
 {
     public const int MAX_PLY = 246;
@@ -80,7 +87,7 @@ public PieceValue()
         SetDefaults();
     }
 
-    public PieceValues[][] _values { get; set; }
+    public PieceValues[][] Values { get; set; }
 
     public Value MaxValueWithoutPawns { get; private set; }
 
@@ -138,9 +145,9 @@ public Value ValueMate
 
     public void SetDefaults()
     {
-        _values = new PieceValues[2][];
-        for (var index = 0; index < _values.Length; index++)
-            _values[index] = new PieceValues[6];
+        Values = new PieceValues[2][];
+        for (var index = 0; index < Values.Length; index++)
+            Values[index] = new PieceValues[6];
 
         Span<Phases> phases = stackalloc Phases[] { Phases.Mg, Phases.Eg };
 
@@ -154,7 +161,7 @@ public void SetDefaults()
 
         foreach (var pt in pieceTypes)
         {
-            var value = new Value(_values[0][pt.AsInt()]);
+            var value = new Value(Values[0][pt.AsInt()]);
             switch (pt)
             {
                 case PieceTypes.Pawn:
@@ -188,11 +195,11 @@ public void SetDefaults()
     }
 
     public void SetPieceValues(PieceValues[] values, Phases phase)
-        => Array.Copy(values, _values[phase.AsInt()], values.Length);
+        => Array.Copy(values, Values[phase.AsInt()], values.Length);
 
     public PieceValues GetPieceValue(Piece pc, Phases phase)
-        => _values[(int)phase][pc.Type().AsInt()];
+        => Values[(int)phase][pc.Type().AsInt()];
 
     public PieceValues GetPieceValue(PieceTypes pt, Phases phase)
-        => _values[phase.AsInt()][pt.AsInt()];
+        => Values[phase.AsInt()][pt.AsInt()];
 }
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index 16a18aa8..d31853d3 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -62,6 +62,12 @@ public Score(Value mg, Value eg)
     public Score(Score s)
         => _data = s._data;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Score Create(int mg, int eg) => new(mg, eg);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Score Create(in Vector2 v) => new(v);
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Score(int v)
         => new(v);
diff --git a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
index 739e8757..7454b411 100644
--- a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
+++ b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
@@ -13,7 +13,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="DryIoc" Version="5.3.1" />
+    <PackageReference Include="DryIoc" Version="5.3.2" />
     <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index a20507df..1e82b235 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -36,7 +36,7 @@
   <ItemGroup>
     <PackageReference Include="CommandLineParser" Version="2.9.1" />
     <PackageReference Include="ConsoleGUI" Version="1.4.1" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.1" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.2" />
     <PackageReference Include="Serilog" Version="2.12.0" />
     <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
     <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />

From 543d49d5b191ab9f3a359d1e44b41ee4f09fdcc8 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 12 Feb 2023 09:20:41 +0100
Subject: [PATCH 004/119] Update copyright year

---
 LICENSE                                       |  2 +-
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |  2 +-
 .../PerftBenchRunner.cs                       |  2 +-
 .../IPerft.cs                                 |  2 +-
 .../PerftPosition.cs                          |  2 +-
 src/Rudzoft.ChessLib.Perft/Perft.cs           |  2 +-
 src/Rudzoft.ChessLib.Perft/PerftFactory.cs    |  2 +-
 .../PerftPositionFactory.cs                   |  2 +-
 src/Rudzoft.ChessLib.Perft/PerftTable.cs      |  2 +-
 .../BitboardTests/BitboardDataTests.cs        |  2 +-
 .../BitboardTests/BitboardTests.cs            |  2 +-
 .../BitboardTests/LineTests.cs                |  2 +-
 .../BookTests/BookFixture.cs                  |  2 +-
 .../BookTests/PolyglotTests.cs                |  2 +-
 .../CastleTests/BasicCastleTests.cs           |  2 +-
 .../CastleTests/CastleRightTests.cs           |  2 +-
 .../DataTests/DataTests.cs                    |  2 +-
 .../DataTests/PieceSquareTests.cs             |  2 +-
 .../DataTests/StringExtensionsTests.cs        |  2 +-
 .../EvaluationTests/KpkBitBaseTests.cs        |  2 +-
 .../FENTests/FenTests.cs                      |  2 +-
 .../FenceTests/FenceTests.cs                  |  2 +-
 .../FileTests/FileEdgeDistanceTests.cs        |  2 +-
 .../FileTests/FileTests.cs                    |  2 +-
 .../GameplayTests/FoolsCheckMateTests.cs      |  2 +-
 .../MaterialTests/MaterialTests.cs            |  2 +-
 .../MoveTests/MoveGen_49.cs                   |  2 +-
 .../MoveTests/MoveGeneratorTests.cs           |  2 +-
 .../MoveTests/MoveTests.cs                    |  2 +-
 .../MoveTests/PerftTest.cs                    |  2 +-
 .../NotationTests/FanTests.cs                 |  2 +-
 .../NotationTests/IccfTests.cs                |  2 +-
 .../NotationTests/RanTests.cs                 |  2 +-
 .../NotationTests/SanTests.cs                 |  2 +-
 .../PiecesTests/PawnDoubleAttackTests.cs      |  2 +-
 .../PiecesTests/PawnPushTests.cs              |  2 +-
 .../PiecesTests/PieceAttacks.cs               |  2 +-
 .../PiecesTests/PieceAttacksBishopTests.cs    |  2 +-
 .../PiecesTests/PieceAttacksKingTests.cs      |  2 +-
 .../PiecesTests/PieceAttacksKnightTests.cs    |  2 +-
 .../PiecesTests/PieceAttacksPawnTests.cs      |  2 +-
 .../PiecesTests/PieceAttacksRookTests.cs      |  2 +-
 .../PiecesTests/RegularMobilityFixture.cs     |  2 +-
 .../PiecesTests/SliderMobilityFixture.cs      |  2 +-
 .../PiecesTests/SliderMobilityTests.cs        |  2 +-
 .../PositionTests/EnPassantFenTests.cs        |  2 +-
 .../PositionTests/PositionTests.cs            |  2 +-
 .../PositionTests/ValidationTests.cs          |  2 +-
 .../ProtocolTests/UciTests.cs                 |  2 +-
 .../RankTests/RankEdgeDistanceTests.cs        |  2 +-
 .../ScoreTests/ScoreTests.cs                  |  2 +-
 .../SizesTests/BaseTypesSizeTests.cs          |  2 +-
 .../SquareTests/DistanceTests.cs              |  2 +-
 .../SquareTests/SquareFromStringTests.cs      |  2 +-
 .../SquareTests/SquareTests.cs                |  2 +-
 .../TablesTests/KillerMovesTests.cs           |  2 +-
 src/Rudzoft.ChessLib/Blockage.cs              |  2 +-
 src/Rudzoft.ChessLib/Board.cs                 |  2 +-
 src/Rudzoft.ChessLib/Cuckoo.cs                |  2 +-
 src/Rudzoft.ChessLib/Enums/ChessMode.cs       |  2 +-
 src/Rudzoft.ChessLib/Enums/GameEndTypes.cs    |  2 +-
 src/Rudzoft.ChessLib/Enums/GameResults.cs     |  2 +-
 src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs |  2 +-
 .../Enums/MoveGenerationType.cs               |  2 +-
 src/Rudzoft.ChessLib/Enums/Phases.cs          |  2 +-
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |  2 +-
 src/Rudzoft.ChessLib/Exceptions/InvalidFen.cs |  2 +-
 .../Exceptions/InvalidMove.cs                 |  2 +-
 .../Exceptions/InvalidSquare.cs               |  2 +-
 .../Exceptions/TranspositionTableFailure.cs   |  2 +-
 .../Extensions/ArrayExtensions.cs             |  2 +-
 .../Extensions/MathExtensions.cs              |  2 +-
 src/Rudzoft.ChessLib/Extensions/Maths.cs      |  2 +-
 .../Extensions/PieceExtensions.cs             |  2 +-
 .../Extensions/SpanExtensions.cs              |  2 +-
 .../Extensions/StringExtensions.cs            |  2 +-
 .../Factories/BlockageFactory.cs              |  2 +-
 src/Rudzoft.ChessLib/Factories/GameFactory.cs |  2 +-
 src/Rudzoft.ChessLib/Fen/Fen.cs               |  2 +-
 src/Rudzoft.ChessLib/Fen/FenData.cs           |  2 +-
 src/Rudzoft.ChessLib/Fen/IFenData.cs          |  2 +-
 src/Rudzoft.ChessLib/Game.cs                  |  2 +-
 .../Hash/Tables/Transposition/Bound.cs        |  2 +-
 .../Hash/Tables/Transposition/ITTCluster.cs   |  2 +-
 .../Transposition/ITranspositionTable.cs      |  2 +-
 .../Transposition/TranspositionTable.cs       |  2 +-
 .../Transposition/TranspositionTableEntry.cs  |  2 +-
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          |  2 +-
 src/Rudzoft.ChessLib/IBlockage.cs             |  2 +-
 src/Rudzoft.ChessLib/IBoard.cs                |  2 +-
 src/Rudzoft.ChessLib/IGame.cs                 |  2 +-
 src/Rudzoft.ChessLib/IPieceValue.cs           |  2 +-
 src/Rudzoft.ChessLib/IPosition.cs             |  4 +++-
 .../MoveGeneration/IMoveList.cs               |  2 +-
 .../MoveGeneration/MoveFactory.cs             |  2 +-
 .../MoveGeneration/MoveGenerator.cs           |  2 +-
 .../MoveGeneration/MoveList.cs                |  2 +-
 .../Notation/IMoveNotation.cs                 |  2 +-
 src/Rudzoft.ChessLib/Notation/MoveNotation.cs |  2 +-
 .../Notation/Notations/CoordinateNotation.cs  |  2 +-
 .../Notation/Notations/FanNotation.cs         |  2 +-
 .../Notation/Notations/INotation.cs           |  2 +-
 .../Notation/Notations/IccfNotation.cs        |  2 +-
 .../Notation/Notations/LanNotation.cs         |  2 +-
 .../Notation/Notations/Notation.cs            |  2 +-
 .../Notation/Notations/RanNotation.cs         |  2 +-
 .../Notation/Notations/SanNotation.cs         |  2 +-
 .../Notation/Notations/SmithNotation.cs       |  2 +-
 .../Notation/Notations/UciNotation.cs         |  2 +-
 .../ObjectPoolPolicies/MoveListPolicy.cs      |  2 +-
 .../LittleEndianBinaryStreamReader.cs         |  2 +-
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |  2 +-
 .../Polyglot/PolyglotBookEntry.cs             |  2 +-
 .../Polyglot/PolyglotBookZobrist.cs           |  2 +-
 src/Rudzoft.ChessLib/Position.cs              | 23 ++++++++++++++++++-
 src/Rudzoft.ChessLib/Protocol/UCI/CPU.cs      |  2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs    |  2 +-
 .../Protocol/UCI/CopyProtections.cs           |  2 +-
 .../Protocol/UCI/HiResTimer.cs                |  2 +-
 .../Protocol/UCI/HiResTimerArgs.cs            |  2 +-
 .../Protocol/UCI/IHiResTimer.cs               |  2 +-
 .../Protocol/UCI/IInputOutput.cs              |  2 +-
 .../Protocol/UCI/IMovesToGoModel.cs           |  2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs  |  2 +-
 .../Protocol/UCI/ISearchParameters.cs         |  2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs     |  2 +-
 .../Protocol/UCI/InputOutput.cs               |  2 +-
 .../Protocol/UCI/InputOutputAction.cs         |  2 +-
 .../Protocol/UCI/MovesToGoModel.cs            |  2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Option.cs   |  2 +-
 .../Protocol/UCI/OptionComparer.cs            |  2 +-
 .../Protocol/UCI/SearchParameters.cs          |  2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |  2 +-
 .../Protocol/UCI/UciOptionType.cs             |  2 +-
 src/Rudzoft.ChessLib/State.cs                 |  2 +-
 .../Tables/HistoryHeuristic.cs                |  2 +-
 .../Tables/IHistoryHeuristic.cs               |  2 +-
 src/Rudzoft.ChessLib/Tables/IKillerMoves.cs   |  2 +-
 src/Rudzoft.ChessLib/Tables/KillerMoves.cs    |  2 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |  2 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       |  2 +-
 src/Rudzoft.ChessLib/Types/CastleRight.cs     |  2 +-
 src/Rudzoft.ChessLib/Types/Depth.cs           |  2 +-
 src/Rudzoft.ChessLib/Types/Direction.cs       |  2 +-
 src/Rudzoft.ChessLib/Types/ExtMove.cs         |  2 +-
 src/Rudzoft.ChessLib/Types/File.cs            |  2 +-
 src/Rudzoft.ChessLib/Types/HashKey.cs         |  2 +-
 src/Rudzoft.ChessLib/Types/IPieceSquare.cs    |  2 +-
 src/Rudzoft.ChessLib/Types/IValidationType.cs |  2 +-
 src/Rudzoft.ChessLib/Types/Move.cs            |  2 +-
 src/Rudzoft.ChessLib/Types/Piece.cs           |  2 +-
 src/Rudzoft.ChessLib/Types/PieceSquare.cs     |  2 +-
 src/Rudzoft.ChessLib/Types/PieceValue.cs      |  2 +-
 src/Rudzoft.ChessLib/Types/Player.cs          |  2 +-
 src/Rudzoft.ChessLib/Types/Rank.cs            |  2 +-
 src/Rudzoft.ChessLib/Types/Score.cs           |  2 +-
 src/Rudzoft.ChessLib/Types/Square.cs          |  2 +-
 src/Rudzoft.ChessLib/Types/StateStack.cs      |  2 +-
 src/Rudzoft.ChessLib/Types/Value.cs           |  2 +-
 .../Validation/IPositionValidator.cs          |  2 +-
 .../Validation/PositionValidator.cs           |  2 +-
 .../Environment/FrameworkEnvironment.cs       |  2 +-
 .../Environment/IFrameworkEnvironment.cs      |  2 +-
 .../Extensions/ArrayExtensions.cs             |  2 +-
 .../Extensions/ObjectExtensions.cs            |  2 +-
 .../Factories/ConfigurationFactory.cs         |  2 +-
 src/Rudzoft.Perft.Framework/Framework.cs      |  2 +-
 167 files changed, 190 insertions(+), 167 deletions(-)

diff --git a/LICENSE b/LICENSE
index a6dc3fea..fb3b2aa7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index 9bb50157..a50bf1fc 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBenchRunner.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBenchRunner.cs
index a2684dd9..e4eb483a 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBenchRunner.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBenchRunner.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
index 7dc52092..cc6e3c07 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs b/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
index 385c1be8..f33cfaaa 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/Rudzoft.ChessLib.Perft/Perft.cs
index 0f7e62c5..4f8c8392 100644
--- a/src/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/Rudzoft.ChessLib.Perft/Perft.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Perft/PerftFactory.cs b/src/Rudzoft.ChessLib.Perft/PerftFactory.cs
index 78be81ab..4a85e4d2 100644
--- a/src/Rudzoft.ChessLib.Perft/PerftFactory.cs
+++ b/src/Rudzoft.ChessLib.Perft/PerftFactory.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs b/src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs
index b80fc694..4d2faaa2 100644
--- a/src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs
+++ b/src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Perft/PerftTable.cs b/src/Rudzoft.ChessLib.Perft/PerftTable.cs
index b2a2af52..92ef9058 100644
--- a/src/Rudzoft.ChessLib.Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib.Perft/PerftTable.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs
index a46f8e0c..05b53266 100644
--- a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
index 165256f7..af42a80a 100644
--- a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/LineTests.cs b/src/Rudzoft.ChessLib.Test/BitboardTests/LineTests.cs
index 4ffa61ee..b67ab7b2 100644
--- a/src/Rudzoft.ChessLib.Test/BitboardTests/LineTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BitboardTests/LineTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs b/src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs
index cf54a074..b1a2d4f0 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index 4b1794cc..d1b8243b 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
index a2cd0d9a..151e4753 100644
--- a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
+++ b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/CastleRightTests.cs b/src/Rudzoft.ChessLib.Test/CastleTests/CastleRightTests.cs
index e9a7a971..62ec17e9 100644
--- a/src/Rudzoft.ChessLib.Test/CastleTests/CastleRightTests.cs
+++ b/src/Rudzoft.ChessLib.Test/CastleTests/CastleRightTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs b/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
index 407b933c..afe2a247 100644
--- a/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
+++ b/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/PieceSquareTests.cs b/src/Rudzoft.ChessLib.Test/DataTests/PieceSquareTests.cs
index 379862bd..f1da85d4 100644
--- a/src/Rudzoft.ChessLib.Test/DataTests/PieceSquareTests.cs
+++ b/src/Rudzoft.ChessLib.Test/DataTests/PieceSquareTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs b/src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs
index 365a9901..f7646b44 100644
--- a/src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs
+++ b/src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
index 9418d724..11769796 100644
--- a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
+++ b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index a9cb41f4..a245e77b 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
index c7e92640..ebfaf36c 100644
--- a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs b/src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs
index d80c3263..cb2f302c 100644
--- a/src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs b/src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs
index a065a4a6..d3b23ce5 100644
--- a/src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
index 583c7af1..cc9f75bc 100644
--- a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
+++ b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/MaterialTests/MaterialTests.cs b/src/Rudzoft.ChessLib.Test/MaterialTests/MaterialTests.cs
index 94e5a42f..593dfeef 100644
--- a/src/Rudzoft.ChessLib.Test/MaterialTests/MaterialTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MaterialTests/MaterialTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
index 9a5636ba..b3afb050 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
index 9a366e0c..56de2c48 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index b8a74961..760c358b 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs b/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs
index 35e04163..fbcc1fd6 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index e7c688aa..d14f3c34 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index a1bc50c1..72281e2d 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index 1dc1a7bf..92642437 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index 2e83e214..a4a33dd1 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
index befbaf0c..184aabef 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnPushTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnPushTests.cs
index 37b092a5..b0bc2d84 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnPushTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnPushTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs
index 301121ea..2a45ace4 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs
index 0cbf647b..96e9c8d9 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
index db128990..f4f5cd99 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs
index a84ea1e5..aeb665f4 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs
index 0cd6bc9b..cf3bb69f 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
index deabcc1c..f5fc15f0 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
index c4cdf0cb..a932b569 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
index bbc598e4..fa30b72d 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
index aa96171d..776b6f94 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
index 8e9085f4..e5abef72 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 5f62ac35..00b2fbaf 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index c3cd48df..4e0b5d4d 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
index 48f27eca..05fcd65b 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs b/src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs
index e97107e5..b985d647 100644
--- a/src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs b/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
index fea91aad..01aa360a 100644
--- a/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
index a41e8d68..9e26a7ac 100644
--- a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/DistanceTests.cs b/src/Rudzoft.ChessLib.Test/SquareTests/DistanceTests.cs
index 0b43ba4b..eabe9d2b 100644
--- a/src/Rudzoft.ChessLib.Test/SquareTests/DistanceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SquareTests/DistanceTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareFromStringTests.cs b/src/Rudzoft.ChessLib.Test/SquareTests/SquareFromStringTests.cs
index 104d8cbb..f77f8f6f 100644
--- a/src/Rudzoft.ChessLib.Test/SquareTests/SquareFromStringTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SquareTests/SquareFromStringTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs b/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
index fff4a7a0..3f535f8c 100644
--- a/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
index 73c9aa7f..997f76ef 100644
--- a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
+++ b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 04b925e4..8f719967 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index 6549cb6f..100b663a 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index c21ac4fe..b4f07529 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Enums/ChessMode.cs b/src/Rudzoft.ChessLib/Enums/ChessMode.cs
index 3b691f0c..f40181d7 100644
--- a/src/Rudzoft.ChessLib/Enums/ChessMode.cs
+++ b/src/Rudzoft.ChessLib/Enums/ChessMode.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Enums/GameEndTypes.cs b/src/Rudzoft.ChessLib/Enums/GameEndTypes.cs
index 8bca4dbb..a6ffd4fe 100644
--- a/src/Rudzoft.ChessLib/Enums/GameEndTypes.cs
+++ b/src/Rudzoft.ChessLib/Enums/GameEndTypes.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Enums/GameResults.cs b/src/Rudzoft.ChessLib/Enums/GameResults.cs
index 2b1cb48e..cdec8cc4 100644
--- a/src/Rudzoft.ChessLib/Enums/GameResults.cs
+++ b/src/Rudzoft.ChessLib/Enums/GameResults.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs b/src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs
index a5b9a5e3..8742bef9 100644
--- a/src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs
+++ b/src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Enums/MoveGenerationType.cs b/src/Rudzoft.ChessLib/Enums/MoveGenerationType.cs
index 083c87a9..baa846e9 100644
--- a/src/Rudzoft.ChessLib/Enums/MoveGenerationType.cs
+++ b/src/Rudzoft.ChessLib/Enums/MoveGenerationType.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Enums/Phases.cs b/src/Rudzoft.ChessLib/Enums/Phases.cs
index da06afbc..b182149d 100644
--- a/src/Rudzoft.ChessLib/Enums/Phases.cs
+++ b/src/Rudzoft.ChessLib/Enums/Phases.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index 41558198..f5c5e6df 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidFen.cs b/src/Rudzoft.ChessLib/Exceptions/InvalidFen.cs
index a9721487..ac8f4eac 100644
--- a/src/Rudzoft.ChessLib/Exceptions/InvalidFen.cs
+++ b/src/Rudzoft.ChessLib/Exceptions/InvalidFen.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs b/src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
index fa745f49..d3e912c4 100644
--- a/src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
+++ b/src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs b/src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
index 64d54ac8..24df47c1 100644
--- a/src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
+++ b/src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs b/src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
index 59db951e..f8e09cee 100644
--- a/src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
+++ b/src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
index 735762d0..f5d97340 100644
--- a/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
index 2ae4d36f..c0c746bc 100644
--- a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Extensions/Maths.cs b/src/Rudzoft.ChessLib/Extensions/Maths.cs
index 6430f37d..1ed50875 100644
--- a/src/Rudzoft.ChessLib/Extensions/Maths.cs
+++ b/src/Rudzoft.ChessLib/Extensions/Maths.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
index 25fbf58e..04903a6f 100644
--- a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs b/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
index d66b9ec5..fe8610a8 100644
--- a/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
index 254d0d2c..e5d02726 100644
--- a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Factories/BlockageFactory.cs b/src/Rudzoft.ChessLib/Factories/BlockageFactory.cs
index 69b3734f..b176ab11 100644
--- a/src/Rudzoft.ChessLib/Factories/BlockageFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/BlockageFactory.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Factories/GameFactory.cs b/src/Rudzoft.ChessLib/Factories/GameFactory.cs
index dbcb2ee3..84e6b815 100644
--- a/src/Rudzoft.ChessLib/Factories/GameFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/GameFactory.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index 051f9436..b03a907a 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Fen/FenData.cs b/src/Rudzoft.ChessLib/Fen/FenData.cs
index 33116f5e..4e7f5cd0 100644
--- a/src/Rudzoft.ChessLib/Fen/FenData.cs
+++ b/src/Rudzoft.ChessLib/Fen/FenData.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Fen/IFenData.cs b/src/Rudzoft.ChessLib/Fen/IFenData.cs
index 9c5dfb9d..566dc652 100644
--- a/src/Rudzoft.ChessLib/Fen/IFenData.cs
+++ b/src/Rudzoft.ChessLib/Fen/IFenData.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 356955df..86bb1776 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
index 255848e8..e1b86f23 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs
index fe72ee94..4e6d8718 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
index 9a21a3df..c1ba285b 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index 8473215a..e6f957e6 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
index eb8911df..15c467c7 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index c37c895b..272b08c3 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/IBlockage.cs b/src/Rudzoft.ChessLib/IBlockage.cs
index 91e87c40..364bde7d 100644
--- a/src/Rudzoft.ChessLib/IBlockage.cs
+++ b/src/Rudzoft.ChessLib/IBlockage.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/IBoard.cs b/src/Rudzoft.ChessLib/IBoard.cs
index 5c7f1ba0..8532068f 100644
--- a/src/Rudzoft.ChessLib/IBoard.cs
+++ b/src/Rudzoft.ChessLib/IBoard.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/IGame.cs b/src/Rudzoft.ChessLib/IGame.cs
index ef20d316..235e9eb3 100644
--- a/src/Rudzoft.ChessLib/IGame.cs
+++ b/src/Rudzoft.ChessLib/IGame.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/IPieceValue.cs b/src/Rudzoft.ChessLib/IPieceValue.cs
index c69ad547..4a550284 100644
--- a/src/Rudzoft.ChessLib/IPieceValue.cs
+++ b/src/Rudzoft.ChessLib/IPieceValue.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index fdfae929..7e4fbad5 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -190,6 +190,8 @@ public interface IPosition : IEnumerable<Piece>
 
     IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool validate = false, int searcher = 0);
 
+    IPosition Set(ReadOnlySpan<char> code, Player p, State state);
+    
     HashKey GetPiecesKey();
 
     HashKey GetPawnKey();
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
index d894829f..f976f9c3 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs
index 585a63cc..5ad0653a 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index e2648e5d..07927f94 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index 470f9204..ae648174 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/IMoveNotation.cs b/src/Rudzoft.ChessLib/Notation/IMoveNotation.cs
index 8d675814..7eaefd11 100644
--- a/src/Rudzoft.ChessLib/Notation/IMoveNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/IMoveNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
index 57126a5c..c9501334 100644
--- a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
index 956d8db7..5fad669b 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
index 9c0089a1..5ca7930c 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/INotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/INotation.cs
index d5789acb..16d7c631 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/INotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/INotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index 4c727b9f..6fcd8921 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
index 8d3361bc..21857aea 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
index 9d7b0e78..7e4440c0 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
index 930be6a2..80b786e8 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
index b5768a6d..7a958cad 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
index 90b2f56d..43058d6e 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
index 38362b8e..479ddcf6 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/ObjectPoolPolicies/MoveListPolicy.cs b/src/Rudzoft.ChessLib/ObjectPoolPolicies/MoveListPolicy.cs
index f66b30bb..af915754 100644
--- a/src/Rudzoft.ChessLib/ObjectPoolPolicies/MoveListPolicy.cs
+++ b/src/Rudzoft.ChessLib/ObjectPoolPolicies/MoveListPolicy.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs b/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs
index d518f93a..dc4f7d62 100644
--- a/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index a16f7fd7..e4e7734b 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs
index 319cfb29..dfc01003 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 36007b0f..8e4d4e5a 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index f8694a8e..16d8e83c 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -1217,6 +1217,27 @@ public IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool
         return this;
     }
 
+    public IPosition Set(ReadOnlySpan<char> code, Player p, State state)
+    {
+        Debug.Assert(code[0] == 'K' && code[1..].IndexOf('K') != -1);
+        Debug.Assert(code.Length.InBetween(0, 8));
+        Debug.Assert(code[0] == 'K');
+
+        var kingPos = code[1..].IndexOf('K');
+        var sides = new[]
+        {
+            code[1..kingPos].ToString(),
+            code[kingPos].ToString()
+        };
+        
+        sides[p.Side] = sides[p.Side].ToLower();
+
+        var fenStr = $"{sides[0]}{8 - sides[0].Length}/8/8/8/8/8/8/{sides[1]}{8 - sides[1].Length} w - - 0 10";
+        var fen = new FenData(fenStr);
+
+        return Set(in fen, ChessMode.Normal, state);
+    }
+    
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public ReadOnlySpan<Square> Squares(PieceTypes pt, Player p)
         => Board.Squares(pt, p);
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/CPU.cs b/src/Rudzoft.ChessLib/Protocol/UCI/CPU.cs
index 7b42b549..47b9101d 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/CPU.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/CPU.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
index 2b3b3243..59d08a51 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/CopyProtections.cs b/src/Rudzoft.ChessLib/Protocol/UCI/CopyProtections.cs
index e8b3661d..18fcee04 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/CopyProtections.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/CopyProtections.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
index 7f257766..374b100d 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimerArgs.cs b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimerArgs.cs
index 447c87ff..803f6a1f 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimerArgs.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimerArgs.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IHiResTimer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IHiResTimer.cs
index 8324c64b..13ee196f 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IHiResTimer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IHiResTimer.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs
index 1ed592f3..511e6cd7 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs
index d4136c29..bd8a961a 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs
index c7a6fc8a..0671f819 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
index b7590f75..2aa9c9c8 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
index c63161da..a867d0c5 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs b/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
index 2ae44919..7294162d 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutputAction.cs b/src/Rudzoft.ChessLib/Protocol/UCI/InputOutputAction.cs
index ce73a02b..9c561c4e 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutputAction.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/InputOutputAction.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
index e96d6297..9e40a464 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
index a8a9cacd..2b62d251 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs
index 9de1bd20..ae7f28b6 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index a38801c9..d3aa5ab0 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index 3be7a3bd..84361cb7 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/UciOptionType.cs b/src/Rudzoft.ChessLib/Protocol/UCI/UciOptionType.cs
index 0503a849..6961fee7 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/UciOptionType.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/UciOptionType.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index d3816392..ef0ee7f5 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Tables/HistoryHeuristic.cs b/src/Rudzoft.ChessLib/Tables/HistoryHeuristic.cs
index 3c87023b..7a11d4d1 100644
--- a/src/Rudzoft.ChessLib/Tables/HistoryHeuristic.cs
+++ b/src/Rudzoft.ChessLib/Tables/HistoryHeuristic.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Tables/IHistoryHeuristic.cs b/src/Rudzoft.ChessLib/Tables/IHistoryHeuristic.cs
index 514e38f1..2e8476b0 100644
--- a/src/Rudzoft.ChessLib/Tables/IHistoryHeuristic.cs
+++ b/src/Rudzoft.ChessLib/Tables/IHistoryHeuristic.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs b/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs
index 191b1d4f..8be3caee 100644
--- a/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
index aa962f01..235f38c8 100644
--- a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index 0fa57475..7fdcd397 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 00f9a973..1bd10823 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index d566a437..3e068dc4 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/Depth.cs b/src/Rudzoft.ChessLib/Types/Depth.cs
index 3e51893e..688bb37a 100644
--- a/src/Rudzoft.ChessLib/Types/Depth.cs
+++ b/src/Rudzoft.ChessLib/Types/Depth.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/Direction.cs b/src/Rudzoft.ChessLib/Types/Direction.cs
index fb5cc49a..f2bfaa5b 100644
--- a/src/Rudzoft.ChessLib/Types/Direction.cs
+++ b/src/Rudzoft.ChessLib/Types/Direction.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/ExtMove.cs b/src/Rudzoft.ChessLib/Types/ExtMove.cs
index 9d6e800f..079027fc 100644
--- a/src/Rudzoft.ChessLib/Types/ExtMove.cs
+++ b/src/Rudzoft.ChessLib/Types/ExtMove.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index 1535b583..d2201e11 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/HashKey.cs b/src/Rudzoft.ChessLib/Types/HashKey.cs
index 15b9eaed..20841b2a 100644
--- a/src/Rudzoft.ChessLib/Types/HashKey.cs
+++ b/src/Rudzoft.ChessLib/Types/HashKey.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/IPieceSquare.cs b/src/Rudzoft.ChessLib/Types/IPieceSquare.cs
index d1a6c649..6165d533 100644
--- a/src/Rudzoft.ChessLib/Types/IPieceSquare.cs
+++ b/src/Rudzoft.ChessLib/Types/IPieceSquare.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/IValidationType.cs b/src/Rudzoft.ChessLib/Types/IValidationType.cs
index 414a5a3a..4b82e1d2 100644
--- a/src/Rudzoft.ChessLib/Types/IValidationType.cs
+++ b/src/Rudzoft.ChessLib/Types/IValidationType.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index b26530ad..b6f6dcdf 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index db544c03..5bda6f7f 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/PieceSquare.cs b/src/Rudzoft.ChessLib/Types/PieceSquare.cs
index d7761b4f..00718b3e 100644
--- a/src/Rudzoft.ChessLib/Types/PieceSquare.cs
+++ b/src/Rudzoft.ChessLib/Types/PieceSquare.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/PieceValue.cs b/src/Rudzoft.ChessLib/Types/PieceValue.cs
index 5bdd4b3a..34dbd86a 100644
--- a/src/Rudzoft.ChessLib/Types/PieceValue.cs
+++ b/src/Rudzoft.ChessLib/Types/PieceValue.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Player.cs
index 49e3c9b0..d597bd27 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Player.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index f699400d..c2176727 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index d31853d3..65d1dfe9 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index cd582639..496faa33 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/StateStack.cs b/src/Rudzoft.ChessLib/Types/StateStack.cs
index e8f8f041..e8050e1e 100644
--- a/src/Rudzoft.ChessLib/Types/StateStack.cs
+++ b/src/Rudzoft.ChessLib/Types/StateStack.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Types/Value.cs b/src/Rudzoft.ChessLib/Types/Value.cs
index 9c9f4f95..2f73cc27 100644
--- a/src/Rudzoft.ChessLib/Types/Value.cs
+++ b/src/Rudzoft.ChessLib/Types/Value.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
index ffe74d70..633ae8ec 100644
--- a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index e100dfd0..b64b0e9e 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs b/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
index 45b6e4b2..6822671c 100644
--- a/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
+++ b/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.Perft.Framework/Environment/IFrameworkEnvironment.cs b/src/Rudzoft.Perft.Framework/Environment/IFrameworkEnvironment.cs
index 77fff42d..ecbe5c03 100644
--- a/src/Rudzoft.Perft.Framework/Environment/IFrameworkEnvironment.cs
+++ b/src/Rudzoft.Perft.Framework/Environment/IFrameworkEnvironment.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.Perft.Framework/Extensions/ArrayExtensions.cs b/src/Rudzoft.Perft.Framework/Extensions/ArrayExtensions.cs
index 934beff0..a940aa03 100644
--- a/src/Rudzoft.Perft.Framework/Extensions/ArrayExtensions.cs
+++ b/src/Rudzoft.Perft.Framework/Extensions/ArrayExtensions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs b/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
index d4dd3c22..d3b5fe01 100644
--- a/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
+++ b/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.Perft.Framework/Factories/ConfigurationFactory.cs b/src/Rudzoft.Perft.Framework/Factories/ConfigurationFactory.cs
index 98b78d08..ad350414 100644
--- a/src/Rudzoft.Perft.Framework/Factories/ConfigurationFactory.cs
+++ b/src/Rudzoft.Perft.Framework/Factories/ConfigurationFactory.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.Perft.Framework/Framework.cs b/src/Rudzoft.Perft.Framework/Framework.cs
index 0d8359a7..fdc56137 100644
--- a/src/Rudzoft.Perft.Framework/Framework.cs
+++ b/src/Rudzoft.Perft.Framework/Framework.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2017-2022 Rudy Alex Kohn
+Copyright (c) 2017-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

From f266641666f7de1bbc96860184a338ef387f67e6 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 12 Feb 2023 19:25:45 +0100
Subject: [PATCH 005/119] Updated tables etc

---
 src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs | 78 -----------------
 .../Hash/Tables/IHashTable.cs                 | 39 ---------
 .../Hash/Tables/ITableEntry.cs                | 12 ---
 src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs | 54 ------------
 .../Hash/Tables/Transposition/ITTCluster.cs   | 16 ++--
 .../Transposition/ITranspositionTable.cs      |  6 +-
 .../Hash/Tables/Transposition/TTCluster.cs    | 18 ++--
 .../Transposition/TranspositionTable.cs       | 68 ++++++++-------
 .../Transposition/TranspositionTableEntry.cs  |  4 +-
 src/Rudzoft.ChessLib/Tables/IKillerMoves.cs   |  2 +-
 src/Rudzoft.ChessLib/Tables/KillerMoves.cs    | 52 ++++++------
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 13 +++
 src/Rudzoft.ChessLib/Types/CastleRight.cs     | 11 ++-
 src/Rudzoft.ChessLib/Types/PieceSquare.cs     | 83 +------------------
 .../Types/PieceSquareEventArgs.cs             | 80 ++++++++++++++++++
 15 files changed, 187 insertions(+), 349 deletions(-)
 delete mode 100644 src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs
 delete mode 100644 src/Rudzoft.ChessLib/Hash/Tables/IHashTable.cs
 delete mode 100644 src/Rudzoft.ChessLib/Hash/Tables/ITableEntry.cs
 delete mode 100644 src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs
 create mode 100644 src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs

diff --git a/src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs
deleted file mode 100644
index fef44434..00000000
--- a/src/Rudzoft.ChessLib/Hash/Tables/HashTable.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using System;
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Hash.Tables;
-
-public abstract class HashTable<T> : IHashTable<T> where T : ITableEntry
-{
-    private T[] _table;
-
-    public int Count => _table.Length;
-
-    public ref T this[HashKey key] => ref _table[key.LowerKey & (_table.Length - 1)];
-
-    /// <summary>
-    /// Initialized the table array. In case the table array is initialized with a different
-    /// size the existing elements are being kept. The index operator[] will return a reference
-    /// struct to avoid any copying, which actually edits the entry "in-place" which avoids
-    /// having to store the entry again.
-    ///
-    /// The process is a bit tricky:
-    /// - Because of the nature of C# it requires a custom object initializer function
-    /// - The initializer should initialize a single entry object
-    /// </summary>
-    /// <param name="elementSize">The size of <see cref="T"/></param>
-    /// <param name="tableSizeMb">The number of elements to store in the array</param>
-    /// <param name="initializer">Initializer function to initialize a single entry object</param>
-    public void Initialize(int elementSize, int tableSizeMb, Func<T> initializer)
-    {
-        var arraySize = tableSizeMb * 1024 * 1024 / elementSize;
-        if (_table != null)
-        {
-            if (_table.Length == arraySize)
-                return;
-
-            var currentLength = _table.Length;
-
-            Array.Resize(ref _table, arraySize);
-
-            if (currentLength > arraySize)
-                return;
-
-            for (var i = currentLength; i < arraySize; ++i)
-                _table[i] = initializer();
-        }
-        else
-        {
-            _table = new T[arraySize];
-            for (var i = 0; i < _table.Length; ++i)
-                _table[i] = initializer();
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/IHashTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/IHashTable.cs
deleted file mode 100644
index 655f045a..00000000
--- a/src/Rudzoft.ChessLib/Hash/Tables/IHashTable.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using System;
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Hash.Tables;
-
-public interface IHashTable<T> where T : ITableEntry
-{
-    int Count { get; }
-
-    ref T this[HashKey key] { get; }
-
-    void Initialize(int elementSize, int tableSizeMb, Func<T> initializer);
-}
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/ITableEntry.cs b/src/Rudzoft.ChessLib/Hash/Tables/ITableEntry.cs
deleted file mode 100644
index b25c6ef8..00000000
--- a/src/Rudzoft.ChessLib/Hash/Tables/ITableEntry.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Hash.Tables;
-
-public interface ITableEntry
-{
-    HashKey Key { get; set; }
-
-    Score Evaluate(IPosition pos);
-    
-    Score Evaluate(IPosition pos, in BitBoard attacks, Player own);
-}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs
deleted file mode 100644
index c75d29de..00000000
--- a/src/Rudzoft.ChessLib/Hash/Tables/PawnTable.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Hash.Tables;
-
-public class PawnTableEntry : ITableEntry
-{
-    public Score Score;
-    public BitBoard[] PassedPawns;
-
-    public PawnTableEntry(ulong key)
-    {
-        Key = key;
-        Score = Score.Zero;
-        PassedPawns = new BitBoard[Player.Count];
-    }
-
-    public HashKey Key { get; set; }
-    
-    public Score Evaluate(IPosition pos)
-    {
-        throw new System.NotImplementedException();
-    }
-
-    public Score Evaluate(IPosition pos, in BitBoard attacks, Player own)
-    {
-        throw new System.NotImplementedException();
-    }
-}
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs
index 4e6d8718..da007be2 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs
@@ -26,10 +26,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 
-public interface ITTCluster
-{
-    TranspositionTableEntry[] Cluster { get; }
-    TranspositionTableEntry this[int key] { get; set; }
-
-    public void Reset();
-}
+// public interface ITTCluster
+// {
+//     // TranspositionTableEntry[] Cluster { get; }
+//     ref TranspositionTableEntry this[int key] { get; }
+//
+//     public void Add(int key, in TranspositionTableEntry entry);
+//     
+//     public void Reset();
+// }
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
index c1ba285b..87343ecf 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
@@ -54,9 +54,9 @@ public interface ITranspositionTable
     /// </summary>
     /// <param name="key">The position key</param>
     /// <returns>The cluster of the keys position in the table</returns>
-    ITTCluster FindCluster(in HashKey key);
+    ref TTCluster FindCluster(in HashKey key);
 
-    void Refresh(TranspositionTableEntry tte);
+    void Refresh(ref TranspositionTableEntry tte);
 
     /// <summary>
     /// Probes the transposition table for a entry that matches the position key.
@@ -71,7 +71,7 @@ public interface ITranspositionTable
     /// </summary>
     /// <param name="key">The position key</param>
     /// <returns>The cluster entry</returns>
-    TranspositionTableEntry ProbeFirst(in HashKey key);
+    ref TranspositionTableEntry ProbeFirst(in HashKey key);
 
     /// <summary>
     /// Stores a move in the transposition table. It will automatically detect the best cluster
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TTCluster.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TTCluster.cs
index ed6890a5..ecabd517 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TTCluster.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TTCluster.cs
@@ -32,7 +32,7 @@ namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 /// Stores an array of <see cref="TranspositionTableEntry"/>. In essence it acts like a entry
 /// bucket of 4 elements for each position stored in the <see cref="TranspositionTable"/>
 /// </summary>
-public sealed class TTCluster : ITTCluster
+public readonly struct TTCluster //: ITTCluster
 {
     public static readonly TranspositionTableEntry DefaultEntry = new(
         0,
@@ -43,19 +43,25 @@ public sealed class TTCluster : ITTCluster
         int.MaxValue,
         Bound.Void);
 
+    private readonly TranspositionTableEntry[] _cluster = new TranspositionTableEntry[4];
+    
     public TTCluster()
     {
         Reset();
     }
 
-    public TranspositionTableEntry[] Cluster { get; private set; }
+    public ref TranspositionTableEntry this[int key] => ref _cluster[key];
 
-    public TranspositionTableEntry this[int key]
+    public void Add(int key, in TranspositionTableEntry entry)
     {
-        get => Cluster[key];
-        set => Cluster[key] = value;
+        _cluster[key] = entry;
     }
 
     public void Reset()
-        => Cluster = new[] { DefaultEntry, DefaultEntry, DefaultEntry, DefaultEntry };
+    {
+        _cluster[0] = DefaultEntry;
+        _cluster[1] = DefaultEntry;
+        _cluster[2] = DefaultEntry;
+        _cluster[3] = DefaultEntry;
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index e6f957e6..fd3084d5 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -25,7 +25,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
-using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Exceptions;
@@ -37,7 +36,7 @@ public sealed class TranspositionTable : ITranspositionTable
 {
     private static readonly int ClusterSize;
 
-    private ITTCluster[] _table;
+    private TTCluster[] _table;
     private ulong _elements;
     private int _fullnessElements;
     private sbyte _generation;
@@ -52,7 +51,7 @@ static TranspositionTable()
 
     public TranspositionTable(int mbSize)
     {
-        _table = Array.Empty<ITTCluster>();
+        _table = Array.Empty<TTCluster>();
         SetSize(mbSize);
     }
 
@@ -85,7 +84,7 @@ public ulong SetSize(int mbSize)
         var currentSize = _table.Length;
         if (_table.Length == 0)
         {
-            _table = new ITTCluster[size];
+            _table = new TTCluster[size];
             PopulateTable(0, size);
         }
         else if (currentSize != size)
@@ -111,14 +110,14 @@ public ulong SetSize(int mbSize)
     /// <param name="key">The position key</param>
     /// <returns>The cluster of the keys position in the table</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ITTCluster FindCluster(in HashKey key)
+    public ref TTCluster FindCluster(in HashKey key)
     {
         var idx = (int)(key.LowerKey & (_elements - 1));
-        return _table[idx];
+        return ref _table[idx];
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void Refresh(TranspositionTableEntry tte) => tte.Generation = _generation;
+    public void Refresh(ref TranspositionTableEntry tte) => tte.Generation = _generation;
 
     /// <summary>
     /// Probes the transposition table for a entry that matches the position key.
@@ -127,17 +126,17 @@ public ITTCluster FindCluster(in HashKey key)
     /// <returns>(true, entry) if one was found, (false, empty) if not found</returns>
     public bool Probe(in HashKey key, ref TranspositionTableEntry e)
     {
-        var ttc = FindCluster(in key);
+        ref var ttc = ref FindCluster(in key);
         var g = _generation;
 
         // Probing the Table will automatically update the generation of the entry in case the
         // probing retrieves an element.
 
         var set = false;
-        ref var clusterSpace = ref MemoryMarshal.GetArrayDataReference(ttc.Cluster);
-        for (var i = 0; i < ttc.Cluster.Length; ++i)
+
+        for (var i = 0; i < ClusterSize; ++i)
         {
-            var entry = Unsafe.Add(ref clusterSpace, i);
+            ref var entry = ref ttc[i];
             if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
                 continue;
 
@@ -147,7 +146,7 @@ public bool Probe(in HashKey key, ref TranspositionTableEntry e)
             Hits++;
             break;
         }
-
+        
         if (!set)
             e = default;
 
@@ -156,20 +155,19 @@ public bool Probe(in HashKey key, ref TranspositionTableEntry e)
 
     public void Save(in HashKey key, in TranspositionTableEntry e)
     {
-        var ttc = FindCluster(in key);
+        ref var ttc = ref FindCluster(in key);
         var entryIndex = 0;
 
-        ref var clusterSpace = ref MemoryMarshal.GetArrayDataReference(ttc.Cluster);
-        for (var i = 0; i < ttc.Cluster.Length; ++i)
+        for (var i = 0; i < ClusterSize; ++i)
         {
-            var entry = Unsafe.Add(ref clusterSpace, i);
+            ref var entry = ref ttc[i];
             if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
                 continue;
 
             entryIndex = i;
             break;
         }
-
+        
         ttc[entryIndex] = e;
     }
 
@@ -179,7 +177,7 @@ public void Save(in HashKey key, in TranspositionTableEntry e)
     /// <param name="key">The position key</param>
     /// <returns>The cluster entry</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public TranspositionTableEntry ProbeFirst(in HashKey key) => FindCluster(key)[0];
+    public ref TranspositionTableEntry ProbeFirst(in HashKey key) => ref FindCluster(key)[0];
 
     /// <summary>
     /// Stores a move in the transposition table. It will automatically detect the best cluster
@@ -194,15 +192,14 @@ public void Save(in HashKey key, in TranspositionTableEntry e)
     /// <param name="statValue">The static value of the move</param>
     public void Store(in HashKey key, int value, Bound type, sbyte depth, Move move, int statValue)
     {
-        var ttc = FindCluster(in key);
+        ref var ttc = ref FindCluster(in key);
 
         var clusterIndex = 0;
         var found = false;
 
-        ref var clusterSpace = ref MemoryMarshal.GetArrayDataReference(ttc.Cluster);
-        for (var i = 0; i < ttc.Cluster.Length; ++i)
+        for (var i = 0; i < ClusterSize; ++i)
         {
-            var entry = Unsafe.Add(ref clusterSpace, i);
+            ref var entry = ref ttc[i];
             if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
                 continue;
 
@@ -214,21 +211,18 @@ public void Store(in HashKey key, int value, Bound type, sbyte depth, Move move,
         if (!found)
         {
             var index = 0;
-            var candidate = ttc.Cluster[index];
+            ref var candidate = ref ttc[index];
             var g = _generation;
 
-            ref var entrySpace = ref MemoryMarshal.GetArrayDataReference(ttc.Cluster);
-            for (var i = 0; i < ttc.Cluster.Length; ++i)
+            for (var i = 0; i < ClusterSize; ++i)
             {
-                var entry = Unsafe.Add(ref entrySpace, i);
+                ref var entry = ref ttc[i];
                 var (cc1, cc2, cc3, cc4) = (candidate.Generation == g, entry.Generation == g, entry.Type == Bound.Exact, entry.Depth <= candidate.Depth);
                 if ((cc1 && cc4) || (!(cc2 || cc3) && (cc4 || cc1)))
                 {
                     index = i;
                     candidate = entry;
                 }
-
-                i++;
             }
 
             clusterIndex = index;
@@ -236,7 +230,8 @@ public void Store(in HashKey key, int value, Bound type, sbyte depth, Move move,
 
         var e = new TranspositionTableEntry(key.UpperKey, move, depth, _generation, value, statValue, type);
 
-        ttc.Cluster[clusterIndex].Save(e);
+        ref var target = ref ttc[clusterIndex];
+        target.Save(in e);
     }
 
     /// <summary>
@@ -251,7 +246,14 @@ public int Fullness()
         var sum = 0;
 
         for (var i = 0; i < _fullnessElements; ++i)
-            sum += _table[i].Cluster.Count(x => x.Generation == gen);
+        {
+            for (var j = 0; j < ClusterSize; ++j)
+            {
+                ref var entry = ref _table[i][j];
+                if (entry.Generation == gen)
+                    sum++;
+            }
+        }
 
         return sum * 250 / _fullnessElements;
     }
@@ -271,10 +273,6 @@ public void Clear()
     private void PopulateTable(int from, int to)
     {
         for (var i = from; i < to; ++i)
-        {
-            var ttc = new TTCluster();
-            Array.Fill(ttc.Cluster, TTCluster.DefaultEntry);
-            _table[i] = ttc;
-        }
+            _table[i] = new TTCluster();
     }
 }
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
index 15c467c7..35c3cdb1 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
@@ -52,7 +52,7 @@ public TranspositionTableEntry(uint k, Move m, sbyte d, sbyte g, int v, int sv,
         Type = b;
     }
 
-    public TranspositionTableEntry(TranspositionTableEntry tte)
+    public TranspositionTableEntry(in TranspositionTableEntry tte)
     {
         this = tte;
     }
@@ -63,7 +63,7 @@ public TranspositionTableEntry(TranspositionTableEntry tte)
     public static bool operator !=(TranspositionTableEntry left, TranspositionTableEntry right)
         => !(left == right);
 
-    public void Save(TranspositionTableEntry tte)
+    public void Save(in TranspositionTableEntry tte)
     {
         Key32 = tte.Key32;
         if (!tte.Move.IsNullMove())
diff --git a/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs b/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs
index 8be3caee..6565e8b2 100644
--- a/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs
@@ -33,7 +33,7 @@ public interface IKillerMoves : IEquatable<IKillerMoves>
 {
     int GetValue(int depth, Move m, Piece fromPc);
 
-    IPieceSquare Get(int depth, int index);
+    ref PieceSquare Get(int depth, int index);
 
     void UpdateValue(int depth, Move m, Piece fromPc);
 
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
index 235f38c8..2754b8d6 100644
--- a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
@@ -32,22 +32,26 @@ namespace Rudzoft.ChessLib.Tables;
 
 public sealed class KillerMoves : IKillerMoves
 {
-    private IPieceSquare[,] _killerMoves;
+    private static readonly PieceSquare EmptyPieceSquare = new(Piece.EmptyPiece, Square.None);
+    
+    private readonly PieceSquare[,] _killerMoves;
     private readonly int _maxDepth;
 
     public static IKillerMoves Create(int maxDepth)
         => new KillerMoves(maxDepth).Initialize();
 
-    public KillerMoves(int maxDepth)
-        => _maxDepth = maxDepth + 1;
+    private KillerMoves(int maxDepth)
+    {
+        _maxDepth = maxDepth + 1;
+        _killerMoves = new PieceSquare[_maxDepth, 2];
+    }
 
     private IKillerMoves Initialize()
     {
-        _killerMoves = new IPieceSquare[_maxDepth, 2];
         for (var depth = 0; depth < _maxDepth; depth++)
         {
-            _killerMoves[depth, 0] = new PieceSquareEventArgs(Piece.EmptyPiece, Square.None);
-            _killerMoves[depth, 1] = new PieceSquareEventArgs(Piece.EmptyPiece, Square.None);
+            _killerMoves[depth, 0] = EmptyPieceSquare;
+            _killerMoves[depth, 1] = EmptyPieceSquare;
         }
 
         Reset();
@@ -55,29 +59,28 @@ private IKillerMoves Initialize()
     }
 
     public int GetValue(int depth, Move m, Piece fromPc)
-        => Equals(_killerMoves[depth, 0], m, fromPc)
+        => Equals(in _killerMoves[depth, 0], m, fromPc)
             ? 2
-            : Equals(_killerMoves[depth, 1], m, fromPc).AsByte();
+            : Equals(in _killerMoves[depth, 1], m, fromPc).AsByte();
 
     public void UpdateValue(int depth, Move m, Piece fromPc)
     {
-        if (Equals(_killerMoves[depth, 0], m, fromPc))
+        if (Equals(in _killerMoves[depth, 0], m, fromPc))
             return;
 
         // Shift killer move.
-        _killerMoves[depth, 1].Piece = _killerMoves[depth, 0].Piece;
-        _killerMoves[depth, 1].Square = _killerMoves[depth, 0].Square;
+        _killerMoves[depth, 1] = _killerMoves[depth, 0];
         // Update killer move.
         _killerMoves[depth, 0].Piece = fromPc;
         _killerMoves[depth, 0].Square = m.ToSquare();
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static bool Equals(IPieceSquare killerMove, Move m, Piece fromPc)
+    private static bool Equals(in PieceSquare killerMove, Move m, Piece fromPc)
         => killerMove.Piece == fromPc && killerMove.Square == m.ToSquare();
 
-    public IPieceSquare Get(int depth, int index)
-        => _killerMoves[depth, index];
+    public ref PieceSquare Get(int depth, int index)
+        => ref _killerMoves[depth, index];
 
     public void Shift(int depth)
     {
@@ -85,31 +88,24 @@ public void Shift(int depth)
         var lastDepth = _killerMoves.Length - depth - 1;
         for (var i = 0; i <= lastDepth; i++)
         {
-            _killerMoves[i, 0].Piece = _killerMoves[i + depth, 0].Piece;
-            _killerMoves[i, 0].Square = _killerMoves[i + depth, 0].Square;
-            _killerMoves[i, 1].Piece = _killerMoves[i + depth, 1].Piece;
-            _killerMoves[i, 1].Square = _killerMoves[i + depth, 1].Square;
+            _killerMoves[i, 0] = _killerMoves[i + depth, 0];
+            _killerMoves[i, 1] = _killerMoves[i + depth, 1];
         }
 
         // Reset killer moves far from root position.
         for (var i = lastDepth + 1; i < _killerMoves.Length; i++)
         {
-            _killerMoves[i, 0].Piece = Piece.EmptyPiece;
-            _killerMoves[i, 0].Square = Square.None;
-            _killerMoves[i, 1].Piece = Piece.EmptyPiece;
-            _killerMoves[i, 1].Square = Square.None;
+            _killerMoves[i, 0] = EmptyPieceSquare;
+            _killerMoves[i, 1] = EmptyPieceSquare;
         }
     }
 
     public void Reset()
     {
-        //Array.Clear(_killerMoves, 0, _killerMoves.Length);
         for (var i = 0; i < _maxDepth; i++)
         {
-            _killerMoves[i, 0].Piece = Piece.EmptyPiece;
-            _killerMoves[i, 0].Square = Square.None;
-            _killerMoves[i, 1].Piece = Piece.EmptyPiece;
-            _killerMoves[i, 1].Square = Square.None;
+            _killerMoves[i, 0] = EmptyPieceSquare;
+            _killerMoves[i, 1] = EmptyPieceSquare;
         }
     }
 
@@ -134,5 +130,5 @@ public override bool Equals(object obj)
         => ReferenceEquals(this, obj) || obj is KillerMoves other && Equals(other);
 
     public override int GetHashCode()
-        => _killerMoves != null ? _killerMoves.GetHashCode() : 0;
+        => _killerMoves.GetHashCode();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 1bd10823..b8354d6d 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -179,6 +179,8 @@ public static class BitBoards
 
     private static readonly BitBoard[][] DistanceRingBB;
 
+    private static readonly BitBoard[] SlotFileBB;
+
     private static readonly Direction[] PawnPushDirections = { Direction.North, Direction.South };
 
     private static readonly IDictionary<Direction, Func<BitBoard, BitBoard>> ShiftFuncs = MakeShiftFuncs();
@@ -341,6 +343,13 @@ static BitBoard ComputeKnightAttack(in BitBoard bb)
             // Compute KingRings
             InitializeKingRing(s1, sq, file);
         }
+        
+        SlotFileBB = new[]
+        {
+            File.FileE.FileBB() | File.FileF.FileBB() | File.FileG.FileBB() | File.FileH.FileBB(), // King
+            File.FileA.FileBB() | File.FileB.FileBB() | File.FileC.FileBB() | File.FileD.FileBB(), // Queen
+            File.FileC.FileBB() | File.FileD.FileBB() | File.FileE.FileBB() | File.FileF.FileBB()  // Center
+        };
     }
 
     private static void InitializeKingRing(Square s1, int sq, File file)
@@ -513,6 +522,10 @@ public static Square Last(this in BitBoard bb)
     public static BitBoard Line(this Square sq1, Square sq2)
         => LineBB[sq1.AsInt()][sq2.AsInt()];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static BitBoard SlotFile(CastleSides cs)
+        => SlotFileBB[cs.AsInt()];
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard AdjacentFiles(File f)
         => AdjacentFilesBB[f.AsInt()];
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index 3e068dc4..5603ad24 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -50,16 +50,19 @@ public enum CastleRights
 
 public enum CastleSides
 {
-    King,
-    Queen,
-    Center,
-    Count
+    King = 0,
+    Queen = 1,
+    Center = 2,
+    Count = 3
 }
 
 public static class CastleSidesExtensions
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static int AsInt(this CastleSides cs) => (int)cs;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static BitBoard SlotFile(this CastleSides cs) => BitBoards.SlotFile(cs);
 }
 
 public enum CastlePerform
diff --git a/src/Rudzoft.ChessLib/Types/PieceSquare.cs b/src/Rudzoft.ChessLib/Types/PieceSquare.cs
index 00718b3e..2b69db47 100644
--- a/src/Rudzoft.ChessLib/Types/PieceSquare.cs
+++ b/src/Rudzoft.ChessLib/Types/PieceSquare.cs
@@ -1,80 +1,3 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using System;
-using System.Runtime.CompilerServices;
-using Rudzoft.ChessLib.Extensions;
-
-namespace Rudzoft.ChessLib.Types;
-
-/// <summary>
-/// Model for data transfer of piece and square Used for notification when a piece is updated in
-/// the chess structure
-/// </summary>
-public sealed class PieceSquareEventArgs : EventArgs, ISpanFormattable, IPieceSquare
-{
-    public PieceSquareEventArgs(Piece pc, Square sq)
-    {
-        Piece = pc;
-        Square = sq;
-    }
-
-    public Piece Piece { get; set; }
-
-    public Square Square { get; set; }
-
-    public void Deconstruct(out Piece pc, out Square sq)
-    {
-        pc = Piece;
-        sq = Square;
-    }
-
-    public bool Equals(IPieceSquare other)
-        => other is not null &&
-           (ReferenceEquals(this, other) || (Piece.Equals(other.Piece) && Square.Equals(other.Square)));
-
-    public override bool Equals(object obj)
-        => obj is not null &&
-           (ReferenceEquals(this, obj) || (obj.GetType() == GetType() && Equals((PieceSquareEventArgs)obj)));
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => HashCode.Combine(Piece, Square);
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public string ToString(string format, IFormatProvider formatProvider)
-        => string.Format(formatProvider, format, Piece + Square.ToString());
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null)
-    {
-        Span<char> s = stackalloc char[3];
-        Square.TryFormat(s, out charsWritten, format, provider);
-        s[2] = Piece.GetPieceChar();
-        charsWritten++;
-        return true;
-    }
-}
\ No newline at end of file
+namespace Rudzoft.ChessLib.Types;
+
+public record struct PieceSquare(Piece Piece, Square Square);
diff --git a/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs b/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
new file mode 100644
index 00000000..00718b3e
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
@@ -0,0 +1,80 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System;
+using System.Runtime.CompilerServices;
+using Rudzoft.ChessLib.Extensions;
+
+namespace Rudzoft.ChessLib.Types;
+
+/// <summary>
+/// Model for data transfer of piece and square Used for notification when a piece is updated in
+/// the chess structure
+/// </summary>
+public sealed class PieceSquareEventArgs : EventArgs, ISpanFormattable, IPieceSquare
+{
+    public PieceSquareEventArgs(Piece pc, Square sq)
+    {
+        Piece = pc;
+        Square = sq;
+    }
+
+    public Piece Piece { get; set; }
+
+    public Square Square { get; set; }
+
+    public void Deconstruct(out Piece pc, out Square sq)
+    {
+        pc = Piece;
+        sq = Square;
+    }
+
+    public bool Equals(IPieceSquare other)
+        => other is not null &&
+           (ReferenceEquals(this, other) || (Piece.Equals(other.Piece) && Square.Equals(other.Square)));
+
+    public override bool Equals(object obj)
+        => obj is not null &&
+           (ReferenceEquals(this, obj) || (obj.GetType() == GetType() && Equals((PieceSquareEventArgs)obj)));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public override int GetHashCode()
+        => HashCode.Combine(Piece, Square);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public string ToString(string format, IFormatProvider formatProvider)
+        => string.Format(formatProvider, format, Piece + Square.ToString());
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null)
+    {
+        Span<char> s = stackalloc char[3];
+        Square.TryFormat(s, out charsWritten, format, provider);
+        s[2] = Piece.GetPieceChar();
+        charsWritten++;
+        return true;
+    }
+}
\ No newline at end of file

From f6919fd7127ac7ed6883d5d2dcdef53824fb20c7 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 12 Feb 2023 21:29:58 +0100
Subject: [PATCH 006/119] Minor refactor

- Renamed some TT related stuff
- Fixed minor issue with Player ToString(....)
- Added Position.Set() tests for code - to be used with endgame setup
---
 .../PositionTests/PositionTests.cs            | 21 +++++++++++
 .../{TTCluster.cs => Cluster.cs}              |  4 +-
 .../Hash/Tables/Transposition/ITTCluster.cs   | 37 -------------------
 .../Transposition/ITranspositionTable.cs      |  2 +-
 .../Transposition/TranspositionTable.cs       | 10 ++---
 src/Rudzoft.ChessLib/Position.cs              |  8 +---
 src/Rudzoft.ChessLib/Types/Player.cs          |  2 +-
 7 files changed, 32 insertions(+), 52 deletions(-)
 rename src/Rudzoft.ChessLib/Hash/Tables/Transposition/{TTCluster.cs => Cluster.cs} (96%)
 delete mode 100644 src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs

diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 00b2fbaf..6eb0389c 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -88,4 +88,25 @@ public void KingBlocker()
 
         Assert.Equal(expectedSquare, actual);
     }
+    
+    [Theory]
+    [InlineData("KPK", "k7/8/8/8/8/8/8/KP6 w - - 0 10")]
+    [InlineData("KNNK", "k7/8/8/8/8/8/8/KNN5 w - - 0 10")]
+    [InlineData("KBNK", "k7/8/8/8/8/8/8/KBN5 w - - 0 10")]
+    public void SetByCodeCreatesSameMaterialKey(string code, string fen)
+    {
+        var g = GameFactory.Create(fen);
+        var pos = g.Pos;
+        var materialKey = pos.State.MaterialKey;
+
+        var state = new State();
+        var posCode = pos.Set(code, Player.White, state);
+        var codeMaterialKey = posCode.State.MaterialKey;
+        
+        Assert.Equal(materialKey, codeMaterialKey);
+
+        var codeFen = pos.GenerateFen().ToString();
+
+        Assert.Equal(fen, codeFen);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TTCluster.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Cluster.cs
similarity index 96%
rename from src/Rudzoft.ChessLib/Hash/Tables/Transposition/TTCluster.cs
rename to src/Rudzoft.ChessLib/Hash/Tables/Transposition/Cluster.cs
index ecabd517..7ca85a54 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TTCluster.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Cluster.cs
@@ -32,7 +32,7 @@ namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 /// Stores an array of <see cref="TranspositionTableEntry"/>. In essence it acts like a entry
 /// bucket of 4 elements for each position stored in the <see cref="TranspositionTable"/>
 /// </summary>
-public readonly struct TTCluster //: ITTCluster
+public readonly struct Cluster
 {
     public static readonly TranspositionTableEntry DefaultEntry = new(
         0,
@@ -45,7 +45,7 @@ namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 
     private readonly TranspositionTableEntry[] _cluster = new TranspositionTableEntry[4];
     
-    public TTCluster()
+    public Cluster()
     {
         Reset();
     }
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs
deleted file mode 100644
index da007be2..00000000
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITTCluster.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
-
-// public interface ITTCluster
-// {
-//     // TranspositionTableEntry[] Cluster { get; }
-//     ref TranspositionTableEntry this[int key] { get; }
-//
-//     public void Add(int key, in TranspositionTableEntry entry);
-//     
-//     public void Reset();
-// }
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
index 87343ecf..1be774b6 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
@@ -54,7 +54,7 @@ public interface ITranspositionTable
     /// </summary>
     /// <param name="key">The position key</param>
     /// <returns>The cluster of the keys position in the table</returns>
-    ref TTCluster FindCluster(in HashKey key);
+    ref Cluster FindCluster(in HashKey key);
 
     void Refresh(ref TranspositionTableEntry tte);
 
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index fd3084d5..c33c255b 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -36,7 +36,7 @@ public sealed class TranspositionTable : ITranspositionTable
 {
     private static readonly int ClusterSize;
 
-    private TTCluster[] _table;
+    private Cluster[] _table;
     private ulong _elements;
     private int _fullnessElements;
     private sbyte _generation;
@@ -51,7 +51,7 @@ static TranspositionTable()
 
     public TranspositionTable(int mbSize)
     {
-        _table = Array.Empty<TTCluster>();
+        _table = Array.Empty<Cluster>();
         SetSize(mbSize);
     }
 
@@ -84,7 +84,7 @@ public ulong SetSize(int mbSize)
         var currentSize = _table.Length;
         if (_table.Length == 0)
         {
-            _table = new TTCluster[size];
+            _table = new Cluster[size];
             PopulateTable(0, size);
         }
         else if (currentSize != size)
@@ -110,7 +110,7 @@ public ulong SetSize(int mbSize)
     /// <param name="key">The position key</param>
     /// <returns>The cluster of the keys position in the table</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref TTCluster FindCluster(in HashKey key)
+    public ref Cluster FindCluster(in HashKey key)
     {
         var idx = (int)(key.LowerKey & (_elements - 1));
         return ref _table[idx];
@@ -273,6 +273,6 @@ public void Clear()
     private void PopulateTable(int from, int to)
     {
         for (var i = from; i < to; ++i)
-            _table[i] = new TTCluster();
+            _table[i] = new Cluster();
     }
 }
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 16d8e83c..07b8f821 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1223,12 +1223,8 @@ public IPosition Set(ReadOnlySpan<char> code, Player p, State state)
         Debug.Assert(code.Length.InBetween(0, 8));
         Debug.Assert(code[0] == 'K');
 
-        var kingPos = code[1..].IndexOf('K');
-        var sides = new[]
-        {
-            code[1..kingPos].ToString(),
-            code[kingPos].ToString()
-        };
+        var kingPos = code.LastIndexOf('K');
+        var sides = new [] { code[kingPos..].ToString(), code[..kingPos].ToString() };
         
         sides[p.Side] = sides[p.Side].ToLower();
 
diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Player.cs
index d597bd27..81cc9aaf 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Player.cs
@@ -135,7 +135,7 @@ public override string ToString()
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string ToString(string format, IFormatProvider formatProvider)
-        => string.Format(formatProvider, format, Side);
+        => ToString();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider)

From acd7091ba721e50794b658396831738c0914781a Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 12 Feb 2023 21:36:32 +0100
Subject: [PATCH 007/119] Fixed unintended non-cleared span for piece
 validation

---
 src/Rudzoft.ChessLib/Fen/Fen.cs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index b03a907a..066c7199 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -103,6 +103,7 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
 
         // piece count storage, using index 0 = '/' count
         Span<int> pieceCount = stackalloc int[Pieces.PieceNb.AsInt()];
+        pieceCount.Clear();
 
         ref var mainSectionSpace = ref MemoryMarshal.GetReference(mainSection);
 
@@ -136,7 +137,7 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
             var limit = limits[pt.AsInt()];
 
             if (pieceCount[pc.AsInt()] > limit)
-                throw new InvalidFen($"Invalid fen (piece limit exceeded for {pc}) {s.ToString()}");
+                throw new InvalidFen($"Invalid fen (piece limit exceeded for {pc}. index={i},limit={limit},count={pieceCount[pc.AsInt()]}) {s.ToString()}");
         }
 
         // check for summed up values

From c16f3b49555e92b827580d2c752ba3fe379508d0 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Feb 2023 23:27:12 +0100
Subject: [PATCH 008/119] Updated some types to be more clear

- ExtMove -> ValMove
- Added RootMove to act as easy use of list of moves
- IPieceValue -> IValues
- PieceValues -> DefaultPieceValues
- Move struct now readonly
---
 .../FenBenchmark.cs                           |  2 +-
 .../MoveTests/MoveGen_49.cs                   |  2 +-
 .../NotationTests/IccfTests.cs                |  4 +-
 .../PiecesTests/PieceAttacksRookTests.cs      |  2 +-
 .../PositionTests/PositionTests.cs            |  2 +-
 .../Rudzoft.ChessLib.Test.csproj              |  2 +-
 src/Rudzoft.ChessLib.WebApi/Program.cs        |  2 +-
 .../Extensions/MathExtensions.cs              |  4 ++
 src/Rudzoft.ChessLib/Factories/GameFactory.cs |  2 +-
 src/Rudzoft.ChessLib/IPosition.cs             |  2 +-
 .../{IPieceValue.cs => IValues.cs}            |  6 +--
 .../MoveGeneration/IMoveList.cs               | 10 ++---
 .../MoveGeneration/MoveGenerator.cs           | 18 ++++----
 .../MoveGeneration/MoveList.cs                | 24 +++++-----
 src/Rudzoft.ChessLib/Position.cs              | 26 +++++------
 src/Rudzoft.ChessLib/Types/Move.cs            |  8 ++--
 src/Rudzoft.ChessLib/Types/RootMove.cs        | 39 ++++++++++++++++
 .../Types/{ExtMove.cs => ValMove.cs}          | 32 +++++++-------
 src/Rudzoft.ChessLib/Types/Value.cs           | 33 +++++++-------
 .../Types/{PieceValue.cs => Values.cs}        | 44 +++++++++----------
 20 files changed, 155 insertions(+), 109 deletions(-)
 rename src/Rudzoft.ChessLib/{IPieceValue.cs => IValues.cs} (92%)
 create mode 100644 src/Rudzoft.ChessLib/Types/RootMove.cs
 rename src/Rudzoft.ChessLib/Types/{ExtMove.cs => ValMove.cs} (72%)
 rename src/Rudzoft.ChessLib/Types/{PieceValue.cs => Values.cs} (76%)

diff --git a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
index 3296eb21..46510adc 100644
--- a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
@@ -19,7 +19,7 @@ public class FenBenchmark
     public void Setup()
     {
         var board = new Board();
-        var pieceValue = new PieceValue();
+        var pieceValue = new Values();
         var pos = new Position(board, pieceValue);
         _game = new Game(pos);
         var fp = new FenData(F);
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
index b3afb050..8dfb0d5d 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
@@ -39,7 +39,7 @@ public void MoveListContainsMismatchedElement()
         const string fen = "r3kb1r/p3pppp/p1n2n2/2pp1Q2/3P1B2/2P1PN2/Pq3PPP/RN2K2R w KQkq - 0 9";
 
         var board = new Board();
-        var pieceValue = new PieceValue();
+        var pieceValue = new Values();
         var pos = new Position(board, pieceValue);
         var fd = new FenData(fen);
         var state = new State();
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index 72281e2d..3054daa8 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -41,7 +41,7 @@ public void IccfRegularMove()
         const string expectedPrimary = "4263";
 
         var board = new Board();
-        var pieceValue = new PieceValue();
+        var pieceValue = new Values();
         var pos = new Position(board, pieceValue);
         var g = GameFactory.Create(pos);
         g.NewGame(fen);
@@ -65,7 +65,7 @@ public void IccfPromotionMove(string fen, PieceTypes promoPt, string expected)
         const MoveNotations notation = MoveNotations.ICCF;
 
         var board = new Board();
-        var pieceValue = new PieceValue();
+        var pieceValue = new Values();
         var pos = new Position(board, pieceValue);
         var g = GameFactory.Create(pos);
         g.NewGame(fen);
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
index f5fc15f0..d64038a5 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
@@ -63,7 +63,7 @@ public void RookBorderBlocked()
 
         // just to get the attacks
         var board = new Board();
-        var pieceValue = new PieceValue();
+        var pieceValue = new Values();
         var pos = new Position(board, pieceValue);
 
         while (border)
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 6eb0389c..49430655 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -34,7 +34,7 @@ public sealed class PositionTests
     [Fact]
     public void AddPieceTest()
     {
-        var pieceValue = new PieceValue();
+        var pieceValue = new Values();
         var board = new Board();
         var position = new Position(board, pieceValue);
 
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 19e477ca..5f01cd2f 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -26,7 +26,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="FluentAssertions" Version="6.9.0" />
+    <PackageReference Include="FluentAssertions" Version="6.10.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.analyzers" Version="1.1.0" />
diff --git a/src/Rudzoft.ChessLib.WebApi/Program.cs b/src/Rudzoft.ChessLib.WebApi/Program.cs
index b5b1a70b..07b0337e 100644
--- a/src/Rudzoft.ChessLib.WebApi/Program.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Program.cs
@@ -14,7 +14,7 @@
 builder.Services.AddTransient<IMoveGeneratorService, MoveGeneratorService>();
 builder.Services.AddTransient<IPosition, Position>();
 builder.Services.AddTransient<IBoard, Board>();
-builder.Services.AddSingleton<IPieceValue, PieceValue>();
+builder.Services.AddSingleton<IValues, Values>();
 builder.Services.AddTransient<IGame, Game>();
 
 var app = builder.Build();
diff --git a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
index c0c746bc..2efe716b 100644
--- a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
@@ -108,4 +108,8 @@ public static bool IsOdd(this int value)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static long MidPoint(this long @this, long that)
         => (@this + that) >> 1;
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Value Min(this Value @this, Value other)
+        => @this < other ? @this : other;
 }
diff --git a/src/Rudzoft.ChessLib/Factories/GameFactory.cs b/src/Rudzoft.ChessLib/Factories/GameFactory.cs
index 84e6b815..703c2379 100644
--- a/src/Rudzoft.ChessLib/Factories/GameFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/GameFactory.cs
@@ -43,5 +43,5 @@ public static IGame Create(string fen, bool validate = false)
     }
 
     public static IGame Create()
-        => new Game(new Position(new Board(), new PieceValue()));
+        => new Game(new Position(new Board(), new Values()));
 }
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index 7e4fbad5..6702ce8f 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -50,7 +50,7 @@ public interface IPosition : IEnumerable<Piece>
 
     IBoard Board { get; }
 
-    IPieceValue PieceValue { get; }
+    IValues Values { get; }
 
     BitBoard Checkers { get; }
 
diff --git a/src/Rudzoft.ChessLib/IPieceValue.cs b/src/Rudzoft.ChessLib/IValues.cs
similarity index 92%
rename from src/Rudzoft.ChessLib/IPieceValue.cs
rename to src/Rudzoft.ChessLib/IValues.cs
index 4a550284..418bf915 100644
--- a/src/Rudzoft.ChessLib/IPieceValue.cs
+++ b/src/Rudzoft.ChessLib/IValues.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib;
 
-public interface IPieceValue
+public interface IValues
 {
     Value MaxValueWithoutPawns { get; }
     Value MaxValue { get; }
@@ -72,7 +72,7 @@ public interface IPieceValue
 
     void SetDefaults();
 
-    void SetPieceValues(PieceValues[] values, Phases phase);
+    void SetPieceValues(DefaultPieceValues[] values, Phases phase);
 
-    PieceValues GetPieceValue(Piece pc, Phases phase);
+    DefaultPieceValues GetPieceValue(Piece pc, Phases phase);
 }
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
index f976f9c3..75dfc2ae 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
@@ -31,13 +31,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.MoveGeneration;
 
-public interface IMoveList : IReadOnlyCollection<ExtMove>
+public interface IMoveList : IReadOnlyCollection<ValMove>
 {
-    ExtMove this[int index] { get; set; }
+    ValMove this[int index] { get; set; }
 
     int Length { get; }
     Move CurrentMove { get; }
-    void Add(in ExtMove item);
+    void Add(in ValMove item);
     void Add(Move item);
 
     /// <summary>
@@ -45,9 +45,9 @@ public interface IMoveList : IReadOnlyCollection<ExtMove>
     /// </summary>
     void Clear();
 
-    bool Contains(in ExtMove item);
+    bool Contains(in ValMove item);
     bool Contains(Move item);
     bool Contains(Square from, Square to);
     void Generate(in IPosition pos, MoveGenerationType type = MoveGenerationType.Legal);
-    ReadOnlySpan<ExtMove> Get();
+    ReadOnlySpan<ValMove> Get();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 07927f94..45187024 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -43,7 +43,7 @@ private static readonly (CastleRight, CastleRight)[] CastleSideRights =
 
     public static int Generate(
         in IPosition pos,
-        Span<ExtMove> moves,
+        Span<ValMove> moves,
         int index,
         Player us,
         MoveGenerationType type)
@@ -72,7 +72,7 @@ public static int Generate(
 
     private static int GenerateAll(
         in IPosition pos,
-        Span<ExtMove> moves,
+        Span<ValMove> moves,
         int index,
         in BitBoard target,
         Player us,
@@ -117,7 +117,7 @@ private static int GenerateAll(
     /// <returns></returns>
     private static int GenerateCapturesQuietsNonEvasions(
         in IPosition pos,
-        Span<ExtMove> moves,
+        Span<ValMove> moves,
         int index,
         Player us,
         MoveGenerationType type)
@@ -147,7 +147,7 @@ private static int GenerateCapturesQuietsNonEvasions(
     /// <returns></returns>
     private static int GenerateEvasions(
         in IPosition pos,
-        Span<ExtMove> moves,
+        Span<ValMove> moves,
         int index,
         Player us)
     {
@@ -193,7 +193,7 @@ private static int GenerateEvasions(
     /// <returns></returns>
     private static int GenerateLegal(
         in IPosition pos,
-        Span<ExtMove> moves,
+        Span<ValMove> moves,
         int index,
         Player us)
     {
@@ -230,7 +230,7 @@ private static int GenerateLegal(
     /// <returns>The new move index</returns>
     private static int GenerateMoves(
         in IPosition pos,
-        Span<ExtMove> moves,
+        Span<ValMove> moves,
         int index,
         Player us,
         in BitBoard target,
@@ -272,7 +272,7 @@ private static int GenerateMoves(
     /// <returns>The new move index</returns>
     private static int GeneratePawnMoves(
         in IPosition pos,
-        Span<ExtMove> moves,
+        Span<ValMove> moves,
         int index,
         in BitBoard target,
         Player us,
@@ -436,7 +436,7 @@ private static int GeneratePawnMoves(
     /// <returns></returns>
     private static int GenerateQuietChecks(
         in IPosition pos,
-        Span<ExtMove> moves,
+        Span<ValMove> moves,
         int index,
         Player us)
     {
@@ -476,7 +476,7 @@ private static int GenerateQuietChecks(
     /// <param name="type"></param>
     /// <returns></returns>
     private static int MakePromotions(
-        Span<ExtMove> moves,
+        Span<ValMove> moves,
         int index,
         Square to,
         Square ksq,
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index ae648174..d682ba27 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -36,12 +36,12 @@ namespace Rudzoft.ChessLib.MoveGeneration;
 
 public sealed class MoveList : IMoveList
 {
-    private readonly ExtMove[] _moves;
+    private readonly ValMove[] _moves;
     private int _cur;
 
-    public MoveList() => _moves = new ExtMove[218];
+    public MoveList() => _moves = new ValMove[218];
 
-    int IReadOnlyCollection<ExtMove>.Count => Length;
+    int IReadOnlyCollection<ValMove>.Count => Length;
 
     public int Length { get; private set; }
 
@@ -54,14 +54,14 @@ public sealed class MoveList : IMoveList
         return moveList;
     }
 
-    public ExtMove this[int index]
+    public ValMove this[int index]
     {
         get => _moves[index];
         set => _moves[index] = value;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void Add(in ExtMove item) => _moves[Length++] = item;
+    public void Add(in ValMove item) => _moves[Length++] = item;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Add(Move item) => _moves[Length++].Move = item;
@@ -74,11 +74,11 @@ public ExtMove this[int index]
     public void Clear()
     {
         _cur = Length = 0;
-        _moves[0] = ExtMove.Empty;
+        _moves[0] = ValMove.Empty;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Contains(in ExtMove item)
+    public bool Contains(in ValMove item)
         => Contains(item.Move);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -109,24 +109,24 @@ public void Generate(in IPosition pos, MoveGenerationType type = MoveGenerationT
     {
         _cur = 0;
         Length = MoveGenerator.Generate(in pos, _moves.AsSpan(), 0, pos.SideToMove, type);
-        _moves[Length] = ExtMove.Empty;
+        _moves[Length] = ValMove.Empty;
     }
 
     [SkipLocalsInit]
     public static int GenerateMoveCount(in IPosition pos, MoveGenerationType type = MoveGenerationType.Legal)
     {
-        Span<ExtMove> moves = stackalloc ExtMove[218];
+        Span<ValMove> moves = stackalloc ValMove[218];
         return MoveGenerator.Generate(in pos, moves, 0, pos.SideToMove, type);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ReadOnlySpan<ExtMove> Get() =>
+    public ReadOnlySpan<ValMove> Get() =>
         Length == 0
-            ? ReadOnlySpan<ExtMove>.Empty
+            ? ReadOnlySpan<ValMove>.Empty
             : _moves.AsSpan()[..Length];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public IEnumerator<ExtMove> GetEnumerator()
+    public IEnumerator<ValMove> GetEnumerator()
         => _moves.Take(Length).GetEnumerator();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 07b8f821..2c3d0c7b 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -57,10 +57,10 @@ public sealed class Position : IPosition
     private readonly IPositionValidator _positionValidator;
     private Player _sideToMove;
 
-    public Position(IBoard board, IPieceValue pieceValues)
+    public Position(IBoard board, IValues valueses)
     {
         Board = board;
-        PieceValue = pieceValues;
+        Values = valueses;
         State = default;
         _outputObjectPool = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
         _positionValidator = new PositionValidator(this, Board);
@@ -102,7 +102,7 @@ public bool IsRepetition
     /// </summary>
     public Action<IPieceSquare> PieceUpdated { get; set; }
 
-    public IPieceValue PieceValue { get; }
+    public IValues Values { get; }
 
     public int Ply { get; private set; }
 
@@ -740,7 +740,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             }
             else
             {
-                State.NonPawnMaterial[them.Side] -= PieceValue.GetPieceValue(capturedPiece, Phases.Mg);
+                State.NonPawnMaterial[them.Side] -= Values.GetPieceValue(capturedPiece, Phases.Mg);
                 // TODO : Update material here
             }
 
@@ -804,7 +804,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 k ^= pc.GetZobristPst(to) ^ promotionPiece.GetZobristPst(to);
                 State.PawnStructureKey ^= pc.GetZobristPst(to);
 
-                State.NonPawnMaterial[us.Side] += PieceValue.GetPieceValue(promotionPiece, Phases.Mg);
+                State.NonPawnMaterial[us.Side] += Values.GetPieceValue(promotionPiece, Phases.Mg);
             }
 
             // Update pawn hash key
@@ -1003,11 +1003,11 @@ public bool SeeGe(Move m, Value threshold)
 
         var (from, to) = m;
 
-        var swap = PieceValue.GetPieceValue(GetPiece(to), Phases.Mg) - threshold;
+        var swap = Values.GetPieceValue(GetPiece(to), Phases.Mg) - threshold;
         if (swap < Value.ValueZero)
             return false;
 
-        swap = PieceValue.GetPieceValue(GetPiece(from), Phases.Mg) - swap;
+        swap = Values.GetPieceValue(GetPiece(from), Phases.Mg) - swap;
         if (swap <= Value.ValueZero)
             return true;
 
@@ -1041,7 +1041,7 @@ public bool SeeGe(Move m, Value threshold)
             var bb = stmAttackers & Board.Pieces(PieceTypes.Pawn);
             if (!bb.IsEmpty)
             {
-                if ((swap = PieceValue.PawnValueMg - swap) < res)
+                if ((swap = Values.PawnValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
@@ -1050,14 +1050,14 @@ public bool SeeGe(Move m, Value threshold)
             }
             else if (!(bb = stmAttackers & Board.Pieces(PieceTypes.Knight)).IsEmpty)
             {
-                if ((swap = PieceValue.KnightValueMg - swap) < res)
+                if ((swap = Values.KnightValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
             }
             else if (!(bb = stmAttackers & Board.Pieces(PieceTypes.Bishop)).IsEmpty)
             {
-                if ((swap = PieceValue.BishopValueMg - swap) < res)
+                if ((swap = Values.BishopValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
@@ -1066,7 +1066,7 @@ public bool SeeGe(Move m, Value threshold)
             }
             else if (!(bb = stmAttackers & Board.Pieces(PieceTypes.Rook)).IsEmpty)
             {
-                if ((swap = PieceValue.RookValueMg - swap) < res)
+                if ((swap = Values.RookValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
@@ -1075,7 +1075,7 @@ public bool SeeGe(Move m, Value threshold)
             }
             else if (!(bb = stmAttackers & Board.Pieces(PieceTypes.Queen)).IsEmpty)
             {
-                if ((swap = PieceValue.QueenValueMg - swap) < res)
+                if ((swap = Values.QueenValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
@@ -1463,7 +1463,7 @@ private void SetState()
             if (pt == PieceTypes.Pawn)
                 pawnKey ^= pc.GetZobristPst(sq);
             else if (pt != PieceTypes.King)
-                State.NonPawnMaterial[pc.ColorOf().Side] += PieceValue.GetPieceValue(pc, Phases.Mg);
+                State.NonPawnMaterial[pc.ColorOf().Side] += Values.GetPieceValue(pc, Phases.Mg);
         }
 
         if (State.EnPassantSquare != Square.None)
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index b6f6dcdf..8f66d904 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -48,7 +48,7 @@ public static class MoveTypesExtensions
 /// Move struct. Contains a single ushort for move related information. Also includes set and
 /// get functions for the relevant data stored in the bits.
 /// </summary>
-public record struct Move(ushort Data) : ISpanFormattable
+public readonly record struct Move(ushort Data) : ISpanFormattable
 {
     private const int MaxMoveStringSize = 5;
 
@@ -83,8 +83,8 @@ public static implicit operator Move(string value)
         => new(new Square(value[1] - '1', value[0] - 'a'), new Square(value[3] - '1', value[2] - 'a'));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Move(ExtMove extMove)
-        => extMove.Move;
+    public static implicit operator Move(ValMove valMove)
+        => valMove.Move;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Move(ushort value)
@@ -95,7 +95,7 @@ public static Move Create(Square from, Square to)
         => new(from, to);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int Create(Span<ExtMove> moves, int index, Square from, ref BitBoard to)
+    public static int Create(Span<ValMove> moves, int index, Square from, ref BitBoard to)
     {
         while (to)
             moves[index++].Move = Create(from, BitBoards.PopLsb(ref to));
diff --git a/src/Rudzoft.ChessLib/Types/RootMove.cs b/src/Rudzoft.ChessLib/Types/RootMove.cs
new file mode 100644
index 00000000..11ece856
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Types/RootMove.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Rudzoft.ChessLib.Types;
+
+public sealed class RootMove : List<Move>
+{
+    public RootMove(Move m)
+    {
+        Add(m);
+    }
+    
+    public Value OldValue { get; set; }
+    
+    public Value NewValue { get; set; }
+    
+    public Depth SelDepth { get; set; }
+    
+    public int TbRank { get; set; }
+    
+    public Value TbValue { get; set; }
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator RootMove(Move m)
+        => new(m);
+
+    public static bool operator !=(RootMove left, Move right)
+        => left.FirstOrDefault() != right;
+
+    public static bool operator ==(RootMove left, Move right)
+        => left.FirstOrDefault() == right;
+    
+    public static bool operator <(RootMove left, RootMove right)
+        => left.NewValue > right.NewValue || left.NewValue == right.NewValue && left.OldValue > right.OldValue;
+
+    public static bool operator >(RootMove left, RootMove right)
+        => left.NewValue < right.NewValue || left.NewValue == right.NewValue && left.OldValue < right.OldValue;
+}
diff --git a/src/Rudzoft.ChessLib/Types/ExtMove.cs b/src/Rudzoft.ChessLib/Types/ValMove.cs
similarity index 72%
rename from src/Rudzoft.ChessLib/Types/ExtMove.cs
rename to src/Rudzoft.ChessLib/Types/ValMove.cs
index 079027fc..727466dd 100644
--- a/src/Rudzoft.ChessLib/Types/ExtMove.cs
+++ b/src/Rudzoft.ChessLib/Types/ValMove.cs
@@ -30,45 +30,45 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 namespace Rudzoft.ChessLib.Types;
 
 /// <summary>
-/// Extended move structure which combines Move and Score
+/// Value based move structure which combines move and a simple move ordering score
 /// </summary>
-public struct ExtMove : IEquatable<ExtMove>, IComparable<ExtMove>
+public struct ValMove : IEquatable<ValMove>, IComparable<ValMove>
 {
-    public static readonly ExtMove Empty = new();
+    public static readonly ValMove Empty = new();
 
     public Move Move { get; set; }
 
     public int Score { get; set; }
 
-    private ExtMove(Move m, int s)
+    private ValMove(Move m, int s)
     {
         Move = m;
         Score = s;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator ExtMove(Move m)
+    public static implicit operator ValMove(Move m)
         => new(m, 0);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator ExtMove(int s)
+    public static implicit operator ValMove(int s)
         => new(Move.EmptyMove, s);
 
-    public static bool operator !=(ExtMove left, ExtMove right)
-        => !(left == right);
+    public static bool operator !=(ValMove left, ValMove right)
+        => !(left.Move == right.Move);
 
-    public static bool operator ==(ExtMove left, ExtMove right)
-        => left.Equals(right);
+    public static bool operator ==(ValMove left, ValMove right)
+        => left.Move == right.Move;
 
-    public bool Equals(ExtMove other)
+    public override bool Equals(object obj)
+        => obj is ValMove other && Equals(other);
+
+    public bool Equals(ValMove other)
         => Move.Equals(other.Move);
 
-    public readonly int CompareTo(ExtMove other)
+    public readonly int CompareTo(ValMove other)
         => other.Score.CompareTo(Score);
-
-    public override bool Equals(object obj)
-        => obj is ExtMove other && Equals(other);
-
+    
     public readonly override int GetHashCode()
         => Move.GetHashCode();
 
diff --git a/src/Rudzoft.ChessLib/Types/Value.cs b/src/Rudzoft.ChessLib/Types/Value.cs
index 2f73cc27..7e2a7323 100644
--- a/src/Rudzoft.ChessLib/Types/Value.cs
+++ b/src/Rudzoft.ChessLib/Types/Value.cs
@@ -33,26 +33,26 @@ namespace Rudzoft.ChessLib.Types;
 /// </summary>
 public readonly struct Value : IEquatable<Value>
 {
-    public readonly PieceValues Raw;
+    public readonly DefaultPieceValues Raw;
 
     public Value(Value value)
         : this(value.Raw) { }
 
     public Value(int value)
-        : this((PieceValues)value) { }
+        : this((DefaultPieceValues)value) { }
 
-    private Value(PieceValues value) => Raw = value;
+    private Value(DefaultPieceValues value) => Raw = value;
 
-    public static Value ValueZero { get; } = new(PieceValues.ValueZero);
+    public static Value ValueZero { get; } = new(DefaultPieceValues.ValueZero);
 
-    public static Value Infinite { get; } = new(PieceValues.ValueInfinite);
+    public static Value Infinite { get; } = new(DefaultPieceValues.ValueInfinite);
 
-    public static Value MinusInfinite { get; } = new(PieceValues.ValueMinusInfinite);
+    public static Value MinusInfinite { get; } = new(DefaultPieceValues.ValueMinusInfinite);
 
     public static implicit operator Value(int value)
         => new(value);
 
-    public static implicit operator Value(PieceValues value)
+    public static implicit operator Value(DefaultPieceValues value)
         => new(value);
 
     public static bool operator ==(Value left, Value right)
@@ -67,7 +67,7 @@ public static implicit operator Value(PieceValues value)
     public static Value operator +(Value left, int right)
         => new((int)left.Raw + right);
 
-    public static Value operator +(Value left, PieceValues right)
+    public static Value operator +(Value left, DefaultPieceValues right)
         => new((int)left.Raw + (int)right);
 
     public static Value operator +(int left, Value right)
@@ -79,10 +79,10 @@ public static implicit operator Value(PieceValues value)
     public static Value operator -(Value left, int right)
         => new((int)left.Raw - right);
 
-    public static Value operator -(PieceValues left, Value right)
+    public static Value operator -(DefaultPieceValues left, Value right)
         => new((int)left - right.Raw);
 
-    public static Value operator -(Value left, PieceValues right)
+    public static Value operator -(Value left, DefaultPieceValues right)
         => new(left.Raw - (int)right);
 
     public static Value operator -(int left, Value right)
@@ -97,6 +97,9 @@ public static implicit operator Value(PieceValues value)
     public static Value operator *(Value left, int right)
         => new((int)left.Raw * right);
 
+    public static Value operator *(int left, Value right)
+        => new(left * right.Raw.AsInt());
+
     public static bool operator >(Value left, Value right)
         => left.Raw > right.Raw;
 
@@ -109,16 +112,16 @@ public static implicit operator Value(PieceValues value)
     public static bool operator >=(Value left, Value right)
         => left.Raw >= right.Raw;
 
-    public static bool operator >(Value left, PieceValues right)
+    public static bool operator >(Value left, DefaultPieceValues right)
         => left.Raw > right;
 
-    public static bool operator <(Value left, PieceValues right)
+    public static bool operator <(Value left, DefaultPieceValues right)
         => left.Raw < right;
 
-    public static bool operator <=(Value left, PieceValues right)
+    public static bool operator <=(Value left, DefaultPieceValues right)
         => left.Raw <= right;
 
-    public static bool operator >=(Value left, PieceValues right)
+    public static bool operator >=(Value left, DefaultPieceValues right)
         => left.Raw >= right;
 
     public static bool operator true(Value value)
@@ -141,4 +144,4 @@ public override int GetHashCode()
 
     public override string ToString()
         => Raw.ToString();
-}
\ No newline at end of file
+}
diff --git a/src/Rudzoft.ChessLib/Types/PieceValue.cs b/src/Rudzoft.ChessLib/Types/Values.cs
similarity index 76%
rename from src/Rudzoft.ChessLib/Types/PieceValue.cs
rename to src/Rudzoft.ChessLib/Types/Values.cs
index 34dbd86a..7843d8c6 100644
--- a/src/Rudzoft.ChessLib/Types/PieceValue.cs
+++ b/src/Rudzoft.ChessLib/Types/Values.cs
@@ -31,7 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Types;
 
-public enum PieceValues
+public enum DefaultPieceValues
 {
     ValueZero = 0,
     ValueDraw = 0,
@@ -41,8 +41,8 @@ public enum PieceValues
     ValueMinusInfinite = -32001,
     ValueNone = 32002,
 
-    ValueMateInMaxPly = ValueMate - 2 * PieceValue.MAX_PLY,
-    ValueMatedInMaxPly = -ValueMate + 2 * PieceValue.MAX_PLY,
+    ValueMateInMaxPly = ValueMate - 2 * Values.MAX_PLY,
+    ValueMatedInMaxPly = -ValueMate + 2 * Values.MAX_PLY,
 
     PawnValueMg = 128,
     PawnValueEg = 213,
@@ -62,32 +62,32 @@ public enum PieceValues
 public static class PieceValuesExtensions
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int AsInt(this PieceValues @this) => (int)@this;
+    public static int AsInt(this DefaultPieceValues @this) => (int)@this;
 }
 
-public sealed class PieceValue : IPieceValue
+public sealed class Values : IValues
 {
     public const int MAX_PLY = 246;
 
-    private readonly PieceValues[][] _defaults;
+    private readonly DefaultPieceValues[][] _defaults;
 
     private Value _valueMateInMaxPly;
     private Value _valueMatedInMaxPly;
     private Value _valueMate;
 
-    public PieceValue()
+    public Values()
     {
-        _defaults = new PieceValues[2][];
+        _defaults = new DefaultPieceValues[2][];
         for (var index = 0; index < _defaults.Length; index++)
-            _defaults[index] = new PieceValues[6];
+            _defaults[index] = new DefaultPieceValues[6];
 
-        _defaults[0] = new[] { PieceValues.ValueZero, PieceValues.PawnValueMg, PieceValues.KnightValueMg, PieceValues.BishopValueMg, PieceValues.RookValueMg, PieceValues.QueenValueMg };
-        _defaults[1] = new[] { PieceValues.ValueZero, PieceValues.PawnValueEg, PieceValues.KnightValueEg, PieceValues.BishopValueEg, PieceValues.RookValueEg, PieceValues.QueenValueEg };
+        _defaults[0] = new[] { DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueMg, DefaultPieceValues.KnightValueMg, DefaultPieceValues.BishopValueMg, DefaultPieceValues.RookValueMg, DefaultPieceValues.QueenValueMg };
+        _defaults[1] = new[] { DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueEg, DefaultPieceValues.KnightValueEg, DefaultPieceValues.BishopValueEg, DefaultPieceValues.RookValueEg, DefaultPieceValues.QueenValueEg };
 
         SetDefaults();
     }
 
-    public PieceValues[][] Values { get; set; }
+    public DefaultPieceValues[][] PieceValues { get; set; }
 
     public Value MaxValueWithoutPawns { get; private set; }
 
@@ -145,9 +145,9 @@ public Value ValueMate
 
     public void SetDefaults()
     {
-        Values = new PieceValues[2][];
-        for (var index = 0; index < Values.Length; index++)
-            Values[index] = new PieceValues[6];
+        PieceValues = new DefaultPieceValues[2][];
+        for (var index = 0; index < PieceValues.Length; index++)
+            PieceValues[index] = new DefaultPieceValues[6];
 
         Span<Phases> phases = stackalloc Phases[] { Phases.Mg, Phases.Eg };
 
@@ -161,7 +161,7 @@ public void SetDefaults()
 
         foreach (var pt in pieceTypes)
         {
-            var value = new Value(Values[0][pt.AsInt()]);
+            var value = new Value(PieceValues[0][pt.AsInt()]);
             switch (pt)
             {
                 case PieceTypes.Pawn:
@@ -194,12 +194,12 @@ public void SetDefaults()
         QueenValueEg = GetPieceValue(PieceTypes.Queen, Phases.Eg);
     }
 
-    public void SetPieceValues(PieceValues[] values, Phases phase)
-        => Array.Copy(values, Values[phase.AsInt()], values.Length);
+    public void SetPieceValues(DefaultPieceValues[] values, Phases phase)
+        => Array.Copy(values, PieceValues[phase.AsInt()], values.Length);
 
-    public PieceValues GetPieceValue(Piece pc, Phases phase)
-        => Values[(int)phase][pc.Type().AsInt()];
+    public DefaultPieceValues GetPieceValue(Piece pc, Phases phase)
+        => PieceValues[(int)phase][pc.Type().AsInt()];
 
-    public PieceValues GetPieceValue(PieceTypes pt, Phases phase)
-        => Values[phase.AsInt()][pt.AsInt()];
+    public DefaultPieceValues GetPieceValue(PieceTypes pt, Phases phase)
+        => PieceValues[phase.AsInt()][pt.AsInt()];
 }

From 6a779d29dc6a8d2a1f23ff2759eefdf7833522d8 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Feb 2023 23:27:26 +0100
Subject: [PATCH 009/119] Modernized Perft application

---
 src/Rudzoft.ChessLib/Types/Direction.cs       |   2 +
 src/Rudzoft.ChessLib/Types/RootMove.cs        |   5 +
 src/Rudzoft.Perft.Framework/Framework.cs      | 118 -------------
 .../Rudzoft.Perft.Framework.csproj            |   2 +-
 src/Rudzoft.Perft/GlobalUsings.cs             |   3 +
 src/Rudzoft.Perft/Host/CommandLineArgs.cs     |  29 ++++
 src/Rudzoft.Perft/Host/PerftHost.cs           |  73 ++++++++
 .../Host/PerftServiceProviderFactory.cs       | 138 +++++++++++++++
 src/Rudzoft.Perft/Options/EpdOptions.cs       |   4 +-
 src/Rudzoft.Perft/Options/Fens.cs             |   4 +-
 src/Rudzoft.Perft/Options/IOptions.cs         |   6 +-
 src/Rudzoft.Perft/Options/IOptionsFactory.cs  |  34 ++++
 src/Rudzoft.Perft/Options/OptionType.cs       |  12 +-
 src/Rudzoft.Perft/Options/OptionsFactory.cs   |  95 +++++++++++
 src/Rudzoft.Perft/Options/TTOptions.cs        |   4 +-
 src/Rudzoft.Perft/Parsers/EpdParser.cs        |   3 +-
 .../Parsers/EpdParserSettings.cs              |   2 +-
 src/Rudzoft.Perft/Parsers/EpdSet.cs           |   3 +-
 src/Rudzoft.Perft/Parsers/IEpdParser.cs       |   2 +-
 .../Parsers/IEpdParserSettings.cs             |   2 +-
 src/Rudzoft.Perft/Parsers/IEpdSet.cs          |   3 +-
 src/Rudzoft.Perft/{ => Perft}/IPerftResult.cs |   6 +-
 src/Rudzoft.Perft/{ => Perft}/IPerftRunner.cs |   8 +-
 src/Rudzoft.Perft/{ => Perft}/PerftResult.cs  |   6 +-
 src/Rudzoft.Perft/{ => Perft}/PerftRunner.cs  |  73 ++++----
 src/Rudzoft.Perft/Program.cs                  | 157 ++----------------
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |   1 +
 src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs |   7 +-
 .../TimeStamp/IBuildTimeStamp.cs              |   2 +-
 29 files changed, 463 insertions(+), 341 deletions(-)
 delete mode 100644 src/Rudzoft.Perft.Framework/Framework.cs
 create mode 100644 src/Rudzoft.Perft/GlobalUsings.cs
 create mode 100644 src/Rudzoft.Perft/Host/CommandLineArgs.cs
 create mode 100644 src/Rudzoft.Perft/Host/PerftHost.cs
 create mode 100644 src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs
 create mode 100644 src/Rudzoft.Perft/Options/IOptionsFactory.cs
 create mode 100644 src/Rudzoft.Perft/Options/OptionsFactory.cs
 rename src/Rudzoft.Perft/{ => Perft}/IPerftResult.cs (94%)
 rename src/Rudzoft.Perft/{ => Perft}/IPerftRunner.cs (88%)
 rename src/Rudzoft.Perft/{ => Perft}/PerftResult.cs (95%)
 rename src/Rudzoft.Perft/{ => Perft}/PerftRunner.cs (83%)

diff --git a/src/Rudzoft.ChessLib/Types/Direction.cs b/src/Rudzoft.ChessLib/Types/Direction.cs
index f2bfaa5b..6d070dc2 100644
--- a/src/Rudzoft.ChessLib/Types/Direction.cs
+++ b/src/Rudzoft.ChessLib/Types/Direction.cs
@@ -47,6 +47,8 @@ public enum Directions
     SouthFill = -NorthFill
 }
 
+public record struct BiDirection(Direction One, Direction Two);
+
 public readonly record struct Direction(Directions Value)
 {
     public static Direction North { get; } = new(Directions.North);
diff --git a/src/Rudzoft.ChessLib/Types/RootMove.cs b/src/Rudzoft.ChessLib/Types/RootMove.cs
index 11ece856..5b3fa8c5 100644
--- a/src/Rudzoft.ChessLib/Types/RootMove.cs
+++ b/src/Rudzoft.ChessLib/Types/RootMove.cs
@@ -37,3 +37,8 @@ public static implicit operator RootMove(Move m)
     public static bool operator >(RootMove left, RootMove right)
         => left.NewValue < right.NewValue || left.NewValue == right.NewValue && left.OldValue < right.OldValue;
 }
+
+public sealed class RootMoves : List<RootMove>
+{
+    
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft.Framework/Framework.cs b/src/Rudzoft.Perft.Framework/Framework.cs
deleted file mode 100644
index fdc56137..00000000
--- a/src/Rudzoft.Perft.Framework/Framework.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
-Perft, a chess perft test library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using System;
-using System.IO;
-using DryIoc;
-using Microsoft.Extensions.Configuration;
-using Perft.Environment;
-using Perft.Factories;
-using Serilog;
-
-namespace Perft;
-
-/// <summary>
-/// The main entry point into the Dna Framework library
-/// </summary>
-public static class Framework
-{
-    #region Public Properties
-
-    /// <summary>
-    /// The main ioc container
-    /// </summary>
-    public static IContainer IoC { get; private set; }
-
-    /// <summary>
-    /// Gets the configuration for the framework environment
-    /// </summary>
-    public static IConfiguration Configuration => IoC.Resolve<IConfiguration>();
-
-    /// <summary>
-    /// Gets the default logger for the framework
-    /// </summary>
-    public static ILogger Logger => IoC.Resolve<ILogger>();
-
-    /// <summary>
-    /// Gets the framework environment of this class
-    /// </summary>
-    public static IFrameworkEnvironment Environment => IoC.Resolve<IFrameworkEnvironment>();
-
-    #endregion Public Properties
-
-    #region Public Methods
-
-    /// <summary>
-    /// Should be called at the very start of your application to configure and setup
-    /// the Dna Framework
-    /// </summary>
-    /// <param name="configure">The action to add custom configurations to the configuration builder</param>
-    /// <param name="injection">The action to inject services into the service collection</param>
-    public static void Startup(Action<IConfigurationBuilder> configure = null,
-        Action<IContainer, IConfiguration> injection = null)
-    {
-        // Process passed in module list
-        IoC = new Container();
-
-        // Load internal based modules
-        // This step is required as they contain information which are used from this point on
-        IoC.Register<IFrameworkEnvironment, FrameworkEnvironment>(Reuse.Singleton);
-
-        var configBuilder = ConfigurationBuilder();
-
-        // Let custom configuration happen
-        configure?.Invoke(configBuilder);
-
-        // Construct a configuration binding
-        IoC.Register(made: Made.Of(() => ConfigurationFactory.CreateConfiguration(configBuilder)),
-            reuse: Reuse.Singleton);
-
-        // Allow custom service injection
-        injection?.Invoke(IoC, Configuration);
-
-        // Log the startup complete
-        Logger.Information("Perft Framework started in {0}...", Environment.Configuration);
-    }
-
-    #endregion Public Methods
-
-    #region Private ioc helper methods
-
-    private static IConfigurationBuilder ConfigurationBuilder()
-    {
-        // Create our configuration sources
-        return new ConfigurationBuilder()
-            // Add environment variables
-            .AddEnvironmentVariables()
-            // Set base path for Json files as the startup location of the application
-            .SetBasePath(Directory.GetCurrentDirectory())
-            // Add application settings json files
-            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
-            .AddJsonFile($"appsettings.{Environment.Configuration}.json", optional: true, reloadOnChange: true);
-    }
-
-    #endregion Private ioc helper methods
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
index 7454b411..7e6c5377 100644
--- a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
+++ b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
@@ -13,7 +13,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="DryIoc" Version="5.3.2" />
+    <PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="6.1.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
diff --git a/src/Rudzoft.Perft/GlobalUsings.cs b/src/Rudzoft.Perft/GlobalUsings.cs
new file mode 100644
index 00000000..711b0253
--- /dev/null
+++ b/src/Rudzoft.Perft/GlobalUsings.cs
@@ -0,0 +1,3 @@
+// Global using directives
+
+global using System;
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Host/CommandLineArgs.cs b/src/Rudzoft.Perft/Host/CommandLineArgs.cs
new file mode 100644
index 00000000..fb50f255
--- /dev/null
+++ b/src/Rudzoft.Perft/Host/CommandLineArgs.cs
@@ -0,0 +1,29 @@
+/*
+Perft, a chess perft testing application
+
+MIT License
+
+Copyright (c) 2019-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+namespace Rudzoft.Perft.Host;
+
+public record CommandLineArgs(string[] Args);
diff --git a/src/Rudzoft.Perft/Host/PerftHost.cs b/src/Rudzoft.Perft/Host/PerftHost.cs
new file mode 100644
index 00000000..09be270e
--- /dev/null
+++ b/src/Rudzoft.Perft/Host/PerftHost.cs
@@ -0,0 +1,73 @@
+/*
+Perft, a chess perft testing application
+
+MIT License
+
+Copyright (c) 2019-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Hosting;
+using Perft.Environment;
+using Rudzoft.Perft.Options;
+using Rudzoft.Perft.Perft;
+using Serilog;
+
+namespace Rudzoft.Perft.Host;
+
+public sealed class PerftHost : IHostedService
+{
+    private readonly IPerftRunner _perftRunner;
+    private readonly ILogger _logger;
+
+    public PerftHost(
+        IOptionsFactory optionsFactory,
+        IPerftRunner perftRunner,
+        IFrameworkEnvironment environment,
+        ILogger logger)
+    {
+        _perftRunner = perftRunner;
+        _logger = logger;
+        foreach (var option in optionsFactory.Parse())
+        {
+            if (option.Type == OptionType.TTOptions)
+                _perftRunner.TranspositionTableOptions = option.PerftOptions;
+            else
+                _perftRunner.Options = option.PerftOptions;
+        }
+        logger.Information("Perft Framework started in {0}...", environment.Configuration);
+
+    }
+    
+    public async Task StartAsync(CancellationToken cancellationToken)
+    {
+        var errors = await _perftRunner.Run(cancellationToken);
+        if (errors != 0)
+            _logger.Error("Total errors: {0}", errors);
+    }
+
+    public Task StopAsync(CancellationToken cancellationToken)
+    {
+        _logger.Information("Stopped");
+        return Task.CompletedTask;
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs b/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs
new file mode 100644
index 00000000..b623e845
--- /dev/null
+++ b/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs
@@ -0,0 +1,138 @@
+/*
+Perft, a chess perft testing application
+
+MIT License
+
+Copyright (c) 2019-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System.IO;
+using DryIoc;
+using DryIoc.Microsoft.DependencyInjection;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
+using Perft.Environment;
+using Perft.Factories;
+using Rudzoft.ChessLib;
+using Rudzoft.ChessLib.Perft.Interfaces;
+using Rudzoft.ChessLib.Protocol.UCI;
+using Rudzoft.ChessLib.Tables;
+using Rudzoft.ChessLib.Types;
+using Rudzoft.Perft.Options;
+using Rudzoft.Perft.Parsers;
+using Rudzoft.Perft.Perft;
+using Rudzoft.Perft.TimeStamp;
+using Serilog;
+
+namespace Rudzoft.Perft.Host;
+
+public class PerftServiceProviderFactory : IServiceProviderFactory<Container>
+{
+    public Container CreateBuilder(IServiceCollection services)
+    {
+        var configBuilder = ConfigurationBuilder();
+        var configuration = ConfigurationFactory.CreateConfiguration(configBuilder);
+        
+        var container = new Container(Rules.MicrosoftDependencyInjectionRules.WithConcreteTypeDynamicRegistrations(reuse: Reuse.Transient));
+        
+        // Construct a configuration binding
+        container.RegisterInstance(configuration);
+
+        AddServices(container, configuration);
+
+        container.Populate(services);
+        
+        return container;
+    }
+
+    public IServiceProvider CreateServiceProvider(Container container)
+    {
+        return container.BuildServiceProvider();
+    }
+
+    private static IConfigurationBuilder ConfigurationBuilder()
+    {
+#if RELEASE
+        const string envName = "Production";
+#else
+        const string envName = "Development";
+#endif
+        // Create our configuration sources
+        return new ConfigurationBuilder()
+            // Add environment variables
+            .AddEnvironmentVariables()
+            // Set base path for Json files as the startup location of the application
+            .SetBasePath(Directory.GetCurrentDirectory())
+            // Add application settings json files
+            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+            .AddJsonFile($"appsettings.{envName}.json", optional: true, reloadOnChange: true);
+    }
+
+    private static void AddServices(IContainer container, IConfiguration configuration)
+    {
+        // Bind logger with configuration
+        container.Register(Made.Of(() => ConfigureLogger(configuration)), Reuse.Singleton);
+
+        // Bind build time stamp class
+        container.Register<IBuildTimeStamp, BuildTimeStamp>(Reuse.Singleton);
+
+        // Bind chess classes
+        container.Register<IGame, Game>(Reuse.Transient);
+        container.Register<IBoard, Board>(Reuse.Singleton);
+        container.Register<IPosition, Position>(Reuse.Transient);
+        container.Register(made: Made.Of(static () => KillerMoves.Create(64)), Reuse.Transient);
+        container.Register<IUci, Uci>(Reuse.Singleton);
+        container.Register<IValues, Values>(Reuse.Singleton);
+
+        // Bind chess perft classes
+        container.Register<IFrameworkEnvironment, FrameworkEnvironment>(Reuse.Singleton);
+        container.Register<IPerft, ChessLib.Perft.Perft>(Reuse.Transient);
+        container.Register<IPerftRunner, PerftRunner>(Reuse.Transient);
+        container.Register<IOptionsFactory, OptionsFactory>(Reuse.Singleton);
+        
+        // Bind perft classes
+        container.Register<IEpdParserSettings, EpdParserSettings>(Reuse.Singleton);
+        container.Register<IEpdSet, EpdSet>(Reuse.Transient);
+        container.Register<IEpdParser, EpdParser>(Reuse.Singleton);
+
+        // Bind object pool for perft result
+        container.Register<ObjectPoolProvider, DefaultObjectPoolProvider>(Reuse.Singleton);
+        container.RegisterDelegate(context =>
+        {
+            var provider = context.Resolve<ObjectPoolProvider>();
+            var policy = new DefaultPooledObjectPolicy<PerftResult>();
+            return provider.Create(policy);
+        });
+    }
+
+    private static ILogger ConfigureLogger(IConfiguration configuration)
+    {
+        // Apply the config to the logger
+        Log.Logger = new LoggerConfiguration()
+            .ReadFrom.Configuration(configuration)
+            .Enrich.WithThreadId()
+            .Enrich.FromLogContext()
+            .CreateLogger();
+        AppDomain.CurrentDomain.ProcessExit += static (_, _) => Log.CloseAndFlush();
+        return Log.Logger;
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Options/EpdOptions.cs b/src/Rudzoft.Perft/Options/EpdOptions.cs
index 94b1d40f..f67d8080 100644
--- a/src/Rudzoft.Perft/Options/EpdOptions.cs
+++ b/src/Rudzoft.Perft/Options/EpdOptions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -30,7 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 namespace Rudzoft.Perft.Options;
 
 [Verb("epd", HelpText = "Add parsing of an epd file containing perft information")]
-public sealed class EpdOptions : IOptions
+public sealed class EpdOptions : IPerftOptions
 {
     [Option('f', "files", Required = true, HelpText = "List of epd files to parse.")]
     public IEnumerable<string> Epds { get; set; }
diff --git a/src/Rudzoft.Perft/Options/Fens.cs b/src/Rudzoft.Perft/Options/Fens.cs
index d02c3c89..a9851567 100644
--- a/src/Rudzoft.Perft/Options/Fens.cs
+++ b/src/Rudzoft.Perft/Options/Fens.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -30,7 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 namespace Rudzoft.Perft.Options;
 
 [Verb("fens", HelpText = "Add fens to run")]
-public class FenOptions : IOptions
+public class FenOptions : IPerftOptions
 {
     [Option('f', "fen", Required = true, HelpText = "Set the FEN string to perform perft test on.")]
     public IEnumerable<string> Fens { get; set; }
diff --git a/src/Rudzoft.Perft/Options/IOptions.cs b/src/Rudzoft.Perft/Options/IOptions.cs
index 354269ca..b21ed276 100644
--- a/src/Rudzoft.Perft/Options/IOptions.cs
+++ b/src/Rudzoft.Perft/Options/IOptions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.Perft.Options;
 
-public interface IOptions
+public record PerftOption(OptionType Type, IPerftOptions PerftOptions);
+
+public interface IPerftOptions
 {
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Options/IOptionsFactory.cs b/src/Rudzoft.Perft/Options/IOptionsFactory.cs
new file mode 100644
index 00000000..b1afead6
--- /dev/null
+++ b/src/Rudzoft.Perft/Options/IOptionsFactory.cs
@@ -0,0 +1,34 @@
+/*
+Perft, a chess perft testing application
+
+MIT License
+
+Copyright (c) 2019-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System.Collections.Generic;
+
+namespace Rudzoft.Perft.Options;
+
+public interface IOptionsFactory
+{
+    IEnumerable<PerftOption> Parse();
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Options/OptionType.cs b/src/Rudzoft.Perft/Options/OptionType.cs
index 5f8b03b7..741db333 100644
--- a/src/Rudzoft.Perft/Options/OptionType.cs
+++ b/src/Rudzoft.Perft/Options/OptionType.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
 namespace Rudzoft.Perft.Options;
 
 [Flags]
@@ -35,4 +33,12 @@ public enum OptionType
     EdpOptions = 1,
     FenOptions = 2,
     TTOptions = 4
+}
+
+public static class OptionTypeExtensions
+{
+    public static bool HasFlagFast(this OptionType value, OptionType flag)
+    {
+        return (value & flag) != 0;
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Options/OptionsFactory.cs b/src/Rudzoft.Perft/Options/OptionsFactory.cs
new file mode 100644
index 00000000..0b5c296a
--- /dev/null
+++ b/src/Rudzoft.Perft/Options/OptionsFactory.cs
@@ -0,0 +1,95 @@
+/*
+Perft, a chess perft testing application
+
+MIT License
+
+Copyright (c) 2019-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System.Collections.Generic;
+using CommandLine;
+using Rudzoft.Perft.Host;
+using Serilog;
+
+namespace Rudzoft.Perft.Options;
+
+public sealed class OptionsFactory : IOptionsFactory
+{
+    private readonly string[] _args;
+    private readonly ILogger _logger;
+
+    public OptionsFactory(CommandLineArgs args, ILogger logger)
+    {
+        _args = args.Args;
+        _logger = logger;
+    }
+    
+    public IEnumerable<PerftOption> Parse()
+    {
+        var optionsUsed = OptionType.None;
+        IPerftOptions options = null;
+        IPerftOptions ttOptions = null;
+
+        var setEdp = new Func<EpdOptions, int>(o =>
+        {
+            optionsUsed |= OptionType.EdpOptions;
+            options = o;
+            return 0;
+        });
+
+        var setFen = new Func<FenOptions, int>(o =>
+        {
+            optionsUsed |= OptionType.FenOptions;
+            options = o;
+            return 0;
+        });
+
+        var setTT = new Func<TTOptions, int>(o =>
+        {
+            optionsUsed |= OptionType.TTOptions;
+            ttOptions = o;
+            return 0;
+        });
+
+        // fens -f "rnkq1bnr/p3ppp1/1ppp3p/3B4/6b1/2PQ3P/PP1PPP2/RNB1K1NR w KQ -" -d 6
+        // fens -f "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1" -d 6
+        // epd -f D:\perft-random.epd
+        var returnValue = Parser.Default.ParseArguments<EpdOptions, FenOptions, TTOptions>(_args)
+            .MapResult(
+                (EpdOptions opts) => setEdp(opts),
+                (FenOptions opts) => setFen(opts),
+                (TTOptions opts) => setTT(opts),
+                static _ => 1);
+
+        if (returnValue != 0)
+            yield break;
+
+        if (optionsUsed.HasFlagFast(OptionType.EdpOptions))
+            yield return new PerftOption(OptionType.EdpOptions, options);
+        else
+            yield return new PerftOption(OptionType.FenOptions, options);
+
+        if (optionsUsed.HasFlagFast(OptionType.TTOptions))
+            yield return new PerftOption(OptionType.TTOptions, ttOptions);
+
+
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Options/TTOptions.cs b/src/Rudzoft.Perft/Options/TTOptions.cs
index 2e394899..243a752d 100644
--- a/src/Rudzoft.Perft/Options/TTOptions.cs
+++ b/src/Rudzoft.Perft/Options/TTOptions.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 namespace Rudzoft.Perft.Options;
 
 [Verb("tt", HelpText = "Configuration for transposition table")]
-public class TTOptions : IOptions
+public class TTOptions : IPerftOptions
 {
     [Option('u', "use", Required = false, Default = true, HelpText = "Dis/En-able use of transposition table")]
     public bool Use { get; set; }
diff --git a/src/Rudzoft.Perft/Parsers/EpdParser.cs b/src/Rudzoft.Perft/Parsers/EpdParser.cs
index fa23bc82..5521753f 100644
--- a/src/Rudzoft.Perft/Parsers/EpdParser.cs
+++ b/src/Rudzoft.Perft/Parsers/EpdParser.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
diff --git a/src/Rudzoft.Perft/Parsers/EpdParserSettings.cs b/src/Rudzoft.Perft/Parsers/EpdParserSettings.cs
index c984f214..6a960636 100644
--- a/src/Rudzoft.Perft/Parsers/EpdParserSettings.cs
+++ b/src/Rudzoft.Perft/Parsers/EpdParserSettings.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.Perft/Parsers/EpdSet.cs b/src/Rudzoft.Perft/Parsers/EpdSet.cs
index 009cccaa..b014a436 100644
--- a/src/Rudzoft.Perft/Parsers/EpdSet.cs
+++ b/src/Rudzoft.Perft/Parsers/EpdSet.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections.Generic;
 using Rudzoft.ChessLib.Perft.Interfaces;
 
diff --git a/src/Rudzoft.Perft/Parsers/IEpdParser.cs b/src/Rudzoft.Perft/Parsers/IEpdParser.cs
index cb54c002..889827f2 100644
--- a/src/Rudzoft.Perft/Parsers/IEpdParser.cs
+++ b/src/Rudzoft.Perft/Parsers/IEpdParser.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.Perft/Parsers/IEpdParserSettings.cs b/src/Rudzoft.Perft/Parsers/IEpdParserSettings.cs
index d96ad8f5..d49bce2b 100644
--- a/src/Rudzoft.Perft/Parsers/IEpdParserSettings.cs
+++ b/src/Rudzoft.Perft/Parsers/IEpdParserSettings.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/Rudzoft.Perft/Parsers/IEpdSet.cs b/src/Rudzoft.Perft/Parsers/IEpdSet.cs
index 22d189c7..c3ca7995 100644
--- a/src/Rudzoft.Perft/Parsers/IEpdSet.cs
+++ b/src/Rudzoft.Perft/Parsers/IEpdSet.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections.Generic;
 using Rudzoft.ChessLib.Perft.Interfaces;
 
diff --git a/src/Rudzoft.Perft/IPerftResult.cs b/src/Rudzoft.Perft/Perft/IPerftResult.cs
similarity index 94%
rename from src/Rudzoft.Perft/IPerftResult.cs
rename to src/Rudzoft.Perft/Perft/IPerftResult.cs
index 77e48c97..cd052f18 100644
--- a/src/Rudzoft.Perft/IPerftResult.cs
+++ b/src/Rudzoft.Perft/Perft/IPerftResult.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
-namespace Rudzoft.Perft;
+namespace Rudzoft.Perft.Perft;
 
 public interface IPerftResult
 {
diff --git a/src/Rudzoft.Perft/IPerftRunner.cs b/src/Rudzoft.Perft/Perft/IPerftRunner.cs
similarity index 88%
rename from src/Rudzoft.Perft/IPerftRunner.cs
rename to src/Rudzoft.Perft/Perft/IPerftRunner.cs
index f5ad7c76..f3c34cc1 100644
--- a/src/Rudzoft.Perft/IPerftRunner.cs
+++ b/src/Rudzoft.Perft/Perft/IPerftRunner.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -28,13 +28,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Threading.Tasks;
 using Rudzoft.Perft.Options;
 
-namespace Rudzoft.Perft;
+namespace Rudzoft.Perft.Perft;
 
 public interface IPerftRunner
 {
     bool SaveResults { get; set; }
-    IOptions Options { get; set; }
-    TTOptions TranspositionTableOptions { get; set; }
+    IPerftOptions Options { get; set; }
+    IPerftOptions TranspositionTableOptions { get; set; }
 
     Task<int> Run(CancellationToken cancellationToken = default);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/PerftResult.cs b/src/Rudzoft.Perft/Perft/PerftResult.cs
similarity index 95%
rename from src/Rudzoft.Perft/PerftResult.cs
rename to src/Rudzoft.Perft/Perft/PerftResult.cs
index 1767b625..a2527a6d 100644
--- a/src/Rudzoft.Perft/PerftResult.cs
+++ b/src/Rudzoft.Perft/Perft/PerftResult.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
-namespace Rudzoft.Perft;
+namespace Rudzoft.Perft.Perft;
 
 public sealed class PerftResult : IPerftResult
 {
diff --git a/src/Rudzoft.Perft/PerftRunner.cs b/src/Rudzoft.Perft/Perft/PerftRunner.cs
similarity index 83%
rename from src/Rudzoft.Perft/PerftRunner.cs
rename to src/Rudzoft.Perft/Perft/PerftRunner.cs
index 0bd3326e..8ab98e02 100644
--- a/src/Rudzoft.Perft/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Perft/PerftRunner.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
@@ -34,10 +33,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
-using DryIoc;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.ObjectPool;
-using Perft;
 using Rudzoft.ChessLib;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Perft;
@@ -48,7 +45,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.Perft.TimeStamp;
 using Serilog;
 
-namespace Rudzoft.Perft;
+namespace Rudzoft.Perft.Perft;
 
 public sealed class PerftRunner : IPerftRunner
 {
@@ -97,7 +94,6 @@ public PerftRunner(
 
         _runners = new Func<CancellationToken, IAsyncEnumerable<PerftPosition>>[] { ParseEpd, ParseFen };
 
-        TranspositionTableOptions = Framework.IoC.Resolve<IOptions>(OptionType.TTOptions) as TTOptions;
         configuration.Bind("TranspositionTable", TranspositionTableOptions);
 
         _cpu = new CPU();
@@ -105,9 +101,9 @@ public PerftRunner(
 
     public bool SaveResults { get; set; }
 
-    public IOptions Options { get; set; }
+    public IPerftOptions Options { get; set; }
 
-    public TTOptions TranspositionTableOptions { get; set; }
+    public IPerftOptions TranspositionTableOptions { get; set; }
 
     public Task<int> Run(CancellationToken cancellationToken = default) => InternalRun(cancellationToken);
 
@@ -117,8 +113,8 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
 
         InternalRunArgumentCheck(Options);
 
-        if (TranspositionTableOptions.Use)
-            Game.Table.SetSize(TranspositionTableOptions.Size);
+        if (TranspositionTableOptions is TTOptions { Use: true } ttOptions)
+            Game.Table.SetSize(ttOptions.Size);
 
         var errors = 0;
         var runnerIndex = (Options is FenOptions).AsByte();
@@ -135,7 +131,7 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
             try
             {
                 var result = await ComputePerft(cancellationToken).ConfigureAwait(false);
-                errors = result.Errors;
+                Interlocked.Add(ref errors, result.Errors);
                 if (errors != 0)
                     _log.Error("Parsing failed for Id={0}", position.Id);
                 _resultPool.Return(result);
@@ -143,7 +139,7 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
             catch (AggregateException e)
             {
                 _log.Error(e.GetBaseException(), "Cancel requested.");
-                errors = 1;
+                Interlocked.Increment(ref errors);
                 break;
             }
         }
@@ -153,7 +149,7 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
         return errors;
     }
 
-    private static void InternalRunArgumentCheck(IOptions options)
+    private static void InternalRunArgumentCheck(IPerftOptions options)
     {
         if (options == null)
             throw new ArgumentNullException(nameof(options), "Cannot be null");
@@ -237,7 +233,7 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
 
             ComputeResults(perftResult, depth, expected, elapsedMs, result);
 
-            errors += await LogResults(result).ConfigureAwait(false);
+            errors += LogResults(result);
 
             if (baseFileName.IsNullOrEmpty())
                 continue;
@@ -284,41 +280,38 @@ private void LogInfoHeader()
         _log.Information("Initializing..");
     }
 
-    private Task<int> LogResults(IPerftResult result)
+    private int LogResults(IPerftResult result)
     {
-        return Task.Run(() =>
+        _log.Information("Time passed : {0}", result.Elapsed);
+        _log.Information("Nps         : {0}", result.Nps);
+        if (_usingEpd)
         {
-            _log.Information("Time passed : {0}", result.Elapsed);
-            _log.Information("Nps         : {0}", result.Nps);
-            if (_usingEpd)
+            _log.Information("Result      : {0} - should be {1}", result.Result, result.CorrectResult);
+            if (result.Result != result.CorrectResult)
             {
-                _log.Information("Result      : {0} - should be {1}", result.Result, result.CorrectResult);
-                if (result.Result != result.CorrectResult)
-                {
-                    var difference = (long)(result.CorrectResult - result.Result);
-                    _log.Information("Difference  : {0}", Math.Abs(difference));
-                }
+                var difference = (long)(result.CorrectResult - result.Result);
+                _log.Information("Difference  : {0}", Math.Abs(difference));
             }
-            else
-                _log.Information("Result      : {0}", result.Result);
+        }
+        else
+            _log.Information("Result      : {0}", result.Result);
 
-            _log.Information("TT hits     : {0}", Game.Table.Hits);
+        _log.Information("TT hits     : {0}", Game.Table.Hits);
 
-            var error = 0;
+        var error = 0;
 
-            if (!_usingEpd)
-                return error;
+        if (!_usingEpd)
+            return error;
 
-            if (result.CorrectResult == result.Result)
-                _log.Information("Move count matches!");
-            else
-            {
-                _log.Error("Failed for position: {0}", _perft.CurrentGame.Pos.GenerateFen());
-                error = 1;
-            }
+        if (result.CorrectResult == result.Result)
+            _log.Information("Move count matches!");
+        else
+        {
+            _log.Error("Failed for position: {0}", _perft.CurrentGame.Pos.GenerateFen());
+            error = 1;
+        }
 
-            return error;
-        });
+        return error;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.Perft/Program.cs b/src/Rudzoft.Perft/Program.cs
index af66a495..43ab9029 100644
--- a/src/Rudzoft.Perft/Program.cs
+++ b/src/Rudzoft.Perft/Program.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2020 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -24,150 +24,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using CommandLine;
-using DryIoc;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.ObjectPool;
-using Perft;
-using Rudzoft.ChessLib;
-using Rudzoft.ChessLib.Perft.Interfaces;
-using Rudzoft.ChessLib.Protocol.UCI;
-using Rudzoft.ChessLib.Tables;
-using Rudzoft.ChessLib.Types;
-using Rudzoft.Perft.Options;
-using Rudzoft.Perft.Parsers;
-using Rudzoft.Perft.TimeStamp;
-using Serilog;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Rudzoft.Perft.Host;
 
-namespace Rudzoft.Perft;
-
-/*
- *
- *    TODO: Fens to investigate:
- * r2n3r/1bNk2pp/6P1/pP3p2/3pPqnP/1P1P1p1R/2P3B1/Q1B1bKN1 b - e3
- *
- *
- *
- */
-
-internal static class Program
-{
-    private const string ConfigFileName = "appsettings.json";
-
-    static Program()
+var host = new HostBuilder()
+    .UseServiceProviderFactory(new PerftServiceProviderFactory())
+    .ConfigureServices((_, services) =>
     {
-        Framework.Startup(BuildConfiguration, AddServices);
-    }
-
-    public static async Task<int> Main(string[] args)
-    {
-        var optionsUsed = OptionType.None;
-        IOptions options = null;
-        IOptions ttOptions = null;
-
-        var setEdp = new Func<EpdOptions, int>(o =>
-        {
-            optionsUsed |= OptionType.EdpOptions;
-            options = o;
-            return 0;
-        });
-
-        var setFen = new Func<FenOptions, int>(o =>
-        {
-            optionsUsed |= OptionType.FenOptions;
-            options = o;
-            return 0;
-        });
-
-        var setTT = new Func<TTOptions, int>(o =>
-        {
-            optionsUsed |= OptionType.TTOptions;
-            ttOptions = o;
-            return 0;
-        });
-
-        /*
-         * fens -f "rnkq1bnr/p3ppp1/1ppp3p/3B4/6b1/2PQ3P/PP1PPP2/RNB1K1NR w KQ -" -d 6
-         * fens -f "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1" -d 6
-         * epd -f D:\perft-random.epd
-         */
-
-        var returnValue = Parser.Default.ParseArguments<EpdOptions, FenOptions, TTOptions>(args)
-            .MapResult(
-                (EpdOptions opts) => setEdp(opts),
-                (FenOptions opts) => setFen(opts),
-                (TTOptions opts) => setTT(opts),
-                static errs => 1);
-
-        if (returnValue != 0)
-            return returnValue;
-
-        var perftRunner = Framework.IoC.Resolve<IPerftRunner>();
-        perftRunner.Options = options;
-        perftRunner.SaveResults = true;
+        services.AddSingleton(new CommandLineArgs(args));
+        services.AddHostedService<PerftHost>();
+    })
+    .Build();
 
-        return await perftRunner.Run().ConfigureAwait(false);
-    }
-
-    private static void BuildConfiguration(IConfigurationBuilder builder)
-    {
-        // Read the configuration file for this assembly
-        builder.SetBasePath(Directory.GetCurrentDirectory())
-            .AddJsonFile(ConfigFileName);
-    }
-
-    private static void AddServices(IContainer container, IConfiguration configuration)
-    {
-        // Bind logger with configuration
-        container.Register(Made.Of(() => ConfigureLogger(configuration)), Reuse.Singleton);
-
-        // Bind build time stamp class
-        container.Register<IBuildTimeStamp, BuildTimeStamp>(Reuse.Singleton);
-
-        // Bind chess classes
-        container.Register<IGame, Game>(Reuse.Transient);
-        container.Register<IBoard, Board>(Reuse.Singleton);
-        container.Register<IPosition, Position>(Reuse.Transient);
-        container.Register<IKillerMoves, KillerMoves>(Reuse.Transient);
-        container.Register<IUci, Uci>(Reuse.Singleton);
-        container.Register<IPieceValue, PieceValue>(Reuse.Singleton);
-
-        // Bind options
-        container.Register<IOptions, EpdOptions>(Reuse.Singleton, serviceKey: OptionType.EdpOptions);
-        container.Register<IOptions, FenOptions>(Reuse.Singleton, serviceKey: OptionType.FenOptions);
-        container.Register<IOptions, TTOptions>(Reuse.Singleton, serviceKey: OptionType.TTOptions);
-
-        // Bind chess perft classes
-        container.Register<IPerft, ChessLib.Perft.Perft>(Reuse.Transient);
-        container.Register<IPerftRunner, PerftRunner>(Reuse.Transient);
-
-        // Bind perft classes
-        container.Register<IEpdParserSettings, EpdParserSettings>(Reuse.Singleton);
-        container.Register<IEpdSet, EpdSet>(Reuse.Transient);
-        container.Register<IEpdParser, EpdParser>(Reuse.Singleton);
-
-        // Bind object pool for perft result
-        container.Register<ObjectPoolProvider, DefaultObjectPoolProvider>(Reuse.Singleton);
-        container.RegisterDelegate(context =>
-        {
-            var provider = context.Resolve<ObjectPoolProvider>();
-            var policy = new DefaultPooledObjectPolicy<PerftResult>();
-            return provider.Create(policy);
-        });
-    }
-
-    private static ILogger ConfigureLogger(IConfiguration configuration)
-    {
-        // Apply the config to the logger
-        Log.Logger = new LoggerConfiguration()
-            .ReadFrom.Configuration(configuration)
-            .Enrich.WithThreadId()
-            .Enrich.FromLogContext()
-            .CreateLogger();
-        AppDomain.CurrentDomain.ProcessExit += static (s, e) => Log.CloseAndFlush();
-        return Log.Logger;
-    }
-}
\ No newline at end of file
+host.Run();
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index 1e82b235..e8f32c1e 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -36,6 +36,7 @@
   <ItemGroup>
     <PackageReference Include="CommandLineParser" Version="2.9.1" />
     <PackageReference Include="ConsoleGUI" Version="1.4.1" />
+    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.2" />
     <PackageReference Include="Serilog" Version="2.12.0" />
     <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
diff --git a/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs b/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
index b4a03e7c..f020a7ec 100644
--- a/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
+++ b/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Linq;
 using System.Reflection;
 
@@ -34,9 +33,9 @@ internal sealed class BuildTimeStamp : IBuildTimeStamp
 {
     private const string AttributeName = "TimestampAttribute";
 
-    private static readonly Lazy<string> _timeStamp = new(GetTimestamp);
+    private static readonly Lazy<string> LazyTimeStamp = new(GetTimestamp);
 
-    public string TimeStamp => _timeStamp.Value;
+    public string TimeStamp => LazyTimeStamp.Value;
 
     private static string GetTimestamp()
     {
diff --git a/src/Rudzoft.Perft/TimeStamp/IBuildTimeStamp.cs b/src/Rudzoft.Perft/TimeStamp/IBuildTimeStamp.cs
index d6b2bcd6..e2f10b56 100644
--- a/src/Rudzoft.Perft/TimeStamp/IBuildTimeStamp.cs
+++ b/src/Rudzoft.Perft/TimeStamp/IBuildTimeStamp.cs
@@ -3,7 +3,7 @@
 
 MIT License
 
-Copyright (c) 2019-2022 Rudy Alex Kohn
+Copyright (c) 2019-2023 Rudy Alex Kohn
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

From 150faf65b10c08f5927a2d713b306bd844ad702f Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 18 Feb 2023 09:27:01 +0100
Subject: [PATCH 010/119] HiResTimer no longer computes delay if never going to
 be needed

---
 src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
index 374b100d..b132bf42 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
@@ -200,8 +200,11 @@ private void ExecuteTimer(CancellationToken cancellationToken)
                     return;
             } while (true);
 
-            var delay = elapsed - nextTrigger;
-            Elapsed?.Invoke(new HiResTimerArgs(delay, Id));
+            if (Elapsed != null)
+            {
+                var delay = elapsed - nextTrigger;
+                Elapsed(new HiResTimerArgs(delay, Id));
+            }
 
             if (cancellationToken.IsCancellationRequested)
                 return;

From b0082a8cc10d186c5283ba8ab25f99e62901da0c Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 18 Feb 2023 09:27:18 +0100
Subject: [PATCH 011/119] Updated KillerMoves to jagged array

---
 src/Rudzoft.ChessLib/Tables/KillerMoves.cs | 46 +++++++++++-----------
 1 file changed, 24 insertions(+), 22 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
index 2754b8d6..855faad3 100644
--- a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
@@ -34,7 +34,7 @@ public sealed class KillerMoves : IKillerMoves
 {
     private static readonly PieceSquare EmptyPieceSquare = new(Piece.EmptyPiece, Square.None);
     
-    private readonly PieceSquare[,] _killerMoves;
+    private readonly PieceSquare[][] _killerMoves;
     private readonly int _maxDepth;
 
     public static IKillerMoves Create(int maxDepth)
@@ -43,15 +43,17 @@ public static IKillerMoves Create(int maxDepth)
     private KillerMoves(int maxDepth)
     {
         _maxDepth = maxDepth + 1;
-        _killerMoves = new PieceSquare[_maxDepth, 2];
+        _killerMoves = new PieceSquare[_maxDepth][];
+        for (var i = 0; i < _killerMoves.Length; ++i)
+            _killerMoves[i] = new[] { EmptyPieceSquare, EmptyPieceSquare };
     }
 
     private IKillerMoves Initialize()
     {
         for (var depth = 0; depth < _maxDepth; depth++)
         {
-            _killerMoves[depth, 0] = EmptyPieceSquare;
-            _killerMoves[depth, 1] = EmptyPieceSquare;
+            _killerMoves[depth][0] = EmptyPieceSquare;
+            _killerMoves[depth][1] = EmptyPieceSquare;
         }
 
         Reset();
@@ -59,20 +61,19 @@ private IKillerMoves Initialize()
     }
 
     public int GetValue(int depth, Move m, Piece fromPc)
-        => Equals(in _killerMoves[depth, 0], m, fromPc)
+        => Equals(in _killerMoves[depth][0], m, fromPc)
             ? 2
-            : Equals(in _killerMoves[depth, 1], m, fromPc).AsByte();
+            : Equals(in _killerMoves[depth][1], m, fromPc).AsByte();
 
     public void UpdateValue(int depth, Move m, Piece fromPc)
     {
-        if (Equals(in _killerMoves[depth, 0], m, fromPc))
+        if (Equals(in _killerMoves[depth][0], m, fromPc))
             return;
 
         // Shift killer move.
-        _killerMoves[depth, 1] = _killerMoves[depth, 0];
+        _killerMoves[depth][1] = _killerMoves[depth][0];
         // Update killer move.
-        _killerMoves[depth, 0].Piece = fromPc;
-        _killerMoves[depth, 0].Square = m.ToSquare();
+        _killerMoves[depth][0] = new PieceSquare(fromPc, m.ToSquare());
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -80,7 +81,7 @@ private static bool Equals(in PieceSquare killerMove, Move m, Piece fromPc)
         => killerMove.Piece == fromPc && killerMove.Square == m.ToSquare();
 
     public ref PieceSquare Get(int depth, int index)
-        => ref _killerMoves[depth, index];
+        => ref _killerMoves[depth][index];
 
     public void Shift(int depth)
     {
@@ -88,15 +89,15 @@ public void Shift(int depth)
         var lastDepth = _killerMoves.Length - depth - 1;
         for (var i = 0; i <= lastDepth; i++)
         {
-            _killerMoves[i, 0] = _killerMoves[i + depth, 0];
-            _killerMoves[i, 1] = _killerMoves[i + depth, 1];
+            _killerMoves[i][0] = _killerMoves[i + depth][0];
+            _killerMoves[i][1] = _killerMoves[i + depth][1];
         }
 
         // Reset killer moves far from root position.
         for (var i = lastDepth + 1; i < _killerMoves.Length; i++)
         {
-            _killerMoves[i, 0] = EmptyPieceSquare;
-            _killerMoves[i, 1] = EmptyPieceSquare;
+            _killerMoves[i][0] = EmptyPieceSquare;
+            _killerMoves[i][1] = EmptyPieceSquare;
         }
     }
 
@@ -104,8 +105,8 @@ public void Reset()
     {
         for (var i = 0; i < _maxDepth; i++)
         {
-            _killerMoves[i, 0] = EmptyPieceSquare;
-            _killerMoves[i, 1] = EmptyPieceSquare;
+            _killerMoves[i][0] = EmptyPieceSquare;
+            _killerMoves[i][1] = EmptyPieceSquare;
         }
     }
 
@@ -113,13 +114,14 @@ public bool Equals(IKillerMoves other)
     {
         if (other is null) return false;
         if (ReferenceEquals(this, other)) return true;
-        for (var j = 0; j < _maxDepth; ++j)
+        for (var i = 0; i < _maxDepth; ++i)
         {
-            var otherKm = other.Get(j, 0);
-            if (otherKm.Piece != _killerMoves[j, 0].Piece || otherKm.Square != _killerMoves[j, 0].Square)
+            ref var otherKm = ref other.Get(i, 0);
+            if (_killerMoves[i][0] != otherKm)
                 return false;
-            otherKm = other.Get(j, 1);
-            if (otherKm.Piece != _killerMoves[j, 1].Piece || otherKm.Square != _killerMoves[j, 1].Square)
+
+            otherKm = ref other.Get(i, 1);
+            if (_killerMoves[i][1] != otherKm)
                 return false;
         }
 

From 74088322504d7332f848e7107bfc3473806f586f Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 18 Feb 2023 09:28:19 +0100
Subject: [PATCH 012/119] Various minor code updates

---
 .../PiecesTests/PieceAttacksKingTests.cs      | 29 +++++++++++--------
 .../ProtocolTests/OptionsTests.cs             | 11 ++-----
 .../Rudzoft.ChessLib.Test.csproj              |  1 -
 src/Rudzoft.ChessLib/Board.cs                 |  2 +-
 src/Rudzoft.ChessLib/Position.cs              |  2 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |  6 ++--
 src/Rudzoft.ChessLib/Types/Depth.cs           |  2 +-
 src/Rudzoft.ChessLib/Types/Direction.cs       |  2 --
 src/Rudzoft.ChessLib/Types/File.cs            |  2 +-
 src/Rudzoft.ChessLib/Types/Piece.cs           |  1 -
 src/Rudzoft.ChessLib/Types/Score.cs           |  5 ++++
 src/Rudzoft.ChessLib/Types/Values.cs          |  2 +-
 .../Rudzoft.Perft.Framework.csproj            |  2 +-
 .../Host/PerftServiceProviderFactory.cs       |  2 +-
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |  2 +-
 15 files changed, 36 insertions(+), 35 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
index f4f5cd99..3af43c83 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
@@ -25,7 +25,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Linq;
-using FluentAssertions;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
@@ -65,10 +64,12 @@ public void BetaPattern()
     {
         const int index = (int)EBands.Beta;
         const int attackIndex = 2;
-        var expected = _fixture.KingExpected[index];
-        var actuals = Bands[index].Select(x => _fixture.RegAttacks[attackIndex](x).Count);
-
-        actuals.Should().AllBeEquivalentTo(expected);
+        var bands = Bands[index];
+        var expected = _fixture.KingExpected[index] * bands.Count;
+        var actuals = bands.Select(x => _fixture.RegAttacks[attackIndex](x).Count);
+        var actual = actuals.Sum();
+        
+        Assert.Equal(expected, actual);
     }
 
     [Fact]
@@ -76,10 +77,12 @@ public void GammaPattern()
     {
         const int index = (int)EBands.Gamma;
         const int attackIndex = 2;
-        var expected = _fixture.KingExpected[index];
-        var actuals = Bands[index].Select(x => _fixture.RegAttacks[attackIndex](x).Count);
+        var band = Bands[index];
+        var expected = _fixture.KingExpected[index] * band.Count;
+        var actuals = band.Select(x => _fixture.RegAttacks[attackIndex](x).Count);
+        var actual = actuals.Sum();
 
-        actuals.Should().AllBeEquivalentTo(expected);
+        Assert.Equal(expected, actual);
     }
 
     [Fact]
@@ -87,9 +90,11 @@ public void DeltaPattern()
     {
         const int index = (int)EBands.Delta;
         const int attackIndex = 2;
-        var expected = _fixture.KingExpected[index];
-        var actuals = Bands[index].Select(x => _fixture.RegAttacks[attackIndex](x).Count);
-
-        actuals.Should().AllBeEquivalentTo(expected);
+        var band = Bands[index];
+        var expected = _fixture.KingExpected[index] * band.Count;
+        var actuals = band.Select(x => _fixture.RegAttacks[attackIndex](x).Count);
+        var actual = actuals.Sum();
+        
+        Assert.Equal(expected, actual);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
index 622f2f95..98273d66 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
@@ -31,9 +31,9 @@ namespace Rudzoft.ChessLib.Test.ProtocolTests;
 public class OptionsTests
 {
     [Theory]
-    [InlineData("Boolean Test", true, "true", true, "option name Boolean Test type Check default true")]
-    [InlineData("Boolean Test", false, "false", false, "option name Boolean Test type Check default false")]
-    public void Boolean(string name, bool value, string expectedText, bool expected, string uciString)
+    [InlineData("Boolean Test", true, true, "option name Boolean Test type Check default true")]
+    [InlineData("Boolean Test", false, false, "option name Boolean Test type Check default false")]
+    public void Boolean(string name, bool value, bool expected, string uciString)
     {
         IUci uci = new Uci();
         
@@ -42,10 +42,6 @@ public void Boolean(string name, bool value, string expectedText, bool expected,
         var option = new Option(name, 0, value);
         uci.AddOption(name, option);
 
-        var actualText = option.GetText();
-        
-        Assert.Equal(expectedText, actualText);
-
         var actual = option.GetBool();
         
         Assert.Equal(expected, actual);
@@ -55,5 +51,4 @@ public void Boolean(string name, bool value, string expectedText, bool expected,
         
         Assert.Contains(uciString, t);
     }
-    
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 5f01cd2f..d44cf703 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -26,7 +26,6 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="FluentAssertions" Version="6.10.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.analyzers" Version="1.1.0" />
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index 100b663a..d62d423e 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -69,7 +69,7 @@ public void Clear()
     {
         Array.Fill(_pieces, Piece.EmptyPiece);
         _bySide[0] = _bySide[1] = BitBoard.Empty;
-        _byType.Fill(in BitBoard.Empty);
+        _byType.Fill(in _bySide[0]);
         Array.Fill(_pieceCount, 0);
         foreach (var s in _pieceList)
             Array.Fill(s, Types.Square.None);
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 2c3d0c7b..6c23992d 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1476,7 +1476,7 @@ private void SetState()
 
         var materialKey = HashKey.Empty;
         ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.AllPieces);
-        for (int i = 0; i < Piece.AllPieces.Length; i++)
+        for (var i = 0; i < Piece.AllPieces.Length; i++)
         {
             var pc = Unsafe.Add(ref piecesSpace, i);
             for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index 7fdcd397..a55581bd 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -59,11 +59,11 @@ private BitBoard(int value)
 
     public bool IsEmpty => Value == 0;
 
-    public static readonly BitBoard Empty = BitBoards.EmptyBitBoard;
+    public static BitBoard Empty => BitBoards.EmptyBitBoard;
 
-    public static BitBoard MaxValue { get; } = BitBoards.EmptyBitBoard;
+    public static BitBoard MaxValue => BitBoards.EmptyBitBoard;
 
-    public static BitBoard MinValue { get; } = BitBoards.AllSquares;
+    public static BitBoard MinValue => BitBoards.AllSquares;
 
     public string String => Convert.ToString((long)Value, 2).PadLeft(64, '0');
 
diff --git a/src/Rudzoft.ChessLib/Types/Depth.cs b/src/Rudzoft.ChessLib/Types/Depth.cs
index 688bb37a..3562a8a3 100644
--- a/src/Rudzoft.ChessLib/Types/Depth.cs
+++ b/src/Rudzoft.ChessLib/Types/Depth.cs
@@ -40,7 +40,7 @@ public enum Depths
     QsRecap = -5,
     None = -6,
     Offset = None - 1,
-    MaxPly = 256 + Offset - 4, // Used only for TT entry occupancy check
+    MaxPly = 256 + Offset - 4 // Used only for TT entry occupancy check
 }
 
 public static class DepthsExtensions
diff --git a/src/Rudzoft.ChessLib/Types/Direction.cs b/src/Rudzoft.ChessLib/Types/Direction.cs
index 6d070dc2..f2bfaa5b 100644
--- a/src/Rudzoft.ChessLib/Types/Direction.cs
+++ b/src/Rudzoft.ChessLib/Types/Direction.cs
@@ -47,8 +47,6 @@ public enum Directions
     SouthFill = -NorthFill
 }
 
-public record struct BiDirection(Direction One, Direction Two);
-
 public readonly record struct Direction(Directions Value)
 {
     public static Direction North { get; } = new(Directions.North);
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index d2201e11..a0d999de 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -65,7 +65,7 @@ public readonly record struct File(Files Value) : IComparable<File>, ISpanFormat
     public static File FileF { get; } = new(Files.FileF);
     public static File FileG { get; } = new(Files.FileG);
     public static File FileH { get; } = new(Files.FileH);
-    public static File[] AllFiles { get; } = new[] { FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH };
+    public static File[] AllFiles { get; } = { FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public File(int file)
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 5bda6f7f..d15cf2c9 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -110,7 +110,6 @@ private Piece(Piece pc)
     public static Piece BlackQueen { get; } = new(Pieces.BlackQueen);
     public static Piece BlackKing { get; } = new(Pieces.BlackKing);
 
-
     public static Piece[] AllPieces { get; } =
     {
         Pieces.WhitePawn,
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index 65d1dfe9..2353968d 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -143,4 +143,9 @@ public readonly override int GetHashCode()
 
     public static bool operator !=(Score left, Score right)
         => !(left == right);
+
+    public Score Negate()
+    {
+        return -_data;
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Values.cs b/src/Rudzoft.ChessLib/Types/Values.cs
index 7843d8c6..3375a4af 100644
--- a/src/Rudzoft.ChessLib/Types/Values.cs
+++ b/src/Rudzoft.ChessLib/Types/Values.cs
@@ -34,7 +34,7 @@ namespace Rudzoft.ChessLib.Types;
 public enum DefaultPieceValues
 {
     ValueZero = 0,
-    ValueDraw = 0,
+    ValueDraw = ValueZero,
     ValueKnownWin = 10000,
     ValueMate = 32000,
     ValueInfinite = 32001,
diff --git a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
index 7e6c5377..acd99a98 100644
--- a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
+++ b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
@@ -13,7 +13,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="6.1.0" />
+    <PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="6.1.1" />
     <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
diff --git a/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs b/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs
index b623e845..bebf5214 100644
--- a/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs
+++ b/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs
@@ -52,7 +52,7 @@ public Container CreateBuilder(IServiceCollection services)
         var configBuilder = ConfigurationBuilder();
         var configuration = ConfigurationFactory.CreateConfiguration(configBuilder);
         
-        var container = new Container(Rules.MicrosoftDependencyInjectionRules.WithConcreteTypeDynamicRegistrations(reuse: Reuse.Transient));
+        var container = new Container(Rules.MicrosoftDependencyInjectionRules);
         
         // Construct a configuration binding
         container.RegisterInstance(configuration);
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index e8f32c1e..c4b791a8 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -36,7 +36,7 @@
   <ItemGroup>
     <PackageReference Include="CommandLineParser" Version="2.9.1" />
     <PackageReference Include="ConsoleGUI" Version="1.4.1" />
-    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
     <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.2" />
     <PackageReference Include="Serilog" Version="2.12.0" />
     <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />

From 46a16d6e223ef85be68a64e669fad1a84275570a Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 18 Feb 2023 19:24:03 +0100
Subject: [PATCH 013/119] Improved HiResTimer a bit

---
 .../Protocol/UCI/HiResTimer.cs                 | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
index b132bf42..863c6ade 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
@@ -81,7 +81,7 @@ public float Interval
 
         set
         {
-            if (value < 0f || float.IsNaN(value))
+            if (value is < 0f or float.NaN)
                 throw new ArgumentOutOfRangeException(nameof(value));
             lock (_intervalLock)
                 _interval = value;
@@ -155,18 +155,16 @@ public override bool Equals(object obj)
         return obj.GetType() == GetType() && Equals((HiResTimer)obj);
     }
 
-    public bool Equals(HiResTimer other) => Id == other.Id;
+    public bool Equals(HiResTimer other) => Id == other?.Id;
 
-    private static double ElapsedHiRes(Stopwatch stopwatch) => stopwatch.ElapsedTicks * TickLength;
+    private static double ElapsedHiRes(in long timeStamp) => Stopwatch.GetElapsedTime(timeStamp).Ticks * TickLength;
 
     private void ExecuteTimer(CancellationToken cancellationToken)
     {
         Debug.Print($"Timer ExecuteTimer on thread {Environment.CurrentManagedThreadId}");
 
         var nextTrigger = 0f;
-
-        var stopwatch = new Stopwatch();
-        stopwatch.Start();
+        var timeStamp = Stopwatch.GetTimestamp();
 
         while (!cancellationToken.IsCancellationRequested)
         {
@@ -175,7 +173,7 @@ private void ExecuteTimer(CancellationToken cancellationToken)
 
             do
             {
-                elapsed = ElapsedHiRes(stopwatch);
+                elapsed = ElapsedHiRes(in timeStamp);
                 var diff = nextTrigger - elapsed;
                 if (diff <= 0f)
                     break;
@@ -210,12 +208,10 @@ private void ExecuteTimer(CancellationToken cancellationToken)
                 return;
 
             // restarting the timer in every hour to prevent precision problems
-            if (stopwatch.Elapsed.TotalHours < 1d)
+            if (Stopwatch.GetElapsedTime(timeStamp).TotalHours < 1d)
                 continue;
-            stopwatch.Restart();
+            timeStamp = Stopwatch.GetTimestamp();
             nextTrigger = 0f;
         }
-
-        stopwatch.Stop();
     }
 }

From 8bd0fcf3538608c0cad6aea6e221c55e1e8d01ec Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 19 Feb 2023 09:57:43 +0100
Subject: [PATCH 014/119] Reworked basic setup to be IoC friendly

- Added AddChessLib() IServiceCollection extension
- More types are now more friendly towards IoC in general
- Any other developmental changes can be observed through the unit tests
- Improved use of PolyglotBook, Blockage
- Added IOptions usage for TT
- Added PolyglotBook injectable factory with IOptions injected for base path
- Perft tests that takes a long time is now disabled in debug mode
- Perft time improved.. from ~3.2-3.4 seconds to ~3.0 seconds for start pos d=6
- Updated WebApi demo app
---
 src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs  |   1 -
 .../CharConvertBenchmark.cs                   |   1 -
 .../FenBenchmark.cs                           |  14 +-
 .../IsNumericBenchmark.cs                     |   4 +-
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |  51 ++--
 .../{PerftBenchRunner.cs => Program.cs}       |   4 +-
 .../Rudzoft.ChessLib.Benchmark.csproj         |   2 +-
 .../IPerft.cs                                 |   4 +-
 .../PerftPosition.cs                          |   2 +-
 src/Rudzoft.ChessLib.Perft/Perft.cs           |  23 +-
 src/Rudzoft.ChessLib.Perft/PerftTable.cs      |  30 ++-
 .../Rudzoft.ChessLib.Perft.csproj             |   2 +
 .../BoardTests/BoardTests.cs                  |  25 +-
 .../BookTests/PolyglotTests.cs                |  99 ++++++--
 .../CastleTests/BasicCastleTests.cs           |  28 ++-
 .../EvaluationTests/KpkBitBaseTests.cs        |  45 +++-
 .../FENTests/FenTests.cs                      |  42 +++-
 .../FenceTests/FenceTests.cs                  |  27 +-
 .../GameplayTests/FoolsCheckMateTests.cs      |  27 +-
 .../MoveTests/MoveGeneratorTests.cs           |  23 +-
 .../MoveTests/MoveTests.cs                    |  37 ++-
 .../MoveTests/PerftTest.cs                    |  40 ++-
 .../MoveTests/PerftVerify.cs                  |  44 ++++
 .../NotationTests/FanTests.cs                 |  40 ++-
 .../NotationTests/IccfTests.cs                |  38 ++-
 .../NotationTests/RanTests.cs                 |  28 ++-
 .../NotationTests/SanTests.cs                 |  76 ++++--
 .../PiecesTests/PawnDoubleAttackTests.cs      |  27 +-
 .../PositionTests/EnPassantFenTests.cs        |  26 +-
 .../PositionTests/PositionTests.cs            |  71 +++---
 .../PositionTests/ValidationTests.cs          |  39 ++-
 .../ProtocolTests/OptionsTests.cs             |   2 +-
 .../ProtocolTests/UciTests.cs                 |  42 +++-
 .../Rudzoft.ChessLib.Test.csproj              |   1 +
 src/Rudzoft.ChessLib.WebApi/Moves.cs          |   2 +-
 src/Rudzoft.ChessLib.WebApi/Program.cs        |  10 +-
 .../Queries/MoveQuery.cs                      |   2 +-
 .../Rudzoft.ChessLib.WebApi.csproj            |  10 +
 .../Services/MoveGeneratorService.cs          |  18 +-
 src/Rudzoft.ChessLib/Blockage.cs              | 236 +++++++++---------
 .../Evaluation/IKpkBitBase.cs                 |  63 +++++
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs | 112 +++------
 .../ChessLibServiceCollectionExtensions.cs    | 104 ++++++++
 .../Extensions/StringExtensions.cs            |   2 +-
 ...kageFactory.cs => IPolyglotBookFactory.cs} |  13 +-
 .../{GameFactory.cs => IServiceFactory.cs}    |  21 +-
 .../Factories/PolyglotBookFactory.cs          |  54 ++++
 .../Factories/ServiceFactory.cs}              |  18 +-
 src/Rudzoft.ChessLib/Game.cs                  |  38 +--
 .../Transposition/TranspositionTable.cs       |   7 +-
 .../TranspositionTableConfiguration.cs        |  37 +++
 src/Rudzoft.ChessLib/IBlockage.cs             |   2 +-
 src/Rudzoft.ChessLib/IGame.cs                 |   2 +-
 .../Polyglot/IPolyglotBook.cs                 |  37 +++
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |  74 +++---
 .../Polyglot/PolyglotBookConfiguration.cs     |  35 +++
 .../Polyglot/PolyglotBookZobrist.cs           |   1 +
 src/Rudzoft.ChessLib/Position.cs              |  87 ++++---
 .../Protocol/UCI/ISearchParameters.cs         |   2 +
 src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs     |  16 +-
 .../Protocol/UCI/SearchParameters.cs          |   6 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |  21 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |   9 +-
 .../Host/PerftServiceProviderFactory.cs       | 138 ----------
 src/Rudzoft.Perft/Perft/PerftRunner.cs        |  19 +-
 src/Rudzoft.Perft/Program.cs                  |  69 ++++-
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |   2 +-
 67 files changed, 1471 insertions(+), 761 deletions(-)
 rename src/Rudzoft.ChessLib.Benchmark/{PerftBenchRunner.cs => Program.cs} (97%)
 create mode 100644 src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
 create mode 100644 src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs
 create mode 100644 src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
 rename src/Rudzoft.ChessLib/Factories/{BlockageFactory.cs => IPolyglotBookFactory.cs} (85%)
 rename src/Rudzoft.ChessLib/Factories/{GameFactory.cs => IServiceFactory.cs} (68%)
 create mode 100644 src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
 rename src/{Rudzoft.ChessLib.Perft/PerftFactory.cs => Rudzoft.ChessLib/Factories/ServiceFactory.cs} (69%)
 create mode 100644 src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
 create mode 100644 src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
 create mode 100644 src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs
 delete mode 100644 src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs b/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
index 975ba0f3..fd5d1041 100644
--- a/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
@@ -1,6 +1,5 @@
 using System.Collections.Generic;
 using System.Numerics;
-using BenchmarkDotNet.Attributes;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
diff --git a/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
index a3c7279f..efae835a 100644
--- a/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
@@ -1,6 +1,5 @@
 using System.Collections.Generic;
 using System.Linq;
-using BenchmarkDotNet.Attributes;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
diff --git a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
index 46510adc..033cdd61 100644
--- a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
@@ -1,5 +1,4 @@
-using BenchmarkDotNet.Attributes;
-using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Types;
 
@@ -10,7 +9,7 @@ public class FenBenchmark
 {
     private const string F = "rnkq1bnr/p3ppp1/1ppp3p/3B4/6b1/2PQ3P/PP1PPP2/RNB1K1NR w KQ 4 10";
 
-    private IGame _game;
+    private IPosition _pos;
 
     [Params(10000, 50000)]
     public int N;
@@ -20,11 +19,10 @@ public void Setup()
     {
         var board = new Board();
         var pieceValue = new Values();
-        var pos = new Position(board, pieceValue);
-        _game = new Game(pos);
         var fp = new FenData(F);
         var state = new State();
-        _game.Pos.Set(in fp, ChessMode.Normal, state);
+        _pos = new Position(board, pieceValue);
+        _pos.Set(in fp, ChessMode.Normal, state);
     }
 
     [Benchmark(Description = "StringBuilder - NOT PRESENT")]
@@ -32,7 +30,7 @@ public void GetFen()
     {
         for (var i = 0; i < N; ++i)
         {
-            var fd = _game.Pos.GenerateFen();
+            var fd = _pos.GenerateFen();
         }
     }
 
@@ -41,7 +39,7 @@ public void GetFenOp()
     {
         for (var i = 0; i < N; ++i)
         {
-            var fd = _game.Pos.GenerateFen();
+            var fd = _pos.GenerateFen();
         }
     }
 }
diff --git a/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
index 22453fc5..3d344143 100644
--- a/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
@@ -1,8 +1,6 @@
 using Rudzoft.ChessLib.Extensions;
 using System;
 using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
@@ -25,7 +23,7 @@ public void MathExtInBetween()
     {
         foreach (var c in _s.AsSpan())
         {
-            var b = MathExtensions.InBetween(c, '0', '9');
+            var b = c.InBetween('0', '9');
         }
     }
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index a50bf1fc..c41c08d7 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -27,8 +27,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Collections.Generic;
 using System.Threading.Tasks;
+using Microsoft.Extensions.Options;
+using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.Perft;
 using Rudzoft.ChessLib.Perft.Interfaces;
+using Rudzoft.ChessLib.Protocol.UCI;
+using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
@@ -38,22 +42,9 @@ public class PerftBench
 {
     private IPerft _perft;
 
-    private readonly PerftPosition _pp;
-
     public PerftBench()
     {
-        _pp = PerftPositionFactory.Create(
-            Guid.NewGuid().ToString(),
-            Fen.Fen.StartPositionFen,
-            new List<PerftPositionValue>(6)
-            {
-                    new(1, 20),
-                    new(2, 400),
-                    new(3, 8902),
-                    new(4, 197281),
-                    new(5, 4865609),
-                    new(6, 119060324)
-            });
+
     }
 
     [Params(5, 6)]
@@ -62,8 +53,35 @@ public PerftBench()
     [GlobalSetup]
     public void Setup()
     {
-        _perft = PerftFactory.Create();
-        _perft.AddPosition(_pp);
+        
+        var pp = PerftPositionFactory.Create(
+            Guid.NewGuid().ToString(),
+            Fen.Fen.StartPositionFen,
+            new List<PerftPositionValue>(6)
+            {
+                new(1, 20),
+                new(2, 400),
+                new(3, 8902),
+                new(4, 197281),
+                new(5, 4865609),
+                new(6, 119060324)
+            });
+        
+        var ttConfig = new TranspositionTableConfiguration { DefaultSize = 1 };
+        var options = Options.Create(ttConfig);
+        var tt = new TranspositionTable(options);
+
+        var uci = new Uci();
+        uci.Initialize();
+
+        var sp = new SearchParameters();
+        
+        var board = new Board();
+        var values = new Values();
+        var pos = new Position(board, values);
+        
+        var game = new Game(tt, uci, sp, pos);
+        _perft = new Perft.Perft(game, new []{ pp });
     }
 
     [Benchmark]
@@ -85,5 +103,4 @@ public async Task<ulong> Perft()
 
         return total;
     }
-
 }
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBenchRunner.cs b/src/Rudzoft.ChessLib.Benchmark/Program.cs
similarity index 97%
rename from src/Rudzoft.ChessLib.Benchmark/PerftBenchRunner.cs
rename to src/Rudzoft.ChessLib.Benchmark/Program.cs
index e4eb483a..f91602ae 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBenchRunner.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/Program.cs
@@ -92,7 +92,7 @@ Result	3	379.0 us	1.897 us	1.584 us
 
 */
 
-public static class PerftBenchRunner
+public static class Program
 {
-    public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(PerftBenchRunner).Assembly).Run(args);
+    public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
 }
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 564edc4c..5a045d92 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -12,7 +12,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.13.4" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
     <PackageReference Include="System.Runtime" Version="4.3.1" />
   </ItemGroup>
 
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
index cc6e3c07..d8d9e006 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
@@ -34,13 +34,13 @@ public interface IPerft
 {
     Action<string> BoardPrintCallback { get; set; }
 
-    IList<PerftPosition> Positions { get; set; }
+    List<PerftPosition> Positions { get; set; }
 
     int Depth { get; set; }
 
     ulong Expected { get; set; }
 
-    public IGame CurrentGame { get; set; }
+    public IGame Game { get; set; }
 
     IAsyncEnumerable<ulong> DoPerft(int depth);
 
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs b/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
index f33cfaaa..32a32c98 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
@@ -30,4 +30,4 @@ namespace Rudzoft.ChessLib.Perft.Interfaces;
 
 public record struct PerftPositionValue(int Depth, ulong MoveCount);
 
-public sealed record PerftPosition(string Id, string Fen, IList<PerftPositionValue> Value);
+public sealed record PerftPosition(string Id, string Fen, List<PerftPositionValue> Value);
diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/Rudzoft.ChessLib.Perft/Perft.cs
index 4f8c8392..2bd13469 100644
--- a/src/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/Rudzoft.ChessLib.Perft/Perft.cs
@@ -59,17 +59,17 @@ public sealed class Perft : IPerft
     public Perft(IGame game, IEnumerable<PerftPosition> positions)
     {
         Positions = positions.ToList();
-        CurrentGame = game;
+        Game = game;
     }
 
-    public Action<string> BoardPrintCallback { get; set; }
+    public Action<string>? BoardPrintCallback { get; set; }
 
     /// <summary>
     /// The positional data for the run
     /// </summary>
-    public IList<PerftPosition> Positions { get; set; }
+    public List<PerftPosition> Positions { get; set; }
 
-    public IGame CurrentGame { get; set; }
+    public IGame Game { get; set; }
     public int Depth { get; set; }
     public ulong Expected { get; set; }
 
@@ -83,16 +83,15 @@ public async IAsyncEnumerable<ulong> DoPerft(int depth)
 
         foreach (var fd in Positions.Select(static p => new FenData(p.Fen)))
         {
-            Game.Table.NewSearch();
             var state = new State();
-            CurrentGame.Pos.Set(in fd, ChessMode.Normal, state);
+            Game.Pos.Set(in fd, ChessMode.Normal, state);
 
-            if (PerftTable.Retrieve(CurrentGame.Pos.State.Key, depth, out var result))
+            if (PerftTable.Retrieve(Game.Pos.State.Key, depth, out var result))
                 yield return result;
             else
             {
-                result = CurrentGame.Perft(depth);
-                PerftTable.Store(CurrentGame.Pos.State.Key, depth, result);
+                result = Game.Perft(depth);
+                PerftTable.Store(Game.Pos.State.Key, depth, result);
                 yield return result;
             }
         }
@@ -100,16 +99,16 @@ public async IAsyncEnumerable<ulong> DoPerft(int depth)
 
     public Task<ulong> DoPerftAsync(int depth)
         => Task.Run(()
-            => CurrentGame.Perft(depth));
+            => Game.Perft(depth));
 
     public string GetBoard()
-        => CurrentGame.ToString();
+        => Game.ToString();
 
     public void SetGamePosition(PerftPosition pp)
     {
         var fp = new FenData(pp.Fen);
         var state = new State();
-        CurrentGame.Pos.Set(in fp, ChessMode.Normal, state);
+        Game.Pos.Set(in fp, ChessMode.Normal, state);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib.Perft/PerftTable.cs b/src/Rudzoft.ChessLib.Perft/PerftTable.cs
index 92ef9058..ce19cb59 100644
--- a/src/Rudzoft.ChessLib.Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib.Perft/PerftTable.cs
@@ -31,11 +31,20 @@ namespace Rudzoft.ChessLib.Perft;
 
 internal static class PerftTable
 {
-    private const int EntrySize = 24;
     private const int HashMemory = 256;
-    private const int TtSize = HashMemory * 1024 * 1024 / EntrySize;
-    private static readonly PerftHashEntry[] Table = new PerftHashEntry[TtSize];
+    private static readonly ulong TtSize;
+    private static readonly PerftHashEntry[] Table;
 
+    static PerftTable()
+    {
+        unsafe
+        {
+            var entrySize = sizeof(PerftHashEntry);
+            TtSize = (ulong)(HashMemory * 1024 * 1024 / entrySize);
+            Table = new PerftHashEntry[TtSize];
+        }
+    }
+    
     private struct PerftHashEntry
     {
         public HashKey Hash;
@@ -46,22 +55,23 @@ private struct PerftHashEntry
     public static void Store(in HashKey key, int depth, in ulong childCount)
     {
         var slot = (int)(key.Key % TtSize);
-        Table[slot].Hash = key;
-        Table[slot].Count = childCount;
-        Table[slot].Depth = depth;
+        ref var entry = ref Table[slot];
+        entry.Hash = key;
+        entry.Count = childCount;
+        entry.Depth = depth;
     }
 
     public static bool Retrieve(in HashKey key, int depth, out ulong childCount)
     {
         var slot = (int)(key.Key % TtSize);
-        var match = Table[slot].Depth == depth && Table[slot].Hash == key;
-
-        childCount = match ? Table[slot].Count : 0;
+        ref var entry = ref Table[slot];
+        var match = entry.Depth == depth && entry.Hash == key;
+        childCount = match ? entry.Count : 0;
         return match;
     }
 
     public static void Clear()
     {
-        Array.Clear(Table, 0, TtSize);
+        Array.Clear(Table, 0, (int)TtSize);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
index 1bfd5451..9820e964 100644
--- a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
+++ b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
@@ -3,6 +3,8 @@
   <PropertyGroup>
     <TargetFramework>net7.0</TargetFramework>
     <LangVersion>default</LangVersion>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <Nullable>enable</Nullable>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 3c25dffb..5784e4f0 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -24,13 +24,27 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Factories;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.BoardTests;
 
 public sealed class BoardTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public BoardTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+    
     [Theory]
     [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Pawn, Players.White, 8)]
     [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Pawn, Players.Black, 8)]
@@ -58,8 +72,13 @@ public sealed class BoardTests
     [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.King, Players.Black, 1)]
     public void BoardPieceCount(string fen, PieceTypes pt, Player p, int expected)
     {
-        var game = GameFactory.Create(fen);
-        var pos = game.Pos;
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
         var board = pos.Board;
 
         var posCount = pos.Pieces(pt, p).Count;
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index d1b8243b..7eeb36e5 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -24,10 +24,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
 using System.IO;
 using System.Linq;
 using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
@@ -37,10 +42,27 @@ namespace Rudzoft.ChessLib.Test.BookTests;
 public sealed class PolyglotTests : IClassFixture<BookFixture>
 {
     private readonly BookFixture _fixture;
+    private readonly IServiceProvider _serviceProvider;
 
     public PolyglotTests(BookFixture fixture)
     {
+        var polyConfig = new PolyglotBookConfiguration { BookPath = string.Empty };
+        var polyOptions = Options.Create(polyConfig);
+        
         _fixture = fixture;
+        _serviceProvider = new ServiceCollection()
+            .AddSingleton(polyOptions)
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>()
+            .AddSingleton(static _ =>
+            {
+                IUci uci = new Uci();
+                uci.Initialize();
+                return uci;
+            })
+            .BuildServiceProvider();
     }
 
     [Fact]
@@ -48,10 +70,18 @@ public void UnsetBookFileYieldsEmptyMove()
     {
         const string fen = Fen.Fen.StartPositionFen;
 
-        var game = GameFactory.Create(fen);
-        var book = new PolyglotBook(game.Pos);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
 
-        var bookMove = book.Probe(game.Pos);
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var book = _serviceProvider
+            .GetRequiredService<IPolyglotBookFactory>()
+            .Create();
+
+        var bookMove = book.Probe(pos);
 
         var m = Move.EmptyMove;
 
@@ -63,17 +93,28 @@ public void BookFileLoadsCorrectly()
     {
         const string fen = Fen.Fen.StartPositionFen;
 
-        var game = GameFactory.Create(fen);
-        var path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), _fixture.BookFile);
-        var book = new PolyglotBook(game.Pos)
-        {
-            FileName = path
-        };
+        var bookPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+        
+        Assert.NotNull(bookPath);
+        Assert.NotEmpty(bookPath);
+        
+        var path = Path.Combine(bookPath, _fixture.BookFile);
+        
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
 
-        Assert.NotNull(book.FileName);
+        var book = _serviceProvider
+            .GetRequiredService<IPolyglotBookFactory>()
+            .Create(path);
+
+        Assert.NotNull(book.BookFile);
 
         var expected = Move.Create(Square.D2, Square.D4);
-        var actual = book.Probe(game.Pos);
+        var actual = book.Probe(pos);
 
         Assert.False(actual.IsNullMove());
         Assert.Equal(expected, actual);
@@ -85,10 +126,18 @@ public void BookStartPositionCorrectlyCalculatesPolyglotHash()
         const ulong expected = 5060803636482931868UL;
         const string fen = Fen.Fen.StartPositionFen;
 
-        var game = GameFactory.Create(fen);
-        var book = new PolyglotBook(game.Pos);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
 
-        var actual = book.ComputePolyglotKey().Key;
+        var book = _serviceProvider
+            .GetRequiredService<IPolyglotBookFactory>()
+            .Create();
+
+        var actual = book.ComputePolyglotKey(pos).Key;
 
         Assert.Equal(expected, actual);
     }
@@ -97,7 +146,7 @@ public void BookStartPositionCorrectlyCalculatesPolyglotHash()
     [InlineData(new string[] { }, 5060803636482931868UL)]
     [InlineData(new[] { "e2e4" }, 9384546495678726550UL)]
     [InlineData(new[] { "e2e4", "d7d5" }, 528813709611831216UL)]
-    [InlineData(new[] { "e2e4", "d7d5", "e4e5", }, 7363297126586722772UL)]
+    [InlineData(new[] { "e2e4", "d7d5", "e4e5" }, 7363297126586722772UL)]
     [InlineData(new[] { "e2e4", "d7d5", "e4e5", "f7f5" }, 2496273314520498040UL)]
     [InlineData(new[] { "e2e4", "d7d5", "e4e5", "f7f5", "e1e2" }, 7289745035295343297UL)]
     [InlineData(new[] { "e2e4", "d7d5", "e4e5", "f7f5", "e1e2", "e8f7" }, 71445182323015129UL)]
@@ -107,18 +156,26 @@ public void HashKeyFromInitialPositionIsComputedCorrectly(string[] uciMoves, ulo
     {
         const string fen = Fen.Fen.StartPositionFen;
 
-        var uci = new Uci();
-        var game = GameFactory.Create(fen);
-        var book = new PolyglotBook(game.Pos);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
         var state = new State();
 
-        foreach (var m in uciMoves.Select(uciMove => uci.MoveFromUci(game.Pos, uciMove)))
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var uci = _serviceProvider.GetRequiredService<IUci>();
+        
+        var book = _serviceProvider
+            .GetRequiredService<IPolyglotBookFactory>()
+            .Create();
+
+        foreach (var m in uciMoves.Select(uciMove => uci.MoveFromUci(pos, uciMove)))
         {
             Assert.False(m.IsNullMove());
-            game.Pos.MakeMove(m, state);
+            pos.MakeMove(m, state);
         }
 
-        var actualKey = book.ComputePolyglotKey().Key;
+        var actualKey = book.ComputePolyglotKey(pos).Key;
 
         Assert.Equal(expected, actualKey);
     }
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
index 151e4753..58eff966 100644
--- a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
+++ b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
@@ -24,8 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using Microsoft.Extensions.DependencyInjection;
 using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Factories;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Types;
 
@@ -33,6 +34,17 @@ namespace Rudzoft.ChessLib.Test.CastleTests;
 
 public sealed class BasicCastleTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public BasicCastleTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Theory]
     [InlineData(Fen.Fen.StartPositionFen, CastleRights.None, false)]
     [InlineData("rnbqk2r/pppppppp/8/8/8/8/PPPPPPPP/RNBQK2R w KQkq - 0 1", CastleRights.King, true)]
@@ -41,10 +53,11 @@ public sealed class BasicCastleTests
     [InlineData("r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w KQkq - 0 1", CastleRights.Queen, true)]
     public void CanPotentiallyCastle(string fen, CastleRights cr, bool expected)
     {
-        var pos = GameFactory.Create(fen).Pos;
-        var fd = new FenData(fen);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(fen);
         var state = new State();
-        pos.Set(in fd, ChessMode.Normal, state);
+        pos.Set(in fenData, ChessMode.Normal, state);
+
         var actual = pos.CanCastle(cr);
         Assert.Equal(expected, actual);
     }
@@ -64,10 +77,11 @@ public void CanPotentiallyCastle(string fen, CastleRights cr, bool expected)
     [InlineData("r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w KQkq - 0 1", CastleRights.BlackQueen, false)]
     public void IsCastleImpeeded(string fen, CastleRights cr, bool expected)
     {
-        var pos = GameFactory.Create(fen).Pos;
-        var fd = new FenData(fen);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(fen);
         var state = new State();
-        pos.Set(in fd, ChessMode.Normal, state);
+        pos.Set(in fenData, ChessMode.Normal, state);
+
         var actual = pos.CastlingImpeded(cr);
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
index 11769796..8ed0c40f 100644
--- a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
+++ b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
@@ -24,36 +24,55 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Evaluation;
-using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.EvaluationTests;
 
 public sealed class KpkBitBaseTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public KpkBitBaseTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .AddSingleton<IKpkBitBase, KpkBitBase>()
+            .BuildServiceProvider();
+    }
+
     // theory data layout:
-    // fen, player as raw int (0 = white, 1 = black), expected win by strong side
+    // fen, player, expected win by strong side
     [Theory]
-    [InlineData("2k5/8/8/8/1PK5/8/8/8 w - - 0 1", 0, true)]
-    [InlineData("5k2/8/6KP/8/8/8/8/8 b - - 0 1", 0, true)]
-    [InlineData("8/8/8/K7/P2k4/8/8/8 w - - 0 1", 0, true)]
-    [InlineData("8/8/8/K7/P2k4/8/8/8 b - - 0 1", 0, true)]
-    public void KpkWin(string fen, int rawPlayer, bool expected)
+    [InlineData("2k5/8/8/8/1PK5/8/8/8 w - - 0 1", Players.White, true)]
+    [InlineData("5k2/8/6KP/8/8/8/8/8 b - - 0 1", Players.White, true)]
+    [InlineData("8/8/8/K7/P2k4/8/8/8 w - - 0 1", Players.White, true)]
+    [InlineData("8/8/8/K7/P2k4/8/8/8 b - - 0 1", Players.White, true)]
+    public void KpkWin(string fen, Players rawPlayer, bool expected)
     {
-        var game = GameFactory.Create(fen);
-        var pos = game.Pos;
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(fen);
+        var state = new State();
+        pos.Set(in fenData, ChessMode.Normal, state);
 
         Player strongSide = rawPlayer;
         var weakSide = ~strongSide;
 
-        var strongKing = KpkBitBase.Normalize(pos, strongSide,  pos.GetPieceSquare(PieceTypes.King, strongSide));
-        var strongPawn = KpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceTypes.Pawn, strongSide));
-        var weakKing = KpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceTypes.King, weakSide));
+        var kpkBitBase = _serviceProvider.GetRequiredService<IKpkBitBase>();
+        
+        var strongKing = kpkBitBase.Normalize(pos, strongSide,  pos.GetPieceSquare(PieceTypes.King, strongSide));
+        var strongPawn = kpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceTypes.Pawn, strongSide));
+        var weakKing = kpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceTypes.King, weakSide));
 
         var us = strongSide == pos.SideToMove ? Player.White : Player.Black;
 
-        var won = !KpkBitBase.Probe(strongKing, strongPawn, weakKing, us);
+        var won = !kpkBitBase.Probe(strongKing, strongPawn, weakKing, us);
 
         Assert.Equal(expected, won);
     }
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index a245e77b..b57d73b1 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -24,13 +24,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Exceptions;
-using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.FENTests;
 
 public sealed class FenTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public FenTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Theory]
     [InlineData(Fen.Fen.StartPositionFen)]
     [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1")]
@@ -41,18 +56,29 @@ public sealed class FenTests
     [InlineData("r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w KQkq - 0 1")]
     public void GetFen(string fen)
     {
-        var g = GameFactory.Create(fen);
-        var actualFen = g.GetFen().ToString();
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(fen);
+        var state = new State();
+        pos.Set(in fenData, ChessMode.Normal, state);
+        
+        var actualFen = pos.GenerateFen().ToString();
         Assert.Equal(fen, actualFen);
     }
 
     [Theory]
-    [InlineData("z3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1")]
-    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/ip2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1")]
-    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/lPPBBPPP/R3K2R w KQkq - 0 1")]
-    public void Validate(string fen)
+    [InlineData("z3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", typeof(InvalidFen))]
+    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/ip2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", typeof(InvalidFen))]
+    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/lPPBBPPP/R3K2R w KQkq - 0 1", typeof(InvalidFen))]
+    public void Validate(string fen, Type expectedException)
     {
-        var exception = Assert.Throws<InvalidFen>(() => GameFactory.Create(fen, true));
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var exception = Assert.Throws(expectedException, () =>
+        {
+            var fenData = new FenData(fen);
+            var state = new State();
+            pos.Set(in fenData, ChessMode.Normal, state);
+        });
         Assert.NotNull(exception.Message);
         Assert.StartsWith("Invalid char detected", exception.Message);
     }
diff --git a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
index ebfaf36c..a86b51fa 100644
--- a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
@@ -24,12 +24,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Factories;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.FenceTests;
 
 public sealed class FenceTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public FenceTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .AddSingleton<IBlockage, Blockage>()
+            .BuildServiceProvider();
+    }
+
     [Theory]
     [InlineData("8/8/k7/p1p1p1p1/P1P1P1P1/8/8/4K3 w - - 0 1", true)]
     [InlineData("4k3/5p2/1p1p1P1p/1P1P1P1P/3P4/8/4K3/8 w - - 0 1", true)]
@@ -39,10 +55,13 @@ public sealed class FenceTests
     [InlineData("8/2p5/kp2p1p1/p1p1P1P1/P1P2P2/1P4K1/8/8 w - - 0 1", false)]
     public void Blocked(string fen, bool expected)
     {
-        var g = GameFactory.Create(fen);
-        var pos = g.Pos;
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(fen);
+        var state = new State();
+        pos.Set(in fenData, ChessMode.Normal, state);
 
-        var actual = BlockageFactory.IsBlocked(in pos);
+        var blockage = _serviceProvider.GetRequiredService<IBlockage>();
+        var actual = blockage.IsBlocked(in pos);
 
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
index cc9f75bc..761a607d 100644
--- a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
+++ b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
@@ -24,7 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Factories;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
@@ -33,6 +35,17 @@ namespace Rudzoft.ChessLib.Test.GameplayTests;
 
 public sealed class FoolsCheckMateTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public FoolsCheckMateTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Fact]
     public void FoolsCheckMate()
     {
@@ -45,19 +58,19 @@ public void FoolsCheckMate()
             Move.Create(Square.D8, Square.H4)
         };
 
-        // construct game and start a new game
-        var game = GameFactory.Create(Fen.Fen.StartPositionFen);
-        var position = game.Pos;
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(Fen.Fen.StartPositionFen);
         var state = new State();
+        pos.Set(in fenData, ChessMode.Normal, state);
 
         // make the moves necessary to create a mate
         foreach (var move in moves)
-            position.MakeMove(move, state);
+            pos.MakeMove(move, state);
 
         // verify in check is actually true
-        Assert.True(position.InCheck);
+        Assert.True(pos.InCheck);
 
-        var resultingMoves = position.GenerateMoves();
+        var resultingMoves = pos.GenerateMoves();
 
         // verify that no legal moves actually exists.
         Assert.True(resultingMoves.Length == 0);
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
index 56de2c48..7d908819 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
@@ -24,8 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using Microsoft.Extensions.DependencyInjection;
 using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Factories;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
@@ -34,19 +35,33 @@ namespace Rudzoft.ChessLib.Test.MoveTests;
 
 public sealed class MoveGeneratorTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public MoveGeneratorTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Fact]
     public void InCheckMoveGeneration()
     {
         const string fen = "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6";
         const int expectedMoves = 4;
 
-        var g = GameFactory.Create(fen);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(fen);
+        var state = new State();
+        pos.Set(in fenData, ChessMode.Normal, state);
 
         // make sure black is in check
-        Assert.True(g.Pos.InCheck);
+        Assert.True(pos.InCheck);
 
         // generate moves for black
-        var mg = g.Pos.GenerateMoves();
+        var mg = pos.GenerateMoves();
         var actualMoves = mg.Length;
 
         Assert.Equal(expectedMoves, actualMoves);
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index 760c358b..f4c0f761 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -28,13 +28,26 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using System.Text;
-using Rudzoft.ChessLib.Factories;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.MoveTests;
 
 public sealed class MoveTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public MoveTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Fact]
     public void MoveSquares()
     {
@@ -99,10 +112,6 @@ public void MoveToString()
         var moves = new List<Move>(3528);
         var movesString = new List<MoveStrings>(3528);
 
-        var game = GameFactory.Create();
-
-        game.NewGame();
-
         var tmp = new StringBuilder(8);
 
         // build move list and expected result
@@ -124,12 +133,17 @@ public void MoveToString()
 
         var result = new StringBuilder(128);
 
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(Fen.Fen.StartPositionFen);
+        var state = new State();
+        pos.Set(in fenData, ChessMode.Normal, state);
+
         var i = 0;
         foreach (var move in CollectionsMarshal.AsSpan(moves))
         {
             result.Clear();
             result.Append(' ');
-            game.Pos.MoveToString(move, in result);
+            pos.MoveToString(move, in result);
             Assert.Equal(result.ToString(), movesString[i++].ToString());
 
         }
@@ -138,10 +152,6 @@ public void MoveToString()
     [Fact]
     public void MoveListToStringTest()
     {
-        var game = GameFactory.Create();
-
-        game.NewGame();
-
         var result = new StringBuilder(1024 * 16);
         var expected = new StringBuilder(1024 * 16);
 
@@ -166,11 +176,16 @@ public void MoveListToStringTest()
             expected.Append(rndSquareTo.ToString());
         }
 
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(Fen.Fen.StartPositionFen);
+        var state = new State();
+        pos.Set(in fenData, ChessMode.Normal, state);
+
         // generate a bitch string for them all.
         foreach (var move in CollectionsMarshal.AsSpan(moves))
         {
             result.Append(' ');
-            game.Pos.MoveToString(move, result);
+            pos.MoveToString(move, result);
         }
 
         Assert.Equal(expected.ToString(), result.ToString());
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs b/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs
index fbcc1fd6..a1af53c2 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs
@@ -24,11 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Factories;
-
 namespace Rudzoft.ChessLib.Test.MoveTests;
 
-public sealed class PerftOneTest
+public sealed class PerftOneTest : PerftVerify
 {
     [Theory]
     [InlineData(Fen.Fen.StartPositionFen, 1, 20UL)]
@@ -36,24 +34,28 @@ public sealed class PerftOneTest
     [InlineData(Fen.Fen.StartPositionFen, 3, 8_902UL)]
     [InlineData(Fen.Fen.StartPositionFen, 4, 197_281UL)]
     [InlineData(Fen.Fen.StartPositionFen, 5, 4_865_609UL)]
+#if !DEBUG
     [InlineData(Fen.Fen.StartPositionFen, 6, 119_060_324UL)]
+#endif
     public void Perft(string fen, int depth, ulong expected)
-        => PerftVerify.AssertPerft(fen, depth, in expected);
+        => AssertPerft(fen, depth, in expected);
 }
 
-public sealed class PerftTwoTest
+public sealed class PerftTwoTest : PerftVerify
 {
     [Theory]
     [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 1, 48UL)]
     [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 2, 2_039UL)]
     [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 3, 97_862UL)]
     [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 4, 4_085_603UL)]
+#if !DEBUG
     [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 5, 193_690_690UL)]
+#endif
     public void Perft(string fen, int depth, ulong expected)
-        => PerftVerify.AssertPerft(fen, depth, in expected);
+        => AssertPerft(fen, depth, in expected);
 }
 
-public sealed class PerftThreeTest
+public sealed class PerftThreeTest : PerftVerify
 {
     [Theory]
     [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 1, 14UL)]
@@ -61,13 +63,15 @@ public sealed class PerftThreeTest
     [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 3, 2_812UL)]
     [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 4, 43_238UL)]
     [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 5, 674_624UL)]
+#if !DEBUG
     [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 6, 11_030_083UL)]
     [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 7, 178_633_661UL)]
+#endif
     public void Perft(string fen, int depth, ulong expected)
-        => PerftVerify.AssertPerft(fen, depth, in expected);
+        => AssertPerft(fen, depth, in expected);
 }
 
-public sealed class PerftFourTest
+public sealed class PerftFourTest : PerftVerify
 {
     [Theory]
     [InlineData("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 1, 6UL)]
@@ -76,27 +80,19 @@ public sealed class PerftFourTest
     [InlineData("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 4, 422_333UL)]
     [InlineData("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 5, 15_833_292UL)]
     public void Perft(string fen, int depth, ulong expected)
-        => PerftVerify.AssertPerft(fen, depth, in expected);
+        => AssertPerft(fen, depth, in expected);
 }
 
-public sealed class PerftFiveTest
+public sealed class PerftFiveTest : PerftVerify
 {
     [Theory]
     [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 1, 46UL)]
     [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 2, 2_079UL)]
     [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 3, 89_890UL)]
     [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 4, 3_894_594UL)]
+#if !DEBUG
     [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 5, 164_075_551UL)]
+#endif
     public void Perft(string fen, int depth, ulong expected)
-        => PerftVerify.AssertPerft(fen, depth, in expected);
-}
-
-internal static class PerftVerify
-{
-    public static void AssertPerft(string fen, int depth, in ulong expected)
-    {
-        var g = GameFactory.Create(fen);
-        var actual = g.Perft(depth);
-        Assert.Equal(expected, actual);
-    }
+        => AssertPerft(fen, depth, in expected);
 }
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
new file mode 100644
index 00000000..6b3e93d2
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
@@ -0,0 +1,44 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash.Tables.Transposition;
+using Rudzoft.ChessLib.Protocol.UCI;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Test.MoveTests;
+
+public abstract class PerftVerify
+{
+    private readonly IServiceProvider _serviceProvider;
+
+    protected PerftVerify()
+    {
+        var ttConfig = new TranspositionTableConfiguration { DefaultSize = 1 };
+        var ttOptions = Options.Create(ttConfig);
+
+        _serviceProvider = new ServiceCollection()
+            .AddSingleton(ttOptions)
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .AddSingleton<ISearchParameters, SearchParameters>()
+            .AddSingleton<IUci, Uci>()
+            .AddTransient<IGame, Game>()
+            .AddSingleton<ITranspositionTable, TranspositionTable>()
+            .BuildServiceProvider();
+    }
+
+    protected void AssertPerft(string fen, int depth, in ulong expected)
+    {
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        var g = _serviceProvider.GetRequiredService<IGame>();
+        g.Pos.Set(in fenData, ChessMode.Normal, state);
+        
+        var actual = g.Perft(depth);
+        Assert.Equal(expected, actual);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index d14f3c34..fd83eba1 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -24,8 +24,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
-using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.Types;
@@ -34,14 +37,31 @@ namespace Rudzoft.ChessLib.Test.NotationTests;
 
 public sealed class FanTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public FanTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Theory]
     [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.Fan, PieceTypes.Knight, Squares.d2, Squares.f2,
         Squares.e4)]
     public void FanRankAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
         Squares fromSqTwo, Squares toSq)
     {
-        var g = GameFactory.Create(fen);
-        var pc = movingPt.MakePiece(g.Pos.SideToMove);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+        
+        var pc = movingPt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
@@ -57,7 +77,7 @@ public void FanRankAmbiguities(string fen, MoveNotations moveNotation, PieceType
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(g.Pos);
+        var ambiguity = MoveNotation.Create(pos);
 
         var expectedOne = $"{pieceChar}{fromOne.FileChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.FileChar}{toString}";
@@ -75,8 +95,14 @@ public void FanRankAmbiguities(string fen, MoveNotations moveNotation, PieceType
     public void FanFileAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
         Squares fromSqTwo, Squares toSq)
     {
-        var g = GameFactory.Create(fen);
-        var pc = movingPt.MakePiece(g.Pos.SideToMove);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+        
+        var pc = movingPt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
@@ -92,7 +118,7 @@ public void FanFileAmbiguities(string fen, MoveNotations moveNotation, PieceType
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(g.Pos);
+        var ambiguity = MoveNotation.Create(pos);
 
         var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index 3054daa8..157fa25d 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -24,7 +24,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Factories;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.Types;
@@ -33,6 +36,17 @@ namespace Rudzoft.ChessLib.Test.NotationTests;
 
 public sealed class IccfTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public IccfTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Fact]
     public void IccfRegularMove()
     {
@@ -40,11 +54,12 @@ public void IccfRegularMove()
         const MoveNotations notation = MoveNotations.ICCF;
         const string expectedPrimary = "4263";
 
-        var board = new Board();
-        var pieceValue = new Values();
-        var pos = new Position(board, pieceValue);
-        var g = GameFactory.Create(pos);
-        g.NewGame(fen);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
 
         var w1 = Move.Create(Squares.d2, Squares.f3);
 
@@ -64,11 +79,12 @@ public void IccfPromotionMove(string fen, PieceTypes promoPt, string expected)
     {
         const MoveNotations notation = MoveNotations.ICCF;
 
-        var board = new Board();
-        var pieceValue = new Values();
-        var pos = new Position(board, pieceValue);
-        var g = GameFactory.Create(pos);
-        g.NewGame(fen);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
 
         var w1 = Move.Create(Squares.b7, Squares.b8, MoveTypes.Promotion, promoPt);
 
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index 92642437..34458932 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -24,8 +24,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
-using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.Types;
@@ -34,6 +37,17 @@ namespace Rudzoft.ChessLib.Test.NotationTests;
 
 public sealed class RanTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public RanTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Theory]
     [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.Ran, PieceTypes.Knight, Squares.d2, Squares.d4,
         Squares.f3)]
@@ -46,8 +60,14 @@ public sealed class RanTests
     public void RanLanAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
         Squares fromSqTwo, Squares toSq)
     {
-        var g = GameFactory.Create(fen);
-        var pc = movingPt.MakePiece(g.Pos.SideToMove);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var pc = movingPt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
@@ -63,7 +83,7 @@ public void RanLanAmbiguities(string fen, MoveNotations moveNotation, PieceTypes
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(g.Pos);
+        var ambiguity = MoveNotation.Create(pos);
 
         var expectedOne = $"{pieceChar}{fromOne}-{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo}-{toString}";
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index a4a33dd1..48288624 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -24,9 +24,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
 using System.Linq;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
-using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
@@ -36,14 +39,31 @@ namespace Rudzoft.ChessLib.Test.NotationTests;
 
 public sealed class SanTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public SanTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Theory]
     [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.San, PieceTypes.Knight, Squares.d2, Squares.f2,
         Squares.e4)]
     public void SanRankAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
         Squares fromSqTwo, Squares toSq)
     {
-        var g = GameFactory.Create(fen);
-        var pc = movingPt.MakePiece(g.Pos.SideToMove);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var pc = movingPt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
@@ -59,7 +79,7 @@ public void SanRankAmbiguities(string fen, MoveNotations moveNotation, PieceType
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(g.Pos);
+        var ambiguity = MoveNotation.Create(pos);
 
         var expectedOne = $"{pieceChar}{fromOne.FileChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.FileChar}{toString}";
@@ -77,8 +97,14 @@ public void SanRankAmbiguities(string fen, MoveNotations moveNotation, PieceType
     public void SanFileAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
         Squares fromSqTwo, Squares toSq)
     {
-        var g = GameFactory.Create(fen);
-        var pc = movingPt.MakePiece(g.Pos.SideToMove);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var pc = movingPt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
@@ -94,7 +120,7 @@ public void SanFileAmbiguities(string fen, MoveNotations moveNotation, PieceType
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(g.Pos);
+        var ambiguity = MoveNotation.Create(pos);
 
         var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
@@ -115,17 +141,22 @@ public void RookSanAmbiguity()
         const MoveNotations notation = MoveNotations.San;
         var expectedNotations = new[] { "Ree2", "Rge2" };
 
-        var game = GameFactory.Create(fen);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
 
-        var sideToMove = game.Pos.SideToMove;
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var sideToMove = pos.SideToMove;
         var targetPiece = PieceTypes.Rook.MakePiece(sideToMove);
 
-        var moveNotation = MoveNotation.Create(game.Pos);
+        var moveNotation = MoveNotation.Create(pos);
 
-        var sanMoves = game.Pos
+        var sanMoves = pos
             .GenerateMoves()
             .Select(static em => em.Move)
-            .Where(m => game.Pos.GetPiece(m.FromSquare()) == targetPiece)
+            .Where(m => pos.GetPiece(m.FromSquare()) == targetPiece)
             .Select(m => moveNotation.ToNotation(notation).Convert(m))
             .ToArray();
 
@@ -139,8 +170,14 @@ public void SanCaptureWithCheck(string fen, string expected)
     {
         // author: skotz
 
-        var game = GameFactory.Create(fen);
-        var notation = MoveNotation.Create(game.Pos);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var notation = MoveNotation.Create(pos);
 
         var move = Move.Create(Squares.d1, Squares.d8, MoveTypes.Normal);
         var sanMove = notation.ToNotation(MoveNotations.San).Convert(move);
@@ -155,10 +192,15 @@ public void SanRecaptureNotCheckmate(string fen, params string[] expected)
     {
         // author: skotz
         
-        var game = GameFactory.Create(fen);
-        var pos = game.Pos;
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
         var notation = MoveNotation.Create(pos);
-        var allMoves = game.Pos.GenerateMoves().Get();
+        var allMoves = pos.GenerateMoves().Get();
 
         foreach (var move in allMoves)
         {
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
index 184aabef..e7fb2268 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
@@ -24,20 +24,37 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Factories;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
 
 public sealed class PawnDoubleAttackTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public PawnDoubleAttackTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+    
     [Fact]
-    public void b()
+    public void WhitePlayerPawnDoubleAttacksAttackSpan()
     {
-        var g = GameFactory.Create(Fen.Fen.StartPositionFen);
-
-        var pos = g.Pos;
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
 
+        var fenData = new FenData(Fen.Fen.StartPositionFen);
+        var state = new State();
+        
+        pos.Set(in fenData, ChessMode.Normal, state);
+        
         var whitePawns = pos.Pieces(PieceTypes.Pawn, Player.White);
 
         var attackSpan = whitePawns.PawnDoubleAttacks(Player.White);
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
index e5abef72..c668a78f 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
@@ -24,13 +24,27 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Factories;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PositionTests;
 
 public sealed class EnPassantFenTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public EnPassantFenTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Theory]
     [InlineData("rnbkqbnr/pp1pp1pp/5p2/2pP4/8/8/PPP1PPPP/RNBKQBNR w KQkq c6 0 1", Squares.c6)] // valid
     [InlineData("rnbkqbnr/pp1pp1pp/5p2/2pP4/8/8/PPP1PPPP/RNBKQBNR w KQkq c7 0 1", Squares.none)] // invalid rank
@@ -44,8 +58,14 @@ public sealed class EnPassantFenTests
     [InlineData("rnbqkbnr/2pppp2/p6p/1p2PB2/3P2pP/5P2/PPP3P1/RNBQK1NR b KQkq h3 0 1", Squares.h3)] // valid
     public void EnPassantSquare(string fen, Squares expected)
     {
-        var game = GameFactory.Create(fen);
-        var actual = game.Pos.EnPassantSquare;
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var actual = pos.EnPassantSquare;
         Assert.Equal(expected, actual.Value);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 49430655..7c188f64 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -24,44 +24,47 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Factories;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PositionTests;
 
 public sealed class PositionTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public PositionTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Fact]
     public void AddPieceTest()
     {
-        var pieceValue = new Values();
-        var board = new Board();
-        var position = new Position(board, pieceValue);
-
-        position.AddPiece(Pieces.WhiteKing, Square.A7);
-        var pieces = position.Pieces();
-        var square = pieces.Lsb();
-        Assert.Equal(square, Square.A7);
+        const string expectedFen = "rnbqkbnr/Pppppppp/8/8/8/8/PPP1PPPP/RNBQKBNR w KQkq - 0 1";
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
 
-        var piece = position.GetPiece(square);
-        Assert.Equal(Pieces.WhiteKing, piece.Value);
-
-        // test overload
-        pieces = position.Pieces(piece);
-        Assert.False((pieces & square).IsEmpty);
-
-        // Test piece type overload
+        var fenData = new FenData("rnbqkbnr/pppppppp/8/8/8/8/PPP1PPPP/RNBQKBNR w KQkq - 0 1");
+        var state = new State();
 
-        board = new Board();
-        position = new Position(board, pieceValue);
+        pos.Set(in fenData, ChessMode.Normal, state);
 
-        position.AddPiece(PieceTypes.Knight.MakePiece(Player.Black), Square.D5);
-        pieces = position.Pieces();
-        square = pieces.Lsb();
-        Assert.Equal(Square.D5, square.Value);
+        var square = Square.A7;
+        pos.AddPiece(Piece.WhitePawn, square);
 
-        piece = position.GetPiece(square);
-        Assert.Equal(Pieces.BlackKnight, piece.Value);
+        var actualFen = pos.FenNotation;
+        
+        Assert.Equal(expectedFen, actualFen);
+        
+        var piece = pos.GetPiece(square);
+        Assert.Equal(Piece.WhitePawn, piece);
     }
 
     [Fact]
@@ -73,8 +76,12 @@ public void KingBlocker()
         const int expected = 1;
         var expectedSquare = new Square(Ranks.Rank6, Files.FileE);
 
-        var game = GameFactory.Create(fen);
-        var pos = game.Pos;
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
 
         var b = pos.KingBlockers(Player.Black);
 
@@ -95,11 +102,15 @@ public void KingBlocker()
     [InlineData("KBNK", "k7/8/8/8/8/8/8/KBN5 w - - 0 10")]
     public void SetByCodeCreatesSameMaterialKey(string code, string fen)
     {
-        var g = GameFactory.Create(fen);
-        var pos = g.Pos;
-        var materialKey = pos.State.MaterialKey;
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
 
+        var fenData = new FenData(fen);
         var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var materialKey = pos.State.MaterialKey;
+
         var posCode = pos.Set(code, Player.White, state);
         var codeMaterialKey = posCode.State.MaterialKey;
         
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index 4e0b5d4d..9a6e19b8 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -24,8 +24,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
-using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -33,20 +36,35 @@ namespace Rudzoft.ChessLib.Test.PositionTests;
 
 public sealed class ValidationTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public ValidationTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IPosition, Position>()
+            .BuildServiceProvider();
+    }
+
     [Fact]
     public void ValidationKingsNegative()
     {
         const PositionValidationTypes type = PositionValidationTypes.Kings;
         var expectedErrorMsg = $"king count for player {Player.White} was 2";
 
-        var game = GameFactory.Create();
-        game.NewGame();
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(Fen.Fen.StartPositionFen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
 
         var pc = PieceTypes.King.MakePiece(Player.White);
 
-        game.Pos.AddPiece(pc, Square.E4);
+        pos.AddPiece(pc, Square.E4);
 
-        var validator = game.Pos.Validate(type);
+        var validator = pos.Validate(type);
 
         Assert.NotEmpty(validator.ErrorMsg);
 
@@ -62,10 +80,15 @@ public void ValidateCastleling()
         // position only has pawns, rooks and kings
         const string fen = "r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w KQkq - 0 1";
         const PositionValidationTypes validationType = PositionValidationTypes.Castle;
-        var game = GameFactory.Create();
-        game.NewGame(fen);
+        
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
 
-        var validator = game.Pos.Validate(validationType);
+        var validator = pos.Validate(validationType);
 
         Assert.True(validator.IsOk);
         Assert.True(validator.ErrorMsg.IsNullOrEmpty());
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
index 98273d66..78b7de68 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
@@ -28,7 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Test.ProtocolTests;
 
-public class OptionsTests
+public sealed class OptionsTests
 {
     [Theory]
     [InlineData("Boolean Test", true, true, "option name Boolean Test type Check default true")]
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
index 05fcd65b..aef25240 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
@@ -25,8 +25,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
-using System.Runtime.CompilerServices;
-using Rudzoft.ChessLib.Factories;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
 
@@ -34,6 +37,27 @@ namespace Rudzoft.ChessLib.Test.ProtocolTests;
 
 public sealed class UciTests
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public UciTests()
+    {
+        var transpositionTableConfiguration = new TranspositionTableConfiguration { DefaultSize = 1 };
+        var options = Options.Create(transpositionTableConfiguration);
+
+        _serviceProvider = new ServiceCollection()
+            .AddSingleton(options)
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IBoard, Board>()
+            .AddTransient<IPosition, Position>()
+            .AddSingleton(static _ =>
+            {
+                IUci uci = new Uci();
+                uci.Initialize();
+                return uci;
+            })
+            .BuildServiceProvider();
+    }
+
     [Fact]
     public void NpsSimple()
     {
@@ -43,7 +67,7 @@ public void NpsSimple()
 
         var ts = TimeSpan.FromSeconds(1);
 
-        var uci = new Uci();
+        var uci = _serviceProvider.GetRequiredService<IUci>();
 
         var actual = uci.Nps(nodes, in ts);
 
@@ -55,12 +79,16 @@ public void MoveFromUciBasic()
     {
         const string uciMove = "a2a3";
         var expected = Move.Create(Square.A2, Square.A3);
-        var uci = new Uci();
 
-        var game = GameFactory.Create();
-        game.NewGame();
+        var uci = _serviceProvider.GetRequiredService<IUci>();
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(Fen.Fen.StartPositionFen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
 
-        var actual = uci.MoveFromUci(game.Pos, uciMove);
+        var actual = uci.MoveFromUci(pos, uciMove);
 
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index d44cf703..0f694022 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -26,6 +26,7 @@
   </ItemGroup>
 
   <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.analyzers" Version="1.1.0" />
diff --git a/src/Rudzoft.ChessLib.WebApi/Moves.cs b/src/Rudzoft.ChessLib.WebApi/Moves.cs
index 5c475e9e..dec6300a 100644
--- a/src/Rudzoft.ChessLib.WebApi/Moves.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Moves.cs
@@ -1,4 +1,4 @@
 
 namespace Rudzoft.ChessLib.WebApi;
 
-public record Moves(IList<string> MoveList, int Size);
+public record Moves(List<string> MoveList, int Size);
diff --git a/src/Rudzoft.ChessLib.WebApi/Program.cs b/src/Rudzoft.ChessLib.WebApi/Program.cs
index 07b0337e..d58907cb 100644
--- a/src/Rudzoft.ChessLib.WebApi/Program.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Program.cs
@@ -1,5 +1,4 @@
-using Rudzoft.ChessLib;
-using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.WebApi.Services;
 
 var builder = WebApplication.CreateBuilder(args);
@@ -11,11 +10,8 @@
 builder.Services.AddEndpointsApiExplorer();
 builder.Services.AddSwaggerGen();
 
-builder.Services.AddTransient<IMoveGeneratorService, MoveGeneratorService>();
-builder.Services.AddTransient<IPosition, Position>();
-builder.Services.AddTransient<IBoard, Board>();
-builder.Services.AddSingleton<IValues, Values>();
-builder.Services.AddTransient<IGame, Game>();
+builder.Services.AddChessLib(null)
+    .AddTransient<IMoveGeneratorService, MoveGeneratorService>();
 
 var app = builder.Build();
 
diff --git a/src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs b/src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs
index 08910639..eb4456a4 100644
--- a/src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs
@@ -2,4 +2,4 @@
 
 namespace Rudzoft.ChessLib.WebApi.Queries;
 
-public sealed record MoveQuery(string Fen = Fen.Fen.StartPositionFen, MoveGenerationType Type = MoveGenerationType.Legal);
+public sealed record MoveQuery(string Fen = Fen.Fen.StartPositionFen, MoveGenerationType Type = MoveGenerationType.Legal, ChessMode Mode = ChessMode.Normal);
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index 078997cf..32fcd239 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -5,11 +5,21 @@
         <Nullable>enable</Nullable>
         <ImplicitUsings>enable</ImplicitUsings>
         <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
+        <LangVersion>default</LangVersion>
     </PropertyGroup>
 
     <ItemGroup>
+        <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="7.0.3">
+          <PrivateAssets>all</PrivateAssets>
+          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+        </PackageReference>
         <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
+        <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
+        <PackageReference Include="Microsoft.OpenApi" Version="1.6.1" />
         <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
+        <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
+        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
+        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
     </ItemGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
index f5f38ea6..ac3d4d3d 100644
--- a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
@@ -1,5 +1,4 @@
-using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.WebApi.Queries;
 
@@ -8,26 +7,21 @@ namespace Rudzoft.ChessLib.WebApi.Services;
 public sealed class MoveGeneratorService : IMoveGeneratorService
 {
     private readonly ILogger<MoveGeneratorService> _logger;
-
     private readonly IPosition _position;
 
-    private readonly State _state;
-
-    public MoveGeneratorService(
-        ILogger<MoveGeneratorService> logger,
-        IGame game)
+    public MoveGeneratorService(ILogger<MoveGeneratorService> logger, IPosition pos)
     {
         _logger = logger;
-        _position = game.Pos;
-        _state = new State();
+        _position = pos;
     }
 
     public IEnumerable<string> GenerateMoves(MoveQuery parameters)
     {
-        _logger.LogInformation("Generating moves. fen={Fen},type={Type}", parameters.Fen, parameters.Type);
+        _logger.LogInformation("Generating moves. fen={Fen},type={Type},mode={Mode}", parameters.Fen, parameters.Type, parameters.Mode);
 
         var fd = new FenData(parameters.Fen);
-        _position.Set(in fd, ChessMode.Normal, _state);
+        var state = new State();
+        _position.Set(in fd, parameters.Mode, state);
         return _position.GenerateMoves(parameters.Type).Select(static em => em.Move.ToString());
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 8f719967..4c9a548c 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -43,195 +43,172 @@ public sealed class Blockage : IBlockage
     private static readonly BitBoard PawnFileASquares =
         File.FileA.FileBB() & ~(Rank.Rank1.RankBB() | Rank.Rank8.RankBB());
 
-    private readonly IPosition _pos;
-
-    private readonly BitBoard _ourPawns;
-
-    private readonly BitBoard _theirPawns;
-
-    private readonly Piece _ourPawn;
-
-    private readonly Piece _theirPawn;
-
-    private readonly Player _us;
+    /// <inheritdoc />
+    public bool IsBlocked(in IPosition pos)
+    {
+        // Quick check if there is only pawns and kings on the board
+        // It might be possible to have a minor piece and exchange it into a passing pawn
+        if (pos.PieceCount(PieceTypes.AllPieces) > pos.PieceCount(PieceTypes.Pawn) + 2)
+            return false;
 
-    private readonly Player _them;
+        // Contains the rank for each file which has a fence
+        Span<Rank> fenceRank = stackalloc Rank[File.Count];
 
-    /// <summary>
-    /// Contains the rank for each file which has a fence
-    /// </summary>
-    private readonly Rank[] _fenceRank;
+        var us = pos.SideToMove;
+        var them = ~us;
 
-    private BitBoard _dynamicPawns;
-    private BitBoard _fixedPawn;
-    private BitBoard _marked;
-    private BitBoard _fence;
-    private BitBoard _processed;
+        var ourPawns = pos.Pieces(PieceTypes.Pawn, us);
+        var theirPawns = pos.Pieces(PieceTypes.Pawn, them);
+        var ourPawn = PieceTypes.Pawn.MakePiece(us);
 
-    public Blockage(in IPosition pos)
-    {
-        _pos = pos;
-        _fenceRank = new Rank[File.Count];
-        _us = pos.SideToMove;
-        _them = ~_us;
-        _ourPawns = pos.Pieces(PieceTypes.Pawn, _us);
-        _theirPawns = pos.Pieces(PieceTypes.Pawn, _them);
-        _ourPawn = PieceTypes.Pawn.MakePiece(_us);
-        _theirPawn = ~_ourPawn;
-    }
+        var up = us.PawnPushDistance();
 
-    /// <summary>
-    /// Computes whether the current position contains a pawn fence which makes the game a draw.
-    /// </summary>
-    /// <returns>true if the game is a draw position - otherwise false</returns>
-    public bool IsBlocked()
-    {
-        // Quick check if there is only pawns and kings on the board It might be possible to
-        // have a minor piece and exchange it into a passing pawn
-        if (_pos.Board.PieceCount(PieceTypes.AllPieces) > _pos.Board.PieceCount(PieceTypes.Pawn) + 2)
-            return false;
+        var fixedPawn = BitBoards.EmptyBitBoard;
+        var marked = BitBoards.EmptyBitBoard;
+        var dynamicPawns = BitBoards.EmptyBitBoard;
+        var processed = BitBoards.EmptyBitBoard;
 
-        var up = _us.PawnPushDistance();
+        var fence = BitBoards.EmptyBitBoard;
 
-        MarkOurPawns(up);
-        MarkTheirPawns();
+        MarkOurPawns(in pos, ref fixedPawn, ref marked, ref dynamicPawns);
+        MarkTheirPawns(ref marked, in theirPawns, us);
 
-        var isFenceFormed = IsFenceFormed();
+        var isFenceFormed = FormFence(in marked, ref fence, ref processed, us);
 
         if (!isFenceFormed)
             return false;
 
-        ComputeFenceRanks();
+        ComputeFenceRanks(fenceRank, in fence);
 
-        var ourKsq = _pos.GetKingSquare(_us);
+        var ourKsq = pos.GetKingSquare(us);
 
-        if (ourKsq.RelativeRank(_us) > _fenceRank[ourKsq.File.AsInt()].Relative(_us))
+        if (ourKsq.RelativeRank(us) > fenceRank[ourKsq.File.AsInt()].Relative(us))
             return false;
 
-        var theirKsq = _pos.GetKingSquare(_them);
-        _dynamicPawns |= ComputeDynamicFencedPawns();
+        var theirKsq = pos.GetKingSquare(them);
+        dynamicPawns |= ComputeDynamicFencedPawns(in pos, fenceRank, in theirPawns, us);
 
-        while (_dynamicPawns)
+        while (dynamicPawns)
         {
-            var sq = BitBoards.PopLsb(ref _dynamicPawns);
+            var sq = BitBoards.PopLsb(ref dynamicPawns);
             var (r, f) = sq;
-            var rr = r.Relative(_us);
+            var rr = r.Relative(us);
 
-            if (r > _fenceRank[f.AsInt()])
+            if (r > fenceRank[f.AsInt()])
             {
-                if ((_theirPawns & sq.PassedPawnFrontAttackSpan(_us)).IsEmpty &&
-                    (theirKsq.File != f || theirKsq.Rank.Relative(_us) < rr))
+                if ((theirPawns & sq.PassedPawnFrontAttackSpan(us)).IsEmpty &&
+                    (theirKsq.File != f || theirKsq.Rank.Relative(us) < rr))
                     return false;
             }
-            else if (_fence & sq)
+            else if (fence & sq)
             {
                 if (rr >= Ranks.Rank6)
                     return false;
 
-                if (_pos.GetPiece(sq + _us.PawnDoublePushDistance()) != _theirPawn)
+                if (pos.GetPiece(sq + us.PawnDoublePushDistance()) != ~ourPawn)
                 {
-                    if (theirKsq.File != f || theirKsq.RelativeRank(_us) < rr)
+                    if (theirKsq.File != f || theirKsq.RelativeRank(us) < rr)
                         return false;
 
                     if (f != File.FileA)
                     {
-                        if (_pos.GetPiece(sq + Direction.West) != _ourPawn)
+                        if (pos.GetPiece(sq + Direction.West) != ourPawn)
                             return false;
 
-                        if (BitBoards.PopCount(_ourPawns & PreviousFile(f)) > 1)
+                        if (BitBoards.PopCount(ourPawns & PreviousFile(f)) > 1)
                             return false;
 
-                        if ((_fixedPawn & (sq + Direction.West)).IsEmpty)
+                        if ((fixedPawn & (sq + Direction.West)).IsEmpty)
                             return false;
 
-                        if ((_fence & (sq + Direction.West)).IsEmpty)
+                        if ((fence & (sq + Direction.West)).IsEmpty)
                             return false;
                     }
 
                     if (f != File.FileH)
                     {
-                        if (_pos.GetPiece(sq + Direction.East) != _ourPawn)
+                        if (pos.GetPiece(sq + Direction.East) != ourPawn)
                             return false;
 
-                        if (BitBoards.PopCount(_ourPawns & NextFile(f)) > 1)
+                        if (BitBoards.PopCount(ourPawns & NextFile(f)) > 1)
                             return false;
 
-                        if ((_fixedPawn & (sq + Direction.East)).IsEmpty)
+                        if ((fixedPawn & (sq + Direction.East)).IsEmpty)
                             return false;
 
-                        if ((_fence & (sq + Direction.East)).IsEmpty)
+                        if ((fence & (sq + Direction.East)).IsEmpty)
                             return false;
                     }
                 }
 
-                if ((sq + _us.PawnDoublePushDistance()).PawnAttack(_us) & _theirPawns)
+                if ((sq + us.PawnDoublePushDistance()).PawnAttack(us) & theirPawns)
                     return false;
 
-                if (BitBoards.MoreThanOne(_ourPawns & f))
+                if (BitBoards.MoreThanOne(ourPawns & f))
                     return false;
             }
-            else if (r < _fenceRank[f.AsInt()])
+            else if (r < fenceRank[f.AsInt()])
             {
                 sq += up;
                 r = sq.Rank;
-                rr = r.Relative(_us);
+                rr = r.Relative(us);
 
-                while ((_fence & Square.Create(r, f)).IsEmpty)
+                while ((fence & Square.Create(r, f)).IsEmpty)
                 {
-                    var pawnAttacks = sq.PawnAttack(_us);
-                    if (_theirPawns & pawnAttacks)
+                    var pawnAttacks = sq.PawnAttack(us);
+                    if (theirPawns & pawnAttacks)
                         return false;
 
-                    if (_ourPawns & sq)
+                    if (ourPawns & sq)
                         break;
 
                     sq += up;
                     r = sq.Rank;
                 }
 
-                if (_pos.IsOccupied(sq) || (_fence & Square.Create(r, f)).IsEmpty)
+                if (pos.IsOccupied(sq) || (fence & Square.Create(r, f)).IsEmpty)
                     continue;
 
                 if (rr >= Ranks.Rank6)
                     return false;
 
-                if ((_theirPawns & (sq + _us.PawnDoublePushDistance())).IsEmpty)
+                if ((theirPawns & (sq + us.PawnDoublePushDistance())).IsEmpty)
                 {
-                    if (theirKsq.File != f || theirKsq.RelativeRank(_us) < rr)
+                    if (theirKsq.File != f || theirKsq.RelativeRank(us) < rr)
                         return false;
 
                     if (f != File.FileA)
                     {
-                        if (_pos.GetPiece(sq + Direction.West) != _ourPawn)
+                        if (pos.GetPiece(sq + Direction.West) != ourPawn)
                             return false;
 
-                        if (BitBoards.PopCount(_ourPawns & (f - 1)) > 1)
+                        if (BitBoards.PopCount(ourPawns & (f - 1)) > 1)
                             return false;
 
-                        if ((_fixedPawn & Square.Create(r, PreviousFile(f))).IsEmpty)
+                        if ((fixedPawn & Square.Create(r, PreviousFile(f))).IsEmpty)
                             return false;
 
-                        if ((_fence & Square.Create(r, PreviousFile(f))).IsEmpty)
+                        if ((fence & Square.Create(r, PreviousFile(f))).IsEmpty)
                             return false;
                     }
 
                     if (f != File.FileH)
                     {
-                        if (_pos.GetPiece(sq + Direction.East) != _ourPawn)
+                        if (pos.GetPiece(sq + Direction.East) != ourPawn)
                             return false;
 
-                        if (BitBoards.PopCount(_ourPawns & (f + 1)) > 1)
+                        if (BitBoards.PopCount(ourPawns & (f + 1)) > 1)
                             return false;
 
-                        if ((_fixedPawn & Square.Create(r, NextFile(f))).IsEmpty)
+                        if ((fixedPawn & Square.Create(r, NextFile(f))).IsEmpty)
                             return false;
 
-                        if ((_fence & Square.Create(r, NextFile(f))).IsEmpty)
+                        if ((fence & Square.Create(r, NextFile(f))).IsEmpty)
                             return false;
                     }
                 }
 
-                if ((sq + up).PawnAttack(_us) & _theirPawns)
+                if ((sq + up).PawnAttack(us) & theirPawns)
                     return false;
             }
         }
@@ -242,52 +219,60 @@ public bool IsBlocked()
     /// <summary>
     /// Computes the fence ranks
     /// </summary>
-    private void ComputeFenceRanks()
+    private void ComputeFenceRanks(Span<Rank> fenceRank, in BitBoard fence)
     {
-        var covered = _fence;
+        var covered = fence;
 
         while (covered)
         {
             var sq = BitBoards.PopLsb(ref covered);
             var (r, f) = sq;
-            _fenceRank[f.AsInt()] = r;
+            fenceRank[f.AsInt()] = r;
         }
     }
 
     /// <summary>
     /// Marks the current players pawns as either fixed and marked or dynamic
     /// </summary>
-    /// <param name="up">The up direction for the current player</param>
-    private void MarkOurPawns(Direction up)
+    private static void MarkOurPawns(
+        in IPosition pos,
+        ref BitBoard fixedPawn,
+        ref BitBoard marked,
+        ref BitBoard dynamicPawns)
     {
-        var ourPawns = _ourPawns;
+        var us = pos.SideToMove;
+        var them = ~us;
+        var up = us.PawnPushDistance();
+        var ourPawns = pos.Pieces(PieceTypes.Pawn, us);
+        var theirPawns = pos.Pieces(PieceTypes.Pawn, them);
+        var theirPawn = PieceTypes.Pawn.MakePiece(them);
 
         while (ourPawns)
         {
             var psq = BitBoards.PopLsb(ref ourPawns);
-            var rr = psq.RelativeRank(_us);
+            var rr = psq.RelativeRank(us);
             if (rr < Rank.Rank7
-                && (_pos.GetPiece(psq + up) == _theirPawn || !(_fixedPawn & (psq + up)).IsEmpty)
-                && (psq.PawnAttack(_us) & _theirPawns).IsEmpty)
+                && (pos.GetPiece(psq + up) == theirPawn || !(fixedPawn & (psq + up)).IsEmpty)
+                && (psq.PawnAttack(us) & theirPawns).IsEmpty)
             {
-                _fixedPawn |= psq;
-                _marked |= psq;
+                fixedPawn |= psq;
+                marked |= psq;
             }
             else
-                _dynamicPawns |= psq;
+                dynamicPawns |= psq;
         }
     }
 
     /// <summary>
     /// Marks the opponent pawn attacks
     /// </summary>
-    private void MarkTheirPawns()
+    private static void MarkTheirPawns(ref BitBoard marked, in BitBoard theirPawns, Player us)
     {
-        var (southEast, southWest) = _us.IsWhite
+        var (southEast, southWest) = us.IsWhite
             ? (Direction.SouthEast, Direction.SouthWest)
             : (Direction.NorthEast, Direction.NorthWest);
 
-        _marked |= _theirPawns.Shift(southEast) | _theirPawns.Shift(southWest);
+        marked |= theirPawns.Shift(southEast) | theirPawns.Shift(southWest);
     }
 
     /// <summary>
@@ -295,70 +280,83 @@ private void MarkTheirPawns()
     /// perform a depth first verification of its surrounding squares.
     /// </summary>
     /// <param name="sq">The square which is currently being looked at</param>
+    /// <param name="processed"></param>
+    /// <param name="fence"></param>
+    /// <param name="marked"></param>
+    /// <param name="us"></param>
     /// <returns>true if the square is in the fence</returns>
-    private bool FormsFence(Square sq)
+    private static bool FormsFence(Square sq, ref BitBoard processed, ref BitBoard fence, in BitBoard marked, Player us)
     {
-        _processed |= sq;
+        processed |= sq;
 
         // File H is marked as fence if it is reached.
         if (sq.File == File.FileH)
         {
-            _fence |= sq;
+            fence |= sq;
             return true;
         }
 
+        var them = ~us;
+
         Span<Direction> directions = stackalloc Direction[]
-            { _us.PawnPushDistance(), Directions.East, _them.PawnPushDistance() };
+            { us.PawnPushDistance(), Directions.East, them.PawnPushDistance() };
 
         ref var directionSpace = ref MemoryMarshal.GetReference(directions);
         for (var i = 0; i < directions.Length; ++i)
         {
             var direction = Unsafe.Add(ref directionSpace, i);
             var s = sq + direction;
-            if ((_marked & s).IsEmpty || !(_processed & s).IsEmpty || !FormsFence(s))
+            if ((marked & s).IsEmpty || !(processed & s).IsEmpty ||
+                !FormsFence(s, ref processed, ref fence, in marked, us))
                 continue;
-            _fence |= s;
+            fence |= s;
             return true;
         }
 
         return false;
     }
 
-    private Square NextFenceRankSquare(File f, Player them)
-        => new Square(_fenceRank[f.AsInt()].AsInt() * 8 + f.AsInt()) + them.PawnPushDistance();
+    private static Square NextFenceRankSquare(Span<Rank> fenceRank, File f, Player them)
+        => new Square(fenceRank[f.AsInt()].AsInt() * 8 + f.AsInt()) + them.PawnPushDistance();
 
-    private bool IsFenceFormed()
+    private static bool FormFence(in BitBoard marked, ref BitBoard fence, ref BitBoard processed, Player us)
     {
         var bb = PawnFileASquares;
         while (bb)
         {
             var startSquare = BitBoards.PopLsb(ref bb);
-            if ((_marked & startSquare).IsEmpty || !FormsFence(startSquare))
+            if ((marked & startSquare).IsEmpty || !FormsFence(startSquare, ref processed, ref fence, in marked, us))
                 continue;
-            _fence |= startSquare;
+            fence |= startSquare;
             return true;
         }
 
         return false;
     }
 
-    private BitBoard ComputeDynamicFencedPawns()
+    private static BitBoard ComputeDynamicFencedPawns(
+        in IPosition pos,
+        Span<Rank> fenceRank,
+        in BitBoard theirPawns,
+        Player us)
     {
         // reverse order of Down
-        var down = _us.PawnPushDistance();
+        var down = us.PawnPushDistance();
 
+        var them = ~us;
+        var ourPawn = PieceTypes.Pawn.MakePiece(us);
         var result = BitBoard.Empty;
 
         ref var fileSpace = ref MemoryMarshal.GetArrayDataReference(File.AllFiles);
         for (var i = 0; i < File.AllFiles.Length; ++i)
         {
             var f = Unsafe.Add(ref fileSpace, i);
-            var sq = NextFenceRankSquare(f, _them);
-            var b = sq.ForwardFile(_them) & _theirPawns;
+            var sq = NextFenceRankSquare(fenceRank, f, them);
+            var b = sq.ForwardFile(them) & theirPawns;
             while (b)
             {
                 sq = BitBoards.PopLsb(ref b) + down;
-                if (_pos.GetPiece(sq) == _ourPawn)
+                if (pos.GetPiece(sq) == ourPawn)
                     result |= sq;
             }
         }
diff --git a/src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs
new file mode 100644
index 00000000..482f1b5e
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs
@@ -0,0 +1,63 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Evaluation;
+
+public interface IKpkBitBase
+{
+    /// <summary>
+    /// Normalizes a square in accordance with the data.
+    /// Kpk bit-base is only used for king pawn king positions!
+    /// </summary>
+    /// <param name="pos">Current position</param>
+    /// <param name="strongSide">The strong side (the one with the pawn)</param>
+    /// <param name="sq">The square that needs to be normalized</param>
+    /// <returns>Normalized square to be used with probing</returns>
+    Square Normalize(IPosition pos, Player strongSide, Square sq);
+    
+    /// <summary>
+    /// Probe with normalized squares and strong player
+    /// </summary>
+    /// <param name="strongKsq">"Strong" side king square</param>
+    /// <param name="strongPawnSq">"Strong" side pawn square</param>
+    /// <param name="weakKsq">"Weak" side king square</param>
+    /// <param name="stm">strong side. fx strongSide == pos.SideToMove ? Player.White : Player.Black</param>
+    /// <returns>true if strong side "won"</returns>
+    bool Probe(Square strongKsq, Square strongPawnSq, Square weakKsq, Player stm);
+
+    /// <summary>
+    /// </summary>
+    /// <param name="stngActive"></param>
+    /// <param name="skSq">White King</param>
+    /// <param name="wkSq">Black King</param>
+    /// <param name="spSq">White Pawn</param>
+    /// <returns></returns>
+    bool Probe(bool stngActive, Square skSq, Square wkSq, Square spSq);
+
+    bool IsDraw(IPosition pos);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index f5c5e6df..0bae64b5 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -39,15 +39,15 @@ namespace Rudzoft.ChessLib.Evaluation;
 /// Based on bit base in Stockfish.
 /// However, some modifications are done to be slightly less confusing.
 /// </summary>
-public static class KpkBitBase
+public sealed class KpkBitBase : IKpkBitBase
 {
     // There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
     // Positions with the pawn on files E to H will be mirrored before probing.
     private const int MaxIndex = 2 * 24 * 64 * 64; // stm * psq * wksq * bksq = 196608
 
-    private static readonly BitArray KpKbb = new(MaxIndex);
+    private readonly BitArray _kpKbb = new(MaxIndex);
 
-    static KpkBitBase()
+    public KpkBitBase()
     {
         // Initialize db with known win / draw positions
         var db = Enumerable
@@ -67,19 +67,18 @@ static KpkBitBase()
             for (var i = 0; i < db.Length; ++i)
             {
                 var kpkPosition = Unsafe.Add(ref dbSpace, i);
-                repeat = (kpkPosition.Result == Result.Unknown && kpkPosition.Classify(db) != Result.Unknown).AsByte();
+                repeat = (kpkPosition.Result == Results.Unknown && kpkPosition.Classify(db) != Results.Unknown).AsByte();
             }
         }
 
-
         // Fill the bitbase with the decisive results
         var idx = 0;
         ref var setDbSpace = ref MemoryMarshal.GetReference(db);
         for (var i = 0; i < db.Length; ++i)
         {
             var kpkPosition = Unsafe.Add(ref setDbSpace, i);
-            if (kpkPosition.Result == Result.Win)
-                KpKbb.Set(idx, true);
+            if (kpkPosition.Result == Results.Win)
+                _kpKbb.Set(idx, true);
             idx++;
         }
     }
@@ -110,31 +109,19 @@ private static int Index(Player stm, Square weakKingSq, Square strongKsq, Square
     }
 
     [Flags]
-    private enum Result
+    private enum Results
     {
         None = 0,
-        Unknown = 1 << 0,
-        Draw = 1 << 1,
-        Win = 1 << 2
+        Unknown = 1,
+        Draw = 2,
+        Win = 4
     }
 
-    private struct KpkPosition
+    private readonly record struct KpkPosition(Results Result, Player Stm, Square[] KingSquares, Square PawnSquare)
     {
-        private KpkPosition(
-            Result result,
-            Player stm,
-            Square[] kingSquares,
-            Square pawnSquare)
-        {
-            Result = result;
-            Stm = stm;
-            KingSquares = kingSquares;
-            PawnSquare = pawnSquare;
-        }
-
         public static KpkPosition Create(int idx)
         {
-            Result result;
+            Results results;
             var stm = new Player((idx >> 12) & 0x01);
             var ksq = new Square[] { (idx >> 0) & 0x3F, (idx >> 6) & 0x3F };
             var psq = Square.Create(new Rank(Ranks.Rank7.AsInt() - ((idx >> 15) & 0x7)), new File((idx >> 13) & 0x3));
@@ -144,7 +131,7 @@ public static KpkPosition Create(int idx)
                 || ksq[Player.White.Side] == psq
                 || ksq[Player.Black.Side] == psq
                 || (stm.IsWhite && !(psq.PawnAttack(Player.White) & ksq[Player.Black.Side]).IsEmpty))
-                result = Result.None;
+                results = Results.None;
 
             // Win if the pawn can be promoted without getting captured
             else if (stm.IsWhite
@@ -152,7 +139,7 @@ public static KpkPosition Create(int idx)
                      && ksq[Player.White.Side] != psq + Directions.North
                      && (ksq[Player.Black.Side].Distance(psq + Directions.North) > 1
                          || ksq[Player.White.Side].Distance(psq + Directions.North) == 1))
-                result = Result.Win;
+                results = Results.Win;
 
             // Draw if it is stalemate or the black king can capture the pawn
             else if (stm.IsBlack
@@ -161,24 +148,19 @@ public static KpkPosition Create(int idx)
                          .IsEmpty
                          || !(PieceTypes.King.PseudoAttacks(ksq[Player.Black.Side]) &
                               ~PieceTypes.King.PseudoAttacks(ksq[Player.White.Side]) & psq).IsEmpty))
-                result = Result.Draw;
+                results = Results.Draw;
 
             // Position will be classified later in initialization
             else
-                result = Result.Unknown;
+                results = Results.Unknown;
 
             return new KpkPosition(
-                result,
+                results,
                 stm,
                 ksq,
                 psq);
         }
 
-        public Result Result { get; }
-        private Player Stm { get; }
-        private Square[] KingSquares { get; }
-        private Square PawnSquare { get; }
-
         /// <summary>
         /// White to move: If one move leads to a position classified as WIN, the result
         /// of the current position is WIN. If all moves lead to positions classified
@@ -192,33 +174,33 @@ public static KpkPosition Create(int idx)
         /// </summary>
         /// <param name="db">Current KpkPositions as list</param>
         /// <returns>Result after classification</returns>
-        public Result Classify(ReadOnlySpan<KpkPosition> db)
+        public Results Classify(ReadOnlySpan<KpkPosition> db)
         {
             var (good, bad) = Stm.IsWhite
-                ? (Result.Win, Result.Draw)
-                : (Result.Draw, Result.Win);
+                ? (Results.Win, Results.Draw)
+                : (Results.Draw, Results.Win);
 
-            var r = Result.None;
+            var r = Results.None;
             var b = PieceTypes.King.PseudoAttacks(KingSquares[Stm.Side]);
 
             while (b)
             {
-                var (bksq, wksq) = Stm.IsWhite
+                var (bkSq, wkSq) = Stm.IsWhite
                     ? (KingSquares[Player.Black.Side], BitBoards.PopLsb(ref b))
                     : (BitBoards.PopLsb(ref b), KingSquares[Player.White.Side]);
-                r |= db[Index(~Stm, bksq, wksq, PawnSquare)].Result;
+                r |= db[Index(~Stm, bkSq, wkSq, PawnSquare)].Result;
             }
 
             if (Stm.IsWhite)
             {
                 // Single push
-                if (PawnSquare.Rank < Ranks.Rank7)
+                if (PawnSquare.Rank < Rank.Rank7)
                     r |= db[
                         Index(Player.Black, KingSquares[Player.Black.Side], KingSquares[Player.White.Side],
                             PawnSquare + Directions.North)].Result;
 
                 // Double push
-                if (PawnSquare.Rank == Ranks.Rank2
+                if (PawnSquare.Rank == Rank.Rank2
                     && PawnSquare + Directions.North != KingSquares[Player.White.Side]
                     && PawnSquare + Directions.North != KingSquares[Player.Black.Side])
                     r |= db[
@@ -229,57 +211,37 @@ public Result Classify(ReadOnlySpan<KpkPosition> db)
             if ((r & good) != 0)
                 return good;
 
-            return (r & Result.Unknown) != 0
-                    ? Result.Unknown
+            return (r & Results.Unknown) != 0
+                    ? Results.Unknown
                     : bad;
         }
     }
 
-    /// <summary>
-    /// Normalizes a square in accordance with the data.
-    /// Kpk bit-base is only used for king pawn king positions!
-    /// </summary>
-    /// <param name="pos">Current position</param>
-    /// <param name="strongSide">The strong side (the one with the pawn)</param>
-    /// <param name="sq">The square that needs to be normalized</param>
-    /// <returns>Normalized square to be used with probing</returns>
-    public static Square Normalize(IPosition pos, Player strongSide, Square sq)
+    /// <inheritdoc />
+    public Square Normalize(IPosition pos, Player strongSide, Square sq)
     {
         Debug.Assert(pos.Board.PieceCount(PieceTypes.Pawn, strongSide) == 1);
 
-        if (pos.GetPieceSquare(PieceTypes.Pawn, strongSide).File >= Files.FileE)
+        if (pos.GetPieceSquare(PieceTypes.Pawn, strongSide).File >= File.FileE)
             sq = sq.FlipFile();
 
         return strongSide.IsWhite ? sq : sq.FlipRank();
     }
 
-    /// <summary>
-    /// Probe with normalized squares and strong player
-    /// </summary>
-    /// <param name="strongKsq">"Strong" side king square</param>
-    /// <param name="strongPawnSq">"Strong" side pawn square</param>
-    /// <param name="weakKsq">"Weak" side king square</param>
-    /// <param name="stm">strong side. fx strongSide == pos.SideToMove ? Player.White : Player.Black</param>
-    /// <returns>true if strong side "won"</returns>
-    public static bool Probe(Square strongKsq, Square strongPawnSq, Square weakKsq, Player stm)
+    /// <inheritdoc />
+    public bool Probe(Square strongKsq, Square strongPawnSq, Square weakKsq, Player stm)
     {
         Debug.Assert(strongPawnSq.File <= File.FileD);
-        return KpKbb[Index(stm, weakKsq, strongKsq, strongPawnSq)];
+        return _kpKbb[Index(stm, weakKsq, strongKsq, strongPawnSq)];
     }
 
-    /// <summary>
-    /// </summary>
-    /// <param name="stngActive"></param>
-    /// <param name="skSq">White King</param>
-    /// <param name="wkSq">Black King</param>
-    /// <param name="spSq">White Pawn</param>
-    /// <returns></returns>
-    public static bool Probe(bool stngActive, Square skSq, Square wkSq, Square spSq)
+    /// <inheritdoc />
+    public bool Probe(bool stngActive, Square skSq, Square wkSq, Square spSq)
     {
-        return KpKbb[Index(stngActive, skSq, wkSq, spSq)];
+        return _kpKbb[Index(stngActive, skSq, wkSq, spSq)];
     }
     
-    public static bool IsDraw(IPosition pos)
+    public bool IsDraw(IPosition pos)
     {
         return !Probe(
             pos.GetPieceSquare(PieceTypes.King, Player.White),
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
new file mode 100644
index 00000000..a1e7eb9c
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -0,0 +1,104 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System;
+using System.IO;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Hash.Tables.Transposition;
+using Rudzoft.ChessLib.Polyglot;
+using Rudzoft.ChessLib.Protocol.UCI;
+using Rudzoft.ChessLib.Tables;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Extensions;
+
+public static class ChessLibServiceCollectionExtensions
+{
+    public static IServiceCollection AddChessLib(
+        this IServiceCollection serviceCollection,
+        IConfiguration? configuration,
+        string? configurationFile = null)
+    {
+        if (serviceCollection == null)
+            throw new ArgumentNullException(nameof(serviceCollection));
+
+        if (configuration == null)
+        {
+            configuration = LoadConfiguration(configurationFile);
+            serviceCollection.AddSingleton(configuration);
+        }
+
+        serviceCollection.AddOptions<TranspositionTableConfiguration>().Configure<IConfiguration>(
+            static (settings, configuration)
+                => configuration
+                    .GetSection(TranspositionTableConfiguration.Section)
+                    .Bind(settings));
+
+        serviceCollection.AddOptions<PolyglotBookConfiguration>().Configure<IConfiguration>(
+            static (settings, configuration)
+                => configuration
+                    .GetSection(PolyglotBookConfiguration.Section)
+                    .Bind(settings));
+
+        serviceCollection.TryAddSingleton<ITranspositionTable, TranspositionTable>();
+
+        return serviceCollection.AddSingleton(static _ =>
+            {
+                IUci uci = new Uci();
+                uci.Initialize();
+                return uci;
+            })
+            .AddTransient(static _ => KillerMoves.Create(64))
+            .AddSingleton<ISearchParameters, SearchParameters>()
+            .AddSingleton<IValues, Values>()
+            .AddTransient<IBoard, Board>()
+            .AddTransient<IPosition, Position>()
+            .AddTransient<IGame, Game>()
+            .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>();
+    }
+
+    private static IConfigurationRoot LoadConfiguration(string? file)
+    {
+        var configurationFile = string.IsNullOrWhiteSpace(file) ? "chesslib.json" : file;
+        var configurationFilePath = Path.Combine(AppContext.BaseDirectory, configurationFile);
+        return new ConfigurationBuilder()
+            .AddJsonFile(configurationFilePath, optional: true, reloadOnChange: true)
+            .AddEnvironmentVariables()
+            .Build();
+    }
+
+    public static void AddFactory<TService, TImplementation>(this IServiceCollection services)
+        where TService : class
+        where TImplementation : class, TService
+    {
+        services.AddTransient<TService, TImplementation>();
+        services.AddSingleton<Func<TService>>(static x => () => x.GetService<TService>()!);
+        services.AddSingleton<IServiceFactory<TService>, ServiceFactory<TService>>();
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
index e5d02726..52d8088d 100644
--- a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
@@ -99,7 +99,7 @@ public static IEnumerable<int> GetLocations(this string @this, char token = ' ')
     public static bool IsNullOrEmpty(this string @this) => string.IsNullOrEmpty(@this);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool IsNullOrWhiteSpace(this string @this) => string.IsNullOrWhiteSpace(@this);
+    public static bool IsNullOrWhiteSpace(this string? @this) => string.IsNullOrWhiteSpace(@this);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static char FlipCase(this char c)
diff --git a/src/Rudzoft.ChessLib/Factories/BlockageFactory.cs b/src/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs
similarity index 85%
rename from src/Rudzoft.ChessLib/Factories/BlockageFactory.cs
rename to src/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs
index b176ab11..6a353d4e 100644
--- a/src/Rudzoft.ChessLib/Factories/BlockageFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs
@@ -1,4 +1,4 @@
-/*
+/*
 ChessLib, a chess data structure library
 
 MIT License
@@ -24,11 +24,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using Rudzoft.ChessLib.Polyglot;
+
 namespace Rudzoft.ChessLib.Factories;
 
-public static class BlockageFactory
+public interface IPolyglotBookFactory
 {
-    public static IBlockage Create(in IPosition pos) => new Blockage(in pos);
-
-    public static bool IsBlocked(in IPosition pos) => Create(in pos).IsBlocked();
-}
+    IPolyglotBook Create(string bookFile);
+    IPolyglotBook Create();
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Factories/GameFactory.cs b/src/Rudzoft.ChessLib/Factories/IServiceFactory.cs
similarity index 68%
rename from src/Rudzoft.ChessLib/Factories/GameFactory.cs
rename to src/Rudzoft.ChessLib/Factories/IServiceFactory.cs
index 703c2379..794324c2 100644
--- a/src/Rudzoft.ChessLib/Factories/GameFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/IServiceFactory.cs
@@ -24,24 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Types;
-
 namespace Rudzoft.ChessLib.Factories;
 
-public static class GameFactory
+public interface IServiceFactory<out T>
 {
-    public static IGame Create(IPosition position) => new Game(position);
-
-    public static IGame Create(string fen, bool validate = false)
-    {
-        var g = Create();
-        var fenData = new FenData(fen);
-        var state = new State();
-        g.Pos.Set(in fenData, Enums.ChessMode.Normal, state, validate);
-        return g;
-    }
-
-    public static IGame Create()
-        => new Game(new Position(new Board(), new Values()));
-}
+    T Create();
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs b/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
new file mode 100644
index 00000000..94d7bbde
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
@@ -0,0 +1,54 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System.Runtime.CompilerServices;
+using Microsoft.Extensions.Options;
+using Rudzoft.ChessLib.Polyglot;
+
+namespace Rudzoft.ChessLib.Factories;
+
+public sealed class PolyglotBookFactory : IPolyglotBookFactory
+{
+    private readonly string _path;
+
+    public PolyglotBookFactory(IOptions<PolyglotBookConfiguration> options)
+    {
+        var config = options.Value;
+        _path = string.IsNullOrWhiteSpace(config.BookPath) ? string.Empty : config.BookPath;
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public IPolyglotBook Create(string bookFile)
+    {
+        return PolyglotBook.Create(_path, bookFile);
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public IPolyglotBook Create()
+    {
+        return PolyglotBook.Create();
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Perft/PerftFactory.cs b/src/Rudzoft.ChessLib/Factories/ServiceFactory.cs
similarity index 69%
rename from src/Rudzoft.ChessLib.Perft/PerftFactory.cs
rename to src/Rudzoft.ChessLib/Factories/ServiceFactory.cs
index 4a85e4d2..d0be1672 100644
--- a/src/Rudzoft.ChessLib.Perft/PerftFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/ServiceFactory.cs
@@ -1,5 +1,5 @@
 /*
-Perft, a chess perft test library
+ChessLib, a chess data structure library
 
 MIT License
 
@@ -25,18 +25,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using Rudzoft.ChessLib.Factories;
-using Rudzoft.ChessLib.Perft.Interfaces;
 
-namespace Rudzoft.ChessLib.Perft;
+namespace Rudzoft.ChessLib.Factories;
 
-public static class PerftFactory
+public sealed class ServiceFactory<T> : IServiceFactory<T>
 {
-    public static IPerft Create(Action<string> boardPrintCallback = null, IEnumerable<PerftPosition> positions = null)
-    {
-        positions ??= Enumerable.Empty<PerftPosition>();
-        return new Perft(GameFactory.Create(), positions);
-    }
+    private readonly Func<T> _initFunc;
+    public ServiceFactory(Func<T> initFunc) => _initFunc = initFunc;
+    public T Create() => _initFunc();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 86bb1776..94097c47 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -45,17 +45,18 @@ public sealed class Game : IGame
     private readonly ObjectPool<IMoveList> _moveLists;
     private readonly IPosition _pos;
 
-    public Game(IPosition pos)
+    public Game(
+        ITranspositionTable transpositionTable,
+        IUci uci,
+        ISearchParameters searchParameters,
+        IPosition pos)
     {
-        _pos = pos;
         _moveLists = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
-        SearchParameters = new SearchParameters();
-    }
-
-    static Game()
-    {
-        Table = new TranspositionTable(256);
-        Uci = new Uci();
+        _pos = pos;
+        
+        Table = transpositionTable;
+        SearchParameters = searchParameters;
+        Uci = uci;
     }
 
     public Action<IPieceSquare> PieceUpdated => _pos.PieceUpdated;
@@ -68,11 +69,11 @@ static Game()
 
     public GameEndTypes GameEndType { get; set; }
 
-    public static TranspositionTable Table { get; }
+    public ITranspositionTable Table { get; }
 
-    public SearchParameters SearchParameters { get; }
+    public ISearchParameters SearchParameters { get; }
 
-    public static IUci Uci { get; }
+    public IUci Uci { get; }
 
     public bool IsRepetition => _pos.IsRepetition;
 
@@ -113,17 +114,16 @@ public void UpdateDrawTypes()
     }
 
     public override string ToString()
-        => _pos.ToString();
+    {
+        return _pos.ToString() ?? string.Empty;
+    }
 
-    IEnumerator IEnumerable.GetEnumerator()
-        => GetEnumerator();
+    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
-    public IEnumerator<Piece> GetEnumerator()
-        => _pos.GetEnumerator();
+    public IEnumerator<Piece> GetEnumerator() => _pos.GetEnumerator();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard OccupiedBySide(Player p)
-        => _pos.Pieces(p);
+    public BitBoard OccupiedBySide(Player p) => _pos.Pieces(p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Player CurrentPlayer() => _pos.SideToMove;
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index c33c255b..c497c519 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Types;
 
@@ -49,10 +50,11 @@ static TranspositionTable()
         }
     }
 
-    public TranspositionTable(int mbSize)
+    public TranspositionTable(IOptions<TranspositionTableConfiguration> options)
     {
         _table = Array.Empty<Cluster>();
-        SetSize(mbSize);
+        Size = options.Value.DefaultSize;
+        SetSize(Size);
     }
 
     /// <summary>
@@ -123,6 +125,7 @@ public ref Cluster FindCluster(in HashKey key)
     /// Probes the transposition table for a entry that matches the position key.
     /// </summary>
     /// <param name="key">The position key</param>
+    /// <param name="e">Target entry</param>
     /// <returns>(true, entry) if one was found, (false, empty) if not found</returns>
     public bool Probe(in HashKey key, ref TranspositionTableEntry e)
     {
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
new file mode 100644
index 00000000..3b4df516
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
@@ -0,0 +1,37 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System.ComponentModel.DataAnnotations;
+
+namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
+
+public sealed class TranspositionTableConfiguration
+{
+    public const string Section = "TranspositionTable";
+    
+    [Range(1, 1 << 31, ErrorMessage = "Default size for TT: {0} must be between {1} and {2}.")]
+    public int DefaultSize { get; init; }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/IBlockage.cs b/src/Rudzoft.ChessLib/IBlockage.cs
index 364bde7d..682f5a97 100644
--- a/src/Rudzoft.ChessLib/IBlockage.cs
+++ b/src/Rudzoft.ChessLib/IBlockage.cs
@@ -32,5 +32,5 @@ public interface IBlockage
     /// Computes whether the current position contains a pawn fence which makes the game a draw.
     /// </summary>
     /// <returns>true if the game is a draw position - otherwise false</returns>
-    bool IsBlocked();
+    bool IsBlocked(in IPosition pos);
 }
diff --git a/src/Rudzoft.ChessLib/IGame.cs b/src/Rudzoft.ChessLib/IGame.cs
index 235e9eb3..de50ee5f 100644
--- a/src/Rudzoft.ChessLib/IGame.cs
+++ b/src/Rudzoft.ChessLib/IGame.cs
@@ -43,7 +43,7 @@ public interface IGame : IEnumerable<Piece>
 
     GameEndTypes GameEndType { get; set; }
 
-    SearchParameters SearchParameters { get; }
+    ISearchParameters SearchParameters { get; }
 
     bool IsRepetition { get; }
 
diff --git a/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
new file mode 100644
index 00000000..590fb952
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
@@ -0,0 +1,37 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Polyglot;
+
+public interface IPolyglotBook : IDisposable
+{
+    string BookFile { get; init; }
+    Move Probe(IPosition pos, bool pickBest = true);
+    HashKey ComputePolyglotKey(in IPosition pos);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index e4e7734b..cc9d1c2c 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -28,13 +28,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Polyglot;
 
-public sealed class PolyglotBook : IDisposable
+public sealed class PolyglotBook : IPolyglotBook
 {
     // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
     private static readonly int[] PieceMapping = { -1, 1, 3, 5, 7, 9, 11, -1, -1, 0, 2, 4, 6, 8, 10 };
@@ -47,44 +48,59 @@ public sealed class PolyglotBook : IDisposable
         CastleRight.BlackQueen
     };
 
-    private readonly IPosition _pos;
-    private readonly FileStream _fileStream;
-    private readonly BinaryReader _binaryReader;
-    private readonly string _fileName;
+    private readonly FileStream? _fileStream;
+    private readonly BinaryReader? _binaryReader;
+    private readonly string _bookFilePath;
     private readonly int _entrySize;
     private readonly Random _rnd;
 
-    public unsafe PolyglotBook(IPosition pos)
+    private unsafe PolyglotBook()
     {
-        _pos = pos;
         _entrySize = sizeof(PolyglotBookEntry);
         _rnd = new Random(DateTime.Now.Millisecond);
     }
 
-    public string FileName
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal static PolyglotBook Create()
     {
-        get => _fileName;
+        return new PolyglotBook();
+    }
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal static PolyglotBook Create(string path, string file)
+    {
+        return new PolyglotBook
+        {
+            BookFile = Path.Combine(path, file)
+        };
+    }
+
+    public string BookFile
+    {
+        get => _bookFilePath;
         init
         {
             if (string.IsNullOrEmpty(value))
                 return;
-            if (_fileName == value)
+            if (_bookFilePath == value)
                 return;
-            _fileName = value;
+            _bookFilePath = value;
             _fileStream = new FileStream(value, FileMode.Open, FileAccess.Read);
-            _binaryReader = new LittleEndianBinaryStreamReader(_fileStream);
+            _binaryReader = BitConverter.IsLittleEndian
+                ? new LittleEndianBinaryStreamReader(_fileStream)
+                : new BinaryReader(_fileStream);
         }
     }
 
     public Move Probe(IPosition pos, bool pickBest = true)
     {
-        if (_fileName.IsNullOrEmpty() || _fileStream == null)
+        if (_bookFilePath.IsNullOrEmpty() || _fileStream == null ||_binaryReader == null)
             return Move.EmptyMove;
 
         var polyMove = ushort.MinValue;
         var best = ushort.MinValue;
         var sum = uint.MinValue;
-        var key = ComputePolyglotKey();
+        var key = ComputePolyglotKey(pos);
         var firstIndex = FindFirst(in key);
 
         _fileStream.Seek(firstIndex * _entrySize, SeekOrigin.Begin);
@@ -120,7 +136,7 @@ public void Dispose()
         _binaryReader?.Dispose();
     }
 
-    private Move ConvertMove(IPosition pos, ushort polyMove)
+    private static Move ConvertMove(IPosition pos, ushort polyMove)
     {
         // A PolyGlot book move is encoded as follows:
         //
@@ -141,7 +157,7 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
             move = Move.Create(from, to, MoveTypes.Promotion, PolyToPt(polyPt));
         }
 
-        var ml = _pos.GenerateMoves();
+        var ml = pos.GenerateMoves();
 
         // Iterate all known moves for current position to find a match.
         var emMoves = ml.Select(static em => em.Move)
@@ -159,16 +175,13 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
         return emMoves.FirstOrDefault(Move.EmptyMove);
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static bool IsInCheck(IPosition pos, Move m)
     {
-        pos.GivesCheck(m);
-        var state = new State();
-        pos.MakeMove(m, in state);
-        var inCheck = pos.InCheck;
-        pos.TakeMove(m);
-        return inCheck;
+        return pos.GivesCheck(m);
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private PolyglotBookEntry ReadEntry() => new(
         _binaryReader.ReadUInt64(),
         _binaryReader.ReadUInt16(),
@@ -176,26 +189,27 @@ private static bool IsInCheck(IPosition pos, Move m)
         _binaryReader.ReadUInt32()
     );
 
-    public HashKey ComputePolyglotKey()
+    public HashKey ComputePolyglotKey(in IPosition pos)
     {
         var k = HashKey.Empty;
-        var b = _pos.Pieces();
+        var b = pos.Pieces();
 
         while (b)
         {
             var s = BitBoards.PopLsb(ref b);
-            var pc = _pos.GetPiece(s);
+            var pc = pos.GetPiece(s);
             var p = PieceMapping[pc.AsInt()];
             k ^= PolyglotBookZobrist.Psq(p, s);
         }
 
-        foreach (var cr in CastleRights.Where(cr => _pos.State.CastlelingRights.Has(cr)))
-            k ^= PolyglotBookZobrist.Castle(cr);
+        foreach (var cr in CastleRights.AsSpan())
+            if (pos.State.CastlelingRights.Has(cr))
+                k ^= PolyglotBookZobrist.Castle(cr);
 
-        if (_pos.State.EnPassantSquare != Square.None)
-            k ^= PolyglotBookZobrist.EnPassant(_pos.State.EnPassantSquare.File);
+        if (pos.State.EnPassantSquare != Square.None)
+            k ^= PolyglotBookZobrist.EnPassant(pos.State.EnPassantSquare.File);
 
-        if (_pos.SideToMove.IsWhite)
+        if (pos.SideToMove.IsWhite)
             k ^= PolyglotBookZobrist.Turn();
 
         return k;
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs
new file mode 100644
index 00000000..c8c41434
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs
@@ -0,0 +1,35 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+namespace Rudzoft.ChessLib.Polyglot;
+
+public sealed class PolyglotBookConfiguration
+{
+    public const string Section = "PolyglotBook";
+    
+    // ReSharper disable once UnusedAutoPropertyAccessor.Global
+    public string? BookPath { get; init; }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 8e4d4e5a..913d755f 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -30,6 +30,7 @@ namespace Rudzoft.ChessLib.Polyglot;
 
 internal static class PolyglotBookZobrist
 {
+    // TODO : re-write to jagged array
     private static readonly ulong[,] PsqKeys =
     {
         {
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 6c23992d..41f1e17f 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -33,6 +33,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Text;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
@@ -57,11 +58,11 @@ public sealed class Position : IPosition
     private readonly IPositionValidator _positionValidator;
     private Player _sideToMove;
 
-    public Position(IBoard board, IValues valueses)
+    public Position(IBoard board, IValues values)
     {
         Board = board;
-        Values = valueses;
-        State = default;
+        Values = values;
+        State = new State();
         _outputObjectPool = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
         _positionValidator = new PositionValidator(this, Board);
         _castleKingPath = new BitBoard[CastleRight.Count];
@@ -75,41 +76,34 @@ public Position(IBoard board, IValues valueses)
 
     public IBoard Board { get; }
 
-    public BitBoard Checkers
-        => State.Checkers;
+    public BitBoard Checkers => State.Checkers;
 
     public ChessMode ChessMode { get; set; }
 
-    public Square EnPassantSquare
-        => State.EnPassantSquare;
+    public Square EnPassantSquare => State.EnPassantSquare;
 
-    public string FenNotation
-        => GenerateFen().ToString();
+    public string FenNotation => GenerateFen().ToString();
 
-    public bool InCheck
-        => State.Checkers;
+    public bool InCheck => State.Checkers;
 
-    public bool IsMate
-        => this.GenerateMoves().Length == 0;
+    public bool IsMate => this.GenerateMoves().Length == 0;
 
     public bool IsProbing { get; set; }
 
-    public bool IsRepetition
-        => State.Repetition >= 3;
+    public bool IsRepetition => State.Repetition >= 3;
 
     /// <summary>
     /// To let something outside the library be aware of changes (like a UI etc)
     /// </summary>
-    public Action<IPieceSquare> PieceUpdated { get; set; }
+    public Action<IPieceSquare>? PieceUpdated { get; set; }
 
     public IValues Values { get; }
 
     public int Ply { get; private set; }
 
-    public int Rule50 => State!.Rule50;
+    public int Rule50 => State.Rule50;
 
-    public Player SideToMove
-        => _sideToMove;
+    public Player SideToMove => _sideToMove;
 
     public State State { get; private set; }
 
@@ -315,25 +309,39 @@ public FenData GenerateFen()
             fen[length++] = dash;
         else
         {
-            if (CanCastle(CastleRight.WhiteKing))
-                fen[length++] = ChessMode == ChessMode.Chess960
-                    ? CastlingRookSquare(CastleRight.WhiteKing).FileChar
-                    : 'K';
-
-            if (CanCastle(CastleRight.WhiteQueen))
-                fen[length++] = ChessMode == ChessMode.Chess960
-                    ? CastlingRookSquare(CastleRight.WhiteQueen).FileChar
-                    : 'Q';
-
-            if (CanCastle(CastleRight.BlackKing))
-                fen[length++] = ChessMode == ChessMode.Chess960
-                    ? CastlingRookSquare(CastleRight.BlackQueen).FileChar
-                    : 'k';
-
-            if (CanCastle(CastleRight.BlackQueen))
-                fen[length++] = ChessMode == ChessMode.Chess960
-                    ? CastlingRookSquare(CastleRight.BlackQueen).FileChar
-                    : 'q';
+            switch (ChessMode)
+            {
+                case ChessMode.Normal:
+                    if (CanCastle(CastleRight.WhiteKing))
+                        fen[length++] = 'K';
+
+                    if (CanCastle(CastleRight.WhiteQueen))
+                        fen[length++] = 'Q';
+
+                    if (CanCastle(CastleRight.BlackKing))
+                        fen[length++] = 'k';
+
+                    if (CanCastle(CastleRight.BlackQueen))
+                        fen[length++] = 'q';
+                    
+                    break;
+                case ChessMode.Chess960:
+                    if (CanCastle(CastleRight.WhiteKing))
+                        fen[length++] = CastlingRookSquare(CastleRight.WhiteKing).FileChar;
+
+                    if (CanCastle(CastleRight.WhiteQueen))
+                        fen[length++] = CastlingRookSquare(CastleRight.WhiteQueen).FileChar;
+
+                    if (CanCastle(CastleRight.BlackKing))
+                        fen[length++] = CastlingRookSquare(CastleRight.BlackQueen).FileChar;
+
+                    if (CanCastle(CastleRight.BlackQueen))
+                        fen[length++] = CastlingRookSquare(CastleRight.BlackQueen).FileChar;
+                    
+                    break;
+                default:
+                    throw new Exception($"Invalid chess mode. mode={ChessMode}");
+            }
         }
 
         fen[length++] = space;
@@ -1121,6 +1129,9 @@ private void SetupPieces(ReadOnlySpan<char> fenChunk)
             {
                 var pieceIndex = PieceExtensions.PieceChars.IndexOf(c);
 
+                if (pieceIndex == -1)
+                    throw new InvalidFen("Invalid char detected");
+
                 Player p = new(char.IsLower(PieceExtensions.PieceChars[pieceIndex]));
 
                 var square = new Square(r - 1, f - 1);
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
index 2aa9c9c8..fde0da8c 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
@@ -52,6 +52,8 @@ public interface ISearchParameters
 
     ulong WhiteTimeMilliseconds { get; set; }
 
+    ref Clock Clock(Player p);
+    
     void Clear();
 
     ulong Inc(Player p);
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
index a867d0c5..c672db55 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
@@ -67,9 +67,19 @@ public interface IUci
 
     string Depth(int depth);
 
-    string Pv(int count, int score, int depth, int selectiveDepth, int alpha, int beta, in TimeSpan time, IEnumerable<Move> pvLine, in ulong nodes);
-
-    string Fullness(in ulong tbHits, in ulong nodes, in TimeSpan time);
+    string Pv(
+        int count,
+        int score,
+        int depth,
+        int selectiveDepth,
+        int alpha,
+        int beta,
+        in TimeSpan time,
+        IEnumerable<Move> pvLine,
+        in ulong nodes,
+        in ulong tableHits);
+
+    string Fullness(int fullNess, in ulong tbHits, in ulong nodes, in TimeSpan time);
 
     string ToString();
 }
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index d3aa5ab0..233de49e 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -78,7 +78,7 @@ public SearchParameters(ulong whiteTimeMilliseconds, ulong blackTimeMilliseconds
         MoveTime = moveTime;
     }
 
-    public List<Move> SearchMoves { get; set; }
+    public List<Move> SearchMoves { get; }
 
     public bool Infinite { get; set; }
 
@@ -177,10 +177,10 @@ private static int ParseValue(int index, in ulong value, Span<char> target)
         return index;
     }
 
-    public string ToString(string format, IFormatProvider formatProvider)
+    public string ToString(string? format, IFormatProvider? formatProvider)
         => string.Format(formatProvider, format, ToString());
 
-    public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null)
+    public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
     {
         var index = 0;
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index 84361cb7..6a0c6e39 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -33,7 +33,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
-using System.Linq;
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
@@ -109,12 +108,12 @@ public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
 
     public IEnumerable<Move> MovesFromUci(IPosition pos, Stack<State> states, IEnumerable<string> moves)
     {
-        var uciMoves = moves
-            .Select(x => MoveFromUci(pos, x))
-            .Where(static x => !x.IsNullMove());
-
-        foreach (var move in uciMoves)
+        foreach (var uciMove in moves)
         {
+            var move = MoveFromUci(pos, uciMove);
+            if (move.IsNullMove())
+                continue;
+            
             var state = new State();
             states.Push(state);
             pos.MakeMove(move, in state);
@@ -157,7 +156,9 @@ public string Pv(
         int beta,
         in TimeSpan time,
         IEnumerable<Move> pvLine,
-        in ulong nodes)
+        in ulong nodes,
+        in ulong tableHits
+        )
     {
         var sb = _pvPool.Get();
 
@@ -168,7 +169,7 @@ public string Pv(
         else if (score <= alpha)
             sb.Append("upperbound ");
 
-        sb.Append($"nodes {nodes} nps {Nps(in nodes, in time)} tbhits {Game.Table.Hits} time {time.Milliseconds} ");
+        sb.Append($"nodes {nodes} nps {Nps(in nodes, in time)} tbhits {tableHits} time {time.Milliseconds} ");
         sb.AppendJoin(' ', pvLine);
 
         var result = sb.ToString();
@@ -176,9 +177,9 @@ public string Pv(
         return result;
     }
 
-    public string Fullness(in ulong tbHits, in ulong nodes, in TimeSpan time)
+    public string Fullness(int fullNess, in ulong tbHits, in ulong nodes, in TimeSpan time)
         =>
-            $"info hashfull {Game.Table.Fullness()} tbhits {tbHits} nodes {nodes} time {time.Milliseconds} nps {Nps(in nodes, in time)}";
+            $"info hashfull {fullNess} tbhits {tbHits} nodes {nodes} time {time.Milliseconds} nps {Nps(in nodes, in time)}";
 
     [SkipLocalsInit]
     public string MoveToString(Move m, ChessMode chessMode = ChessMode.Normal)
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 87cf51b3..e11e83ca 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -19,7 +19,7 @@
     <PackageProjectUrl>https://github.com/rudzen/ChessLib</PackageProjectUrl>
     <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
     <PackageLicenseFile>LICENSE</PackageLicenseFile>
-    <Nullable>disable</Nullable>
+    <Nullable>enable</Nullable>
     <EnforceCodeStyleInBuild>False</EnforceCodeStyleInBuild>
     <Title>Rudzoft.ChessLib</Title>
     <PackageReadmeFile>README.md</PackageReadmeFile>
@@ -54,7 +54,12 @@
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.2" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.3" />
+    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
     <PackageReference Include="ZString" Version="2.5.0" />
   </ItemGroup>
   <ItemGroup>
diff --git a/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs b/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs
deleted file mode 100644
index bebf5214..00000000
--- a/src/Rudzoft.Perft/Host/PerftServiceProviderFactory.cs
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
-Perft, a chess perft testing application
-
-MIT License
-
-Copyright (c) 2019-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using System.IO;
-using DryIoc;
-using DryIoc.Microsoft.DependencyInjection;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.ObjectPool;
-using Perft.Environment;
-using Perft.Factories;
-using Rudzoft.ChessLib;
-using Rudzoft.ChessLib.Perft.Interfaces;
-using Rudzoft.ChessLib.Protocol.UCI;
-using Rudzoft.ChessLib.Tables;
-using Rudzoft.ChessLib.Types;
-using Rudzoft.Perft.Options;
-using Rudzoft.Perft.Parsers;
-using Rudzoft.Perft.Perft;
-using Rudzoft.Perft.TimeStamp;
-using Serilog;
-
-namespace Rudzoft.Perft.Host;
-
-public class PerftServiceProviderFactory : IServiceProviderFactory<Container>
-{
-    public Container CreateBuilder(IServiceCollection services)
-    {
-        var configBuilder = ConfigurationBuilder();
-        var configuration = ConfigurationFactory.CreateConfiguration(configBuilder);
-        
-        var container = new Container(Rules.MicrosoftDependencyInjectionRules);
-        
-        // Construct a configuration binding
-        container.RegisterInstance(configuration);
-
-        AddServices(container, configuration);
-
-        container.Populate(services);
-        
-        return container;
-    }
-
-    public IServiceProvider CreateServiceProvider(Container container)
-    {
-        return container.BuildServiceProvider();
-    }
-
-    private static IConfigurationBuilder ConfigurationBuilder()
-    {
-#if RELEASE
-        const string envName = "Production";
-#else
-        const string envName = "Development";
-#endif
-        // Create our configuration sources
-        return new ConfigurationBuilder()
-            // Add environment variables
-            .AddEnvironmentVariables()
-            // Set base path for Json files as the startup location of the application
-            .SetBasePath(Directory.GetCurrentDirectory())
-            // Add application settings json files
-            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
-            .AddJsonFile($"appsettings.{envName}.json", optional: true, reloadOnChange: true);
-    }
-
-    private static void AddServices(IContainer container, IConfiguration configuration)
-    {
-        // Bind logger with configuration
-        container.Register(Made.Of(() => ConfigureLogger(configuration)), Reuse.Singleton);
-
-        // Bind build time stamp class
-        container.Register<IBuildTimeStamp, BuildTimeStamp>(Reuse.Singleton);
-
-        // Bind chess classes
-        container.Register<IGame, Game>(Reuse.Transient);
-        container.Register<IBoard, Board>(Reuse.Singleton);
-        container.Register<IPosition, Position>(Reuse.Transient);
-        container.Register(made: Made.Of(static () => KillerMoves.Create(64)), Reuse.Transient);
-        container.Register<IUci, Uci>(Reuse.Singleton);
-        container.Register<IValues, Values>(Reuse.Singleton);
-
-        // Bind chess perft classes
-        container.Register<IFrameworkEnvironment, FrameworkEnvironment>(Reuse.Singleton);
-        container.Register<IPerft, ChessLib.Perft.Perft>(Reuse.Transient);
-        container.Register<IPerftRunner, PerftRunner>(Reuse.Transient);
-        container.Register<IOptionsFactory, OptionsFactory>(Reuse.Singleton);
-        
-        // Bind perft classes
-        container.Register<IEpdParserSettings, EpdParserSettings>(Reuse.Singleton);
-        container.Register<IEpdSet, EpdSet>(Reuse.Transient);
-        container.Register<IEpdParser, EpdParser>(Reuse.Singleton);
-
-        // Bind object pool for perft result
-        container.Register<ObjectPoolProvider, DefaultObjectPoolProvider>(Reuse.Singleton);
-        container.RegisterDelegate(context =>
-        {
-            var provider = context.Resolve<ObjectPoolProvider>();
-            var policy = new DefaultPooledObjectPolicy<PerftResult>();
-            return provider.Create(policy);
-        });
-    }
-
-    private static ILogger ConfigureLogger(IConfiguration configuration)
-    {
-        // Apply the config to the logger
-        Log.Logger = new LoggerConfiguration()
-            .ReadFrom.Configuration(configuration)
-            .Enrich.WithThreadId()
-            .Enrich.FromLogContext()
-            .CreateLogger();
-        AppDomain.CurrentDomain.ProcessExit += static (_, _) => Log.CloseAndFlush();
-        return Log.Logger;
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Perft/PerftRunner.cs b/src/Rudzoft.Perft/Perft/PerftRunner.cs
index 8ab98e02..8b939264 100644
--- a/src/Rudzoft.Perft/Perft/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Perft/PerftRunner.cs
@@ -35,8 +35,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Threading.Tasks;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.ObjectPool;
-using Rudzoft.ChessLib;
 using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.Perft;
 using Rudzoft.ChessLib.Perft.Interfaces;
 using Rudzoft.ChessLib.Protocol.UCI;
@@ -49,7 +49,7 @@ namespace Rudzoft.Perft.Perft;
 
 public sealed class PerftRunner : IPerftRunner
 {
-    private const string Version = "v0.1.4";
+    private const string Version = "v0.2.0";
 
     private static readonly string Line = new('-', 65);
 
@@ -65,6 +65,8 @@ public sealed class PerftRunner : IPerftRunner
 
     private readonly IPerft _perft;
 
+    private readonly ITranspositionTable _transpositionTable;
+
     private readonly ObjectPool<PerftResult> _resultPool;
 
     private readonly IUci _uci;
@@ -79,6 +81,7 @@ public PerftRunner(
         IBuildTimeStamp buildTimeStamp,
         IPerft perft,
         IConfiguration configuration,
+        ITranspositionTable transpositionTable,
         ObjectPool<PerftResult> resultPool,
         IUci uci)
     {
@@ -87,11 +90,9 @@ public PerftRunner(
         _buildTimeStamp = buildTimeStamp;
         _perft = perft;
         _perft.BoardPrintCallback ??= s => _log.Information("Board:\n{0}", s);
+        _transpositionTable = transpositionTable;
         _resultPool = resultPool;
-
         _uci = uci;
-        _uci.Initialize();
-
         _runners = new Func<CancellationToken, IAsyncEnumerable<PerftPosition>>[] { ParseEpd, ParseFen };
 
         configuration.Bind("TranspositionTable", TranspositionTableOptions);
@@ -114,7 +115,7 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
         InternalRunArgumentCheck(Options);
 
         if (TranspositionTableOptions is TTOptions { Use: true } ttOptions)
-            Game.Table.SetSize(ttOptions.Size);
+            _transpositionTable.SetSize(ttOptions.Size);
 
         var errors = 0;
         var runnerIndex = (Options is FenOptions).AsByte();
@@ -270,7 +271,7 @@ private void ComputeResults(ulong result, int depth, ulong expected, long elapse
         results.Nps = _uci.Nps(in result, results.Elapsed);
         results.CorrectResult = expected;
         results.Passed = expected == result;
-        results.TableHits = Game.Table.Hits;
+        results.TableHits = _transpositionTable.Hits;
     }
 
     private void LogInfoHeader()
@@ -296,7 +297,7 @@ private int LogResults(IPerftResult result)
         else
             _log.Information("Result      : {0}", result.Result);
 
-        _log.Information("TT hits     : {0}", Game.Table.Hits);
+        _log.Information("TT hits     : {0}", _transpositionTable.Hits);
 
         var error = 0;
 
@@ -307,7 +308,7 @@ private int LogResults(IPerftResult result)
             _log.Information("Move count matches!");
         else
         {
-            _log.Error("Failed for position: {0}", _perft.CurrentGame.Pos.GenerateFen());
+            _log.Error("Failed for position: {0}", _perft.Game.Pos.GenerateFen());
             error = 1;
         }
 
diff --git a/src/Rudzoft.Perft/Program.cs b/src/Rudzoft.Perft/Program.cs
index 43ab9029..e301718a 100644
--- a/src/Rudzoft.Perft/Program.cs
+++ b/src/Rudzoft.Perft/Program.cs
@@ -24,15 +24,82 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.IO;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.ObjectPool;
+using Perft.Environment;
+using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.Perft.Interfaces;
 using Rudzoft.Perft.Host;
+using Rudzoft.Perft.Options;
+using Rudzoft.Perft.Parsers;
+using Rudzoft.Perft.Perft;
+using Rudzoft.Perft.TimeStamp;
+using Serilog;
+
+
+static IConfigurationBuilder ConfigurationBuilder()
+{
+#if RELEASE
+        const string envName = "Production";
+#else
+    const string envName = "Development";
+#endif
+    // Create our configuration sources
+    return new ConfigurationBuilder()
+        // Add environment variables
+        .AddEnvironmentVariables()
+        // Set base path for Json files as the startup location of the application
+        .SetBasePath(Directory.GetCurrentDirectory())
+        // Add application settings json files
+        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+        .AddJsonFile($"appsettings.{envName}.json", optional: true, reloadOnChange: true);
+}
+
+static ILogger ConfigureLogger(IConfiguration configuration)
+{
+    // Apply the config to the logger
+    Log.Logger = new LoggerConfiguration()
+        .ReadFrom.Configuration(configuration)
+        .Enrich.WithThreadId()
+        .Enrich.FromLogContext()
+        .CreateLogger();
+    AppDomain.CurrentDomain.ProcessExit += static (_, _) => Log.CloseAndFlush();
+    return Log.Logger;
+}
 
 var host = new HostBuilder()
-    .UseServiceProviderFactory(new PerftServiceProviderFactory())
     .ConfigureServices((_, services) =>
     {
+        var configuration = ConfigurationBuilder().Build();
+        services.AddSingleton(configuration);
         services.AddSingleton(new CommandLineArgs(args));
+
+        services.AddChessLib(configuration);
+
+        services.AddSingleton(ConfigureLogger(configuration));
+
+        services.AddSingleton<IBuildTimeStamp, BuildTimeStamp>();
+        services.AddSingleton<IFrameworkEnvironment, FrameworkEnvironment>();
+        services.AddTransient<IPerft, Rudzoft.ChessLib.Perft.Perft>();
+        services.AddTransient<IPerftRunner, PerftRunner>();
+        services.AddSingleton<IOptionsFactory, OptionsFactory>();
+
+        services.AddSingleton<IEpdParserSettings, EpdParserSettings>();
+        services.AddTransient<IEpdSet, EpdSet>();
+        services.AddSingleton<IEpdParser, EpdParser>();
+
+        services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
+        services.TryAddSingleton(serviceProvider =>
+        {
+            var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+            var policy = new DefaultPooledObjectPolicy<PerftResult>();
+            return provider.Create(policy);
+        });
+
         services.AddHostedService<PerftHost>();
     })
     .Build();
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index c4b791a8..e0b1c82f 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -37,7 +37,7 @@
     <PackageReference Include="CommandLineParser" Version="2.9.1" />
     <PackageReference Include="ConsoleGUI" Version="1.4.1" />
     <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.2" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.3" />
     <PackageReference Include="Serilog" Version="2.12.0" />
     <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
     <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />

From 2f3cca915043bc652a220da798d07ffd787f36cf Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 19 Feb 2023 16:57:46 +0100
Subject: [PATCH 015/119] Added move list object pool as IoC object

- Fixed a few nullable things
- Corrected a few var names
---
 .../FenBenchmark.cs                           |  8 +-
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  | 14 +--
 .../BoardTests/BoardTests.cs                  |  9 ++
 .../BookTests/PolyglotTests.cs                |  9 ++
 .../CastleTests/BasicCastleTests.cs           |  9 ++
 .../EvaluationTests/KpkBitBaseTests.cs        |  9 ++
 .../FENTests/FenTests.cs                      |  9 ++
 .../FenceTests/FenceTests.cs                  |  9 ++
 .../GameplayTests/FoolsCheckMateTests.cs      | 14 ++-
 .../MoveTests/MoveGen_49.cs                   |  6 +-
 .../MoveTests/MoveGeneratorTests.cs           |  9 ++
 .../MoveTests/MoveTests.cs                    |  9 ++
 .../MoveTests/PerftVerify.cs                  |  9 ++
 .../NotationTests/FanTests.cs                 | 21 ++--
 .../NotationTests/IccfTests.cs                | 17 +++-
 .../NotationTests/RanTests.cs                 | 15 ++-
 .../NotationTests/SanTests.cs                 | 15 ++-
 .../PiecesTests/PawnDoubleAttackTests.cs      |  9 ++
 .../PiecesTests/PieceAttacksRookTests.cs      |  7 +-
 .../PositionTests/EnPassantFenTests.cs        |  9 ++
 .../PositionTests/PositionTests.cs            |  9 ++
 .../PositionTests/ValidationTests.cs          |  9 ++
 .../ProtocolTests/UciTests.cs                 |  9 ++
 .../ChessLibServiceCollectionExtensions.cs    | 11 ++-
 .../Factories/PolyglotBookFactory.cs          | 10 +-
 src/Rudzoft.ChessLib/Game.cs                  | 18 ++--
 src/Rudzoft.ChessLib/Notation/MoveNotation.cs |  4 +-
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs | 96 ++++++++++++++-----
 src/Rudzoft.ChessLib/Position.cs              | 41 ++++++--
 src/Rudzoft.ChessLib/Tables/KillerMoves.cs    |  4 +-
 .../Validation/IPositionValidator.cs          |  2 +-
 .../Validation/PositionValidator.cs           | 20 ++--
 32 files changed, 359 insertions(+), 90 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
index 033cdd61..4efebfb8 100644
--- a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
@@ -1,5 +1,8 @@
-using Rudzoft.ChessLib.Enums;
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
@@ -21,7 +24,8 @@ public void Setup()
         var pieceValue = new Values();
         var fp = new FenData(F);
         var state = new State();
-        _pos = new Position(board, pieceValue);
+        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        _pos = new Position(board, pieceValue, moveListObjectPool);
         _pos.Set(in fp, ChessMode.Normal, state);
     }
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index c41c08d7..8d96c9c8 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -27,8 +27,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Collections.Generic;
 using System.Threading.Tasks;
+using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
+using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Perft;
 using Rudzoft.ChessLib.Perft.Interfaces;
 using Rudzoft.ChessLib.Protocol.UCI;
@@ -42,11 +45,6 @@ public class PerftBench
 {
     private IPerft _perft;
 
-    public PerftBench()
-    {
-
-    }
-
     [Params(5, 6)]
     public int N;
 
@@ -74,13 +72,15 @@ public void Setup()
         var uci = new Uci();
         uci.Initialize();
 
+        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        
         var sp = new SearchParameters();
         
         var board = new Board();
         var values = new Values();
-        var pos = new Position(board, values);
+        var pos = new Position(board, values, moveListObjectPool);
         
-        var game = new Game(tt, uci, sp, pos);
+        var game = new Game(tt, uci, sp, pos, moveListObjectPool);
         _perft = new Perft.Perft(game, new []{ pp });
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 5784e4f0..089c8f00 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -26,8 +26,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.BoardTests;
@@ -42,6 +44,13 @@ public BoardTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
     
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index 7eeb36e5..279d97fa 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -29,10 +29,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Linq;
 using System.Reflection;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Factories;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
@@ -56,6 +58,13 @@ public PolyglotTests(BookFixture fixture)
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .AddSingleton(static _ =>
             {
                 IUci uci = new Uci();
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
index 58eff966..c345ff5a 100644
--- a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
+++ b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
@@ -26,8 +26,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.CastleTests;
@@ -42,6 +44,13 @@ public BasicCastleTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
index 8ed0c40f..7b0ffc40 100644
--- a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
+++ b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
@@ -26,9 +26,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Evaluation;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.EvaluationTests;
@@ -44,6 +46,13 @@ public KpkBitBaseTests()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<IKpkBitBase, KpkBitBase>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index b57d73b1..53ea79a6 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -26,9 +26,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.FENTests;
@@ -43,6 +45,13 @@ public FenTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
index a86b51fa..f17ef8c1 100644
--- a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
@@ -26,8 +26,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.FenceTests;
@@ -43,6 +45,13 @@ public FenceTests()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<IBlockage, Blockage>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
index 761a607d..397355a7 100644
--- a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
+++ b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
@@ -26,9 +26,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.GameplayTests;
@@ -43,6 +45,13 @@ public FoolsCheckMateTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
@@ -70,9 +79,8 @@ public void FoolsCheckMate()
         // verify in check is actually true
         Assert.True(pos.InCheck);
 
-        var resultingMoves = pos.GenerateMoves();
+        var isMate = pos.IsMate;
 
-        // verify that no legal moves actually exists.
-        Assert.True(resultingMoves.Length == 0);
+        Assert.True(isMate);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
index 8dfb0d5d..b8f5098a 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
@@ -24,9 +24,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.MoveTests;
@@ -40,7 +42,9 @@ public void MoveListContainsMismatchedElement()
 
         var board = new Board();
         var pieceValue = new Values();
-        var pos = new Position(board, pieceValue);
+        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+
+        var pos = new Position(board, pieceValue, moveListObjectPool);
         var fd = new FenData(fen);
         var state = new State();
 
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
index 7d908819..de56da87 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
@@ -26,9 +26,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.MoveTests;
@@ -43,6 +45,13 @@ public MoveGeneratorTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index f4c0f761..8d108068 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -29,8 +29,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Runtime.InteropServices;
 using System.Text;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.MoveTests;
@@ -45,6 +47,13 @@ public MoveTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
index 6b3e93d2..fcdb76b2 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
@@ -1,9 +1,11 @@
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
 
@@ -26,6 +28,13 @@ protected PerftVerify()
             .AddSingleton<ISearchParameters, SearchParameters>()
             .AddSingleton<IUci, Uci>()
             .AddTransient<IGame, Game>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .AddSingleton<ITranspositionTable, TranspositionTable>()
             .BuildServiceProvider();
     }
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index fd83eba1..d44222bc 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -26,11 +26,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
@@ -45,6 +47,13 @@ public FanTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
@@ -77,13 +86,13 @@ public void FanRankAmbiguities(string fen, MoveNotations moveNotation, PieceType
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(pos);
+        var notation = MoveNotation.Create(pos);
 
         var expectedOne = $"{pieceChar}{fromOne.FileChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.FileChar}{toString}";
 
-        var actualOne = ambiguity.ToNotation(moveNotation).Convert(moveOne);
-        var actualTwo = ambiguity.ToNotation(moveNotation).Convert(moveTwo);
+        var actualOne = notation.ToNotation(moveNotation).Convert(moveOne);
+        var actualTwo = notation.ToNotation(moveNotation).Convert(moveTwo);
 
         Assert.Equal(expectedOne, actualOne);
         Assert.Equal(expectedTwo, actualTwo);
@@ -118,13 +127,13 @@ public void FanFileAmbiguities(string fen, MoveNotations moveNotation, PieceType
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(pos);
+        var notation = MoveNotation.Create(pos);
 
         var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
 
-        var actualOne = ambiguity.ToNotation(moveNotation).Convert(moveOne);
-        var actualTwo = ambiguity.ToNotation(moveNotation).Convert(moveTwo);
+        var actualOne = notation.ToNotation(moveNotation).Convert(moveOne);
+        var actualTwo = notation.ToNotation(moveNotation).Convert(moveTwo);
 
         Assert.Equal(expectedOne, actualOne);
         Assert.Equal(expectedTwo, actualTwo);
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index 157fa25d..596eff8b 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -26,10 +26,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
@@ -44,6 +46,13 @@ public IccfTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
@@ -63,9 +72,9 @@ public void IccfRegularMove()
 
         var w1 = Move.Create(Squares.d2, Squares.f3);
 
-        var ambiguity = MoveNotation.Create(pos);
+        var moveNotation = MoveNotation.Create(pos);
 
-        var actualPrimary = ambiguity.ToNotation(notation).Convert(w1);
+        var actualPrimary = moveNotation.ToNotation(notation).Convert(w1);
 
         Assert.Equal(expectedPrimary, actualPrimary);
     }
@@ -88,9 +97,9 @@ public void IccfPromotionMove(string fen, PieceTypes promoPt, string expected)
 
         var w1 = Move.Create(Squares.b7, Squares.b8, MoveTypes.Promotion, promoPt);
 
-        var ambiguity = MoveNotation.Create(pos);
+        var moveNotation = MoveNotation.Create(pos);
 
-        var actual = ambiguity.ToNotation(notation).Convert(w1);
+        var actual = moveNotation.ToNotation(notation).Convert(w1);
 
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index 34458932..0cb55b49 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -26,11 +26,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
@@ -45,6 +47,13 @@ public RanTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
@@ -83,13 +92,13 @@ public void RanLanAmbiguities(string fen, MoveNotations moveNotation, PieceTypes
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(pos);
+        var notation = MoveNotation.Create(pos);
 
         var expectedOne = $"{pieceChar}{fromOne}-{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo}-{toString}";
 
-        var actualOne = ambiguity.ToNotation(moveNotation).Convert(moveOne);
-        var actualTwo = ambiguity.ToNotation(moveNotation).Convert(moveTwo);
+        var actualOne = notation.ToNotation(moveNotation).Convert(moveOne);
+        var actualTwo = notation.ToNotation(moveNotation).Convert(moveTwo);
 
         Assert.Equal(expectedOne, actualOne);
         Assert.Equal(expectedTwo, actualTwo);
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index 48288624..df2dfffe 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -27,12 +27,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Linq;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
@@ -47,6 +49,13 @@ public SanTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
@@ -120,13 +129,13 @@ public void SanFileAmbiguities(string fen, MoveNotations moveNotation, PieceType
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(pos);
+        var notation = MoveNotation.Create(pos);
 
         var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
 
-        var actualOne = ambiguity.ToNotation(moveNotation).Convert(moveOne);
-        var actualTwo = ambiguity.ToNotation(moveNotation).Convert(moveTwo);
+        var actualOne = notation.ToNotation(moveNotation).Convert(moveOne);
+        var actualTwo = notation.ToNotation(moveNotation).Convert(moveTwo);
 
         Assert.Equal(expectedOne, actualOne);
         Assert.Equal(expectedTwo, actualTwo);
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
index e7fb2268..0241368c 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
@@ -26,8 +26,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
@@ -42,6 +44,13 @@ public PawnDoubleAttackTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
     
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
index d64038a5..024c65e5 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
@@ -24,6 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
@@ -64,7 +67,9 @@ public void RookBorderBlocked()
         // just to get the attacks
         var board = new Board();
         var pieceValue = new Values();
-        var pos = new Position(board, pieceValue);
+        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+
+        var pos = new Position(board, pieceValue, moveListObjectPool);
 
         while (border)
         {
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
index c668a78f..65c1edc2 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
@@ -26,8 +26,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PositionTests;
@@ -42,6 +44,13 @@ public EnPassantFenTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 7c188f64..42ad0148 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -26,8 +26,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PositionTests;
@@ -42,6 +44,13 @@ public PositionTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index 9a6e19b8..1fcac63c 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -26,9 +26,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -44,6 +46,13 @@ public ValidationTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
index aef25240..1b053eac 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
@@ -26,10 +26,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
 
@@ -55,6 +57,13 @@ public UciTests()
                 uci.Initialize();
                 return uci;
             })
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index a1e7eb9c..2dec2d64 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -29,8 +29,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Factories;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Tables;
@@ -67,7 +69,14 @@ public static IServiceCollection AddChessLib(
                     .Bind(settings));
 
         serviceCollection.TryAddSingleton<ITranspositionTable, TranspositionTable>();
-
+        serviceCollection.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
+        serviceCollection.TryAddSingleton(static serviceProvider =>
+        {
+            var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+            var policy = new MoveListPolicy();
+            return provider.Create(policy);
+        });
+        
         return serviceCollection.AddSingleton(static _ =>
             {
                 IUci uci = new Uci();
diff --git a/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs b/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
index 94d7bbde..5b58f1c8 100644
--- a/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
@@ -25,7 +25,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Polyglot;
 
 namespace Rudzoft.ChessLib.Factories;
@@ -33,22 +35,24 @@ namespace Rudzoft.ChessLib.Factories;
 public sealed class PolyglotBookFactory : IPolyglotBookFactory
 {
     private readonly string _path;
+    private readonly ObjectPool<IMoveList> _objectPool;
 
-    public PolyglotBookFactory(IOptions<PolyglotBookConfiguration> options)
+    public PolyglotBookFactory(IOptions<PolyglotBookConfiguration> options, ObjectPool<IMoveList> objectPool)
     {
         var config = options.Value;
         _path = string.IsNullOrWhiteSpace(config.BookPath) ? string.Empty : config.BookPath;
+        _objectPool = objectPool;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public IPolyglotBook Create(string bookFile)
     {
-        return PolyglotBook.Create(_path, bookFile);
+        return PolyglotBook.Create(_objectPool, _path, bookFile);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public IPolyglotBook Create()
     {
-        return PolyglotBook.Create();
+        return PolyglotBook.Create(_objectPool);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 94097c47..0d328a54 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -34,7 +34,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
 
@@ -42,16 +41,17 @@ namespace Rudzoft.ChessLib;
 
 public sealed class Game : IGame
 {
-    private readonly ObjectPool<IMoveList> _moveLists;
+    private readonly ObjectPool<IMoveList> _moveListPool;
     private readonly IPosition _pos;
 
     public Game(
         ITranspositionTable transpositionTable,
         IUci uci,
         ISearchParameters searchParameters,
-        IPosition pos)
+        IPosition pos,
+        ObjectPool<IMoveList> moveListPoolPool)
     {
-        _moveLists = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        _moveListPool = moveListPoolPool;
         _pos = pos;
         
         Table = transpositionTable;
@@ -96,7 +96,7 @@ public void UpdateDrawTypes()
         if (Pos.Rule50 >= 100)
             gameEndType |= GameEndTypes.FiftyMove;
 
-        var moveList = _moveLists.Get();
+        var moveList = _moveListPool.Get();
         moveList.Generate(in _pos);
 
         var moves = moveList.Get();
@@ -132,7 +132,7 @@ public ulong Perft(int depth, bool root = true)
     {
         var tot = ulong.MinValue;
 
-        var ml = _moveLists.Get();
+        var ml = _moveListPool.Get();
         ml.Generate(in _pos);
         var state = new State();
 
@@ -150,10 +150,10 @@ public ulong Perft(int depth, bool root = true)
 
                 if (depth <= 2)
                 {
-                    var ml2 = _moveLists.Get();
+                    var ml2 = _moveListPool.Get();
                     ml2.Generate(in _pos);
                     tot += (ulong)ml2.Length;
-                    _moveLists.Return(ml2);
+                    _moveListPool.Return(ml2);
                 }
                 else
                     tot += Perft(depth - 1, false);
@@ -162,7 +162,7 @@ public ulong Perft(int depth, bool root = true)
             }
         }
 
-        _moveLists.Return(ml);
+        _moveListPool.Return(ml);
 
         return tot;
     }
diff --git a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
index c9501334..28544530 100644
--- a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
@@ -36,11 +36,11 @@ namespace Rudzoft.ChessLib.Notation;
 /// </summary>
 public sealed class MoveNotation : IMoveNotation
 {
-    private readonly INotation[] _notations;
+    private readonly INotation?[] _notations;
 
     private MoveNotation(IPosition pos)
     {
-        _notations = new INotation[]
+        _notations = new INotation?[]
         {
             new SanNotation(pos),
             new FanNotation(pos),
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index cc9d1c2c..37a2033a 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -27,8 +27,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Diagnostics;
 using System.IO;
-using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
@@ -53,23 +54,25 @@ public sealed class PolyglotBook : IPolyglotBook
     private readonly string _bookFilePath;
     private readonly int _entrySize;
     private readonly Random _rnd;
+    private readonly ObjectPool<IMoveList> _moveListPool;
 
-    private unsafe PolyglotBook()
+    private unsafe PolyglotBook(ObjectPool<IMoveList> pool)
     {
         _entrySize = sizeof(PolyglotBookEntry);
         _rnd = new Random(DateTime.Now.Millisecond);
+        _moveListPool = pool;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal static PolyglotBook Create()
+    internal static PolyglotBook Create(ObjectPool<IMoveList> pool)
     {
-        return new PolyglotBook();
+        return new PolyglotBook(pool);
     }
     
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal static PolyglotBook Create(string path, string file)
+    internal static PolyglotBook Create(ObjectPool<IMoveList> pool, string path, string file)
     {
-        return new PolyglotBook
+        return new PolyglotBook(pool)
         {
             BookFile = Path.Combine(path, file)
         };
@@ -136,7 +139,7 @@ public void Dispose()
         _binaryReader?.Dispose();
     }
 
-    private static Move ConvertMove(IPosition pos, ushort polyMove)
+    private Move ConvertMove(IPosition pos, ushort polyMove)
     {
         // A PolyGlot book move is encoded as follows:
         //
@@ -157,24 +160,68 @@ private static Move ConvertMove(IPosition pos, ushort polyMove)
             move = Move.Create(from, to, MoveTypes.Promotion, PolyToPt(polyPt));
         }
 
-        var ml = pos.GenerateMoves();
+        var ml = _moveListPool.Get();
+        ml.Generate(in pos);
+        var moves = ml.Get();
 
+        var mm = SelectMove(in pos, from, to, move.MoveType(), moves);
+        
+        _moveListPool.Return(ml);
+
+        return mm;
+        
         // Iterate all known moves for current position to find a match.
-        var emMoves = ml.Select(static em => em.Move)
-            .Where(m => from == m.FromSquare())
-            .Where(m => to == m.ToSquare())
-            .Where(m =>
-            {
-                var type = move.MoveType();
-                return m.IsPromotionMove()
-                    ? type == MoveTypes.Promotion
-                    : type != MoveTypes.Promotion;
-            })
-            .Where(m => !IsInCheck(pos, m));
-
-        return emMoves.FirstOrDefault(Move.EmptyMove);
+        // foreach (var valMove in moves)
+        // {
+        //     var m = valMove.Move;
+        //
+        //     if (from != m.FromSquare() || to != m.ToSquare())
+        //         continue;
+        //     
+        //     var type = move.MoveType();
+        //     
+        //     if ((m.IsPromotionMove() ? type == MoveTypes.Promotion : type != MoveTypes.Promotion) && !IsInCheck(pos, m))
+        //         return m;
+        // }
+        //
+        // return Move.EmptyMove;
+        
+        // var emMoves = ml.Select(static em => em.Move)
+        //     .Where(m => from == m.FromSquare())
+        //     .Where(m => to == m.ToSquare())
+        //     .Where(m =>
+        //     {
+        //         var type = move.MoveType();
+        //         return m.IsPromotionMove()
+        //             ? type == MoveTypes.Promotion
+        //             : type != MoveTypes.Promotion;
+        //     })
+        //     .Where(m => !IsInCheck(pos, m));
+        //
+        // return emMoves.FirstOrDefault(Move.EmptyMove);
     }
 
+    private static Move SelectMove(in IPosition pos, Square polyFrom, Square polyTo, MoveTypes polyType, ReadOnlySpan<ValMove> moves)
+    {
+        // Iterate all known moves for current position to find a match.
+        foreach (var valMove in moves)
+        {
+            var m = valMove.Move;
+
+            if (polyFrom != m.FromSquare() || polyTo != m.ToSquare())
+                continue;
+
+            var promotionMatches = m.IsPromotionMove()
+                ? polyType == MoveTypes.Promotion
+                : polyType != MoveTypes.Promotion;
+            
+            if (promotionMatches && !IsInCheck(pos, m))
+                return m;
+        }
+
+        return Move.EmptyMove;
+    }
+    
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static bool IsInCheck(IPosition pos, Move m)
     {
@@ -202,9 +249,14 @@ public HashKey ComputePolyglotKey(in IPosition pos)
             k ^= PolyglotBookZobrist.Psq(p, s);
         }
 
-        foreach (var cr in CastleRights.AsSpan())
+        var crSpan = CastleRights.AsSpan();
+        ref var crSpace = ref MemoryMarshal.GetReference(crSpan);
+        for (var i = 0; i < CastleRights.Length; ++i)
+        {
+            var cr = Unsafe.Add(ref crSpace, i);
             if (pos.State.CastlelingRights.Has(cr))
                 k ^= PolyglotBookZobrist.Castle(cr);
+        }
 
         if (pos.State.EnPassantSquare != Square.None)
             k ^= PolyglotBookZobrist.EnPassant(pos.State.EnPassantSquare.File);
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 41f1e17f..466095bc 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -55,22 +55,24 @@ public sealed class Position : IPosition
     private readonly CastleRight[] _castlingRightsMask;
     private readonly Square[] _castlingRookSquare;
     private readonly ObjectPool<StringBuilder> _outputObjectPool;
+    private readonly ObjectPool<IMoveList> _moveListPool;
     private readonly IPositionValidator _positionValidator;
     private Player _sideToMove;
 
-    public Position(IBoard board, IValues values)
+    public Position(IBoard board, IValues values, ObjectPool<IMoveList> moveListPool)
     {
-        Board = board;
-        Values = values;
-        State = new State();
-        _outputObjectPool = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
-        _positionValidator = new PositionValidator(this, Board);
         _castleKingPath = new BitBoard[CastleRight.Count];
         _castleRookPath = new BitBoard[CastleRight.Count];
-        _castlingRookSquare = new Square[CastleRight.Count];
         _castlingRightsMask = new CastleRight[Square.Count];
         _castlingRightsMask.Fill(CastleRight.None);
+        _castlingRookSquare = new Square[CastleRight.Count];
+        _outputObjectPool = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
+        _moveListPool = moveListPool;
+        _positionValidator = new PositionValidator(this, board);
+        Board = board;
         IsProbing = true;
+        Values = values;
+        State = new State();
         Clear();
     }
 
@@ -86,7 +88,7 @@ public Position(IBoard board, IValues values)
 
     public bool InCheck => State.Checkers;
 
-    public bool IsMate => this.GenerateMoves().Length == 0;
+    public bool IsMate => !HasMoves();
 
     public bool IsProbing { get; set; }
 
@@ -522,7 +524,7 @@ public bool HasRepetition()
     public bool IsDraw(int ply)
         => State.Rule50 switch
         {
-            > 99 when State.Checkers.IsEmpty || this.GenerateMoves().Length > 0 => true,
+            > 99 when State.Checkers.IsEmpty || HasMoves() => true,
             _ => State.Repetition > 0 && State.Repetition < ply
         };
 
@@ -621,7 +623,7 @@ public bool IsPseudoLegal(Move m)
     {
         // Use a slower but simpler function for uncommon cases
         if (m.MoveType() != MoveTypes.Normal)
-            return this.GenerateMoves().Contains(m);
+            return ContainsMove(m);
 
         // Is not a promotion, so promotion piece must be empty
         if (m.PromotedPieceType() - 2 != PieceTypes.NoPieceType)
@@ -1562,4 +1564,23 @@ Square RookSquare(Square startSq, Piece rook)
 
         return result;
     }
+
+    private bool HasMoves()
+    {
+        var ml = _moveListPool.Get();
+        ml.Generate(this);
+        var moves = ml.Get();
+        var hasMoves = !moves.IsEmpty;
+        _moveListPool.Return(ml);
+        return hasMoves;
+    }
+
+    private bool ContainsMove(Move m)
+    {
+        var ml = _moveListPool.Get();
+        ml.Generate(this);
+        var contains = ml.Contains(m);
+        _moveListPool.Return(ml);
+        return contains;
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
index 855faad3..b75051fe 100644
--- a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
@@ -110,7 +110,7 @@ public void Reset()
         }
     }
 
-    public bool Equals(IKillerMoves other)
+    public bool Equals(IKillerMoves? other)
     {
         if (other is null) return false;
         if (ReferenceEquals(this, other)) return true;
@@ -128,7 +128,7 @@ public bool Equals(IKillerMoves other)
         return false;
     }
 
-    public override bool Equals(object obj)
+    public override bool Equals(object? obj)
         => ReferenceEquals(this, obj) || obj is KillerMoves other && Equals(other);
 
     public override int GetHashCode()
diff --git a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
index 633ae8ec..d57e081b 100644
--- a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
@@ -28,7 +28,7 @@ namespace Rudzoft.ChessLib.Validation;
 
 public interface IPositionValidator
 {
-    string ErrorMsg { get; }
+    string? ErrorMsg { get; }
     bool IsOk { get; }
     IPositionValidator Validate(PositionValidationTypes type = PositionValidationTypes.All);
 }
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index b64b0e9e..9dfd70b3 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -64,35 +64,35 @@ public PositionValidator(in IPosition pos, in IBoard board)
         _board = board;
     }
 
-    public string ErrorMsg { get; private set; }
+    public string? ErrorMsg { get; private set; }
     public bool IsOk { get; private set; }
 
-    public IPositionValidator Validate(PositionValidationTypes types = PositionValidationTypes.All)
+    public IPositionValidator Validate(PositionValidationTypes type = PositionValidationTypes.All)
     {
         var error = string.Empty;
 
-        if (types.HasFlagFast(PositionValidationTypes.Basic))
+        if (type.HasFlagFast(PositionValidationTypes.Basic))
             error = ValidateBasic();
 
-        if (types.HasFlagFast(PositionValidationTypes.Castle))
+        if (type.HasFlagFast(PositionValidationTypes.Castle))
             error = ValidateCastleling(error);
 
-        if (types.HasFlagFast(PositionValidationTypes.Kings))
+        if (type.HasFlagFast(PositionValidationTypes.Kings))
             error = ValidateKings(error);
 
-        if (types.HasFlagFast(PositionValidationTypes.Pawns))
+        if (type.HasFlagFast(PositionValidationTypes.Pawns))
             error = ValidatePawns(error);
 
-        if (types.HasFlagFast(PositionValidationTypes.PieceConsistency))
+        if (type.HasFlagFast(PositionValidationTypes.PieceConsistency))
             error = ValidatePieceConsistency(error);
 
-        if (types.HasFlagFast(PositionValidationTypes.PieceCount))
+        if (type.HasFlagFast(PositionValidationTypes.PieceCount))
             error = ValidatePieceCount(error);
 
-        if (types.HasFlagFast(PositionValidationTypes.PieceTypes))
+        if (type.HasFlagFast(PositionValidationTypes.PieceTypes))
             error = ValidatePieceTypes(error);
 
-        if (types.HasFlagFast(PositionValidationTypes.State))
+        if (type.HasFlagFast(PositionValidationTypes.State))
             error = ValidateState(error);
 
         IsOk = error.IsNullOrEmpty();

From 3d48a7c76f407f9374cd912ee4a49255021680d4 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 19 Feb 2023 17:13:53 +0100
Subject: [PATCH 016/119] PositionValidator can now be controlled through IoC

---
 .../FenBenchmark.cs                           |   4 +-
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |   5 +-
 .../BoardTests/BoardTests.cs                  |   2 +
 .../BookTests/PolyglotTests.cs                |   2 +
 .../CastleTests/BasicCastleTests.cs           |   2 +
 .../EvaluationTests/KpkBitBaseTests.cs        |   2 +
 .../FENTests/FenTests.cs                      |   2 +
 .../FenceTests/FenceTests.cs                  |   2 +
 .../GameplayTests/FoolsCheckMateTests.cs      |   2 +
 .../MoveTests/MoveGen_49.cs                   |   4 +-
 .../MoveTests/MoveGeneratorTests.cs           |   2 +
 .../MoveTests/MoveTests.cs                    |   2 +
 .../MoveTests/PerftVerify.cs                  |   2 +
 .../NotationTests/FanTests.cs                 |   2 +
 .../NotationTests/IccfTests.cs                |   2 +
 .../NotationTests/RanTests.cs                 |   2 +
 .../NotationTests/SanTests.cs                 |   2 +
 .../PiecesTests/PawnDoubleAttackTests.cs      |   2 +
 .../PiecesTests/PieceAttacksRookTests.cs      |   4 +-
 .../PositionTests/EnPassantFenTests.cs        |   2 +
 .../PositionTests/PositionTests.cs            |   2 +
 .../PositionTests/ValidationTests.cs          |   5 +-
 .../ProtocolTests/UciTests.cs                 |   2 +
 .../ChessLibServiceCollectionExtensions.cs    |   2 +
 src/Rudzoft.ChessLib/Position.cs              |  12 +-
 .../Validation/IPositionValidator.cs          |   2 +-
 .../Validation/PositionValidator.cs           | 109 ++++++++----------
 27 files changed, 112 insertions(+), 71 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
index 4efebfb8..89d35e9a 100644
--- a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
@@ -4,6 +4,7 @@
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
@@ -24,8 +25,9 @@ public void Setup()
         var pieceValue = new Values();
         var fp = new FenData(F);
         var state = new State();
+        var validator = new PositionValidator();
         var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
-        _pos = new Position(board, pieceValue, moveListObjectPool);
+        _pos = new Position(board, pieceValue, validator, moveListObjectPool);
         _pos.Set(in fp, ChessMode.Normal, state);
     }
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index 8d96c9c8..2853a1c2 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -36,6 +36,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Perft.Interfaces;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
@@ -78,7 +79,9 @@ public void Setup()
         
         var board = new Board();
         var values = new Values();
-        var pos = new Position(board, values, moveListObjectPool);
+        var validator = new PositionValidator();
+
+        var pos = new Position(board, values, validator, moveListObjectPool);
         
         var game = new Game(tt, uci, sp, pos, moveListObjectPool);
         _perft = new Perft.Perft(game, new []{ pp });
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 089c8f00..3a938ba8 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -31,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.BoardTests;
 
@@ -43,6 +44,7 @@ public BoardTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index 279d97fa..9dcc095e 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -38,6 +38,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.BookTests;
 
@@ -56,6 +57,7 @@ public PolyglotTests(BookFixture fixture)
             .AddSingleton(polyOptions)
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
index c345ff5a..db049843 100644
--- a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
+++ b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
@@ -31,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.CastleTests;
 
@@ -43,6 +44,7 @@ public BasicCastleTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
index 7b0ffc40..15cb31c1 100644
--- a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
+++ b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
@@ -32,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.EvaluationTests;
 
@@ -44,6 +45,7 @@ public KpkBitBaseTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<IKpkBitBase, KpkBitBase>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index 53ea79a6..a614b6d7 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -32,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.FENTests;
 
@@ -44,6 +45,7 @@ public FenTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
index f17ef8c1..54cd2843 100644
--- a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
@@ -31,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.FenceTests;
 
@@ -43,6 +44,7 @@ public FenceTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<IBlockage, Blockage>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
index 397355a7..4542586b 100644
--- a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
+++ b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
@@ -32,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.GameplayTests;
 
@@ -44,6 +45,7 @@ public FoolsCheckMateTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
index b8f5098a..1ac3cf9b 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
@@ -30,6 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.MoveTests;
 
@@ -43,8 +44,9 @@ public void MoveListContainsMismatchedElement()
         var board = new Board();
         var pieceValue = new Values();
         var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        var validator = new PositionValidator();
 
-        var pos = new Position(board, pieceValue, moveListObjectPool);
+        var pos = new Position(board, pieceValue, validator, moveListObjectPool);
         var fd = new FenData(fen);
         var state = new State();
 
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
index de56da87..dc52ccb0 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
@@ -32,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.MoveTests;
 
@@ -44,6 +45,7 @@ public MoveGeneratorTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index 8d108068..ab7010f7 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -34,6 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.MoveTests;
 
@@ -46,6 +47,7 @@ public MoveTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
index fcdb76b2..2b4d725b 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
@@ -8,6 +8,7 @@
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.MoveTests;
 
@@ -24,6 +25,7 @@ protected PerftVerify()
             .AddSingleton(ttOptions)
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ISearchParameters, SearchParameters>()
             .AddSingleton<IUci, Uci>()
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index d44222bc..6fbe7848 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -34,6 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
 
@@ -46,6 +47,7 @@ public FanTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index 596eff8b..730b2844 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -33,6 +33,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
 
@@ -45,6 +46,7 @@ public IccfTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index 0cb55b49..832a72a0 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -34,6 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
 
@@ -46,6 +47,7 @@ public RanTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index df2dfffe..b233b50e 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -36,6 +36,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
 
@@ -48,6 +49,7 @@ public SanTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
index 0241368c..7b390669 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
@@ -31,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
 
@@ -43,6 +44,7 @@ public PawnDoubleAttackTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
index 024c65e5..8d36f8fc 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
@@ -28,6 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
 
@@ -68,8 +69,9 @@ public void RookBorderBlocked()
         var board = new Board();
         var pieceValue = new Values();
         var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        var validator = new PositionValidator();
 
-        var pos = new Position(board, pieceValue, moveListObjectPool);
+        var pos = new Position(board, pieceValue, validator, moveListObjectPool);
 
         while (border)
         {
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
index 65c1edc2..b9182e3f 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
@@ -31,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.PositionTests;
 
@@ -43,6 +44,7 @@ public EnPassantFenTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 42ad0148..35530151 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -31,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.PositionTests;
 
@@ -43,6 +44,7 @@ public PositionTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index 1fcac63c..acfa38d6 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -45,6 +45,7 @@ public ValidationTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
@@ -75,6 +76,7 @@ public void ValidationKingsNegative()
 
         var validator = pos.Validate(type);
 
+        Assert.NotNull(validator.ErrorMsg);
         Assert.NotEmpty(validator.ErrorMsg);
 
         var actualErrorMessage = validator.ErrorMsg;
@@ -100,7 +102,8 @@ public void ValidateCastleling()
         var validator = pos.Validate(validationType);
 
         Assert.True(validator.IsOk);
-        Assert.True(validator.ErrorMsg.IsNullOrEmpty());
+        Assert.NotNull(validator.ErrorMsg);
+        Assert.Empty(validator.ErrorMsg);
     }
 
     // TODO : Add tests for the rest of the validations
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
index 1b053eac..5b5730bd 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
@@ -34,6 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.ProtocolTests;
 
@@ -49,6 +50,7 @@ public UciTests()
         _serviceProvider = new ServiceCollection()
             .AddSingleton(options)
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IBoard, Board>()
             .AddTransient<IPosition, Position>()
             .AddSingleton(static _ =>
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 2dec2d64..8ef5b005 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -37,6 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Tables;
 using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Extensions;
 
@@ -87,6 +88,7 @@ public static IServiceCollection AddChessLib(
             .AddSingleton<ISearchParameters, SearchParameters>()
             .AddSingleton<IValues, Values>()
             .AddTransient<IBoard, Board>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddTransient<IGame, Game>()
             .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>();
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 466095bc..db604fdd 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -59,7 +59,11 @@ public sealed class Position : IPosition
     private readonly IPositionValidator _positionValidator;
     private Player _sideToMove;
 
-    public Position(IBoard board, IValues values, ObjectPool<IMoveList> moveListPool)
+    public Position(
+        IBoard board,
+        IValues values,
+        IPositionValidator positionValidator,
+        ObjectPool<IMoveList> moveListPool)
     {
         _castleKingPath = new BitBoard[CastleRight.Count];
         _castleRookPath = new BitBoard[CastleRight.Count];
@@ -68,7 +72,7 @@ public Position(IBoard board, IValues values, ObjectPool<IMoveList> moveListPool
         _castlingRookSquare = new Square[CastleRight.Count];
         _outputObjectPool = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
         _moveListPool = moveListPool;
-        _positionValidator = new PositionValidator(this, board);
+        _positionValidator = positionValidator;
         Board = board;
         IsProbing = true;
         Values = values;
@@ -867,7 +871,7 @@ public void MakeNullMove(in State newState)
 
         State.Repetition = 0;
 
-        Debug.Assert(_positionValidator.Validate(PositionValidationTypes.Basic).IsOk);
+        Debug.Assert(_positionValidator.Validate(this, PositionValidationTypes.Basic).IsOk);
     }
 
     public Piece MovedPiece(Move m)
@@ -1362,7 +1366,7 @@ public override string ToString()
     }
 
     public IPositionValidator Validate(PositionValidationTypes type = PositionValidationTypes.Basic)
-        => _positionValidator.Validate(type);
+        => _positionValidator.Validate(this, type);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static CastleRights OrCastlingRight(Player c, bool isKingSide)
diff --git a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
index d57e081b..1f492520 100644
--- a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
@@ -30,5 +30,5 @@ public interface IPositionValidator
 {
     string? ErrorMsg { get; }
     bool IsOk { get; }
-    IPositionValidator Validate(PositionValidationTypes type = PositionValidationTypes.All);
+    IPositionValidator Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All);
 }
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index 9dfd70b3..6d1098d1 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -55,70 +55,61 @@ public static bool HasFlagFast(this PositionValidationTypes @this, PositionValid
 
 public sealed class PositionValidator : IPositionValidator
 {
-    private readonly IBoard _board;
-    private readonly IPosition _pos;
-
-    public PositionValidator(in IPosition pos, in IBoard board)
-    {
-        _pos = pos;
-        _board = board;
-    }
-
     public string? ErrorMsg { get; private set; }
     public bool IsOk { get; private set; }
 
-    public IPositionValidator Validate(PositionValidationTypes type = PositionValidationTypes.All)
+    public IPositionValidator Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All)
     {
         var error = string.Empty;
 
         if (type.HasFlagFast(PositionValidationTypes.Basic))
-            error = ValidateBasic();
+            error = ValidateBasic(in pos);
 
         if (type.HasFlagFast(PositionValidationTypes.Castle))
-            error = ValidateCastleling(error);
+            error = ValidateCastleling(in pos, error);
 
         if (type.HasFlagFast(PositionValidationTypes.Kings))
-            error = ValidateKings(error);
+            error = ValidateKings(in pos, error);
 
         if (type.HasFlagFast(PositionValidationTypes.Pawns))
-            error = ValidatePawns(error);
+            error = ValidatePawns(in pos, error);
 
         if (type.HasFlagFast(PositionValidationTypes.PieceConsistency))
-            error = ValidatePieceConsistency(error);
+            error = ValidatePieceConsistency(in pos, error);
 
         if (type.HasFlagFast(PositionValidationTypes.PieceCount))
-            error = ValidatePieceCount(error);
+            error = ValidatePieceCount(pos, error);
 
         if (type.HasFlagFast(PositionValidationTypes.PieceTypes))
-            error = ValidatePieceTypes(error);
+            error = ValidatePieceTypes(in pos, error);
 
         if (type.HasFlagFast(PositionValidationTypes.State))
-            error = ValidateState(error);
+            error = ValidateState(in pos, error);
 
         IsOk = error.IsNullOrEmpty();
         ErrorMsg = error;
         return this;
     }
 
-    private string ValidateBasic()
+    private static string ValidateBasic(in IPosition pos)
     {
         var error = string.Empty;
-        if (_pos.SideToMove != Player.White && _pos.SideToMove != Player.Black)
-            error = AddError(error, $"{nameof(_pos.SideToMove)} is not a valid");
+        if (pos.SideToMove != Player.White && pos.SideToMove != Player.Black)
+            error = AddError(error, $"{nameof(pos.SideToMove)} is not a valid");
 
-        if (_board.PieceAt(_pos.GetKingSquare(Player.White)) != Piece.WhiteKing)
+        if (pos.GetPiece(pos.GetKingSquare(Player.White)) != Piece.WhiteKing)
             error = AddError(error, "white king position is not a white king");
 
-        if (_board.PieceAt(_pos.GetKingSquare(Player.Black)) != Piece.BlackKing)
+        if (pos.GetPiece(pos.GetKingSquare(Player.Black)) != Piece.BlackKing)
             error = AddError(error, "black king position is not a black king");
 
-        if (_pos.EnPassantSquare != Square.None && _pos.EnPassantSquare.RelativeRank(_pos.SideToMove) != Ranks.Rank6)
-            error = AddError(error, $"{nameof(_pos.EnPassantSquare)} square is not on rank 6");
+        if (pos.EnPassantSquare != Square.None && pos.EnPassantSquare.RelativeRank(pos.SideToMove) != Ranks.Rank6)
+            error = AddError(error, $"{nameof(pos.EnPassantSquare)} square is not on rank 6");
 
         return error;
     }
 
-    private string ValidateCastleling(string error)
+    private string ValidateCastleling(in IPosition pos, string error)
     {
         Span<Player> players = stackalloc Player[] { Player.White, Player.Black };
         Span<CastleRight> crs = stackalloc CastleRight[] { CastleRight.None, CastleRight.None };
@@ -131,83 +122,83 @@ private string ValidateCastleling(string error)
             var ourRook = PieceTypes.Rook.MakePiece(c);
             foreach (var cr in crs)
             {
-                if (!_pos.CanCastle(cr))
+                if (!pos.CanCastle(cr))
                     continue;
 
-                var rookSq = _pos.CastlingRookSquare(cr);
+                var rookSq = pos.CastlingRookSquare(cr);
 
-                if (_board.PieceAt(rookSq) != ourRook)
+                if (pos.GetPiece(rookSq) != ourRook)
                     error = AddError(error, $"rook does not appear on its position for {c}");
 
-                if (_pos.GetCastleRightsMask(rookSq) != cr)
+                if (pos.GetCastleRightsMask(rookSq) != cr)
                     error = AddError(error, $"castleling rights mask at {rookSq} does not match for player {c}");
 
-                if ((_pos.GetCastleRightsMask(_pos.GetKingSquare(c)) & cr) != cr)
+                if ((pos.GetCastleRightsMask(pos.GetKingSquare(c)) & cr) != cr)
                     error = AddError(error,
-                        $"castleling rights mask at {_pos.GetKingSquare(c)} does not match for player {c}");
+                        $"castleling rights mask at {pos.GetKingSquare(c)} does not match for player {c}");
             }
         }
 
         return error;
     }
 
-    private string ValidateKings(string error)
+    private static string ValidateKings(in IPosition pos, string error)
     {
         Span<Player> players = stackalloc Player[] { Player.White, Player.Black };
 
         foreach (var player in players)
         {
-            var count = _board.PieceCount(PieceTypes.King, player);
+            var count = pos.PieceCount(PieceTypes.King, player);
             if (count != 1)
                 error = AddError(error, $"king count for player {player} was {count}");
         }
 
-        if (!(_pos.AttacksTo(_pos.GetKingSquare(~_pos.SideToMove)) & _board.Pieces(_pos.SideToMove)).IsEmpty)
+        if (!(pos.AttacksTo(pos.GetKingSquare(~pos.SideToMove)) & pos.Pieces(pos.SideToMove)).IsEmpty)
             error = AddError(error, "kings appear to attack each other");
 
         return error;
     }
 
-    private string ValidatePawns(string error)
+    private static string ValidatePawns(in IPosition pos, string error)
     {
-        if (!(_board.Pieces(PieceTypes.Pawn) & (Rank.Rank1.RankBB() | Rank.Rank8.RankBB())).IsEmpty)
+        if (!(pos.Pieces(PieceTypes.Pawn) & (Rank.Rank1.RankBB() | Rank.Rank8.RankBB())).IsEmpty)
             error = AddError(error, "pawns exists on rank 1 or rank 8");
 
-        if (_board.PieceCount(PieceTypes.Pawn, Player.White) > 8)
+        if (pos.PieceCount(PieceTypes.Pawn, Player.White) > 8)
             error = AddError(error, "white side has more than 8 pawns");
 
-        if (_board.PieceCount(PieceTypes.Pawn, Player.Black) > 8)
+        if (pos.PieceCount(PieceTypes.Pawn, Player.Black) > 8)
             error = AddError(error, "black side has more than 8 pawns");
 
         return error;
     }
 
-    private string ValidatePieceConsistency(string error)
+    private static string ValidatePieceConsistency(in IPosition pos, string error)
     {
-        if (!(_board.Pieces(Player.White) & _board.Pieces(Player.Black)).IsEmpty)
+        if (!(pos.Pieces(Player.White) & pos.Pieces(Player.Black)).IsEmpty)
             error = AddError(error, "white and black pieces overlap");
 
-        if ((_board.Pieces(Player.White) | _board.Pieces(Player.Black)) != _board.Pieces())
+        if ((pos.Pieces(Player.White) | pos.Pieces(Player.Black)) != pos.Pieces())
             error = AddError(error, "white and black pieces do not match all pieces");
 
-        if (_board.Pieces(Player.White).Count > 16)
+        if (pos.Pieces(Player.White).Count > 16)
             error = AddError(error, "white side has more than 16 pieces");
 
-        if (_board.Pieces(Player.Black).Count > 16)
+        if (pos.Pieces(Player.Black).Count > 16)
             error = AddError(error, "black side has more than 16 pieces");
 
         return error;
     }
 
-    private string ValidatePieceCount(string error) =>
+    private static string ValidatePieceCount(IPosition pos, string error) =>
         Piece.AllPieces
             .Select(static pc => new { pc, pt = pc.Type() })
             .Select(static t => new { t, c = t.pc.ColorOf() })
-            .Where(t => _board.PieceCount(t.t.pt, t.c) != _board.Pieces(t.c, t.t.pt).Count)
+            .Where(t => pos.PieceCount(t.t.pt, t.c) != pos.Pieces(t.t.pt, t.c).Count)
             .Select(static t => t.t.pc)
             .Aggregate(error, static (current, pc) => AddError(current, $"piece count does not match for piece {pc}"));
 
-    private string ValidatePieceTypes(string error)
+    private static string ValidatePieceTypes(in IPosition pos, string error)
     {
         Span<PieceTypes> pts = stackalloc PieceTypes[]
         {
@@ -220,31 +211,27 @@ private string ValidatePieceTypes(string error)
         };
 
         foreach (var p1 in pts)
-        foreach (var p2 in pts)
         {
-            if (p1 == p2 || (_board.Pieces(p1) & _board.Pieces(p2)).IsEmpty)
-                continue;
+            foreach (var p2 in pts)
+            {
+                if (p1 == p2 || (pos.Pieces(p1) & pos.Pieces(p2)).IsEmpty)
+                    continue;
 
-            error = AddError(error, $"piece types {p1} and {p2} doesn't align");
+                error = AddError(error, $"piece types {p1} and {p2} doesn't align");
+            }
         }
 
         return error;
     }
 
-    private string ValidateState(string error)
+    private string ValidateState(in IPosition pos, string error)
     {
-        var state = _pos.State;
-
-        if (state == null)
-        {
-            error = AddError(error, "state is null");
-            return error;
-        }
+        var state = pos.State;
 
-        if (state.Key.Key == 0 && !_board.Pieces().IsEmpty)
+        if (state.Key.Key == 0 && !pos.Pieces().IsEmpty)
             error = AddError(error, "state key is invalid");
 
-        if (_board.Pieces(_pos.SideToMove, PieceTypes.Pawn).IsEmpty &&
+        if (pos.Pieces(PieceTypes.Pawn, pos.SideToMove).IsEmpty &&
             state.PawnStructureKey.Key != Zobrist.ZobristNoPawn)
             error = AddError(error, "empty pawn key is invalid");
 

From 9a7a947ddef7fb6224117c58400acb57cd051bee Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 19 Feb 2023 19:36:34 +0100
Subject: [PATCH 017/119] Minor update to PieceSquareEventArgs

---
 src/Rudzoft.ChessLib/Types/PieceSquare.cs          | 2 +-
 src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/Rudzoft.ChessLib/Types/PieceSquare.cs b/src/Rudzoft.ChessLib/Types/PieceSquare.cs
index 2b69db47..3ce165cf 100644
--- a/src/Rudzoft.ChessLib/Types/PieceSquare.cs
+++ b/src/Rudzoft.ChessLib/Types/PieceSquare.cs
@@ -1,3 +1,3 @@
 namespace Rudzoft.ChessLib.Types;
 
-public record struct PieceSquare(Piece Piece, Square Square);
+public readonly record struct PieceSquare(Piece Piece, Square Square);
diff --git a/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs b/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
index 00718b3e..2f34eb0f 100644
--- a/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
+++ b/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
@@ -42,6 +42,12 @@ public PieceSquareEventArgs(Piece pc, Square sq)
         Square = sq;
     }
 
+    public PieceSquareEventArgs(PieceSquare ps)
+    {
+        Piece = ps.Piece;
+        Square = ps.Square;
+    }
+
     public Piece Piece { get; set; }
 
     public Square Square { get; set; }

From bbddc16cb8affaa2ad4cf800ef6f06daa55262c9 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 20 Feb 2023 07:30:08 +0100
Subject: [PATCH 018/119] Update State use a bit + added missing properties
 from IGame

---
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |   3 +-
 .../MoveTests/PerftVerify.cs                  |   6 +-
 .../Services/MoveGeneratorService.cs          |   4 +-
 .../ChessLibServiceCollectionExtensions.cs    |   9 +-
 src/Rudzoft.ChessLib/Game.cs                  |   9 +-
 src/Rudzoft.ChessLib/IGame.cs                 |   4 +
 src/Rudzoft.ChessLib/Position.cs              | 188 +++++++++---------
 .../Protocol/UCI/{CPU.cs => Cpu.cs}           |   9 +-
 src/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs     |   6 +
 .../Protocol/UCI/ISearchParameters.cs         |   3 +-
 .../Protocol/UCI/SearchParameters.cs          |  10 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |   2 +-
 src/Rudzoft.ChessLib/State.cs                 |  56 ++----
 .../Validation/PositionValidator.cs           |   4 +-
 .../Extensions/ObjectExtensions.cs            |   3 +-
 src/Rudzoft.Perft/Perft/PerftRunner.cs        |   4 +-
 src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs |   2 +-
 17 files changed, 157 insertions(+), 165 deletions(-)
 rename src/Rudzoft.ChessLib/Protocol/UCI/{CPU.cs => Cpu.cs} (94%)
 create mode 100644 src/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index 2853a1c2..6085f1ad 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -72,6 +72,7 @@ public void Setup()
 
         var uci = new Uci();
         uci.Initialize();
+        var cpu = new Cpu();
 
         var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
         
@@ -83,7 +84,7 @@ public void Setup()
 
         var pos = new Position(board, values, validator, moveListObjectPool);
         
-        var game = new Game(tt, uci, sp, pos, moveListObjectPool);
+        var game = new Game(tt, uci, cpu, sp, pos, moveListObjectPool);
         _perft = new Perft.Perft(game, new []{ pp });
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
index 2b4d725b..9af70d66 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
@@ -29,6 +29,7 @@ protected PerftVerify()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ISearchParameters, SearchParameters>()
             .AddSingleton<IUci, Uci>()
+            .AddSingleton<ICpu, Cpu>()
             .AddTransient<IGame, Game>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
@@ -43,11 +44,8 @@ protected PerftVerify()
 
     protected void AssertPerft(string fen, int depth, in ulong expected)
     {
-        var fenData = new FenData(fen);
-        var state = new State();
-
         var g = _serviceProvider.GetRequiredService<IGame>();
-        g.Pos.Set(in fenData, ChessMode.Normal, state);
+        g.NewGame(fen);
         
         var actual = g.Perft(depth);
         Assert.Equal(expected, actual);
diff --git a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
index ac3d4d3d..77047499 100644
--- a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
@@ -6,10 +6,10 @@ namespace Rudzoft.ChessLib.WebApi.Services;
 
 public sealed class MoveGeneratorService : IMoveGeneratorService
 {
-    private readonly ILogger<MoveGeneratorService> _logger;
+    private readonly ILogger<IMoveGeneratorService> _logger;
     private readonly IPosition _position;
 
-    public MoveGeneratorService(ILogger<MoveGeneratorService> logger, IPosition pos)
+    public MoveGeneratorService(ILogger<IMoveGeneratorService> logger, IPosition pos)
     {
         _logger = logger;
         _position = pos;
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 8ef5b005..92389ce6 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -45,8 +45,8 @@ public static class ChessLibServiceCollectionExtensions
 {
     public static IServiceCollection AddChessLib(
         this IServiceCollection serviceCollection,
-        IConfiguration? configuration,
-        string? configurationFile = null)
+        IConfiguration configuration,
+        string configurationFile = null)
     {
         if (serviceCollection == null)
             throw new ArgumentNullException(nameof(serviceCollection));
@@ -77,7 +77,7 @@ public static IServiceCollection AddChessLib(
             var policy = new MoveListPolicy();
             return provider.Create(policy);
         });
-        
+
         return serviceCollection.AddSingleton(static _ =>
             {
                 IUci uci = new Uci();
@@ -91,7 +91,8 @@ public static IServiceCollection AddChessLib(
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddTransient<IGame, Game>()
-            .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>();
+            .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>()
+            .AddSingleton<ICpu, Cpu>();
     }
 
     private static IConfigurationRoot LoadConfiguration(string? file)
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 0d328a54..a77366b1 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -31,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Runtime.InteropServices;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
@@ -47,6 +48,7 @@ public sealed class Game : IGame
     public Game(
         ITranspositionTable transpositionTable,
         IUci uci,
+        ICpu cpu,
         ISearchParameters searchParameters,
         IPosition pos,
         ObjectPool<IMoveList> moveListPoolPool)
@@ -57,11 +59,12 @@ public Game(
         Table = transpositionTable;
         SearchParameters = searchParameters;
         Uci = uci;
+        Cpu = cpu;
     }
 
     public Action<IPieceSquare> PieceUpdated => _pos.PieceUpdated;
 
-    public int MoveNumber => 0; //(PositionIndex - 1) / 2 + 1;
+    public int MoveNumber => 1 + (Pos.Ply - Pos.SideToMove.IsBlack.AsByte() / 2);
 
     public BitBoard Occupied => Pos.Pieces();
 
@@ -74,6 +77,8 @@ public Game(
     public ISearchParameters SearchParameters { get; }
 
     public IUci Uci { get; }
+    
+    public ICpu Cpu { get; }
 
     public bool IsRepetition => _pos.IsRepetition;
 
@@ -134,7 +139,6 @@ public ulong Perft(int depth, bool root = true)
 
         var ml = _moveListPool.Get();
         ml.Generate(in _pos);
-        var state = new State();
 
         var moves = ml.Get();
         ref var movesSpace = ref MemoryMarshal.GetReference(moves);
@@ -146,6 +150,7 @@ public ulong Perft(int depth, bool root = true)
             else
             {
                 var m = em.Move;
+                var state = new State();
                 _pos.MakeMove(m, in state);
 
                 if (depth <= 2)
diff --git a/src/Rudzoft.ChessLib/IGame.cs b/src/Rudzoft.ChessLib/IGame.cs
index de50ee5f..0dd156c8 100644
--- a/src/Rudzoft.ChessLib/IGame.cs
+++ b/src/Rudzoft.ChessLib/IGame.cs
@@ -47,6 +47,10 @@ public interface IGame : IEnumerable<Piece>
 
     bool IsRepetition { get; }
 
+    public IUci Uci { get; }
+    
+    public ICpu Cpu { get; }
+
     void NewGame(string fen = Fen.Fen.StartPositionFen);
 
     FenData GetFen();
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index db604fdd..3ce87e16 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -54,6 +54,7 @@ public sealed class Position : IPosition
     private readonly BitBoard[] _castleRookPath;
     private readonly CastleRight[] _castlingRightsMask;
     private readonly Square[] _castlingRookSquare;
+    private readonly Value[] _nonPawnMaterial;
     private readonly ObjectPool<StringBuilder> _outputObjectPool;
     private readonly ObjectPool<IMoveList> _moveListPool;
     private readonly IPositionValidator _positionValidator;
@@ -70,6 +71,7 @@ public Position(
         _castlingRightsMask = new CastleRight[Square.Count];
         _castlingRightsMask.Fill(CastleRight.None);
         _castlingRookSquare = new Square[CastleRight.Count];
+        _nonPawnMaterial = new[] { Value.ValueZero, Value.ValueZero };
         _outputObjectPool = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
         _moveListPool = moveListPool;
         _positionValidator = positionValidator;
@@ -101,7 +103,7 @@ public Position(
     /// <summary>
     /// To let something outside the library be aware of changes (like a UI etc)
     /// </summary>
-    public Action<IPieceSquare>? PieceUpdated { get; set; }
+    public Action<IPieceSquare> PieceUpdated { get; set; }
 
     public IValues Values { get; }
 
@@ -269,8 +271,6 @@ public void Clear()
         _castlingRookSquare.Fill(Square.None);
         _sideToMove = Player.White;
         ChessMode = ChessMode.Normal;
-
-        State ??= new State();
     }
 
     /// <summary>
@@ -315,39 +315,36 @@ public FenData GenerateFen()
             fen[length++] = dash;
         else
         {
-            switch (ChessMode)
+            if (ChessMode == ChessMode.Normal)
             {
-                case ChessMode.Normal:
-                    if (CanCastle(CastleRight.WhiteKing))
-                        fen[length++] = 'K';
+                if (CanCastle(CastleRight.WhiteKing))
+                    fen[length++] = 'K';
 
-                    if (CanCastle(CastleRight.WhiteQueen))
-                        fen[length++] = 'Q';
+                if (CanCastle(CastleRight.WhiteQueen))
+                    fen[length++] = 'Q';
 
-                    if (CanCastle(CastleRight.BlackKing))
-                        fen[length++] = 'k';
+                if (CanCastle(CastleRight.BlackKing))
+                    fen[length++] = 'k';
 
-                    if (CanCastle(CastleRight.BlackQueen))
-                        fen[length++] = 'q';
-                    
-                    break;
-                case ChessMode.Chess960:
-                    if (CanCastle(CastleRight.WhiteKing))
-                        fen[length++] = CastlingRookSquare(CastleRight.WhiteKing).FileChar;
+                if (CanCastle(CastleRight.BlackQueen))
+                    fen[length++] = 'q';
+            }
+            else if (ChessMode == ChessMode.Chess960)
+            {
+                if (CanCastle(CastleRight.WhiteKing))
+                    fen[length++] = CastlingRookSquare(CastleRight.WhiteKing).FileChar;
 
-                    if (CanCastle(CastleRight.WhiteQueen))
-                        fen[length++] = CastlingRookSquare(CastleRight.WhiteQueen).FileChar;
+                if (CanCastle(CastleRight.WhiteQueen))
+                    fen[length++] = CastlingRookSquare(CastleRight.WhiteQueen).FileChar;
 
-                    if (CanCastle(CastleRight.BlackKing))
-                        fen[length++] = CastlingRookSquare(CastleRight.BlackQueen).FileChar;
+                if (CanCastle(CastleRight.BlackKing))
+                    fen[length++] = CastlingRookSquare(CastleRight.BlackQueen).FileChar;
 
-                    if (CanCastle(CastleRight.BlackQueen))
-                        fen[length++] = CastlingRookSquare(CastleRight.BlackQueen).FileChar;
-                    
-                    break;
-                default:
-                    throw new Exception($"Invalid chess mode. mode={ChessMode}");
+                if (CanCastle(CastleRight.BlackQueen))
+                    fen[length++] = CastlingRookSquare(CastleRight.BlackQueen).FileChar;
             }
+            else
+                throw new Exception($"Invalid chess mode. mode={ChessMode}");
         }
 
         fen[length++] = space;
@@ -382,20 +379,15 @@ public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
         };
     }
 
-    public BitBoard GetAttacks(Square sq, PieceTypes pt)
-        => GetAttacks(sq, pt, Pieces());
+    public BitBoard GetAttacks(Square sq, PieceTypes pt) => GetAttacks(sq, pt, Pieces());
 
-    public CastleRight GetCastleRightsMask(Square sq)
-        => _castlingRightsMask[sq.AsInt()];
+    public CastleRight GetCastleRightsMask(Square sq) => _castlingRightsMask[sq.AsInt()];
 
-    public IEnumerator<Piece> GetEnumerator()
-        => Board.GetEnumerator();
+    public IEnumerator<Piece> GetEnumerator() => Board.GetEnumerator();
 
-    IEnumerator IEnumerable.GetEnumerator()
-        => GetEnumerator();
+    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
-    public Square GetKingSquare(Player p)
-        => Board.Square(PieceTypes.King, p);
+    public Square GetKingSquare(Player p) => Board.Square(PieceTypes.King, p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public HashKey GetPawnKey()
@@ -430,8 +422,7 @@ public HashKey GetPiecesKey()
         return result;
     }
 
-    public Square GetPieceSquare(PieceTypes pt, Player p)
-        => Board.Square(pt, p);
+    public Square GetPieceSquare(PieceTypes pt, Player p) => Board.Square(pt, p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public PieceTypes GetPieceType(Square sq) => Board.PieceAt(sq).Type();
@@ -502,21 +493,18 @@ public bool GivesCheck(Move m)
 
     public bool HasGameCycle(int ply)
     {
-        var end = State.Rule50 < State.PliesFromNull
-            ? State.Rule50
-            : State.PliesFromNull;
-
+        var end = State.End();
         return end >= 3 && Cuckoo.HashCuckooCycle(this, end, ply);
     }
 
     public bool HasRepetition()
     {
         var currentState = State;
-        var end = Math.Min(State.Rule50, State.PliesFromNull);
+        var end = currentState.End();
 
         while (end-- >= 4)
         {
-            if (currentState.Repetition > 0)
+            if (currentState!.Repetition > 0)
                 return true;
 
             currentState = currentState.Previous;
@@ -691,18 +679,19 @@ public bool IsPseudoLegal(Move m)
     }
 
     public void MakeMove(Move m, in State newState)
-        => MakeMove(m, newState, GivesCheck(m));
+        => MakeMove(m, in newState, GivesCheck(m));
 
     public void MakeMove(Move m, in State newState, bool givesCheck)
     {
-        State = State.CopyTo(newState);
         State.LastMove = m;
+        State = State.CopyTo(newState);
+        var state = State;
 
-        var k = State.Key ^ Zobrist.GetZobristSide();
+        var k = state.Key ^ Zobrist.GetZobristSide();
 
         Ply++;
-        State.Rule50++;
-        State.PliesFromNull++;
+        state.Rule50++;
+        state.PliesFromNull++;
 
         var us = _sideToMove;
         var them = ~us;
@@ -750,11 +739,11 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                     Debug.Assert(GetPiece(captureSquare) == pt.MakePiece(them));
                 }
 
-                State.PawnStructureKey ^= capturedPiece.GetZobristPst(captureSquare);
+                state.PawnKey ^= capturedPiece.GetZobristPst(captureSquare);
             }
             else
             {
-                State.NonPawnMaterial[them.Side] -= Values.GetPieceValue(capturedPiece, Phases.Mg);
+                _nonPawnMaterial[them.Side] -= Values.GetPieceValue(capturedPiece, Phases.Mg);
                 // TODO : Update material here
             }
 
@@ -768,26 +757,26 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             // TODO : Update other depending keys and psq values here
 
             // Reset rule 50 counter
-            State.Rule50 = 0;
+            state.Rule50 = 0;
         }
 
         // update key with moved piece
         k ^= pc.GetZobristPst(from) ^ pc.GetZobristPst(to);
 
         // reset en-passant square if it is set
-        if (State.EnPassantSquare != Square.None)
+        if (state.EnPassantSquare != Square.None)
         {
-            k ^= State.EnPassantSquare.File.GetZobristEnPassant();
-            State.EnPassantSquare = Square.None;
+            k ^= state.EnPassantSquare.File.GetZobristEnPassant();
+            state.EnPassantSquare = Square.None;
         }
 
         // Update castling rights if needed
-        if (State.CastlelingRights != CastleRight.None &&
+        if (state.CastlelingRights != CastleRight.None &&
             (_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]) != CastleRight.None)
         {
             var cr = _castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()];
-            k ^= (State.CastlelingRights & cr).Key();
-            State.CastlelingRights &= ~cr;
+            k ^= (state.CastlelingRights & cr).Key();
+            state.CastlelingRights &= ~cr;
         }
 
         // Move the piece. The tricky Chess960 castle is handled earlier
@@ -801,8 +790,8 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             if ((to.Value.AsInt() ^ from.Value.AsInt()) == 16
                 && !((to - us.PawnPushDistance()).PawnAttack(us) & Pieces(PieceTypes.Pawn, them)).IsEmpty)
             {
-                State.EnPassantSquare = to - us.PawnPushDistance();
-                k ^= State.EnPassantSquare.File.GetZobristEnPassant();
+                state.EnPassantSquare = to - us.PawnPushDistance();
+                k ^= state.EnPassantSquare.File.GetZobristEnPassant();
             }
             else if (type == MoveTypes.Promotion)
             {
@@ -816,16 +805,16 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
 
                 // Update hash keys
                 k ^= pc.GetZobristPst(to) ^ promotionPiece.GetZobristPst(to);
-                State.PawnStructureKey ^= pc.GetZobristPst(to);
+                state.PawnKey ^= pc.GetZobristPst(to);
 
-                State.NonPawnMaterial[us.Side] += Values.GetPieceValue(promotionPiece, Phases.Mg);
+                _nonPawnMaterial[us.Side] += Values.GetPieceValue(promotionPiece, Phases.Mg);
             }
 
             // Update pawn hash key
-            State.PawnStructureKey ^= pc.GetZobristPst(from) ^ pc.GetZobristPst(to);
+            state.PawnKey ^= pc.GetZobristPst(from) ^ pc.GetZobristPst(to);
 
             // Reset rule 50 draw counter
-            State.Rule50 = 0;
+            state.Rule50 = 0;
         }
 
         // TODO : Update piece values here
@@ -834,15 +823,15 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         Debug.Assert(GetKingSquare(them).IsOk);
 
         // Update state properties
-        State.Key = k;
-        State.CapturedPiece = capturedPiece;
+        state.Key = k;
+        state.CapturedPiece = capturedPiece;
 
-        State.Checkers = givesCheck ? AttacksTo(GetKingSquare(them)) & Board.Pieces(us) : BitBoard.Empty;
+        state.Checkers = givesCheck ? AttacksTo(GetKingSquare(them)) & Board.Pieces(us) : BitBoard.Empty;
 
         _sideToMove = ~_sideToMove;
 
-        SetCheckInfo(State);
-        State.UpdateRepetition();
+        SetCheckInfo(state);
+        state.UpdateRepetition();
 
         //Debug.Assert(_positionValidator.Validate().IsOk);
     }
@@ -967,20 +956,15 @@ public BitBoard Pieces(PieceTypes pt, Player p)
     public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2, Player p)
         => Board.Pieces(p, pt1, pt2);
 
-    public int PieceCount()
-        => Board.PieceCount();
+    public int PieceCount() => Board.PieceCount();
 
-    public int PieceCount(Piece pc)
-        => Board.PieceCount(pc.Type(), pc.ColorOf());
+    public int PieceCount(Piece pc) => Board.PieceCount(pc.Type(), pc.ColorOf());
 
-    public int PieceCount(PieceTypes pt)
-        => Board.PieceCount(pt);
+    public int PieceCount(PieceTypes pt) => Board.PieceCount(pt);
 
-    public int PieceCount(PieceTypes pt, Player p)
-        => Board.PieceCount(pt, p);
+    public int PieceCount(PieceTypes pt, Player p) => Board.PieceCount(pt, p);
 
-    public BitBoard PawnsOnColor(Player p, Square sq)
-        => Pieces(PieceTypes.Pawn, p) & sq.Color().ColorBB();
+    public BitBoard PawnsOnColor(Player p, Square sq) => Pieces(PieceTypes.Pawn, p) & sq.Color().ColorBB();
 
     public bool SemiOpenFileOn(Player p, Square sq)
         => (Board.Pieces(p, PieceTypes.Pawn) & sq.File.BitBoardFile()).IsEmpty;
@@ -997,7 +981,10 @@ public bool BishopOpposed() =>
             .IsOppositeColor(Board.Square(PieceTypes.Bishop, Player.Black));
 
     public BitBoard PinnedPieces(Player p)
-        => State.Pinners[p.Side];
+    {
+        Debug.Assert(State != null);
+        return State.Pinners[p.Side];
+    }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void RemovePiece(Square sq)
@@ -1111,9 +1098,9 @@ public bool SeeGe(Move m, Value threshold)
         return res > 0;
     }
 
-    public Value NonPawnMaterial(Player p) => State.NonPawnMaterial[p.Side];
+    public Value NonPawnMaterial(Player p) => _nonPawnMaterial[p.Side];
 
-    public Value NonPawnMaterial() => State.NonPawnMaterial[0] - State.NonPawnMaterial[1];
+    public Value NonPawnMaterial() => _nonPawnMaterial[0] - _nonPawnMaterial[1];
 
     private void SetupPieces(ReadOnlySpan<char> fenChunk)
     {
@@ -1217,9 +1204,10 @@ public IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool
 
         Clear();
 
+        ChessMode = chessMode;
         Searcher = searcher;
 
-        state.CopyTo(State);
+        State = state.CopyTo(State);
 
         SetupPieces(fenData.Chunk());
         SetupPlayer(fenData.Chunk());
@@ -1227,8 +1215,6 @@ public IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool
         SetupEnPassant(fenData.Chunk());
         SetupMoveNumber(fenData);
 
-        ChessMode = chessMode;
-
         SetState();
 
         return this;
@@ -1257,7 +1243,7 @@ public ReadOnlySpan<Square> Squares(PieceTypes pt, Player p)
 
     public void TakeMove(Move m)
     {
-        Debug.Assert(!m.IsNullMove());
+        Debug.Assert(m.IsValidMove());
 
         // flip sides
         _sideToMove = ~_sideToMove;
@@ -1314,13 +1300,21 @@ public void TakeMove(Move m)
         // Set state to previous state
         State = State.Previous;
         Ply--;
-
-        //Debug.Assert(_positionValidator.Validate().IsOk);
+        
+#if DEBUG
+        Debug.Assert(_positionValidator.Validate(this).IsOk);
+#endif
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void TakeNullMove()
     {
+        Debug.Assert(State != null);
+        Debug.Assert(State.Previous != null);
+        Debug.Assert(State.PliesFromNull == 0);
+        Debug.Assert(State.CapturedPiece == Piece.EmptyPiece);
+        Debug.Assert(State.Checkers.IsEmpty);
+
         Debug.Assert(!InCheck);
         State = State.Previous;
         _sideToMove = ~_sideToMove;
@@ -1465,7 +1459,7 @@ private void SetState()
         var pawnKey = Zobrist.ZobristNoPawn;
 
         State.Checkers = AttacksTo(GetKingSquare(_sideToMove)) & Board.Pieces(~_sideToMove);
-        State.NonPawnMaterial[Player.White.Side] = State.NonPawnMaterial[Player.Black.Side] = Value.ValueZero;
+        _nonPawnMaterial[Player.White.Side] = _nonPawnMaterial[Player.Black.Side] = Value.ValueZero;
         SetCheckInfo(State);
 
         // compute hash keys
@@ -1480,7 +1474,7 @@ private void SetState()
             if (pt == PieceTypes.Pawn)
                 pawnKey ^= pc.GetZobristPst(sq);
             else if (pt != PieceTypes.King)
-                State.NonPawnMaterial[pc.ColorOf().Side] += Values.GetPieceValue(pc, Phases.Mg);
+                _nonPawnMaterial[pc.ColorOf().Side] += Values.GetPieceValue(pc, Phases.Mg);
         }
 
         if (State.EnPassantSquare != Square.None)
@@ -1501,7 +1495,7 @@ private void SetState()
         }
 
         State.Key = key;
-        State.PawnStructureKey = pawnKey;
+        State.PawnKey = pawnKey;
         State.MaterialKey = materialKey;
     }
 
@@ -1509,11 +1503,9 @@ private void SetupCastleling(ReadOnlySpan<char> castleling)
     {
         Square RookSquare(Square startSq, Piece rook)
         {
-            Square targetSq;
-            for (targetSq = startSq; Board.PieceAt(targetSq) != rook; --targetSq)
-            {
-            }
-
+            var targetSq = startSq;
+            while (Board.PieceAt(targetSq) != rook)
+                --targetSq;
             return targetSq;
         }
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/CPU.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
similarity index 94%
rename from src/Rudzoft.ChessLib/Protocol/UCI/CPU.cs
rename to src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
index 47b9101d..d0a9e618 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/CPU.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
-public sealed class CPU
+public sealed class Cpu : ICpu
 {
     private const int Interval = 500;
 
@@ -45,15 +45,15 @@ public sealed class CPU
 
     private TimeSpan _lastUserCpu;
 
-    public CPU()
+    public Cpu()
     {
         _currentProcessName = Process.GetCurrentProcess();
         _numProcessors = Environment.ProcessorCount;
     }
 
-    public double CpuUse { get; set; }
+    public double CpuUse => Usage();
 
-    public double Usage()
+    private double Usage()
     {
         var now = DateTime.UtcNow;
         var total = _currentProcessName.TotalProcessorTime;
@@ -77,7 +77,6 @@ public double Usage()
         _lastCpu = now;
         _lastSysCpu = total;
         _lastUserCpu = user;
-        CpuUse = percentage;
 
         return percentage switch
         {
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs b/src/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs
new file mode 100644
index 00000000..47ceb63a
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs
@@ -0,0 +1,6 @@
+namespace Rudzoft.ChessLib.Protocol.UCI;
+
+public interface ICpu
+{
+    double CpuUse { get; }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
index fde0da8c..07198e96 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
@@ -24,11 +24,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
-public interface ISearchParameters
+public interface ISearchParameters : ISpanFormattable
 {
     ulong BlackIncrementTimeMilliseconds { get; set; }
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index 233de49e..33a0120a 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -35,8 +35,10 @@ namespace Rudzoft.ChessLib.Protocol.UCI;
 /// <summary>
 /// Contains the information related to search parameters for a UCI chess engine.
 /// </summary>
-public sealed class SearchParameters : ISearchParameters, ISpanFormattable
+public sealed class SearchParameters : ISearchParameters
 {
+    private static readonly Clock ZeroClock = new(0UL, 0UL);
+    
     private readonly Clock[] _clock;
     private ulong _movesToGo;
     private ulong _moveTime;
@@ -145,11 +147,7 @@ public bool UseTimeManagement()
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Clear()
     {
-        _clock[Player.White.Side].Time = ulong.MinValue;
-        _clock[Player.White.Side].Time = ulong.MinValue;
-        _clock[Player.Black.Side].Inc = ulong.MinValue;
-        _clock[Player.Black.Side].Inc = ulong.MinValue;
-        
+        _clock.Fill(ZeroClock);
         MoveTime = 0;
         Infinite = false;
     }
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index e11e83ca..f40c3673 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -19,7 +19,7 @@
     <PackageProjectUrl>https://github.com/rudzen/ChessLib</PackageProjectUrl>
     <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
     <PackageLicenseFile>LICENSE</PackageLicenseFile>
-    <Nullable>enable</Nullable>
+    <Nullable>warnings</Nullable>
     <EnforceCodeStyleInBuild>False</EnforceCodeStyleInBuild>
     <Title>Rudzoft.ChessLib</Title>
     <PackageReadmeFile>README.md</PackageReadmeFile>
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index ef0ee7f5..ab658b78 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
 
@@ -34,23 +35,18 @@ namespace Rudzoft.ChessLib;
 
 public sealed class State : IEquatable<State>
 {
-    public Move LastMove { get; set; }
-
-    public Value[] NonPawnMaterial { get; }
-
-    public HashKey PawnStructureKey { get; set; }
-
     public HashKey MaterialKey { get; set; }
-
-    public int PliesFromNull { get; set; }
+    
+    public HashKey PawnKey { get; set; }
 
     public int Rule50 { get; set; }
+    
+    public int PliesFromNull { get; set; }
 
     public CastleRight CastlelingRights { get; set; }
 
     public Square EnPassantSquare { get; set; }
 
-    public State Previous { get; private set; }
 
     // -----------------------------
     // Properties below this point are not copied from other state
@@ -72,21 +68,24 @@ public sealed class State : IEquatable<State>
 
     public Piece CapturedPiece { get; set; }
 
+    public Move LastMove { get; set; }
+
     public int Repetition { get; set; }
 
+    public State Previous { get; private set; }
+
     /// <summary>
     /// Partial copy from existing state The properties not copied are re-calculated
     /// </summary>
     /// <param name="other">The current state</param>
     public State(State other)
     {
-        PawnStructureKey = other.PawnStructureKey;
+        PawnKey = other.PawnKey;
         MaterialKey = other.MaterialKey;
         CastlelingRights = other.CastlelingRights;
         Rule50 = other.Rule50;
         PliesFromNull = other.PliesFromNull;
         EnPassantSquare = other.EnPassantSquare;
-        NonPawnMaterial = new[] { other.NonPawnMaterial[0], other.NonPawnMaterial[1] };
         Previous = other;
 
         Checkers = BitBoard.Empty;
@@ -99,7 +98,6 @@ public State(State other)
     public State()
     {
         LastMove = Move.EmptyMove;
-        NonPawnMaterial = new[] { Value.ValueZero, Value.ValueZero };
         CastlelingRights = CastleRight.None;
         EnPassantSquare = Square.None;
         Checkers = BitBoard.Empty;
@@ -111,27 +109,20 @@ public State()
 
     public State CopyTo(State other)
     {
+        other ??= new State();
+        
         // copy over preserved values
-        other.PawnStructureKey = PawnStructureKey;
         other.MaterialKey = MaterialKey;
-        other.CastlelingRights = CastlelingRights;
+        other.PawnKey = PawnKey;
         other.Rule50 = Rule50;
         other.PliesFromNull = PliesFromNull;
+        other.CastlelingRights = CastlelingRights;
         other.EnPassantSquare = EnPassantSquare;
         other.Previous = this;
 
-        // copy over material
-        other.NonPawnMaterial[0] = NonPawnMaterial[0];
-        other.NonPawnMaterial[1] = NonPawnMaterial[1];
-
         // initialize the rest of the values
         if (other.CheckedSquares == null)
             other.CheckedSquares = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
-        else
-            other.CheckedSquares.Fill(BitBoard.Empty);
-
-        other.Pinners[0] = other.Pinners[1] = BitBoard.Empty;
-        other.BlockersForKing[0] = other.BlockersForKing[1] = BitBoard.Empty;
 
         return other;
     }
@@ -139,8 +130,7 @@ public State CopyTo(State other)
     public void Clear()
     {
         LastMove = Move.EmptyMove;
-        NonPawnMaterial.Clear();
-        PawnStructureKey = Key = MaterialKey = HashKey.Empty;
+        PawnKey = Key = MaterialKey = HashKey.Empty;
         PliesFromNull = 0;
         Repetition = 0;
         CastlelingRights = CastleRight.None;
@@ -154,11 +144,9 @@ public void Clear()
 
     public void UpdateRepetition()
     {
-        var end = Rule50 < PliesFromNull
-            ? Rule50
-            : PliesFromNull;
-
         Repetition = 0;
+        
+        var end = End();
 
         if (end < 4)
             return;
@@ -174,16 +162,17 @@ public void UpdateRepetition()
         }
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public int End() => Math.Min(Rule50, PliesFromNull);
+
     public bool Equals(State other)
     {
         if (other is null) return false;
         return LastMove.Equals(other.LastMove)
                && Key.Equals(other.Key)
-               && PawnStructureKey.Equals(other.PawnStructureKey)
+               && PawnKey.Equals(other.PawnKey)
                && EnPassantSquare.Equals(other.EnPassantSquare)
                && CastlelingRights == other.CastlelingRights
-               && NonPawnMaterial[0] == other.NonPawnMaterial[0]
-               && NonPawnMaterial[1] == other.NonPawnMaterial[1]
                && PliesFromNull == other.PliesFromNull
                && Rule50 == other.Rule50
                && Pinners.Equals(other.Pinners)
@@ -199,8 +188,7 @@ public override int GetHashCode()
     {
         var hashCode = new HashCode();
         hashCode.Add(LastMove);
-        hashCode.Add(NonPawnMaterial);
-        hashCode.Add(PawnStructureKey);
+        hashCode.Add(PawnKey);
         hashCode.Add(MaterialKey);
         hashCode.Add(PliesFromNull);
         hashCode.Add(Rule50);
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index 6d1098d1..bab4fce0 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -55,7 +55,7 @@ public static bool HasFlagFast(this PositionValidationTypes @this, PositionValid
 
 public sealed class PositionValidator : IPositionValidator
 {
-    public string? ErrorMsg { get; private set; }
+    public string ErrorMsg { get; private set; }
     public bool IsOk { get; private set; }
 
     public IPositionValidator Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All)
@@ -232,7 +232,7 @@ private string ValidateState(in IPosition pos, string error)
             error = AddError(error, "state key is invalid");
 
         if (pos.Pieces(PieceTypes.Pawn, pos.SideToMove).IsEmpty &&
-            state.PawnStructureKey.Key != Zobrist.ZobristNoPawn)
+            state.PawnKey.Key != Zobrist.ZobristNoPawn)
             error = AddError(error, "empty pawn key is invalid");
 
         if (state.Repetition < 0)
diff --git a/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs b/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
index d3b5fe01..06dc0a18 100644
--- a/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
+++ b/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
@@ -58,7 +58,6 @@ public static object[] ConcatParametersCopy(this object @this, params object[] a
     /// <returns></returns>
     public static IEnumerable<object> ConcatParameters(this object @this, params object[] args)
     {
-        var result = new[] { @this };
-        return result.Concat(args);
+        return new[] { @this }.Concat(args);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Perft/PerftRunner.cs b/src/Rudzoft.Perft/Perft/PerftRunner.cs
index 8b939264..87353c6c 100644
--- a/src/Rudzoft.Perft/Perft/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Perft/PerftRunner.cs
@@ -71,7 +71,7 @@ public sealed class PerftRunner : IPerftRunner
 
     private readonly IUci _uci;
 
-    private readonly CPU _cpu;
+    private readonly Cpu _cpu;
 
     private bool _usingEpd;
 
@@ -97,7 +97,7 @@ public PerftRunner(
 
         configuration.Bind("TranspositionTable", TranspositionTableOptions);
 
-        _cpu = new CPU();
+        _cpu = new Cpu();
     }
 
     public bool SaveResults { get; set; }
diff --git a/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs b/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
index f020a7ec..b6bd5059 100644
--- a/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
+++ b/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
@@ -41,7 +41,7 @@ private static string GetTimestamp()
     {
         var attribute = Assembly.GetExecutingAssembly()
             .GetCustomAttributesData()
-            .First(x => x.AttributeType.Name == AttributeName);
+            .First(static x => x.AttributeType.Name == AttributeName);
 
         return (string)attribute.ConstructorArguments.First().Value;
     }

From 00455d7c568a8d5815b8d0832e21f631dc602a53 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Fri, 24 Mar 2023 18:25:25 +0100
Subject: [PATCH 019/119] Refactorings

---
 .../StringBenchmarks.cs                       | 176 ++++++++++++++++++
 src/Rudzoft.ChessLib.Perft/Perft.cs           |  11 +-
 .../{MoveTests => PerftTests}/PerftTest.cs    |  23 ++-
 .../{MoveTests => PerftTests}/PerftVerify.cs  |   4 +-
 .../Rudzoft.ChessLib.Test.csproj              |   2 +-
 .../TablesTests/KillerMovesTests.cs           |   1 +
 .../ChessLibServiceCollectionExtensions.cs    |   1 +
 src/Rudzoft.ChessLib/Game.cs                  |  38 ++--
 src/Rudzoft.ChessLib/IPosition.cs             |   2 +
 src/Rudzoft.ChessLib/IValues.cs               |   2 +
 .../MoveGeneration/IMoveList.cs               |   2 +-
 .../MoveGeneration/MoveList.cs                |  23 ++-
 .../Notation/Notations/SanNotation.cs         |  12 +-
 src/Rudzoft.ChessLib/Position.cs              |  38 ++--
 src/Rudzoft.ChessLib/State.cs                 |   8 +-
 src/Rudzoft.ChessLib/Tables/HashTable.cs      |  78 ++++++++
 .../Tables/{ => History}/HistoryHeuristic.cs  |   2 +-
 .../Tables/{ => History}/IHistoryHeuristic.cs |   2 +-
 src/Rudzoft.ChessLib/Tables/IHashTable.cs     |  39 ++++
 src/Rudzoft.ChessLib/Tables/ITableEntry.cs    |  36 ++++
 .../Tables/{ => KillerMoves}/IKillerMoves.cs  |   2 +-
 .../Tables/{ => KillerMoves}/KillerMoves.cs   |   2 +-
 .../Tables/Perft/IPerftTableEntry.cs          |   7 +
 .../Tables/Perft/PerftTable.cs                |  86 +++++++++
 src/Rudzoft.ChessLib/Types/BitBoards.cs       |   2 +-
 src/Rudzoft.ChessLib/Types/File.cs            | 117 ++++--------
 src/Rudzoft.ChessLib/Types/HashKey.cs         |   4 +
 src/Rudzoft.ChessLib/Types/Rank.cs            | 120 +++++-------
 src/Rudzoft.ChessLib/Types/Value.cs           |   2 +
 29 files changed, 627 insertions(+), 215 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
 rename src/Rudzoft.ChessLib.Test/{MoveTests => PerftTests}/PerftTest.cs (74%)
 rename src/Rudzoft.ChessLib.Test/{MoveTests => PerftTests}/PerftVerify.cs (94%)
 create mode 100644 src/Rudzoft.ChessLib/Tables/HashTable.cs
 rename src/Rudzoft.ChessLib/Tables/{ => History}/HistoryHeuristic.cs (98%)
 rename src/Rudzoft.ChessLib/Tables/{ => History}/IHistoryHeuristic.cs (96%)
 create mode 100644 src/Rudzoft.ChessLib/Tables/IHashTable.cs
 create mode 100644 src/Rudzoft.ChessLib/Tables/ITableEntry.cs
 rename src/Rudzoft.ChessLib/Tables/{ => KillerMoves}/IKillerMoves.cs (96%)
 rename src/Rudzoft.ChessLib/Tables/{ => KillerMoves}/KillerMoves.cs (98%)
 create mode 100644 src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs
 create mode 100644 src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
new file mode 100644
index 00000000..917d80b6
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
@@ -0,0 +1,176 @@
+using System;
+using System.Collections.Generic;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Benchmark;
+
+[MemoryDiagnoser]
+public class StringBenchmarks
+{
+    [Benchmark]
+    [ArgumentsSource(nameof(Squares))]
+    public string ArrayLookup(Square sq)
+    {
+        return sq.ToString();
+    }
+
+    [Benchmark]
+    [ArgumentsSource(nameof(Squares))]
+    public string NameOf(Square sq)
+    {
+        return GetNameOf(sq.Value);
+    }
+    
+    public static IEnumerable<object> Squares()
+    {
+        var sqs = BitBoards.MakeBitboard(Square.A1, Square.H8);
+        // var sqs = BitBoards.AllSquares;
+        while (sqs)
+            yield return BitBoards.PopLsb(ref sqs);
+    }
+
+    private static string GetNameOf(Squares sq)
+    {
+        switch (sq)
+        {
+            case Types.Squares.a1:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.b1:
+                return nameof(Types.Squares.b1);
+            case Types.Squares.c1:
+                return nameof(Types.Squares.c1);
+            case Types.Squares.d1:
+                return nameof(Types.Squares.d1);
+            case Types.Squares.e1:
+                return nameof(Types.Squares.e1);
+            case Types.Squares.f1:
+                return nameof(Types.Squares.f1);
+            case Types.Squares.g1:
+                return nameof(Types.Squares.g1);
+            case Types.Squares.h1:
+                return nameof(Types.Squares.h1);
+            case Types.Squares.a2:
+                return nameof(Types.Squares.a2);
+            case Types.Squares.b2:
+                return nameof(Types.Squares.b2);
+            case Types.Squares.c2:
+                return nameof(Types.Squares.c2);
+            case Types.Squares.d2:
+                return nameof(Types.Squares.d2);
+            case Types.Squares.e2:
+                return nameof(Types.Squares.e2);
+            case Types.Squares.f2:
+                return nameof(Types.Squares.f2);
+            case Types.Squares.g2:
+                return nameof(Types.Squares.g2);
+            case Types.Squares.h2:
+                return nameof(Types.Squares.h2);
+            case Types.Squares.a3:
+                return nameof(Types.Squares.a3);
+            case Types.Squares.b3:
+                return nameof(Types.Squares.b3);
+            case Types.Squares.c3:
+                return nameof(Types.Squares.c3);
+            case Types.Squares.d3:
+                return nameof(Types.Squares.d3);
+            case Types.Squares.e3:
+                return nameof(Types.Squares.e3);
+            case Types.Squares.f3:
+                return nameof(Types.Squares.f3);
+            case Types.Squares.g3:
+                return nameof(Types.Squares.g3);
+            case Types.Squares.h3:
+                return nameof(Types.Squares.h3);
+            case Types.Squares.a4:
+                return nameof(Types.Squares.a4);
+            case Types.Squares.b4:
+                return nameof(Types.Squares.b4);
+            case Types.Squares.c4:
+                return nameof(Types.Squares.c4);
+            case Types.Squares.d4:
+                return nameof(Types.Squares.d4);
+            case Types.Squares.e4:
+                return nameof(Types.Squares.e4);
+            case Types.Squares.f4:
+                return nameof(Types.Squares.f4);
+            case Types.Squares.g4:
+                return nameof(Types.Squares.g4);
+            case Types.Squares.h4:
+                return nameof(Types.Squares.h4);
+            case Types.Squares.a5:
+                return nameof(Types.Squares.a5);
+            case Types.Squares.b5:
+                return nameof(Types.Squares.b5);
+            case Types.Squares.c5:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.d5:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.e5:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.f5:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.g5:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.h5:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.a6:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.b6:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.c6:
+                return nameof(Types.Squares.a1);
+
+                return nameof(Types.Squares.a1);
+
+            case Types.Squares.e6:
+                return nameof(Types.Squares.a1);
+
+            case Types.Squares.f6:
+                return nameof(Types.Squares.a1);
+
+            case Types.Squares.g6:
+                return nameof(Types.Squares.a1);
+
+            case Types.Squares.h6:
+                return nameof(Types.Squares.a1);
+
+            case Types.Squares.a7:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.b7:
+                return nameof(Types.Squares.a1);
+            case Types.Squares.c7:
+                return nameof(Types.Squares.c7);
+            case Types.Squares.d7:
+                return nameof(Types.Squares.d7);
+            case Types.Squares.e7:
+                return nameof(Types.Squares.e7);
+            case Types.Squares.f7:
+                return nameof(Types.Squares.f7);
+            case Types.Squares.g7:
+                return nameof(Types.Squares.g7);
+            case Types.Squares.h7:
+                return nameof(Types.Squares.h7);
+            case Types.Squares.a8:
+                return nameof(Types.Squares.a8);
+            case Types.Squares.b8:
+                return nameof(Types.Squares.b8);
+            case Types.Squares.c8:
+                return nameof(Types.Squares.c8);
+            case Types.Squares.d8:
+                return nameof(Types.Squares.d8);
+            case Types.Squares.e8:
+                return nameof(Types.Squares.e8);
+            case Types.Squares.f8:
+                return nameof(Types.Squares.f8);
+            case Types.Squares.g8:
+                return nameof(Types.Squares.g8);
+            case Types.Squares.h8:
+                return nameof(Types.Squares.h8);
+            case Types.Squares.none:
+                return nameof(Types.Squares.a1);
+
+            default:
+                throw new ArgumentOutOfRangeException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/Rudzoft.ChessLib.Perft/Perft.cs
index 2bd13469..3590f37c 100644
--- a/src/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/Rudzoft.ChessLib.Perft/Perft.cs
@@ -85,15 +85,8 @@ public async IAsyncEnumerable<ulong> DoPerft(int depth)
         {
             var state = new State();
             Game.Pos.Set(in fd, ChessMode.Normal, state);
-
-            if (PerftTable.Retrieve(Game.Pos.State.Key, depth, out var result))
-                yield return result;
-            else
-            {
-                result = Game.Perft(depth);
-                PerftTable.Store(Game.Pos.State.Key, depth, result);
-                yield return result;
-            }
+            var result = Game.Perft(depth);
+            yield return result;
         }
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
similarity index 74%
rename from src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs
rename to src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
index a1af53c2..e7b0e894 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/PerftTest.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
@@ -24,7 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-namespace Rudzoft.ChessLib.Test.MoveTests;
+namespace Rudzoft.ChessLib.Test.PerftTests;
 
 public sealed class PerftOneTest : PerftVerify
 {
@@ -96,3 +96,24 @@ public sealed class PerftFiveTest : PerftVerify
     public void Perft(string fen, int depth, ulong expected)
         => AssertPerft(fen, depth, in expected);
 }
+
+public sealed class TalkChessPerftTests : PerftVerify
+{
+    [Theory]
+    [InlineData("3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1", 6, 1_134_888UL)] //--Illegal ep move #1
+    [InlineData("8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1", 6, 1_015_133UL)] //--Illegal ep move #2
+    [InlineData("8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1", 6, 1_440_467UL)] //--EP Capture Checks Opponent
+    [InlineData("5k2/8/8/8/8/8/8/4K2R w K - 0 1", 6, 661_072UL)] //--Short Castling Gives Check
+    [InlineData("3k4/8/8/8/8/8/8/R3K3 w Q - 0 1", 6, 803_711UL)] //--Long Castling Gives Check
+    [InlineData("r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1", 4, 1_274_206UL)] //--Castle Rights
+    [InlineData("r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1", 4, 1_720_476UL)] //--Castling Prevented
+    [InlineData("2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1", 6, 3_821_001UL)] //--Promote out of Check
+    [InlineData("8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1", 5, 1_004_658UL)] //--Discovered Check
+    [InlineData("4k3/1P6/8/8/8/8/K7/8 w - - 0 1", 6, 217_342UL)] //--Promote to give check
+    [InlineData("8/P1k5/K7/8/8/8/8/8 w - - 0 1", 6, 92_683UL)] //--Under Promote to give check
+    [InlineData("K1k5/8/P7/8/8/8/8/8 w - - 0 1", 6, 2_217UL)] //--Self Stalemate
+    [InlineData("8/k1P5/8/1K6/8/8/8/8 w - - 0 1", 7, 567_584UL)] //--Stalemate & Checkmate
+    [InlineData("8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1", 4, 23_527UL)] //--Stalemate & Checkmate
+    public void Perft(string fen, int depth, ulong expected)
+        => AssertPerft(fen, depth, in expected);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
similarity index 94%
rename from src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
rename to src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
index 9af70d66..484b6ddf 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
@@ -2,15 +2,13 @@
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
-using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
-namespace Rudzoft.ChessLib.Test.MoveTests;
+namespace Rudzoft.ChessLib.Test.PerftTests;
 
 public abstract class PerftVerify
 {
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 0f694022..b3753ce1 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -27,7 +27,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.analyzers" Version="1.1.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
diff --git a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
index 997f76ef..c7ed9a34 100644
--- a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
+++ b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Rudzoft.ChessLib.Tables;
+using Rudzoft.ChessLib.Tables.KillerMoves;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.TablesTests;
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 92389ce6..5a112739 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -36,6 +36,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Tables;
+using Rudzoft.ChessLib.Tables.KillerMoves;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index a77366b1..fc0f1c7f 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -36,6 +36,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Protocol.UCI;
+using Rudzoft.ChessLib.Tables.Perft;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib;
@@ -105,16 +106,12 @@ public void UpdateDrawTypes()
         moveList.Generate(in _pos);
 
         var moves = moveList.Get();
-        ref var movesSpace = ref MemoryMarshal.GetReference(moves);
-        for (var i = 0; i < moves.Length; ++i)
-        {
-            var em = Unsafe.Add(ref movesSpace, i);
-            if (Pos.IsLegal(em.Move))
-                continue;
-            gameEndType |= GameEndTypes.Pat;
-            break;
-        }
 
+        if (moves.IsEmpty)
+            gameEndType |= GameEndTypes.Pat;
+        
+        _moveListPool.Return(moveList);
+        
         GameEndType = gameEndType;
     }
 
@@ -135,8 +132,17 @@ public override string ToString()
 
     public ulong Perft(int depth, bool root = true)
     {
-        var tot = ulong.MinValue;
+        // ref var entry = ref _perftTable.TryGet(in _pos, depth, out var tableValue);
+        //
+        // if (tableValue)
+        //     return entry.Count;
+        //
+        // entry.Count = ulong.MinValue;
+        // entry.Depth = depth;
+        // entry.Key = _pos.State.Key;
 
+        var tot = ulong.MinValue;
+        
         var ml = _moveListPool.Get();
         ml.Generate(in _pos);
 
@@ -144,12 +150,12 @@ public ulong Perft(int depth, bool root = true)
         ref var movesSpace = ref MemoryMarshal.GetReference(moves);
         for (var i = 0; i < moves.Length; ++i)
         {
-            var em = Unsafe.Add(ref movesSpace, i);
+            var valMove = Unsafe.Add(ref movesSpace, i);
             if (root && depth <= 1)
                 tot++;
             else
             {
-                var m = em.Move;
+                var m = valMove.Move;
                 var state = new State();
                 _pos.MakeMove(m, in state);
 
@@ -161,12 +167,18 @@ public ulong Perft(int depth, bool root = true)
                     _moveListPool.Return(ml2);
                 }
                 else
-                    tot += Perft(depth - 1, false);
+                {
+                    var next = Perft(depth - 1, false);
+                    tot += next;
+                }
 
                 _pos.TakeMove(m);
             }
         }
 
+        // if (!root)
+        //     _perftTable.Store(_pos.State.Key, depth, tot);
+        
         _moveListPool.Return(ml);
 
         return tot;
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index 6702ce8f..0c899352 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -170,6 +170,8 @@ public interface IPosition : IEnumerable<Piece>
 
     bool IsPawnPassedAt(Player p, Square sq);
 
+    BitBoard PawnPassSpan(Player p, Square sq);
+    
     bool CanCastle(CastleRight cr);
 
     bool CanCastle(Player p);
diff --git a/src/Rudzoft.ChessLib/IValues.cs b/src/Rudzoft.ChessLib/IValues.cs
index 418bf915..9922d8fa 100644
--- a/src/Rudzoft.ChessLib/IValues.cs
+++ b/src/Rudzoft.ChessLib/IValues.cs
@@ -75,4 +75,6 @@ public interface IValues
     void SetPieceValues(DefaultPieceValues[] values, Phases phase);
 
     DefaultPieceValues GetPieceValue(Piece pc, Phases phase);
+    
+    DefaultPieceValues GetPieceValue(PieceTypes pt, Phases phase);
 }
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
index 75dfc2ae..de8cc483 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
@@ -46,7 +46,7 @@ public interface IMoveList : IReadOnlyCollection<ValMove>
     void Clear();
 
     bool Contains(in ValMove item);
-    bool Contains(Move item);
+    bool Contains(Move move);
     bool Contains(Square from, Square to);
     void Generate(in IPosition pos, MoveGenerationType type = MoveGenerationType.Legal);
     ReadOnlySpan<ValMove> Get();
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index d682ba27..a297c6d6 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Types;
 
@@ -82,10 +83,15 @@ public bool Contains(in ValMove item)
         => Contains(item.Move);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Contains(Move item)
+    public bool Contains(Move move)
     {
-        for (var i = 0; i < Length; ++i)
-            if (_moves[i].Move == item)
+        var moveSpan = _moves.AsSpan()[..Length];
+        if (moveSpan.IsEmpty)
+            return false;
+
+        ref var movesSpace = ref MemoryMarshal.GetReference(moveSpan);
+        for (var i = 0; i < moveSpan.Length; ++i)
+            if (move == Unsafe.Add(ref movesSpace, i).Move)
                 return true;
 
         return false;
@@ -94,10 +100,15 @@ public bool Contains(Move item)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Contains(Square from, Square to)
     {
-        for (var i = 0; i < Length; ++i)
+        var moveSpan = _moves.AsSpan()[..Length];
+        if (moveSpan.IsEmpty)
+            return false;
+
+        ref var movesSpace = ref MemoryMarshal.GetReference(moveSpan);
+        for (var i = 0; i < moveSpan.Length; ++i)
         {
-            var move = _moves[i].Move;
-            if (move.FromSquare() == from && move.ToSquare() == to)
+            var m = Unsafe.Add(ref movesSpace, i).Move;
+            if (m.FromSquare() == from && m.ToSquare() == to)
                 return true;
         }
 
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
index 7a958cad..ce66f02b 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Linq;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.MoveGeneration;
@@ -70,14 +71,11 @@ public override string Convert(Move move)
             re[i++] = 'p';
             re[i++] = from.FileChar;
         }
-        else
+        else if (Pos.GetPiece(to) != Piece.EmptyPiece)
         {
-            if (Pos.GetPiece(to) != Piece.EmptyPiece)
-            {
-                if (pt == PieceTypes.Pawn)
-                    re[i++] = from.FileChar;
-                re[i++] = 'x';
-            }
+            if (pt == PieceTypes.Pawn)
+                re[i++] = from.FileChar;
+            re[i++] = 'x';
         }
 
         re[i++] = to.FileChar;
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 3ce87e16..d339299c 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -60,6 +60,14 @@ public sealed class Position : IPosition
     private readonly IPositionValidator _positionValidator;
     private Player _sideToMove;
 
+    public Position()
+    {
+        Board = new Board();
+        Values = new Values();
+        State = new State();
+        Clear();
+    }
+    
     public Position(
         IBoard board,
         IValues values,
@@ -183,6 +191,9 @@ public bool IsCaptureOrPromotion(Move m)
     public bool IsPawnPassedAt(Player p, Square sq)
         => (Board.Pieces(~p, PieceTypes.Pawn) & sq.PassedPawnFrontAttackSpan(p)).IsEmpty;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public BitBoard PawnPassSpan(Player p, Square sq) => p.FrontSquares(sq) | sq.PawnAttackSpan(p);
+
     public BitBoard AttacksTo(Square sq, in BitBoard occ)
     {
         Debug.Assert(sq.IsOk);
@@ -715,8 +726,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
 
             var (rookFrom, rookTo) = DoCastleling(us, from, ref to, CastlePerform.Do);
 
-            k ^= capturedPiece.GetZobristPst(rookFrom);
-            k ^= capturedPiece.GetZobristPst(rookTo);
+            k ^= capturedPiece.GetZobristPst(rookFrom) ^ capturedPiece.GetZobristPst(rookTo);
 
             // reset captured piece type as castleling is "king-captures-rook"
             capturedPiece = Piece.EmptyPiece;
@@ -774,9 +784,9 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         if (state.CastlelingRights != CastleRight.None &&
             (_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]) != CastleRight.None)
         {
-            var cr = _castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()];
-            k ^= (state.CastlelingRights & cr).Key();
-            state.CastlelingRights &= ~cr;
+            k ^= state.CastlelingRights.Key();
+            state.CastlelingRights &= ~(_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]);
+            k ^= state.CastlelingRights.Key();
         }
 
         // Move the piece. The tricky Chess960 castle is handled earlier
@@ -824,7 +834,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
 
         // Update state properties
         state.Key = k;
-        state.CapturedPiece = capturedPiece;
+        state.CapturedPiece = capturedPiece.Type();
 
         state.Checkers = givesCheck ? AttacksTo(GetKingSquare(them)) & Board.Pieces(us) : BitBoard.Empty;
 
@@ -1253,7 +1263,7 @@ public void TakeMove(Move m)
         var pc = GetPiece(to);
 
         Debug.Assert(!IsOccupied(from) || m.IsCastleMove());
-        Debug.Assert(State.CapturedPiece.Type() != PieceTypes.King);
+        Debug.Assert(State.CapturedPiece != PieceTypes.King);
 
         if (type == MoveTypes.Promotion)
         {
@@ -1264,6 +1274,7 @@ public void TakeMove(Move m)
             RemovePiece(to);
             pc = PieceTypes.Pawn.MakePiece(us);
             AddPiece(pc, to);
+            _nonPawnMaterial[_sideToMove.Side] -= Values.GetPieceValue(pc, Phases.Mg);
         }
 
         if (type == MoveTypes.Castling)
@@ -1275,7 +1286,7 @@ public void TakeMove(Move m)
             MovePiece(to, from);
 #pragma warning restore S2234 // Parameters should be passed in the correct order
 
-            if (State.CapturedPiece != Piece.EmptyPiece)
+            if (State.CapturedPiece != PieceTypes.NoPieceType)
             {
                 var captureSquare = to;
 
@@ -1290,7 +1301,13 @@ public void TakeMove(Move m)
                     Debug.Assert(!IsOccupied(captureSquare));
                 }
 
-                AddPiece(State.CapturedPiece, captureSquare);
+                AddPiece(State.CapturedPiece.MakePiece(~_sideToMove), captureSquare);
+
+                if (State.CapturedPiece != PieceTypes.Pawn)
+                {
+                    var them = ~_sideToMove;
+                    _nonPawnMaterial[them.Side] += Values.GetPieceValue(State.CapturedPiece, Phases.Mg);
+                }
             }
         }
 
@@ -1312,7 +1329,7 @@ public void TakeNullMove()
         Debug.Assert(State != null);
         Debug.Assert(State.Previous != null);
         Debug.Assert(State.PliesFromNull == 0);
-        Debug.Assert(State.CapturedPiece == Piece.EmptyPiece);
+        Debug.Assert(State.CapturedPiece == PieceTypes.NoPieceType);
         Debug.Assert(State.Checkers.IsEmpty);
 
         Debug.Assert(!InCheck);
@@ -1527,7 +1544,6 @@ Square RookSquare(Square startSq, Piece rook)
 
             if (rsq != Square.None)
                 SetCastlingRight(c, rsq);
-
         }
     }
 
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index ab658b78..ca3e51b4 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -66,7 +66,7 @@ public sealed class State : IEquatable<State>
 
     public BitBoard[] CheckedSquares { get; private set; }
 
-    public Piece CapturedPiece { get; set; }
+    public PieceTypes CapturedPiece { get; set; }
 
     public Move LastMove { get; set; }
 
@@ -92,7 +92,7 @@ public State(State other)
         BlockersForKing = new[] { BitBoard.Empty, BitBoard.Empty };
         Pinners = new[] { BitBoard.Empty, BitBoard.Empty };
         CheckedSquares = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
-        CapturedPiece = Piece.EmptyPiece;
+        CapturedPiece = PieceTypes.NoPieceType;
     }
 
     public State()
@@ -104,7 +104,7 @@ public State()
         CheckedSquares = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
         Pinners = new[] { BitBoard.Empty, BitBoard.Empty };
         BlockersForKing = new[] { BitBoard.Empty, BitBoard.Empty };
-        CapturedPiece = Piece.EmptyPiece;
+        CapturedPiece = PieceTypes.NoPieceType;
     }
 
     public State CopyTo(State other)
@@ -138,7 +138,7 @@ public void Clear()
         CheckedSquares.Fill(BitBoard.Empty);
         Pinners[0] = Pinners[1] = BitBoard.Empty;
         BlockersForKing[0] = BlockersForKing[1] = BitBoard.Empty;
-        CapturedPiece = Piece.EmptyPiece;
+        CapturedPiece = PieceTypes.NoPieceType;
         Previous = null;
     }
 
diff --git a/src/Rudzoft.ChessLib/Tables/HashTable.cs b/src/Rudzoft.ChessLib/Tables/HashTable.cs
new file mode 100644
index 00000000..3337c810
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Tables/HashTable.cs
@@ -0,0 +1,78 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Tables;
+
+public abstract class HashTable<T> : IHashTable<T> where T : ITableEntry
+{
+    private T[] _table = Array.Empty<T>();
+
+    public int Count => _table.Length;
+
+    public ref T this[HashKey key] => ref _table[key.Key & (ulong)(_table.Length - 1)];
+
+    /// <summary>
+    /// Initialized the table array. In case the table array is initialized with a different
+    /// size the existing elements are being kept. The index operator[] will return a reference
+    /// struct to avoid any copying, which actually edits the entry "in-place" which avoids
+    /// having to store the entry again.
+    ///
+    /// The process is a bit tricky:
+    /// - Because of the nature of C# it requires a custom object initializer function
+    /// - The initializer should initialize a single entry object
+    /// </summary>
+    /// <param name="elementSize">The size of <see cref="T"/></param>
+    /// <param name="tableSizeMb">The number of elements to store in the array</param>
+    /// <param name="initializer">Initializer function to initialize a single entry object</param>
+    public void Initialize(int elementSize, int tableSizeMb, Func<T> initializer)
+    {
+        var arraySize = tableSizeMb * 1024 * 1024 / elementSize;
+        if (_table.Length != 0)
+        {
+            if (_table.Length == arraySize)
+                return;
+
+            var currentLength = _table.Length;
+
+            Array.Resize(ref _table, arraySize);
+
+            if (currentLength > arraySize)
+                return;
+
+            for (var i = currentLength; i < arraySize; ++i)
+                _table[i] = initializer();
+        }
+        else
+        {
+            _table = new T[arraySize];
+            for (var i = 0; i < _table.Length; ++i)
+                _table[i] = initializer();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Tables/HistoryHeuristic.cs b/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
similarity index 98%
rename from src/Rudzoft.ChessLib/Tables/HistoryHeuristic.cs
rename to src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
index 7a11d4d1..05b17c46 100644
--- a/src/Rudzoft.ChessLib/Tables/HistoryHeuristic.cs
+++ b/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
@@ -27,7 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
 
-namespace Rudzoft.ChessLib.Tables;
+namespace Rudzoft.ChessLib.Tables.History;
 
 public sealed class HistoryHeuristic : IHistoryHeuristic
 {
diff --git a/src/Rudzoft.ChessLib/Tables/IHistoryHeuristic.cs b/src/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs
similarity index 96%
rename from src/Rudzoft.ChessLib/Tables/IHistoryHeuristic.cs
rename to src/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs
index 2e8476b0..b0b6411d 100644
--- a/src/Rudzoft.ChessLib/Tables/IHistoryHeuristic.cs
+++ b/src/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs
@@ -26,7 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using Rudzoft.ChessLib.Types;
 
-namespace Rudzoft.ChessLib.Tables;
+namespace Rudzoft.ChessLib.Tables.History;
 
 public interface IHistoryHeuristic
 {
diff --git a/src/Rudzoft.ChessLib/Tables/IHashTable.cs b/src/Rudzoft.ChessLib/Tables/IHashTable.cs
new file mode 100644
index 00000000..acdd805a
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Tables/IHashTable.cs
@@ -0,0 +1,39 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Tables;
+
+public interface IHashTable<T> where T : ITableEntry
+{
+    int Count { get; }
+
+    ref T this[HashKey key] { get; }
+
+    void Initialize(int elementSize, int tableSizeMb, Func<T> initializer);
+}
diff --git a/src/Rudzoft.ChessLib/Tables/ITableEntry.cs b/src/Rudzoft.ChessLib/Tables/ITableEntry.cs
new file mode 100644
index 00000000..72803f08
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Tables/ITableEntry.cs
@@ -0,0 +1,36 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Tables;
+
+public interface ITableEntry
+{
+    HashKey Key { get; set; }
+
+    Score Evaluate(IPosition pos);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs
similarity index 96%
rename from src/Rudzoft.ChessLib/Tables/IKillerMoves.cs
rename to src/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs
index 6565e8b2..b47eb2a7 100644
--- a/src/Rudzoft.ChessLib/Tables/IKillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs
@@ -27,7 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using Rudzoft.ChessLib.Types;
 
-namespace Rudzoft.ChessLib.Tables;
+namespace Rudzoft.ChessLib.Tables.KillerMoves;
 
 public interface IKillerMoves : IEquatable<IKillerMoves>
 {
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
similarity index 98%
rename from src/Rudzoft.ChessLib/Tables/KillerMoves.cs
rename to src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
index b75051fe..1493625b 100644
--- a/src/Rudzoft.ChessLib/Tables/KillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
@@ -28,7 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
 
-namespace Rudzoft.ChessLib.Tables;
+namespace Rudzoft.ChessLib.Tables.KillerMoves;
 
 public sealed class KillerMoves : IKillerMoves
 {
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs b/src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs
new file mode 100644
index 00000000..0f0e2206
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs
@@ -0,0 +1,7 @@
+namespace Rudzoft.ChessLib.Tables.Perft;
+
+public interface IPerftTableEntry : ITableEntry
+{
+    public ulong Count { get; set; }
+    public int Depth { get; set; }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
new file mode 100644
index 00000000..860c094d
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
@@ -0,0 +1,86 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Tables.Perft;
+
+public struct PerftTableEntry : IPerftTableEntry
+{
+    public HashKey Key { get; set; }
+    
+    public ulong Count { get; set; }
+    
+    public int Depth { get; set; }
+    
+    public Score Evaluate(IPosition pos)
+    {
+        throw new NotImplementedException();
+    }
+}
+
+public sealed class PerftTable : HashTable<IPerftTableEntry>
+{
+    private const int HashMemory = 4;
+    private static readonly int ElementSize;
+
+    private readonly int _mask;
+
+    static PerftTable()
+    {
+        unsafe
+        {
+            ElementSize = sizeof(PerftTableEntry);
+        }
+    }
+    
+    public PerftTable()
+    {
+        Initialize(ElementSize, HashMemory, static () => new PerftTableEntry { Key = 0, Count = ulong.MinValue, Depth = -1 });
+        _mask = Count - 1;
+    }
+    
+    public ref IPerftTableEntry TryGet(in IPosition pos, int depth, out bool found)
+    {
+        var posKey = pos.State.Key;
+        var entryKey = posKey & _mask ^ depth;
+        ref var entry = ref this[entryKey];
+        found = entry.Key == entryKey && entry.Depth == depth;
+        if (!found)
+            entry.Key = entryKey;
+        return ref entry;
+    }
+
+    public void Store(in HashKey key, int depth, ulong count)
+    {
+        var entryKey = key & _mask ^ depth;
+        ref var entry = ref this[entryKey];
+        entry.Key = entryKey;
+        entry.Depth = depth;
+        entry.Count = count;
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index b8354d6d..b16883a0 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -621,7 +621,7 @@ public static int Lsb(this int v)
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Square Msb(this in BitBoard bb)
-        => new(63 - BitOperations.LeadingZeroCount(bb.Value));
+        => new(63 ^ BitOperations.LeadingZeroCount(bb.Value));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Square FrontMostSquare(in BitBoard bb, Player p)
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index a0d999de..708df2e8 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -49,8 +49,7 @@ public enum Files
 public static class FilesExtensions
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int AsInt(this Files f)
-        => (int)f;
+    public static int AsInt(this Files f) => (int)f;
 }
 
 public readonly record struct File(Files Value) : IComparable<File>, ISpanFormattable, IValidationType
@@ -68,132 +67,100 @@ public readonly record struct File(Files Value) : IComparable<File>, ISpanFormat
     public static File[] AllFiles { get; } = { FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public File(int file)
-        : this((Files)file) { }
+    public File(int file) : this((Files)file) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public File(File f)
-        : this(f.Value) { }
+    public File(File f) : this(f.Value) { }
 
-    public char Char
-        => (char)('a' + Value);
+    public char Char => (char)('a' + Value);
 
-    public bool IsOk
-        => Value.AsInt().InBetween(Files.FileA.AsInt(), Files.FileH.AsInt());
+    public bool IsOk => Value.AsInt().InBetween(Files.FileA.AsInt(), Files.FileH.AsInt());
 
     public const int Count = (int)Files.FileNb;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator File(int value)
-        => new(value);
+    public static implicit operator File(int value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator File(Files value)
-        => new(value);
+    public static implicit operator File(Files value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(File left, Files right)
-        => left.Value == right;
+    public static bool operator ==(File left, Files right) => left.Value == right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(File left, Files right)
-        => left.Value != right;
+    public static bool operator !=(File left, Files right) => left.Value != right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(File left, int right)
-        => left.Value == (Files)right;
+    public static bool operator ==(File left, int right) => left.Value == (Files)right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(File left, int right)
-        => left.Value != (Files)right;
+    public static bool operator !=(File left, int right) => left.Value != (Files)right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static File operator +(File left, File right)
-        => new(left.AsInt() + right.AsInt());
+    public static File operator +(File left, File right) => new(left.AsInt() + right.AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static File operator +(File left, int right)
-        => new(left.AsInt() + right);
+    public static File operator +(File left, int right) => new(left.AsInt() + right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static File operator +(File left, Files right)
-        => new(left.AsInt() + (int)right);
+    public static File operator +(File left, Files right) => new(left.AsInt() + (int)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static File operator -(File left, File right)
-        => new(left.AsInt() - right.AsInt());
+    public static File operator -(File left, File right) => new(left.AsInt() - right.AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static File operator -(File left, int right)
-        => new(left.AsInt() - right);
+    public static File operator -(File left, int right) => new(left.AsInt() - right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static File operator -(File left, Files right)
-        => new(left.AsInt() - (int)right);
+    public static File operator -(File left, Files right) => new(left.AsInt() - (int)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static File operator ++(File f)
-        => new(f.Value + 1);
+    public static File operator ++(File f) => new(f.Value + 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(File left, ulong right)
-        => left.BitBoardFile() & right;
+    public static BitBoard operator &(File left, ulong right) => left.BitBoardFile() & right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(ulong left, File right)
-        => left & right.BitBoardFile();
+    public static BitBoard operator &(ulong left, File right) => left & right.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator |(File left, File right)
-        => left.BitBoardFile() | right.BitBoardFile();
+    public static BitBoard operator |(File left, File right) => left.BitBoardFile() | right.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator |(ulong left, File right)
-        => left | right.BitBoardFile();
+    public static BitBoard operator |(ulong left, File right) => left | right.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator |(File left, int right)
-        => left.AsInt() | right;
+    public static int operator |(File left, int right) => left.AsInt() | right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator ~(File left)
-        => ~left.BitBoardFile();
+    public static BitBoard operator ~(File left) => ~left.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator >>(File left, int right)
-        => left.AsInt() >> right;
+    public static int operator >>(File left, int right) => left.AsInt() >> right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >(File left, File right)
-        => left.AsInt() > right.AsInt();
+    public static bool operator >(File left, File right) => left.AsInt() > right.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <(File left, File right)
-        => left.AsInt() < right.AsInt();
+    public static bool operator <(File left, File right) => left.AsInt() < right.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >=(File left, File right)
-        => left.AsInt() >= right.AsInt();
+    public static bool operator >=(File left, File right) => left.AsInt() >= right.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <=(File left, File right)
-        => left.AsInt() <= right.AsInt();
+    public static bool operator <=(File left, File right) => left.AsInt() <= right.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator true(File f)
-        => f.IsOk;
+    public static bool operator true(File f) => f.IsOk;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator false(File f)
-        => !f.IsOk;
+    public static bool operator false(File f) => !f.IsOk;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int AsInt()
-        => (int)Value;
+    public int AsInt() => (int)Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string ToString()
-        => FileStrings[AsInt()];
+    public override string ToString() => FileStrings[AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string ToString(string format, IFormatProvider formatProvider)
@@ -208,26 +175,24 @@ public bool TryFormat(
         => Value.AsInt().TryFormat(destination, out charsWritten, format, provider);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(File other)
-        => Value == other.Value;
+    public bool Equals(File other) => Value == other.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int CompareTo(File other)
-        => Value.AsInt().CompareTo(other.Value.AsInt());
+    public int CompareTo(File other) => Value.AsInt().CompareTo(other.Value.AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => AsInt();
+    public override int GetHashCode() => AsInt();
 
     /// <summary>
     /// Fold file [ABCDEFGH] to file [ABCDDCBA]
     /// </summary>
     /// <returns>The distance to the edge file</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int EdgeDistance()
-        => Math.Min(AsInt() - FileA.AsInt(), FileH.AsInt() - AsInt());
+    public int EdgeDistance() => Math.Min(AsInt() - FileA.AsInt(), FileH.AsInt() - AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public File Clamp(File min, File max)
-        => new(Value.AsInt().Clamp(min.AsInt(), max.AsInt()));
+    public File Clamp(File min, File max) => new(Value.AsInt().Clamp(min.AsInt(), max.AsInt()));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public int Distance(File other) => Math.Abs(AsInt() - other.AsInt());
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/HashKey.cs b/src/Rudzoft.ChessLib/Types/HashKey.cs
index 20841b2a..1d4e40ad 100644
--- a/src/Rudzoft.ChessLib/Types/HashKey.cs
+++ b/src/Rudzoft.ChessLib/Types/HashKey.cs
@@ -83,6 +83,10 @@ public static implicit operator HashKey(uint value)
     public static HashKey operator <<(HashKey left, int right)
         => new(left.Key << right);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static HashKey operator &(HashKey left, int right)
+        => new(left.Key & (ulong)right);
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static HashKey operator ^(HashKey left, int right)
         => new(left.Key ^ (ulong)right);
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index c2176727..4cab4ccf 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -70,143 +70,109 @@ public readonly record struct Rank(Ranks Value) : ISpanFormattable, IValidationT
     public static Rank[] All { get; } = { Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8 };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank(int rank)
-        : this((Ranks)rank) { }
+    public Rank(int rank) : this((Ranks)rank) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank(Rank r)
-        : this(r.Value) { }
+    public Rank(Rank r) : this(r.Value) { }
 
     public char Char => (char)('1' + Value.AsInt());
 
-    public bool IsOk
-        => Value.AsInt().InBetween(Ranks.Rank1.AsInt(), Ranks.Rank8.AsInt());
+    public bool IsOk => Value.AsInt().InBetween(Ranks.Rank1.AsInt(), Ranks.Rank8.AsInt());
 
     public const int Count = (int)Ranks.RankNb;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Rank(int value)
-        => new(value);
+    public static implicit operator Rank(int value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Rank(Ranks value)
-        => new(value);
+    public static implicit operator Rank(Ranks value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(Rank left, Ranks right)
-        => left.Value == right;
+    public static bool operator ==(Rank left, Ranks right) => left.Value == right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(Rank left, Ranks right)
-        => left.Value != right;
+    public static bool operator !=(Rank left, Ranks right) => left.Value != right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(Rank left, int right)
-        => left.Value == (Ranks)right;
+    public static bool operator ==(Rank left, int right) => left.Value == (Ranks)right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(Rank left, int right)
-        => left.Value != (Ranks)right;
+    public static bool operator !=(Rank left, int right) => left.Value != (Ranks)right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank operator +(Rank left, Rank right)
-        => new(left.AsInt() + right.AsInt());
+    public static Rank operator +(Rank left, Rank right) => new(left.AsInt() + right.AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank operator +(Rank left, int right)
-        => new(left.AsInt() + right);
+    public static Rank operator +(Rank left, int right) => new(left.AsInt() + right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank operator +(Rank left, Ranks right)
-        => new(left.AsInt() + (int)right);
+    public static Rank operator +(Rank left, Ranks right) => new(left.AsInt() + (int)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank operator -(Rank left, Rank right)
-        => new(left.AsInt() - right.AsInt());
+    public static Rank operator -(Rank left, Rank right) => new(left.AsInt() - right.AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank operator -(Rank left, int right)
-        => new(left.AsInt() - right);
+    public static Rank operator -(Rank left, int right) => new(left.AsInt() - right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank operator -(Rank left, Ranks right)
-        => new(left.AsInt() - (int)right);
+    public static Rank operator -(Rank left, Ranks right) => new(left.AsInt() - (int)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank operator ++(Rank r)
-        => new(r.Value + 1);
+    public static Rank operator ++(Rank r) => new(r.Value + 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank operator --(Rank r)
-        => new(r.Value - 1);
+    public static Rank operator --(Rank r) => new(r.Value - 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(Rank left, ulong right)
-        => new(left.BitBoardRank().Value & right);
+    public static BitBoard operator &(Rank left, ulong right) => new(left.BitBoardRank().Value & right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(ulong left, Rank right)
-        => new(left & right.BitBoardRank().Value);
+    public static BitBoard operator &(ulong left, Rank right) => new(left & right.BitBoardRank().Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator |(Rank left, Rank right)
-        => left.BitBoardRank() | right.BitBoardRank();
+    public static BitBoard operator |(Rank left, Rank right) => left.BitBoardRank() | right.BitBoardRank();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator |(ulong left, Rank right)
-        => new(left | right.BitBoardRank().Value);
+    public static BitBoard operator |(ulong left, Rank right) => new(left | right.BitBoardRank().Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator |(Rank left, int right)
-        => left.AsInt() | right;
+    public static int operator |(Rank left, int right) => left.AsInt() | right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator ~(Rank left)
-        => ~left.BitBoardRank();
+    public static BitBoard operator ~(Rank left) => ~left.BitBoardRank();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator >>(Rank left, int right)
-        => left.AsInt() >> right;
+    public static int operator >>(Rank left, int right) => left.AsInt() >> right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >=(Rank left, Rank right)
-        => left.Value >= right.Value;
+    public static bool operator >=(Rank left, Rank right) => left.Value >= right.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >=(Rank left, Ranks right)
-        => left.Value >= right;
+    public static bool operator >=(Rank left, Ranks right) => left.Value >= right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >(Rank left, Rank right)
-        => left.AsInt() > right.AsInt();
+    public static bool operator >(Rank left, Rank right) => left.AsInt() > right.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <=(Rank left, Rank right)
-        => left.Value <= right.Value;
+    public static bool operator <=(Rank left, Rank right) => left.Value <= right.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <=(Rank left, Ranks right)
-        => left.Value <= right;
+    public static bool operator <=(Rank left, Ranks right) => left.Value <= right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <(Rank left, Rank right)
-        => left.AsInt() < right.AsInt();
+    public static bool operator <(Rank left, Rank right) => left.AsInt() < right.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator true(Rank r)
-        => r.IsOk;
+    public static bool operator true(Rank r) => r.IsOk;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator false(Rank r)
-        => !r.IsOk;
+    public static bool operator false(Rank r) => !r.IsOk;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int AsInt()
-        => (int)Value;
+    public int AsInt() => (int)Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string ToString()
-        => RankStrings[AsInt()];
+    public override string ToString() => RankStrings[AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string ToString(string format, IFormatProvider formatProvider)
@@ -221,26 +187,24 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(Rank other)
-        => Value == other.Value;
+    public bool Equals(Rank other) => Value == other.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => AsInt();
+    public override int GetHashCode() => AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank Relative(Player p)
-        => new(AsInt() ^ (p.Side * 7));
+    public Rank Relative(Player p) => new(AsInt() ^ (p.Side * 7));
 
     /// <summary>
     /// Fold rank [12345678] to rank [12344321]
     /// </summary>
     /// <returns>The distance to the edge rank</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int EdgeDistance()
-        => Math.Min(AsInt() - Rank1.AsInt(), Rank8.AsInt() - AsInt());
+    public int EdgeDistance() => Math.Min(AsInt() - Rank1.AsInt(), Rank8.AsInt() - AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank Clamp(Rank min, Rank max)
-        => new(Value.AsInt().Clamp(min.AsInt(), max.AsInt()));
+    public Rank Clamp(Rank min, Rank max) => new(Value.AsInt().Clamp(min.AsInt(), max.AsInt()));
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public int Distance(Rank other) => Math.Abs(AsInt() - other.AsInt());
 }
diff --git a/src/Rudzoft.ChessLib/Types/Value.cs b/src/Rudzoft.ChessLib/Types/Value.cs
index 7e2a7323..f94b6dfd 100644
--- a/src/Rudzoft.ChessLib/Types/Value.cs
+++ b/src/Rudzoft.ChessLib/Types/Value.cs
@@ -49,6 +49,8 @@ public Value(int value)
 
     public static Value MinusInfinite { get; } = new(DefaultPieceValues.ValueMinusInfinite);
 
+    public static Value Of(int v) => v;
+
     public static implicit operator Value(int value)
         => new(value);
 

From 9d369f90f14a8b007e9596832be8ce3c353b36b7 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 25 Mar 2023 21:37:31 +0100
Subject: [PATCH 020/119] Nullability warnings fixed + minor pos validator fix

---
 .../StringBenchmarks.cs                           |  2 --
 .../PerftTests/PerftTheoryData.cs                 |  6 ++++++
 .../Rudzoft.ChessLib.WebApi.csproj                |  6 +++---
 .../ChessLibServiceCollectionExtensions.cs        |  2 +-
 .../Extensions/StringExtensions.cs                |  2 +-
 src/Rudzoft.ChessLib/Notation/MoveNotation.cs     |  4 ++--
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs     |  4 ++--
 .../Polyglot/PolyglotBookConfiguration.cs         |  2 +-
 .../Protocol/UCI/SearchParameters.cs              |  4 ++--
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj      |  2 +-
 .../Tables/KillerMoves/KillerMoves.cs             |  4 ++--
 src/Rudzoft.ChessLib/Types/RootMove.cs            | 15 +++++++++++++++
 src/Rudzoft.ChessLib/Types/StateStack.cs          |  2 +-
 .../Validation/IPositionValidator.cs              |  2 +-
 .../Validation/PositionValidator.cs               |  3 +--
 src/Rudzoft.Perft/Rudzoft.Perft.csproj            |  2 +-
 16 files changed, 40 insertions(+), 22 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
index 917d80b6..0c713aa2 100644
--- a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
@@ -120,8 +120,6 @@ private static string GetNameOf(Squares sq)
             case Types.Squares.c6:
                 return nameof(Types.Squares.a1);
 
-                return nameof(Types.Squares.a1);
-
             case Types.Squares.e6:
                 return nameof(Types.Squares.a1);
 
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
new file mode 100644
index 00000000..fbffe6c8
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
@@ -0,0 +1,6 @@
+namespace Rudzoft.ChessLib.Test.PerftTests;
+
+public class PerftTheoryData
+{
+    
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index 32fcd239..fce18996 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -9,14 +9,14 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="7.0.3">
+        <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="7.0.4">
           <PrivateAssets>all</PrivateAssets>
           <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
         <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
         <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
-        <PackageReference Include="Microsoft.OpenApi" Version="1.6.1" />
-        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
+        <PackageReference Include="Microsoft.OpenApi" Version="1.6.3" />
+        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
         <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
         <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
         <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 5a112739..4e88629a 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -96,7 +96,7 @@ public static IServiceCollection AddChessLib(
             .AddSingleton<ICpu, Cpu>();
     }
 
-    private static IConfigurationRoot LoadConfiguration(string? file)
+    private static IConfigurationRoot LoadConfiguration(string file)
     {
         var configurationFile = string.IsNullOrWhiteSpace(file) ? "chesslib.json" : file;
         var configurationFilePath = Path.Combine(AppContext.BaseDirectory, configurationFile);
diff --git a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
index 52d8088d..e5d02726 100644
--- a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
@@ -99,7 +99,7 @@ public static IEnumerable<int> GetLocations(this string @this, char token = ' ')
     public static bool IsNullOrEmpty(this string @this) => string.IsNullOrEmpty(@this);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool IsNullOrWhiteSpace(this string? @this) => string.IsNullOrWhiteSpace(@this);
+    public static bool IsNullOrWhiteSpace(this string @this) => string.IsNullOrWhiteSpace(@this);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static char FlipCase(this char c)
diff --git a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
index 28544530..c9501334 100644
--- a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
@@ -36,11 +36,11 @@ namespace Rudzoft.ChessLib.Notation;
 /// </summary>
 public sealed class MoveNotation : IMoveNotation
 {
-    private readonly INotation?[] _notations;
+    private readonly INotation[] _notations;
 
     private MoveNotation(IPosition pos)
     {
-        _notations = new INotation?[]
+        _notations = new INotation[]
         {
             new SanNotation(pos),
             new FanNotation(pos),
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index 37a2033a..df6e7e16 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -49,8 +49,8 @@ public sealed class PolyglotBook : IPolyglotBook
         CastleRight.BlackQueen
     };
 
-    private readonly FileStream? _fileStream;
-    private readonly BinaryReader? _binaryReader;
+    private readonly FileStream _fileStream;
+    private readonly BinaryReader _binaryReader;
     private readonly string _bookFilePath;
     private readonly int _entrySize;
     private readonly Random _rnd;
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs
index c8c41434..b7bc7e3f 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs
@@ -31,5 +31,5 @@ public sealed class PolyglotBookConfiguration
     public const string Section = "PolyglotBook";
     
     // ReSharper disable once UnusedAutoPropertyAccessor.Global
-    public string? BookPath { get; init; }
+    public string BookPath { get; init; }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index 33a0120a..db687ad2 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -175,10 +175,10 @@ private static int ParseValue(int index, in ulong value, Span<char> target)
         return index;
     }
 
-    public string ToString(string? format, IFormatProvider? formatProvider)
+    public string ToString(string format, IFormatProvider formatProvider)
         => string.Format(formatProvider, format, ToString());
 
-    public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
+    public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null)
     {
         var index = 0;
 
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index f40c3673..d5a0332b 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -58,7 +58,7 @@
     <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.3" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.4" />
     <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
     <PackageReference Include="ZString" Version="2.5.0" />
   </ItemGroup>
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
index 1493625b..0f1ca575 100644
--- a/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
@@ -110,7 +110,7 @@ public void Reset()
         }
     }
 
-    public bool Equals(IKillerMoves? other)
+    public bool Equals(IKillerMoves other)
     {
         if (other is null) return false;
         if (ReferenceEquals(this, other)) return true;
@@ -128,7 +128,7 @@ public bool Equals(IKillerMoves? other)
         return false;
     }
 
-    public override bool Equals(object? obj)
+    public override bool Equals(object obj)
         => ReferenceEquals(this, obj) || obj is KillerMoves other && Equals(other);
 
     public override int GetHashCode()
diff --git a/src/Rudzoft.ChessLib/Types/RootMove.cs b/src/Rudzoft.ChessLib/Types/RootMove.cs
index 5b3fa8c5..53cc618e 100644
--- a/src/Rudzoft.ChessLib/Types/RootMove.cs
+++ b/src/Rudzoft.ChessLib/Types/RootMove.cs
@@ -36,6 +36,21 @@ public static implicit operator RootMove(Move m)
 
     public static bool operator >(RootMove left, RootMove right)
         => left.NewValue < right.NewValue || left.NewValue == right.NewValue && left.OldValue < right.OldValue;
+    
+    private bool Equals(RootMove other)
+    {
+        return this.FirstOrDefault().Equals(other.FirstOrDefault());
+    }
+
+    public override bool Equals(object obj)
+    {
+        return ReferenceEquals(this, obj) || obj is RootMove other && Equals(other);
+    }
+
+    public override int GetHashCode()
+    {
+        return this.FirstOrDefault().GetHashCode();
+    }
 }
 
 public sealed class RootMoves : List<RootMove>
diff --git a/src/Rudzoft.ChessLib/Types/StateStack.cs b/src/Rudzoft.ChessLib/Types/StateStack.cs
index e8050e1e..49c337a4 100644
--- a/src/Rudzoft.ChessLib/Types/StateStack.cs
+++ b/src/Rudzoft.ChessLib/Types/StateStack.cs
@@ -152,7 +152,7 @@ public override string ToString()
             var o = _stack[i];
             sb.Append(' ');
             if (o != null)
-                sb.Append(o.ToString());
+                sb.Append(o.ToString()!);
             else
                 sb.Append(' ');
             sb.Append(' ');
diff --git a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
index 1f492520..b3d23e1a 100644
--- a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
@@ -28,7 +28,7 @@ namespace Rudzoft.ChessLib.Validation;
 
 public interface IPositionValidator
 {
-    string? ErrorMsg { get; }
+    string ErrorMsg { get; }
     bool IsOk { get; }
     IPositionValidator Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All);
 }
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index bab4fce0..47e8a80e 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -231,8 +231,7 @@ private string ValidateState(in IPosition pos, string error)
         if (state.Key.Key == 0 && !pos.Pieces().IsEmpty)
             error = AddError(error, "state key is invalid");
 
-        if (pos.Pieces(PieceTypes.Pawn, pos.SideToMove).IsEmpty &&
-            state.PawnKey.Key != Zobrist.ZobristNoPawn)
+        if (pos.PieceCount(PieceTypes.Pawn) == 0 && state.PawnKey.Key != Zobrist.ZobristNoPawn)
             error = AddError(error, "empty pawn key is invalid");
 
         if (state.Repetition < 0)
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index e0b1c82f..1fa568a2 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -37,7 +37,7 @@
     <PackageReference Include="CommandLineParser" Version="2.9.1" />
     <PackageReference Include="ConsoleGUI" Version="1.4.1" />
     <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.3" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.4" />
     <PackageReference Include="Serilog" Version="2.12.0" />
     <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
     <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />

From 839f5421bda6ec33d9cb9f667381dd0904ab5e0e Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 25 Mar 2023 21:59:14 +0100
Subject: [PATCH 021/119] Fix xunit serializaton fatal error

---
 .../BoardTests/BoardTests.cs                  | 115 ++++++++---
 .../PerftTests/PerftTest.cs                   | 180 +++++++++++++-----
 .../PerftTests/PerftTheoryData.cs             |  17 +-
 src/Rudzoft.ChessLib/Position.cs              |   3 +-
 4 files changed, 235 insertions(+), 80 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 3a938ba8..91f6c440 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -25,6 +25,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
@@ -55,32 +58,90 @@ public BoardTests()
             })
             .BuildServiceProvider();
     }
-    
+
+    public sealed class BoardTestsTheoryData : TheoryData<string, PieceTypes, Player, int>
+    {
+        public BoardTestsTheoryData(string[] fens, PieceTypes[] pts, Player[] players, int[] expectedCounts)
+        {
+            Contract.Assert(fens != null);
+            Contract.Assert(pts != null);
+            Contract.Assert(players != null);
+            Contract.Assert(expectedCounts != null);
+            Contract.Assert(fens.Length == pts.Length);
+            Contract.Assert(fens.Length == players.Length);
+            Contract.Assert(fens.Length == expectedCounts.Length);
+
+            var fensSpan = fens.AsSpan();
+            var ptsSpan = pts.AsSpan();
+            var playersSpan = players.AsSpan();
+            var expectedCountSpan = expectedCounts.AsSpan();
+            
+            ref var fenSpace = ref MemoryMarshal.GetReference(fensSpan);
+            ref var ptsSpace = ref MemoryMarshal.GetReference(ptsSpan);
+            ref var playersSpace = ref MemoryMarshal.GetReference(playersSpan);
+            ref var expectedCountSpace = ref MemoryMarshal.GetReference(expectedCountSpan);
+            
+            for (var i = 0; i < fens.Length; ++i)
+            {
+                var fen = Unsafe.Add(ref fenSpace, i);
+                var pt = Unsafe.Add(ref ptsSpace, i);
+                var player = Unsafe.Add(ref playersSpace, i);
+                var expectedCount = Unsafe.Add(ref expectedCountSpace, i);
+                Add(fen, pt, player, expectedCount);
+            }
+        }
+    }
+
+    private static readonly string[] Fens = {
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+    };
+
+    private static readonly PieceTypes[] PieceType = {
+        PieceTypes.Pawn, PieceTypes.Pawn, PieceTypes.Knight, PieceTypes.Knight, PieceTypes.Bishop, PieceTypes.Bishop,
+        PieceTypes.Rook, PieceTypes.Rook, PieceTypes.Queen, PieceTypes.Queen, PieceTypes.King, PieceTypes.King,
+        PieceTypes.Pawn, PieceTypes.Pawn, PieceTypes.Knight, PieceTypes.Knight, PieceTypes.Bishop, PieceTypes.Bishop,
+        PieceTypes.Rook, PieceTypes.Rook, PieceTypes.Queen, PieceTypes.Queen, PieceTypes.King, PieceTypes.King,
+    };
+
+    private static readonly Player[] Player = {
+        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
+        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
+        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
+        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
+    };
+
+    private static readonly int[] ExpectedCount = {
+        8, 8, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+        4, 3, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1
+    };
+
+    public static readonly BoardTestsTheoryData TheoryData = new(Fens, PieceType, Player, ExpectedCount);
+
     [Theory]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Pawn, Players.White, 8)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Pawn, Players.Black, 8)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Knight, Players.White, 2)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Knight, Players.Black, 2)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Bishop, Players.White, 2)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Bishop, Players.Black, 2)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Rook, Players.White, 2)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Rook, Players.Black, 2)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Queen, Players.White, 1)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.Queen, Players.Black, 1)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.King, Players.White, 1)]
-    [InlineData("rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6", PieceTypes.King, Players.Black, 1)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Pawn, Players.White, 4)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Pawn, Players.Black, 3)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Knight, Players.White, 1)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Knight, Players.Black, 1)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Bishop, Players.White, 0)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Bishop, Players.Black, 0)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Rook, Players.White, 2)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Rook, Players.Black, 2)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Queen, Players.White, 0)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.Queen, Players.Black, 0)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.King, Players.White, 1)]
-    [InlineData("5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53", PieceTypes.King, Players.Black, 1)]
+    [MemberData(nameof(TheoryData))]
     public void BoardPieceCount(string fen, PieceTypes pt, Player p, int expected)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
@@ -94,8 +155,8 @@ public void BoardPieceCount(string fen, PieceTypes pt, Player p, int expected)
 
         var posCount = pos.Pieces(pt, p).Count;
         var boardCount = board.PieceCount(pt, p);
-        
+
         Assert.Equal(posCount, boardCount);
-        Assert.Equal(expected, boardCount);        
+        Assert.Equal(expected, boardCount);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
index e7b0e894..0d10706d 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
@@ -28,92 +28,172 @@ namespace Rudzoft.ChessLib.Test.PerftTests;
 
 public sealed class PerftOneTest : PerftVerify
 {
+    private static readonly string[] Fens = {
+        Fen.Fen.StartPositionFen,
+        Fen.Fen.StartPositionFen,
+        Fen.Fen.StartPositionFen,
+        Fen.Fen.StartPositionFen,
+        Fen.Fen.StartPositionFen,
+        Fen.Fen.StartPositionFen
+    };
+
+    private static readonly int[] Depths = {
+        1, 2, 3, 4, 5, 6
+    };
+
+    private static readonly ulong[] Results = {
+        20UL, 400UL, 8_902UL, 197_281UL, 4_865_609UL, 119_060_324UL
+    };
+
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+
     [Theory]
-    [InlineData(Fen.Fen.StartPositionFen, 1, 20UL)]
-    [InlineData(Fen.Fen.StartPositionFen, 2, 400UL)]
-    [InlineData(Fen.Fen.StartPositionFen, 3, 8_902UL)]
-    [InlineData(Fen.Fen.StartPositionFen, 4, 197_281UL)]
-    [InlineData(Fen.Fen.StartPositionFen, 5, 4_865_609UL)]
-#if !DEBUG
-    [InlineData(Fen.Fen.StartPositionFen, 6, 119_060_324UL)]
-#endif
+    [MemberData(nameof(PerftTheoryData))]
     public void Perft(string fen, int depth, ulong expected)
         => AssertPerft(fen, depth, in expected);
 }
 
 public sealed class PerftTwoTest : PerftVerify
 {
+    private static readonly string[] Fens = {
+        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
+        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
+        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
+        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
+        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"
+    };
+
+    private static readonly int[] Depths = {
+        1, 2, 3, 4, 5
+    };
+
+    private static readonly ulong[] Results = {
+        48UL, 2_039UL, 97_862UL, 4_085_603UL, 193_690_690UL
+    };
+
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+
     [Theory]
-    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 1, 48UL)]
-    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 2, 2_039UL)]
-    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 3, 97_862UL)]
-    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 4, 4_085_603UL)]
-#if !DEBUG
-    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 5, 193_690_690UL)]
-#endif
+    [MemberData(nameof(PerftTheoryData))]
     public void Perft(string fen, int depth, ulong expected)
         => AssertPerft(fen, depth, in expected);
 }
 
 public sealed class PerftThreeTest : PerftVerify
 {
+    private static readonly string[] Fens = {
+        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
+        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
+        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
+        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
+        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
+        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
+        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1"
+    };
+
+    private static readonly int[] Depths = {
+        1, 2, 3, 4, 5, 6, 7
+    };
+
+    private static readonly ulong[] Results = {
+        14UL, 191UL, 2_812UL, 43_238UL, 674_624UL, 11_030_083UL, 178_633_661UL
+    };
+
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+
     [Theory]
-    [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 1, 14UL)]
-    [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 2, 191UL)]
-    [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 3, 2_812UL)]
-    [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 4, 43_238UL)]
-    [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 5, 674_624UL)]
-#if !DEBUG
-    [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 6, 11_030_083UL)]
-    [InlineData("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 7, 178_633_661UL)]
-#endif
+    [MemberData(nameof(PerftTheoryData))]
     public void Perft(string fen, int depth, ulong expected)
         => AssertPerft(fen, depth, in expected);
 }
 
 public sealed class PerftFourTest : PerftVerify
 {
+    private static readonly string[] Fens = {
+        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
+        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
+        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
+        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
+        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
+    };
+
+    private static readonly int[] Depths = {
+        1, 2, 3, 4, 5
+    };
+
+    private static readonly ulong[] Results = {
+        6UL, 264UL, 9_467UL, 422_333UL, 15_833_292UL
+    };
+
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+
     [Theory]
-    [InlineData("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 1, 6UL)]
-    [InlineData("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 2, 264UL)]
-    [InlineData("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 3, 9_467UL)]
-    [InlineData("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 4, 422_333UL)]
-    [InlineData("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 5, 15_833_292UL)]
+    [MemberData(nameof(PerftTheoryData))]
     public void Perft(string fen, int depth, ulong expected)
         => AssertPerft(fen, depth, in expected);
 }
 
 public sealed class PerftFiveTest : PerftVerify
 {
+    private static readonly string[] Fens = {
+        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
+        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
+        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
+        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
+        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10"
+    };
+
+    private static readonly int[] Depths = {
+        1, 2, 3, 4, 5
+    };
+
+    private static readonly ulong[] Results = {
+        46UL, 2_079UL, 89_890UL, 3_894_594UL, 164_075_551UL
+    };
+
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+    
     [Theory]
-    [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 1, 46UL)]
-    [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 2, 2_079UL)]
-    [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 3, 89_890UL)]
-    [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 4, 3_894_594UL)]
-#if !DEBUG
-    [InlineData("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 5, 164_075_551UL)]
-#endif
+    [MemberData(nameof(PerftTheoryData))]
     public void Perft(string fen, int depth, ulong expected)
         => AssertPerft(fen, depth, in expected);
 }
 
 public sealed class TalkChessPerftTests : PerftVerify
 {
+    private static readonly string[] Fens = {
+        "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1", //--Illegal ep move #1
+        "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1", //--Illegal ep move #2
+        "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1", //--EP Capture Checks Opponent
+        "5k2/8/8/8/8/8/8/4K2R w K - 0 1", //--Short Castling Gives Check
+        "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1", //--Long Castling Gives Check
+        "r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1", //--Castle Rights
+        "r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1", //--Castling Prevented
+        "2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1", //--Promote out of Check
+        "8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1", //--Discovered Check
+        "4k3/1P6/8/8/8/8/K7/8 w - - 0 1", //--Promote to give check
+        "8/P1k5/K7/8/8/8/8/8 w - - 0 1", //--Under Promote to give check
+        "K1k5/8/P7/8/8/8/8/8 w - - 0 1", //--Self Stalemate
+        "8/k1P5/8/1K6/8/8/8/8 w - - 0 1", //--Stalemate & Checkmate
+        "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1", //--Stalemate & Checkmate
+    };
+
+    private static readonly int[] Depths = {
+        6, 6, 6, 6, 6,
+        4, 4, 6, 5, 6,
+        6, 6, 7, 4
+    };
+
+    private static readonly ulong[] Results = {
+        1_134_888UL, 1_015_133UL, 1_440_467UL,   661_072UL, 803_711UL,
+        1_274_206UL, 1_720_476UL, 3_821_001UL, 1_004_658UL, 217_342UL,
+           92_683UL,     2_217UL,   567_584UL,    23_527UL
+    };
+
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+
     [Theory]
-    [InlineData("3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1", 6, 1_134_888UL)] //--Illegal ep move #1
-    [InlineData("8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1", 6, 1_015_133UL)] //--Illegal ep move #2
-    [InlineData("8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1", 6, 1_440_467UL)] //--EP Capture Checks Opponent
-    [InlineData("5k2/8/8/8/8/8/8/4K2R w K - 0 1", 6, 661_072UL)] //--Short Castling Gives Check
-    [InlineData("3k4/8/8/8/8/8/8/R3K3 w Q - 0 1", 6, 803_711UL)] //--Long Castling Gives Check
-    [InlineData("r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1", 4, 1_274_206UL)] //--Castle Rights
-    [InlineData("r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1", 4, 1_720_476UL)] //--Castling Prevented
-    [InlineData("2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1", 6, 3_821_001UL)] //--Promote out of Check
-    [InlineData("8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1", 5, 1_004_658UL)] //--Discovered Check
-    [InlineData("4k3/1P6/8/8/8/8/K7/8 w - - 0 1", 6, 217_342UL)] //--Promote to give check
-    [InlineData("8/P1k5/K7/8/8/8/8/8 w - - 0 1", 6, 92_683UL)] //--Under Promote to give check
-    [InlineData("K1k5/8/P7/8/8/8/8/8 w - - 0 1", 6, 2_217UL)] //--Self Stalemate
-    [InlineData("8/k1P5/8/1K6/8/8/8/8 w - - 0 1", 7, 567_584UL)] //--Stalemate & Checkmate
-    [InlineData("8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1", 4, 23_527UL)] //--Stalemate & Checkmate
+    [MemberData(nameof(PerftTheoryData))]
     public void Perft(string fen, int depth, ulong expected)
         => AssertPerft(fen, depth, in expected);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
index fbffe6c8..53a1566b 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
@@ -1,6 +1,19 @@
-namespace Rudzoft.ChessLib.Test.PerftTests;
+using System.Diagnostics.Contracts;
 
-public class PerftTheoryData
+namespace Rudzoft.ChessLib.Test.PerftTests;
+
+public sealed class PerftTheoryData : TheoryData<string, int, ulong>
 {
+    public PerftTheoryData(string[] fens, int[] depths, ulong[] results)
+    {
+        Contract.Assert(fens != null);
+        Contract.Assert(depths != null);
+        Contract.Assert(results != null);
+        Contract.Assert(fens.Length == depths.Length);
+        Contract.Assert(fens.Length == results.Length);
+
+        for (var i = 0; i < fens.Length; i++)
+            Add(fens[i], depths[i], results[i]);
+    }
     
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index d339299c..f3566e07 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1319,7 +1319,8 @@ public void TakeMove(Move m)
         Ply--;
         
 #if DEBUG
-        Debug.Assert(_positionValidator.Validate(this).IsOk);
+        var validator = _positionValidator.Validate(this);
+        Debug.Assert(validator.IsOk);
 #endif
     }
 

From 6c57c0c0243059caf0a113f080981b7009c35042 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 26 Mar 2023 19:47:42 +0200
Subject: [PATCH 022/119] Added basic PGN file parser library

---
 Rudzoft.ChessLib.sln                          |  22 ++++
 Rudzoft.ChessLib.sln.DotSettings              |   1 +
 src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs     |  69 ++++++++++
 .../Rudzoft.ChessLib.PGN.Test.csproj          |  34 +++++
 src/Rudzoft.ChessLib.PGN.Test/Usings.cs       |   1 +
 .../samples/sample.pgn                        |  19 +++
 src/Rudzoft.ChessLib.PGN/IPgnParser.cs        |  32 +++++
 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs |  35 ++++++
 src/Rudzoft.ChessLib.PGN/PgnGame.cs           |  29 +++++
 src/Rudzoft.ChessLib.PGN/PgnMove.cs           |  29 +++++
 src/Rudzoft.ChessLib.PGN/PgnParserFactory.cs  |  42 +++++++
 src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs    | 118 ++++++++++++++++++
 .../Rudzoft.ChessLib.PGN.csproj               |  13 ++
 13 files changed, 444 insertions(+)
 create mode 100644 src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
 create mode 100644 src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
 create mode 100644 src/Rudzoft.ChessLib.PGN.Test/Usings.cs
 create mode 100644 src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn
 create mode 100644 src/Rudzoft.ChessLib.PGN/IPgnParser.cs
 create mode 100644 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
 create mode 100644 src/Rudzoft.ChessLib.PGN/PgnGame.cs
 create mode 100644 src/Rudzoft.ChessLib.PGN/PgnMove.cs
 create mode 100644 src/Rudzoft.ChessLib.PGN/PgnParserFactory.cs
 create mode 100644 src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
 create mode 100644 src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj

diff --git a/Rudzoft.ChessLib.sln b/Rudzoft.ChessLib.sln
index 66ac19ab..68246590 100644
--- a/Rudzoft.ChessLib.sln
+++ b/Rudzoft.ChessLib.sln
@@ -25,6 +25,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perft", "perft", "{96596E23
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web-api", "web-api", "{F82A6A7E-4551-4667-9E0A-57036E1B5117}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN", "src\Rudzoft.ChessLib.PGN\Rudzoft.ChessLib.PGN.csproj", "{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN.Test", "src\Rudzoft.ChessLib.PGN.Test\Rudzoft.ChessLib.PGN.Test.csproj", "{556ADC0D-074D-4B8F-9E45-1275342FCB74}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -97,6 +101,22 @@ Global
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|Any CPU.Build.0 = Release|Any CPU
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|x64.ActiveCfg = Release|Any CPU
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|x64.Build.0 = Release|Any CPU
+		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|x64.Build.0 = Debug|Any CPU
+		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|x64.ActiveCfg = Release|Any CPU
+		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|x64.Build.0 = Release|Any CPU
+		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|x64.Build.0 = Debug|Any CPU
+		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.Build.0 = Release|Any CPU
+		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|x64.ActiveCfg = Release|Any CPU
+		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|x64.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -110,6 +130,8 @@ Global
 		{8BE6ADD1-7347-412E-B974-02C5866E59A4} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
 		{55E449CF-B5AB-4405-8B64-2F1AB1346D90} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8} = {F82A6A7E-4551-4667-9E0A-57036E1B5117}
+		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
+		{556ADC0D-074D-4B8F-9E45-1275342FCB74} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {0B507B2E-EC45-4DDA-94DD-B7CD5A2B8CF3}
diff --git a/Rudzoft.ChessLib.sln.DotSettings b/Rudzoft.ChessLib.sln.DotSettings
index 85dd9501..4050dcf8 100644
--- a/Rudzoft.ChessLib.sln.DotSettings
+++ b/Rudzoft.ChessLib.sln.DotSettings
@@ -1,2 +1,3 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=NONINFRINGEMENT/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Perft/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
new file mode 100644
index 00000000..791bb1b5
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
@@ -0,0 +1,69 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Rudzoft.ChessLib.PGN.Test;
+
+public sealed class PgnTests
+{
+    private const string SampleFilePath = @"samples/sample.pgn";
+    private const int ExpectedGameCount = 2;
+
+    private readonly IServiceProvider _serviceProvider;
+
+    public PgnTests()
+    {
+        _serviceProvider = new ServiceCollection()
+                .AddPgnParser()
+                .BuildServiceProvider();
+    }
+    
+    [Fact]
+    public async Task ParseFile_WithTestContent_ReturnsCorrectGamesAndMoves()
+    {
+        var parser = _serviceProvider.GetRequiredService<IPgnParser>();
+        
+        var games = new List<PgnGame>();
+
+        await foreach (var game in parser.ParseFile(SampleFilePath))
+            games.Add(game);
+        
+        Assert.Equal(ExpectedGameCount, games.Count);
+
+        var game1 = games[0];
+        Assert.Equal("London", game1.Tags["Event"]);
+        Assert.Equal(44, game1.Moves.Count);
+        Assert.Equal("e4", game1.Moves[0].WhiteMove);
+        Assert.Equal("e5", game1.Moves[0].BlackMove);
+
+        var game2 = games[1];
+        Assert.Equal("Reykjavik", game2.Tags["Event"]);
+        Assert.Equal(28, game2.Moves.Count);
+        Assert.Equal("e4", game2.Moves[0].WhiteMove);
+        Assert.Equal("c5", game2.Moves[0].BlackMove);
+    }
+}
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
new file mode 100644
index 00000000..98abfc7b
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -0,0 +1,34 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net7.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+
+        <IsPackable>false</IsPackable>
+        <IsTestProject>true</IsTestProject>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
+        <PackageReference Include="xunit" Version="2.4.2" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+            <PrivateAssets>all</PrivateAssets>
+        </PackageReference>
+        <PackageReference Include="coverlet.collector" Version="3.2.0">
+            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+            <PrivateAssets>all</PrivateAssets>
+        </PackageReference>
+    </ItemGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\Rudzoft.ChessLib.PGN\Rudzoft.ChessLib.PGN.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <Content Include="samples\sample.pgn" CopyToOutputDirectory="PreserveNewest" />
+    </ItemGroup>
+
+</Project>
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Usings.cs b/src/Rudzoft.ChessLib.PGN.Test/Usings.cs
new file mode 100644
index 00000000..8c927eb7
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN.Test/Usings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn b/src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn
new file mode 100644
index 00000000..249a1418
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn
@@ -0,0 +1,19 @@
+[Event "Test event"]
+[Site "Test site"]
+[Date "2021.09.01"]
+[Round "1"]
+[White "Player 1"]
+[Black "Player 2"]
+[Result "1-0"]
+
+1. e4 e5 2.Nf3 Nc6 3.Bb5 1-0
+
+[Event "Test event 2"]
+[Site "Test site 2"]
+[Date "2021.09.02"]
+[Round "2"]
+[White "Player 3"]
+[Black "Player 4"]
+[Result "0-1"]
+
+1. d4 d5 2.c4 c6 3.Nf3 Nf6 0-1
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/IPgnParser.cs b/src/Rudzoft.ChessLib.PGN/IPgnParser.cs
new file mode 100644
index 00000000..90a99330
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN/IPgnParser.cs
@@ -0,0 +1,32 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+namespace Rudzoft.ChessLib.PGN;
+
+public interface IPgnParser
+{
+    IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationToken cancellationToken = default);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
new file mode 100644
index 00000000..cd74d4f3
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
@@ -0,0 +1,35 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+namespace Rudzoft.ChessLib.PGN;
+
+public sealed class NonRegexPgnParser : IPgnParser
+{
+    public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationToken cancellationToken = default)
+    {
+        yield break;
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/PgnGame.cs b/src/Rudzoft.ChessLib.PGN/PgnGame.cs
new file mode 100644
index 00000000..467364de
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN/PgnGame.cs
@@ -0,0 +1,29 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+namespace Rudzoft.ChessLib.PGN;
+
+public sealed record PgnGame(IDictionary<string, string> Tags, List<PgnMove> Moves);
diff --git a/src/Rudzoft.ChessLib.PGN/PgnMove.cs b/src/Rudzoft.ChessLib.PGN/PgnMove.cs
new file mode 100644
index 00000000..2341e60f
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN/PgnMove.cs
@@ -0,0 +1,29 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+namespace Rudzoft.ChessLib.PGN;
+
+public sealed record PgnMove(int MoveNumber, string WhiteMove, string BlackMove);
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/PgnParserFactory.cs b/src/Rudzoft.ChessLib.PGN/PgnParserFactory.cs
new file mode 100644
index 00000000..1d3dd12b
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN/PgnParserFactory.cs
@@ -0,0 +1,42 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Rudzoft.ChessLib.PGN;
+
+public static class PgnParserFactory
+{
+    public static IServiceCollection AddPgnParser(this IServiceCollection services, Func<bool>? useRegex = null)
+    {
+        if (useRegex == null || useRegex())
+            services.AddSingleton<IPgnParser, RegexPgnParser>();
+        else
+            services.AddSingleton<IPgnParser, NonRegexPgnParser>();
+
+        return services;
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
new file mode 100644
index 00000000..c93077c5
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
@@ -0,0 +1,118 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+
+namespace Rudzoft.ChessLib.PGN;
+
+public class RegexPgnParser : IPgnParser
+{
+    private static readonly Regex TagPairRegex = new(@"\[(?<tagName>\w+)\s+""(?<tagValue>[^""]+)""\]",
+        RegexOptions.Compiled | RegexOptions.NonBacktracking);
+
+    private static readonly Regex MoveTextRegex =
+        new(@"(?<moveNumber>\d+)\.\s*(?<whiteMove>\S+)(\s+(?<blackMove>\S+))?",
+            RegexOptions.Compiled | RegexOptions.NonBacktracking);
+
+    private static readonly PgnMove EmptyPgnMove = new(0, string.Empty, string.Empty);
+
+    public async IAsyncEnumerable<PgnGame> ParseFile(
+        string pgnFile,
+        [EnumeratorCancellation] CancellationToken cancellationToken = default)
+    {
+        if (string.IsNullOrWhiteSpace(pgnFile))
+            yield break;
+        
+        await using var fileStream = new FileStream(pgnFile, FileMode.Open, FileAccess.Read);
+        using var streamReader = new StreamReader(fileStream);
+        var currentGameTags = new Dictionary<string, string>(7);
+        var currentGameMoves = new List<PgnMove>(24);
+        var inMoveSection = false;
+
+        var line = await streamReader.ReadLineAsync(cancellationToken);
+
+        while (line != null)
+        {
+            if (line.AsSpan().IsWhiteSpace())
+            {
+                inMoveSection = currentGameTags.Count > 0;
+                line = await streamReader.ReadLineAsync(cancellationToken);
+                continue;
+            }
+
+            if (inMoveSection)
+            {
+                var lineMoves = ParseMovesLine(line);
+
+                currentGameMoves.AddRange(lineMoves);
+
+                if (line.Contains("1-0") || line.Contains("0-1") || line.Contains("1/2-1/2") || line.Contains('*'))
+                {
+                    yield return new PgnGame(currentGameTags, currentGameMoves);
+                    currentGameTags = new Dictionary<string, string>();
+                    currentGameMoves = new List<PgnMove>();
+                    inMoveSection = false;
+                }
+            }
+            else
+                ParseTag(line, currentGameTags);
+
+            line = await streamReader.ReadLineAsync(cancellationToken);
+        }
+
+        if (currentGameTags.Count > 0 && currentGameMoves.Count > 0)
+            yield return new(currentGameTags, currentGameMoves);
+    }
+
+    private static void ParseTag(string line, IDictionary<string, string> currentGameTags)
+    {
+        var tagMatch = TagPairRegex.Match(line);
+        
+        if (!tagMatch.Success)
+            return;
+        
+        var tagName = tagMatch.Groups["tagName"].Value;
+        var tagValue = tagMatch.Groups["tagValue"].Value;
+        currentGameTags[tagName] = tagValue;
+    }
+
+    private static IEnumerable<PgnMove> ParseMovesLine(string line)
+    {
+        var moveMatches = MoveTextRegex.Matches(line);
+        return moveMatches.Select(static x =>
+        {
+            var moveNumber = int.Parse(x.Groups["moveNumber"].Value);
+            var whiteMove = x.Groups["whiteMove"].Value;
+            var blackMove = x.Groups["blackMove"].Value;
+
+            if (string.IsNullOrWhiteSpace(blackMove))
+                return EmptyPgnMove with { MoveNumber = moveNumber, WhiteMove = whiteMove };
+
+            return new PgnMove(moveNumber, whiteMove, blackMove);
+        });
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
new file mode 100644
index 00000000..60f11f07
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net7.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
+    </ItemGroup>
+
+</Project>

From 0b50b9937db8dd24cb6e053f039f72296d6a70f9 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 26 Mar 2023 23:35:35 +0200
Subject: [PATCH 023/119] Update reg ex pgn parser

---
 src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs | 53 ++++++++++++++--------
 1 file changed, 34 insertions(+), 19 deletions(-)

diff --git a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
index c93077c5..31fa43c2 100644
--- a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
@@ -29,10 +29,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.PGN;
 
-public class RegexPgnParser : IPgnParser
+public sealed class RegexPgnParser : IPgnParser
 {
-    private static readonly Regex TagPairRegex = new(@"\[(?<tagName>\w+)\s+""(?<tagValue>[^""]+)""\]",
-        RegexOptions.Compiled | RegexOptions.NonBacktracking);
+
+    private const int DefaultTagCapacity = 7;
+    private const int DefaultMoveListCapacity = 24;
+
+    private static readonly Regex TagPairRegex =
+        new(@"\[(?<tagName>\w+)\s+""(?<tagValue>[^""]+)""\]",
+            RegexOptions.Compiled | RegexOptions.NonBacktracking);
 
     private static readonly Regex MoveTextRegex =
         new(@"(?<moveNumber>\d+)\.\s*(?<whiteMove>\S+)(\s+(?<blackMove>\S+))?",
@@ -46,11 +51,11 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
     {
         if (string.IsNullOrWhiteSpace(pgnFile))
             yield break;
-        
+
         await using var fileStream = new FileStream(pgnFile, FileMode.Open, FileAccess.Read);
         using var streamReader = new StreamReader(fileStream);
-        var currentGameTags = new Dictionary<string, string>(7);
-        var currentGameMoves = new List<PgnMove>(24);
+        var currentGameTags = new Dictionary<string, string>(DefaultTagCapacity);
+        var currentGameMoves = new List<PgnMove>(DefaultMoveListCapacity);
         var inMoveSection = false;
 
         var line = await streamReader.ReadLineAsync(cancellationToken);
@@ -58,23 +63,18 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
         while (line != null)
         {
             if (line.AsSpan().IsWhiteSpace())
-            {
                 inMoveSection = currentGameTags.Count > 0;
-                line = await streamReader.ReadLineAsync(cancellationToken);
-                continue;
-            }
-
-            if (inMoveSection)
+            else if (inMoveSection)
             {
                 var lineMoves = ParseMovesLine(line);
 
                 currentGameMoves.AddRange(lineMoves);
 
-                if (line.Contains("1-0") || line.Contains("0-1") || line.Contains("1/2-1/2") || line.Contains('*'))
+                if (IsPgnGameResult(line))
                 {
-                    yield return new PgnGame(currentGameTags, currentGameMoves);
-                    currentGameTags = new Dictionary<string, string>();
-                    currentGameMoves = new List<PgnMove>();
+                    yield return new(currentGameTags, currentGameMoves);
+                    currentGameTags = new Dictionary<string, string>(DefaultTagCapacity);
+                    currentGameMoves = new List<PgnMove>(DefaultMoveListCapacity);
                     inMoveSection = false;
                 }
             }
@@ -88,13 +88,28 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
             yield return new(currentGameTags, currentGameMoves);
     }
 
+    private static bool IsPgnGameResult(ReadOnlySpan<char> line)
+    {
+        if (line.EndsWith(stackalloc char[] { '*' }, StringComparison.InvariantCultureIgnoreCase))
+            return true;
+
+        if (line.Contains(stackalloc char[] { '1', '-', '0' }, StringComparison.InvariantCultureIgnoreCase))
+            return true;
+
+        if (line.Contains(stackalloc char[] { '0', '-', '1' }, StringComparison.InvariantCultureIgnoreCase))
+            return true;
+
+        return line.Contains(stackalloc char[] { '1', '/', '2', '-', '1', '/', '2' },
+            StringComparison.InvariantCultureIgnoreCase);
+    }
+
     private static void ParseTag(string line, IDictionary<string, string> currentGameTags)
     {
         var tagMatch = TagPairRegex.Match(line);
-        
+
         if (!tagMatch.Success)
             return;
-        
+
         var tagName = tagMatch.Groups["tagName"].Value;
         var tagValue = tagMatch.Groups["tagValue"].Value;
         currentGameTags[tagName] = tagValue;
@@ -112,7 +127,7 @@ private static IEnumerable<PgnMove> ParseMovesLine(string line)
             if (string.IsNullOrWhiteSpace(blackMove))
                 return EmptyPgnMove with { MoveNumber = moveNumber, WhiteMove = whiteMove };
 
-            return new PgnMove(moveNumber, whiteMove, blackMove);
+            return new(moveNumber, whiteMove, blackMove);
         });
     }
 }
\ No newline at end of file

From eb3164cf098e675c6ca1f5112d5dc51cdfa02238 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 27 Mar 2023 06:47:19 +0200
Subject: [PATCH 024/119] Fix using old sample pgn

---
 src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs  | 12 ++++++------
 src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs |  6 +++---
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
index 791bb1b5..e1375b6d 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
@@ -55,15 +55,15 @@ public async Task ParseFile_WithTestContent_ReturnsCorrectGamesAndMoves()
         Assert.Equal(ExpectedGameCount, games.Count);
 
         var game1 = games[0];
-        Assert.Equal("London", game1.Tags["Event"]);
-        Assert.Equal(44, game1.Moves.Count);
+        Assert.Equal("Test event", game1.Tags["Event"]);
+        Assert.Equal(3, game1.Moves.Count);
         Assert.Equal("e4", game1.Moves[0].WhiteMove);
         Assert.Equal("e5", game1.Moves[0].BlackMove);
 
         var game2 = games[1];
-        Assert.Equal("Reykjavik", game2.Tags["Event"]);
-        Assert.Equal(28, game2.Moves.Count);
-        Assert.Equal("e4", game2.Moves[0].WhiteMove);
-        Assert.Equal("c5", game2.Moves[0].BlackMove);
+        Assert.Equal("Test event 2", game2.Tags["Event"]);
+        Assert.Equal(3, game2.Moves.Count);
+        Assert.Equal("d4", game2.Moves[0].WhiteMove);
+        Assert.Equal("d5", game2.Moves[0].BlackMove);
     }
 }
diff --git a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
index 31fa43c2..7eba40d0 100644
--- a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
@@ -93,13 +93,13 @@ private static bool IsPgnGameResult(ReadOnlySpan<char> line)
         if (line.EndsWith(stackalloc char[] { '*' }, StringComparison.InvariantCultureIgnoreCase))
             return true;
 
-        if (line.Contains(stackalloc char[] { '1', '-', '0' }, StringComparison.InvariantCultureIgnoreCase))
+        if (line.EndsWith(stackalloc char[] { '1', '-', '0' }, StringComparison.InvariantCultureIgnoreCase))
             return true;
 
-        if (line.Contains(stackalloc char[] { '0', '-', '1' }, StringComparison.InvariantCultureIgnoreCase))
+        if (line.EndsWith(stackalloc char[] { '0', '-', '1' }, StringComparison.InvariantCultureIgnoreCase))
             return true;
 
-        return line.Contains(stackalloc char[] { '1', '/', '2', '-', '1', '/', '2' },
+        return line.EndsWith(stackalloc char[] { '1', '/', '2', '-', '1', '/', '2' },
             StringComparison.InvariantCultureIgnoreCase);
     }
 

From 0ac7a06f0e123362b0883f616791440ba4e073d0 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 27 Mar 2023 07:23:49 +0200
Subject: [PATCH 025/119] Added basic non-regex pgn parser

---
 src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs     |  2 +-
 .../samples/sample.pgn                        |  4 +-
 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs | 79 ++++++++++++++++++-
 3 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
index e1375b6d..e8952869 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
@@ -38,7 +38,7 @@ public sealed class PgnTests
     public PgnTests()
     {
         _serviceProvider = new ServiceCollection()
-                .AddPgnParser()
+                .AddPgnParser(static () => false)
                 .BuildServiceProvider();
     }
     
diff --git a/src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn b/src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn
index 249a1418..d4cfe7b5 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn
+++ b/src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn
@@ -6,7 +6,7 @@
 [Black "Player 2"]
 [Result "1-0"]
 
-1. e4 e5 2.Nf3 Nc6 3.Bb5 1-0
+1. e4 e5 2. Nf3 Nc6 3. Bb5 1-0
 
 [Event "Test event 2"]
 [Site "Test site 2"]
@@ -16,4 +16,4 @@
 [Black "Player 4"]
 [Result "0-1"]
 
-1. d4 d5 2.c4 c6 3.Nf3 Nf6 0-1
\ No newline at end of file
+1. d4 d5 2. c4 c6 3. Nf3 Nf6 0-1
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
index cd74d4f3..00b2fb6d 100644
--- a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
@@ -30,6 +30,83 @@ public sealed class NonRegexPgnParser : IPgnParser
 {
     public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationToken cancellationToken = default)
     {
-        yield break;
+        await using var fileStream = new FileStream(pgnFile, FileMode.Open, FileAccess.Read);
+        using var streamReader = new StreamReader(fileStream);
+
+        var currentGameTags = new Dictionary<string, string>();
+        var currentGameMoves = new List<PgnMove>();
+        var inMoveSection = false;
+
+        string line;
+        while ((line = streamReader.ReadLine()) != null)
+        {
+            if (string.IsNullOrWhiteSpace(line))
+            {
+                inMoveSection = currentGameTags.Count > 0;
+                continue;
+            }
+
+            if (inMoveSection)
+            {
+                var words = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
+
+                var currentMoveNumber = 0;
+
+                foreach (var word in words)
+                {
+                    if (word.Contains('.'))
+                    {
+                        if (int.TryParse(word.TrimEnd('.'), out var moveNumber))
+                        {
+                            currentMoveNumber = moveNumber;
+                        }
+                    }
+                    else if (char.IsLetter(word[0]))
+                    {
+                        if (currentGameMoves.Count == 0 || !string.IsNullOrEmpty(currentGameMoves[^1].BlackMove))
+                        {
+                            currentGameMoves.Add(new PgnMove(currentMoveNumber, word, string.Empty));
+                        }
+                        else
+                        {
+                            var lastMove = currentGameMoves[^1];
+                            currentGameMoves[^1] = lastMove with { BlackMove = word };
+                        }
+                    }
+                    else if (word.Contains("1-0") || word.Contains("0-1") || word.Contains("1/2-1/2") ||
+                             word.Contains('*'))
+                    {
+                        yield return new PgnGame(currentGameTags, currentGameMoves);
+                        currentGameTags = new Dictionary<string, string>();
+                        currentGameMoves = new List<PgnMove>();
+                        inMoveSection = false;
+                    }
+                }
+            }
+            else
+            {
+                if (line.StartsWith("[") && line.EndsWith("]") && line.Contains("\""))
+                {
+                    var firstSpaceIndex = line.IndexOf(' ');
+                    var firstQuoteIndex = line.IndexOf('"');
+                    var lastQuoteIndex = line.LastIndexOf('"');
+
+                    if (firstSpaceIndex > 0 && firstQuoteIndex > firstSpaceIndex &&
+                        lastQuoteIndex > firstQuoteIndex)
+                    {
+                        var tagName = line.Substring(1, firstSpaceIndex - 1).Trim();
+                        var tagValue = line.Substring(firstQuoteIndex + 1, lastQuoteIndex - firstQuoteIndex - 1)
+                            .Trim();
+
+                        currentGameTags[tagName] = tagValue;
+                    }
+                }
+            }
+        }
+
+        if (currentGameTags.Count > 0 && currentGameMoves.Count > 0)
+        {
+            yield return new PgnGame(currentGameTags, currentGameMoves);
+        }
     }
 }
\ No newline at end of file

From 20b3ee1b9d0e8e9aff3351f17fd84cb0fc3f8092 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 27 Mar 2023 19:15:31 +0200
Subject: [PATCH 026/119] Added simple SAN to move converter

---
 .../PgnMoveNotationTests/SanToMoveTests.cs    | 103 ++++++++++++++++++
 .../{ => PgnTests}/PgnTests.cs                |   2 +-
 .../Rudzoft.ChessLib.PGN.Test.csproj          |   1 +
 src/Rudzoft.ChessLib/Notation/ISanToMove.cs   |  36 ++++++
 src/Rudzoft.ChessLib/Notation/SanToMove.cs    |  73 +++++++++++++
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |  30 -----
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |   1 +
 7 files changed, 215 insertions(+), 31 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
 rename src/Rudzoft.ChessLib.PGN.Test/{ => PgnTests}/PgnTests.cs (98%)
 create mode 100644 src/Rudzoft.ChessLib/Notation/ISanToMove.cs
 create mode 100644 src/Rudzoft.ChessLib/Notation/SanToMove.cs

diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
new file mode 100644
index 00000000..52d85e87
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
@@ -0,0 +1,103 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Notation;
+using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
+
+namespace Rudzoft.ChessLib.PGN.Test.PgnMoveNotationTests;
+
+public sealed class SanToMoveTests
+{
+    private const string SampleFilePath = @"samples/sample.pgn";
+    private const int ExpectedGameCount = 2;
+
+    private readonly IServiceProvider _serviceProvider;
+
+    public SanToMoveTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
+            .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton<ISanToMove, SanToMove>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
+            .AddPgnParser()
+            .BuildServiceProvider();
+    }
+
+    [Fact]
+    public async Task A()
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(Fen.Fen.StartPositionFen);
+        var state = new State();
+        pos.Set(in fenData, ChessMode.Normal, state);
+        
+        var games = new List<PgnGame>();
+        var parser = _serviceProvider.GetRequiredService<IPgnParser>();
+
+        await foreach (var game in parser.ParseFile(SampleFilePath))
+            games.Add(game);
+
+        var sanMoves = new List<string>();
+
+        foreach (var pgnMove in games.First().Moves)
+        {
+            sanMoves.Add(pgnMove.WhiteMove);
+            if (!string.IsNullOrWhiteSpace(pgnMove.BlackMove))
+                sanMoves.Add(pgnMove.BlackMove);
+        }
+
+        var moveNotation = MoveNotation.Create(pos);
+        var notation = moveNotation.ToNotation(MoveNotations.San);
+        var converter = _serviceProvider.GetRequiredService<ISanToMove>();
+
+        var chessMoves = sanMoves
+            .Select(sanMove => converter.FromSan(pos, sanMove, notation))
+            .TakeWhile(static move => move != Move.EmptyMove);
+
+        foreach (var move in chessMoves)
+            pos.MakeMove(move, state);
+        
+        Assert.Equal(ExpectedGameCount, games.Count);
+        Assert.NotEmpty(games);
+        Assert.Equal(sanMoves.Count - 1, pos.Ply);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
similarity index 98%
rename from src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
rename to src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
index e8952869..4a98c69d 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
@@ -26,7 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using Microsoft.Extensions.DependencyInjection;
 
-namespace Rudzoft.ChessLib.PGN.Test;
+namespace Rudzoft.ChessLib.PGN.Test.PgnTests;
 
 public sealed class PgnTests
 {
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index 98abfc7b..4d4c15c0 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -25,6 +25,7 @@
 
     <ItemGroup>
       <ProjectReference Include="..\Rudzoft.ChessLib.PGN\Rudzoft.ChessLib.PGN.csproj" />
+      <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
     </ItemGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib/Notation/ISanToMove.cs b/src/Rudzoft.ChessLib/Notation/ISanToMove.cs
new file mode 100644
index 00000000..f0524bbe
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Notation/ISanToMove.cs
@@ -0,0 +1,36 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System;
+using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Notation;
+
+public interface ISanToMove
+{
+    Move FromSan(IPosition pos, ReadOnlySpan<char> sanMove, INotation notation);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/SanToMove.cs b/src/Rudzoft.ChessLib/Notation/SanToMove.cs
new file mode 100644
index 00000000..f4d4fe98
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Notation/SanToMove.cs
@@ -0,0 +1,73 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System;
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Notation;
+
+public sealed class SanToMove : ISanToMove
+{
+    private readonly ObjectPool<IMoveList> _moveListPool;
+
+    public SanToMove(ObjectPool<IMoveList> moveListPool)
+    {
+        _moveListPool = moveListPool;
+    }
+
+    public Move FromSan(IPosition pos, ReadOnlySpan<char> sanMove, INotation notation)
+    {
+        if (sanMove.IsEmpty || sanMove[0] == '*')
+            return Move.EmptyMove;
+
+        var moveList = _moveListPool.Get();
+        moveList.Generate(in pos);
+        var moves = moveList.Get();
+
+        var m = Move.EmptyMove;
+        
+        foreach (var move in moves)
+        {
+            var san = notation.Convert(move);
+
+            if (string.IsNullOrWhiteSpace(san))
+                continue;
+            
+            if (!sanMove.Equals(san.AsSpan(), StringComparison.InvariantCulture))
+                continue;
+            
+            m = move;
+            break;
+        }
+
+        _moveListPool.Return(moveList);
+
+        return m;
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index df6e7e16..b72cd3c9 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -169,36 +169,6 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
         _moveListPool.Return(ml);
 
         return mm;
-        
-        // Iterate all known moves for current position to find a match.
-        // foreach (var valMove in moves)
-        // {
-        //     var m = valMove.Move;
-        //
-        //     if (from != m.FromSquare() || to != m.ToSquare())
-        //         continue;
-        //     
-        //     var type = move.MoveType();
-        //     
-        //     if ((m.IsPromotionMove() ? type == MoveTypes.Promotion : type != MoveTypes.Promotion) && !IsInCheck(pos, m))
-        //         return m;
-        // }
-        //
-        // return Move.EmptyMove;
-        
-        // var emMoves = ml.Select(static em => em.Move)
-        //     .Where(m => from == m.FromSquare())
-        //     .Where(m => to == m.ToSquare())
-        //     .Where(m =>
-        //     {
-        //         var type = move.MoveType();
-        //         return m.IsPromotionMove()
-        //             ? type == MoveTypes.Promotion
-        //             : type != MoveTypes.Promotion;
-        //     })
-        //     .Where(m => !IsInCheck(pos, m));
-        //
-        // return emMoves.FirstOrDefault(Move.EmptyMove);
     }
 
     private static Move SelectMove(in IPosition pos, Square polyFrom, Square polyTo, MoveTypes polyType, ReadOnlySpan<ValMove> moves)
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index d5a0332b..b9512e7e 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -63,6 +63,7 @@
     <PackageReference Include="ZString" Version="2.5.0" />
   </ItemGroup>
   <ItemGroup>
+    <Folder Include="Pgn" />
     <Folder Include="Protocol\" />
     <Folder Include="Validation\" />
   </ItemGroup>

From 00f86f7cf43ddcda6757865e9787a56bd00d4237 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 27 Mar 2023 20:35:29 +0200
Subject: [PATCH 027/119] Added notational move converter

---
 .../PgnMoveNotationTests/SanToMoveTests.cs    | 39 +++++++++++++++-
 src/Rudzoft.ChessLib/Notation/ISanToMove.cs   |  5 ++-
 src/Rudzoft.ChessLib/Notation/SanToMove.cs    | 45 ++++++++++++++++++-
 3 files changed, 84 insertions(+), 5 deletions(-)

diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
index 52d85e87..1322aee6 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
@@ -63,7 +63,7 @@ public SanToMoveTests()
     }
 
     [Fact]
-    public async Task A()
+    public async Task BasicSanConvert()
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
         var fenData = new FenData(Fen.Fen.StartPositionFen);
@@ -90,7 +90,7 @@ public async Task A()
         var converter = _serviceProvider.GetRequiredService<ISanToMove>();
 
         var chessMoves = sanMoves
-            .Select(sanMove => converter.FromSan(pos, sanMove, notation))
+            .Select(sanMove => converter.FromNotation(pos, sanMove, notation))
             .TakeWhile(static move => move != Move.EmptyMove);
 
         foreach (var move in chessMoves)
@@ -100,4 +100,39 @@ public async Task A()
         Assert.NotEmpty(games);
         Assert.Equal(sanMoves.Count - 1, pos.Ply);
     }
+
+    [Fact]
+    public async Task AllAtOnceConvert()
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        var fenData = new FenData(Fen.Fen.StartPositionFen);
+        var state = new State();
+        pos.Set(in fenData, ChessMode.Normal, state);
+        
+        var games = new List<PgnGame>();
+        var parser = _serviceProvider.GetRequiredService<IPgnParser>();
+
+        await foreach (var game in parser.ParseFile(SampleFilePath))
+            games.Add(game);
+
+        var sanMoves = new List<string>();
+
+        foreach (var pgnMove in games.First().Moves)
+        {
+            sanMoves.Add(pgnMove.WhiteMove);
+            if (!string.IsNullOrWhiteSpace(pgnMove.BlackMove))
+                sanMoves.Add(pgnMove.BlackMove);
+        }
+
+        var moveNotation = MoveNotation.Create(pos);
+        var notation = moveNotation.ToNotation(MoveNotations.San);
+        var converter = _serviceProvider.GetRequiredService<ISanToMove>();
+
+        var actualMoves = converter.FromNotation(pos, sanMoves, notation);
+
+        Assert.Equal(ExpectedGameCount, games.Count);
+        Assert.NotEmpty(games);
+        Assert.Equal(sanMoves.Count - 1, actualMoves.Count);
+        Assert.Equal(sanMoves.Count - 1, pos.Ply);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/ISanToMove.cs b/src/Rudzoft.ChessLib/Notation/ISanToMove.cs
index f0524bbe..92d60a6a 100644
--- a/src/Rudzoft.ChessLib/Notation/ISanToMove.cs
+++ b/src/Rudzoft.ChessLib/Notation/ISanToMove.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Collections.Generic;
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.Types;
 
@@ -32,5 +33,7 @@ namespace Rudzoft.ChessLib.Notation;
 
 public interface ISanToMove
 {
-    Move FromSan(IPosition pos, ReadOnlySpan<char> sanMove, INotation notation);
+    IReadOnlyList<Move> FromNotation(IPosition pos, IEnumerable<string> notationalMoves, INotation notation);
+    
+    Move FromNotation(IPosition pos, ReadOnlySpan<char> sanMove, INotation notation);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/SanToMove.cs b/src/Rudzoft.ChessLib/Notation/SanToMove.cs
index f4d4fe98..3be68433 100644
--- a/src/Rudzoft.ChessLib/Notation/SanToMove.cs
+++ b/src/Rudzoft.ChessLib/Notation/SanToMove.cs
@@ -25,6 +25,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Collections.Generic;
+using System.Linq;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Notation.Notations;
@@ -41,7 +43,46 @@ public SanToMove(ObjectPool<IMoveList> moveListPool)
         _moveListPool = moveListPool;
     }
 
-    public Move FromSan(IPosition pos, ReadOnlySpan<char> sanMove, INotation notation)
+    public IReadOnlyList<Move> FromNotation(IPosition pos, IEnumerable<string> notationalMoves, INotation notation)
+    {
+        var moveList = _moveListPool.Get();
+        var result = new List<Move>();
+
+        var validNotationalMoves = notationalMoves
+            .Where(static notationalMove => !string.IsNullOrWhiteSpace(notationalMove));
+
+        var state = new State();
+
+        foreach (var notationalMove in validNotationalMoves)
+        {
+            moveList.Generate(pos);
+            
+            var moves = moveList.Get();
+            
+            if (moves.IsEmpty)
+                break;
+
+            foreach (var move in moves)
+            {
+                var notatedMove = notation.Convert(move);
+                if (string.IsNullOrWhiteSpace(notatedMove))
+                    continue;
+
+                if (notationalMove.Equals(notatedMove))
+                {
+                    pos.MakeMove(move.Move, in state);
+                    result.Add(move);
+                    break;
+                }
+            }
+        }
+
+        _moveListPool.Return(moveList);
+
+        return result;
+    }
+
+    public Move FromNotation(IPosition pos, ReadOnlySpan<char> sanMove, INotation notation)
     {
         if (sanMove.IsEmpty || sanMove[0] == '*')
             return Move.EmptyMove;
@@ -70,4 +111,4 @@ public Move FromSan(IPosition pos, ReadOnlySpan<char> sanMove, INotation notatio
 
         return m;
     }
-}
\ No newline at end of file
+}

From de0188b7f6575ce893833d93295f1740150a94dd Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 3 Apr 2023 07:58:33 +0200
Subject: [PATCH 028/119] Accumative minor code updates

- TT can now be created with 0 size if it is not needed
---
 .../PgnMoveNotationTests/SanToMoveTests.cs    |  6 +--
 src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs    | 27 +++++++------
 .../SizesTests/BaseTypesSizeTests.cs          |  9 ++++-
 .../TablesTests/KillerMovesTests.cs           |  1 -
 .../Controllers/PgnController.cs              |  6 +++
 src/Rudzoft.ChessLib/Board.cs                 | 36 ++++++-----------
 src/Rudzoft.ChessLib/Cuckoo.cs                |  6 +--
 .../ChessLibServiceCollectionExtensions.cs    |  5 ++-
 src/Rudzoft.ChessLib/Fen/Fen.cs               | 10 +++--
 src/Rudzoft.ChessLib/Fen/FenData.cs           | 20 ++++++----
 src/Rudzoft.ChessLib/Game.cs                  |  4 +-
 src/Rudzoft.ChessLib/Hash/RKiss.cs            | 36 ++++++++---------
 .../Transposition/TranspositionTable.cs       |  9 ++++-
 .../TranspositionTableConfiguration.cs        |  2 +-
 src/Rudzoft.ChessLib/IPosition.cs             |  2 +
 .../{ISanToMove.cs => INotationToMove.cs}     |  4 +-
 .../{SanToMove.cs => NotationToMove.cs}       | 22 +++++------
 src/Rudzoft.ChessLib/Position.cs              | 39 +++++++++++--------
 .../Protocol/UCI/SearchParameters.cs          |  6 ++-
 19 files changed, 135 insertions(+), 115 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.WebApi/Controllers/PgnController.cs
 rename src/Rudzoft.ChessLib/Notation/{ISanToMove.cs => INotationToMove.cs} (91%)
 rename src/Rudzoft.ChessLib/Notation/{SanToMove.cs => NotationToMove.cs} (83%)

diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
index 1322aee6..9937acff 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
@@ -51,7 +51,7 @@ public SanToMoveTests()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
-            .AddSingleton<ISanToMove, SanToMove>()
+            .AddSingleton<INotationToMove, NotationToMove>()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
@@ -87,7 +87,7 @@ public async Task BasicSanConvert()
 
         var moveNotation = MoveNotation.Create(pos);
         var notation = moveNotation.ToNotation(MoveNotations.San);
-        var converter = _serviceProvider.GetRequiredService<ISanToMove>();
+        var converter = _serviceProvider.GetRequiredService<INotationToMove>();
 
         var chessMoves = sanMoves
             .Select(sanMove => converter.FromNotation(pos, sanMove, notation))
@@ -126,7 +126,7 @@ public async Task AllAtOnceConvert()
 
         var moveNotation = MoveNotation.Create(pos);
         var notation = moveNotation.ToNotation(MoveNotations.San);
-        var converter = _serviceProvider.GetRequiredService<ISanToMove>();
+        var converter = _serviceProvider.GetRequiredService<INotationToMove>();
 
         var actualMoves = converter.FromNotation(pos, sanMoves, notation);
 
diff --git a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
index 7eba40d0..344cd558 100644
--- a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
@@ -29,19 +29,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.PGN;
 
-public sealed class RegexPgnParser : IPgnParser
+public sealed partial class RegexPgnParser : IPgnParser
 {
-
     private const int DefaultTagCapacity = 7;
     private const int DefaultMoveListCapacity = 24;
 
-    private static readonly Regex TagPairRegex =
-        new(@"\[(?<tagName>\w+)\s+""(?<tagValue>[^""]+)""\]",
-            RegexOptions.Compiled | RegexOptions.NonBacktracking);
+    [GeneratedRegex(@"\[(?<tagName>\w+)\s+""(?<tagValue>[^""]+)""\]",  RegexOptions.NonBacktracking)]
+    private static partial Regex TagPairRegex();
 
-    private static readonly Regex MoveTextRegex =
-        new(@"(?<moveNumber>\d+)\.\s*(?<whiteMove>\S+)(\s+(?<blackMove>\S+))?",
-            RegexOptions.Compiled | RegexOptions.NonBacktracking);
+    [GeneratedRegex(@"(?<moveNumber>\d+)\.\s*(?<whiteMove>\S+)(\s+(?<blackMove>\S+))?",  RegexOptions.NonBacktracking)]
+    private static partial Regex MoveTextRegex();
 
     private static readonly PgnMove EmptyPgnMove = new(0, string.Empty, string.Empty);
 
@@ -90,22 +87,24 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
 
     private static bool IsPgnGameResult(ReadOnlySpan<char> line)
     {
-        if (line.EndsWith(stackalloc char[] { '*' }, StringComparison.InvariantCultureIgnoreCase))
+        var trimmedLine = line.TrimEnd();
+        
+        if (trimmedLine.EndsWith(stackalloc char[] { '*' }, StringComparison.InvariantCultureIgnoreCase))
             return true;
 
-        if (line.EndsWith(stackalloc char[] { '1', '-', '0' }, StringComparison.InvariantCultureIgnoreCase))
+        if (trimmedLine.EndsWith(stackalloc char[] { '1', '-', '0' }, StringComparison.InvariantCultureIgnoreCase))
             return true;
 
-        if (line.EndsWith(stackalloc char[] { '0', '-', '1' }, StringComparison.InvariantCultureIgnoreCase))
+        if (trimmedLine.EndsWith(stackalloc char[] { '0', '-', '1' }, StringComparison.InvariantCultureIgnoreCase))
             return true;
 
-        return line.EndsWith(stackalloc char[] { '1', '/', '2', '-', '1', '/', '2' },
+        return trimmedLine.EndsWith(stackalloc char[] { '1', '/', '2', '-', '1', '/', '2' },
             StringComparison.InvariantCultureIgnoreCase);
     }
 
     private static void ParseTag(string line, IDictionary<string, string> currentGameTags)
     {
-        var tagMatch = TagPairRegex.Match(line);
+        var tagMatch = TagPairRegex().Match(line);
 
         if (!tagMatch.Success)
             return;
@@ -117,7 +116,7 @@ private static void ParseTag(string line, IDictionary<string, string> currentGam
 
     private static IEnumerable<PgnMove> ParseMovesLine(string line)
     {
-        var moveMatches = MoveTextRegex.Matches(line);
+        var moveMatches = MoveTextRegex().Matches(line);
         return moveMatches.Select(static x =>
         {
             var moveNumber = int.Parse(x.Groups["moveNumber"].Value);
diff --git a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
index 9e26a7ac..685e8559 100644
--- a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
@@ -62,6 +62,14 @@ public unsafe void RankSize()
         Assert.Equal(expected, actual);
     }
 
+    [Fact]
+    public unsafe void FileSize()
+    {
+        const int expected = 4;
+        var actual = sizeof(File);
+        Assert.Equal(expected, actual);
+    }
+
     [Fact]
     public unsafe void DepthSize()
     {
@@ -69,5 +77,4 @@ public unsafe void DepthSize()
         var actual = sizeof(Depth);
         Assert.Equal(expected, actual);
     }
-
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
index c7ed9a34..de8578eb 100644
--- a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
+++ b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Rudzoft.ChessLib.Tables;
 using Rudzoft.ChessLib.Tables.KillerMoves;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib.WebApi/Controllers/PgnController.cs b/src/Rudzoft.ChessLib.WebApi/Controllers/PgnController.cs
new file mode 100644
index 00000000..1e62ef51
--- /dev/null
+++ b/src/Rudzoft.ChessLib.WebApi/Controllers/PgnController.cs
@@ -0,0 +1,6 @@
+namespace Rudzoft.ChessLib.WebApi.Controllers;
+
+public class PgnController
+{
+    
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index d62d423e..83cf41bf 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -76,11 +76,9 @@ public void Clear()
         Array.Fill(_index, 0);
     }
 
-    public Piece PieceAt(Square sq)
-        => _pieces[sq.AsInt()];
+    public Piece PieceAt(Square sq) => _pieces[sq.AsInt()];
 
-    public bool IsEmpty(Square sq)
-        => _pieces[sq.AsInt()] == Piece.EmptyPiece;
+    public bool IsEmpty(Square sq) => _pieces[sq.AsInt()] == Piece.EmptyPiece;
 
     public void AddPiece(Piece pc, Square sq)
     {
@@ -129,23 +127,17 @@ public void MovePiece(Square from, Square to)
         _pieceList[pc.AsInt()][_index[to.AsInt()]] = to;
     }
 
-    public Piece MovedPiece(Move m)
-        => PieceAt(m.FromSquare());
+    public Piece MovedPiece(Move m) => PieceAt(m.FromSquare());
 
-    public BitBoard Pieces()
-        => _byType[PieceTypes.AllPieces.AsInt()];
+    public BitBoard Pieces() => _byType[PieceTypes.AllPieces.AsInt()];
 
-    public BitBoard Pieces(PieceTypes pt)
-        => _byType[pt.AsInt()];
+    public BitBoard Pieces(PieceTypes pt) => _byType[pt.AsInt()];
 
-    public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2)
-        => _byType[pt1.AsInt()] | _byType[pt2.AsInt()];
+    public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2) => _byType[pt1.AsInt()] | _byType[pt2.AsInt()];
 
-    public BitBoard Pieces(Player p)
-        => _bySide[p.Side];
+    public BitBoard Pieces(Player p) => _bySide[p.Side];
 
-    public BitBoard Pieces(Player p, PieceTypes pt)
-        => _bySide[p.Side] & _byType[pt.AsInt()];
+    public BitBoard Pieces(Player p, PieceTypes pt) => _bySide[p.Side] & _byType[pt.AsInt()];
 
     public BitBoard Pieces(Player p, PieceTypes pt1, PieceTypes pt2)
         => _bySide[p.Side] & (_byType[pt1.AsInt()] | _byType[pt2.AsInt()]);
@@ -164,21 +156,17 @@ public ReadOnlySpan<Square> Squares(PieceTypes pt, Player c)
             : squares.AsSpan()[..Array.IndexOf(squares, Types.Square.None)];
     }
 
-    public int PieceCount(Piece pc)
-        => _pieceCount[pc.AsInt()];
+    public int PieceCount(Piece pc) => _pieceCount[pc.AsInt()];
 
-    public int PieceCount(PieceTypes pt, Player p)
-        => PieceCount(pt.MakePiece(p));
+    public int PieceCount(PieceTypes pt, Player p) => PieceCount(pt.MakePiece(p));
 
     public int PieceCount(PieceTypes pt)
         => _pieceCount[pt.MakePiece(Player.White).AsInt()] + _pieceCount[pt.MakePiece(Player.Black).AsInt()];
 
-    public int PieceCount()
-        => PieceCount(PieceTypes.AllPieces);
+    public int PieceCount() => PieceCount(PieceTypes.AllPieces);
 
     public IEnumerator<Piece> GetEnumerator()
         => _pieces.Where(static pc => pc != Piece.EmptyPiece).GetEnumerator();
 
-    IEnumerator IEnumerable.GetEnumerator()
-        => GetEnumerator();
+    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index b4f07529..67e38ebf 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -132,9 +132,7 @@ public static bool HashCuckooCycle(in IPosition pos, int end, int ply)
         return false;
     }
 
-    private static int CuckooHashOne(in HashKey key)
-        => (int)(key.Key & 0x1FFF);
+    private static int CuckooHashOne(in HashKey key) => (int)(key.Key & 0x1FFF);
 
-    private static int CuckooHashTwo(in HashKey key)
-        => (int)((key.Key >> 16) & 0x1FFF);
+    private static int CuckooHashTwo(in HashKey key) => (int)((key.Key >> 16) & 0x1FFF);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 4e88629a..cbfbf895 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -30,12 +30,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.Evaluation;
 using Rudzoft.ChessLib.Factories;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
-using Rudzoft.ChessLib.Tables;
 using Rudzoft.ChessLib.Tables.KillerMoves;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -46,7 +46,7 @@ public static class ChessLibServiceCollectionExtensions
 {
     public static IServiceCollection AddChessLib(
         this IServiceCollection serviceCollection,
-        IConfiguration configuration,
+        IConfiguration configuration = null,
         string configurationFile = null)
     {
         if (serviceCollection == null)
@@ -88,6 +88,7 @@ public static IServiceCollection AddChessLib(
             .AddTransient(static _ => KillerMoves.Create(64))
             .AddSingleton<ISearchParameters, SearchParameters>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IKpkBitBase, KpkBitBase>()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index 066c7199..7a0436b0 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -54,7 +54,7 @@ public static class Fen
 
     private static readonly Lazy<Regex> ValidFenRegex = new(static () => new Regex(
        string.Format(@"^ \s* {0}/{0}/{0}/{0}/{0}/{0}/{0}/{0} \s+ (?:w|b) \s+ (?:[KkQq]+|\-) \s+ (?:[a-h][1-8]|\-) \s+ \d+ \s+ \d+ \s* $", FenRankRegexSnippet),
-       RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.NonBacktracking));
+       RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.NonBacktracking | RegexOptions.ExplicitCapture));
 
     /// <summary>
     /// Performs basic validation of FEN string.
@@ -70,6 +70,9 @@ public static void Validate(string fen)
     {
         var f = fen.AsSpan().Trim();
 
+        if (f.IsEmpty)
+            throw new InvalidFen("Fen is empty.");
+
         var invalidCharIndex = f.IndexOfAnyExcept(ValidChars.AsSpan());
 
         if (invalidCharIndex > -1)
@@ -137,7 +140,8 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
             var limit = limits[pt.AsInt()];
 
             if (pieceCount[pc.AsInt()] > limit)
-                throw new InvalidFen($"Invalid fen (piece limit exceeded for {pc}. index={i},limit={limit},count={pieceCount[pc.AsInt()]}) {s.ToString()}");
+                throw new InvalidFen(
+                    $"Invalid fen (piece limit exceeded for {pc}. index={i},limit={limit},count={pieceCount[pc.AsInt()]}) {s.ToString()}");
         }
 
         // check for summed up values
@@ -219,4 +223,4 @@ private static (int, int) CountSpacesAndSeparators(ReadOnlySpan<char> str)
 
         return result;
     }
-}
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Fen/FenData.cs b/src/Rudzoft.ChessLib/Fen/FenData.cs
index 4e7f5cd0..c2960675 100644
--- a/src/Rudzoft.ChessLib/Fen/FenData.cs
+++ b/src/Rudzoft.ChessLib/Fen/FenData.cs
@@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
@@ -37,13 +38,16 @@ namespace Rudzoft.ChessLib.Fen;
 /// For more information about the format, see
 /// https://chessprogramming.wikispaces.com/Forsyth-Edwards+Notation
 /// </summary>
+[DebuggerDisplay("Fen='{Fen.ToString()}', Chunks={_splitPoints.Count}")]
 public sealed class FenData : EventArgs, IFenData
 {
-    private readonly Queue<(int, int)> _splitPoints;
+    private record struct SplitPoint(int Begin, int End);
+    
+    private readonly Queue<SplitPoint> _splitPoints;
 
     private FenData()
     {
-        _splitPoints = new Queue<(int, int)>(6);
+        _splitPoints = new Queue<SplitPoint>(6);
     }
 
     public FenData(ReadOnlyMemory<char> fen) : this()
@@ -61,12 +65,12 @@ public FenData(ReadOnlyMemory<char> fen) : this()
             if (splitPoint != ' ')
                 continue;
 
-            _splitPoints.Enqueue((start, i));
+            _splitPoints.Enqueue(new(start, i));
             start = i + 1;
         }
 
         // add last
-        _splitPoints.Enqueue((start, s.Length));
+        _splitPoints.Enqueue(new(start, s.Length));
     }
 
     public FenData(ReadOnlySpan<string> fen) : this()
@@ -94,12 +98,12 @@ public FenData(ReadOnlySpan<string> fen) : this()
             if (splitPoint != ' ')
                 continue;
 
-            _splitPoints.Enqueue((start, i));
+            _splitPoints.Enqueue(new(start, i));
             start = i + 1;
         }
 
         // add last
-        _splitPoints.Enqueue((start, s.Length));
+        _splitPoints.Enqueue(new(start, s.Length));
     }
 
     public FenData(char[] fen) : this(fen.AsMemory())
@@ -123,8 +127,8 @@ public ReadOnlySpan<char> Chunk()
     {
         // ReSharper disable once InlineOutVariableDeclaration
         Index++;
-        return _splitPoints.TryDequeue(out (int start, int end) result)
-            ? Fen.Span[result.start..result.end]
+        return _splitPoints.TryDequeue(out var splitPoint)
+            ? Fen.Span[splitPoint.Begin..splitPoint.End]
             : ReadOnlySpan<char>.Empty;
     }
 
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index fc0f1c7f..606da1ab 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -52,9 +52,9 @@ public Game(
         ICpu cpu,
         ISearchParameters searchParameters,
         IPosition pos,
-        ObjectPool<IMoveList> moveListPoolPool)
+        ObjectPool<IMoveList> moveListPool)
     {
-        _moveListPool = moveListPoolPool;
+        _moveListPool = moveListPool;
         _pos = pos;
         
         Table = transpositionTable;
diff --git a/src/Rudzoft.ChessLib/Hash/RKiss.cs b/src/Rudzoft.ChessLib/Hash/RKiss.cs
index 838d978f..5d1cb104 100644
--- a/src/Rudzoft.ChessLib/Hash/RKiss.cs
+++ b/src/Rudzoft.ChessLib/Hash/RKiss.cs
@@ -1,24 +1,20 @@
 /*
-/// xorshift64star Pseudo-Random Number Generator
-/// This class is based on original code written and dedicated
-/// to the public domain by Sebastiano Vigna (2014).
-/// It has the following characteristics:
-///
-///  -  Outputs 64-bit numbers
-///  -  Passes Dieharder and SmallCrush test batteries
-///  -  Does not require warm-up, no zeroland to escape
-///  -  Internal state is a single 64-bit integer
-///  -  Period is 2^64 - 1
-///  -  Speed: 1.60 ns/call (Core i7 @3.40GHz) (not so for C#)
-///
-/// For further analysis see
-///   <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
-///
-/// C# single type (ulong) adaptation by Rudy Alex Kohn (2017).
-/// C# Performance (approx) against
+xorshift64star Pseudo-Random Number Generator
+This class is based on original code written and dedicated
+to the public domain by Sebastiano Vigna (2014).
+It has the following characteristics:
+-  Outputs 64-bit numbers
+-  Passes Dieharder and SmallCrush test batteries
+-  Does not require warm-up, no zeroland to escape
+-  Internal state is a single 64-bit integer
+-  Period is 2^64 - 1
+-  Speed: 1.60 ns/call (Core i7 @3.40GHz) (not so for C#)
 
-/// { ULongRnd.NextBytes(Buffer);
-/// return BitConverter.ToUInt64(Buffer, 0); }
+For further analysis see
+<http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
+
+ C# single type (ulong) adaptation by Rudy Alex Kohn (2017).
+ C# Performance (approx) against
 
       Method |      Mean |     Error |    StdDev |
 ------------ |----------:|----------:|----------:|
@@ -38,7 +34,7 @@ public sealed class RKiss : IRKiss
     private ulong _s;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public RKiss(ulong s)
+    private RKiss(ulong s)
     {
         _s = s;
     }
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index c497c519..c9d17a16 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -77,8 +77,13 @@ public TranspositionTable(IOptions<TranspositionTableConfiguration> options)
     /// <returns>The number of clusters in the table</returns>
     public ulong SetSize(int mbSize)
     {
-        if (mbSize < 0)
-            throw new TranspositionTableFailure($"Unable to create table with negative size: {mbSize}");
+        switch (mbSize)
+        {
+            case < 0:
+                throw new TranspositionTableFailure($"Unable to create table with negative size: {mbSize}");
+            case 0:
+                return 0;
+        }
 
         Size = mbSize;
         var size = (int)(((ulong)mbSize << 20) / (ulong)ClusterSize);
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
index 3b4df516..e065c8d8 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
@@ -32,6 +32,6 @@ public sealed class TranspositionTableConfiguration
 {
     public const string Section = "TranspositionTable";
     
-    [Range(1, 1 << 31, ErrorMessage = "Default size for TT: {0} must be between {1} and {2}.")]
+    [Range(0, 1 << 31, ErrorMessage = "Default size for TT: {0} must be between {1} and {2}.")]
     public int DefaultSize { get; init; }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index 0c899352..62b864e6 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -192,6 +192,8 @@ public interface IPosition : IEnumerable<Piece>
 
     IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool validate = false, int searcher = 0);
 
+    IPosition Set(string fen, ChessMode chessMode, State state, bool validate = false, int searcher = 0);
+
     IPosition Set(ReadOnlySpan<char> code, Player p, State state);
     
     HashKey GetPiecesKey();
diff --git a/src/Rudzoft.ChessLib/Notation/ISanToMove.cs b/src/Rudzoft.ChessLib/Notation/INotationToMove.cs
similarity index 91%
rename from src/Rudzoft.ChessLib/Notation/ISanToMove.cs
rename to src/Rudzoft.ChessLib/Notation/INotationToMove.cs
index 92d60a6a..705b2642 100644
--- a/src/Rudzoft.ChessLib/Notation/ISanToMove.cs
+++ b/src/Rudzoft.ChessLib/Notation/INotationToMove.cs
@@ -31,9 +31,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation;
 
-public interface ISanToMove
+public interface INotationToMove
 {
     IReadOnlyList<Move> FromNotation(IPosition pos, IEnumerable<string> notationalMoves, INotation notation);
     
-    Move FromNotation(IPosition pos, ReadOnlySpan<char> sanMove, INotation notation);
+    Move FromNotation(IPosition pos, ReadOnlySpan<char> notatedMove, INotation notation);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/SanToMove.cs b/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
similarity index 83%
rename from src/Rudzoft.ChessLib/Notation/SanToMove.cs
rename to src/Rudzoft.ChessLib/Notation/NotationToMove.cs
index 3be68433..ed80abf9 100644
--- a/src/Rudzoft.ChessLib/Notation/SanToMove.cs
+++ b/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
@@ -34,11 +34,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation;
 
-public sealed class SanToMove : ISanToMove
+public sealed class NotationToMove : INotationToMove
 {
     private readonly ObjectPool<IMoveList> _moveListPool;
 
-    public SanToMove(ObjectPool<IMoveList> moveListPool)
+    public NotationToMove(ObjectPool<IMoveList> moveListPool)
     {
         _moveListPool = moveListPool;
     }
@@ -68,12 +68,12 @@ public IReadOnlyList<Move> FromNotation(IPosition pos, IEnumerable<string> notat
                 if (string.IsNullOrWhiteSpace(notatedMove))
                     continue;
 
-                if (notationalMove.Equals(notatedMove))
-                {
-                    pos.MakeMove(move.Move, in state);
-                    result.Add(move);
-                    break;
-                }
+                if (!notationalMove.Equals(notatedMove))
+                    continue;
+                
+                pos.MakeMove(move.Move, in state);
+                result.Add(move);
+                break;
             }
         }
 
@@ -82,9 +82,9 @@ public IReadOnlyList<Move> FromNotation(IPosition pos, IEnumerable<string> notat
         return result;
     }
 
-    public Move FromNotation(IPosition pos, ReadOnlySpan<char> sanMove, INotation notation)
+    public Move FromNotation(IPosition pos, ReadOnlySpan<char> notatedMove, INotation notation)
     {
-        if (sanMove.IsEmpty || sanMove[0] == '*')
+        if (notatedMove.IsEmpty || notatedMove[0] == '*')
             return Move.EmptyMove;
 
         var moveList = _moveListPool.Get();
@@ -100,7 +100,7 @@ public Move FromNotation(IPosition pos, ReadOnlySpan<char> sanMove, INotation no
             if (string.IsNullOrWhiteSpace(san))
                 continue;
             
-            if (!sanMove.Equals(san.AsSpan(), StringComparison.InvariantCulture))
+            if (!notatedMove.Equals(san.AsSpan(), StringComparison.InvariantCulture))
                 continue;
             
             m = move;
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index f3566e07..a1f083d1 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -38,6 +38,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -60,14 +61,14 @@ public sealed class Position : IPosition
     private readonly IPositionValidator _positionValidator;
     private Player _sideToMove;
 
-    public Position()
+    public Position() : this(
+        new Board(),
+        new Values(),
+        new PositionValidator(),
+        new DefaultObjectPool<IMoveList>(new MoveListPolicy()))
     {
-        Board = new Board();
-        Values = new Values();
-        State = new State();
-        Clear();
     }
-    
+
     public Position(
         IBoard board,
         IValues values,
@@ -1001,7 +1002,7 @@ public void RemovePiece(Square sq)
     {
         Board.RemovePiece(sq);
         if (!IsProbing)
-            PieceUpdated?.Invoke( new PieceSquareEventArgs(Piece.EmptyPiece, sq));
+            PieceUpdated?.Invoke(new PieceSquareEventArgs(Piece.EmptyPiece, sq));
     }
 
     public bool SeeGe(Move m, Value threshold)
@@ -1165,8 +1166,10 @@ private void SetupEnPassant(ReadOnlySpan<char> fenChunk)
             var otherSide = ~_sideToMove;
 
             enPassant = !(State.EnPassantSquare.PawnAttack(otherSide) & Pieces(PieceTypes.Pawn, _sideToMove)).IsEmpty
-                        && !(Pieces(PieceTypes.Pawn, otherSide) & (State.EnPassantSquare + otherSide.PawnPushDistance())).IsEmpty
-                        && (Pieces() & (State.EnPassantSquare | (State.EnPassantSquare + _sideToMove.PawnPushDistance()))).IsEmpty;
+                        && !(Pieces(PieceTypes.Pawn, otherSide) &
+                             (State.EnPassantSquare + otherSide.PawnPushDistance())).IsEmpty
+                        && (Pieces() &
+                            (State.EnPassantSquare | (State.EnPassantSquare + _sideToMove.PawnPushDistance()))).IsEmpty;
         }
 
         if (!enPassant)
@@ -1230,6 +1233,9 @@ public IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool
         return this;
     }
 
+    public IPosition Set(string fen, ChessMode chessMode, State state, bool validate = false, int searcher = 0)
+        => Set(new FenData(fen), chessMode, state, validate, searcher);
+
     public IPosition Set(ReadOnlySpan<char> code, Player p, State state)
     {
         Debug.Assert(code[0] == 'K' && code[1..].IndexOf('K') != -1);
@@ -1237,16 +1243,15 @@ public IPosition Set(ReadOnlySpan<char> code, Player p, State state)
         Debug.Assert(code[0] == 'K');
 
         var kingPos = code.LastIndexOf('K');
-        var sides = new [] { code[kingPos..].ToString(), code[..kingPos].ToString() };
-        
+        var sides = new[] { code[kingPos..].ToString(), code[..kingPos].ToString() };
+
         sides[p.Side] = sides[p.Side].ToLower();
 
         var fenStr = $"{sides[0]}{8 - sides[0].Length}/8/8/8/8/8/8/{sides[1]}{8 - sides[1].Length} w - - 0 10";
-        var fen = new FenData(fenStr);
 
-        return Set(in fen, ChessMode.Normal, state);
+        return Set(fenStr, ChessMode.Normal, state);
     }
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public ReadOnlySpan<Square> Squares(PieceTypes pt, Player p)
         => Board.Squares(pt, p);
@@ -1317,7 +1322,7 @@ public void TakeMove(Move m)
         // Set state to previous state
         State = State.Previous;
         Ply--;
-        
+
 #if DEBUG
         var validator = _positionValidator.Validate(this);
         Debug.Assert(validator.IsOk);
@@ -1540,7 +1545,9 @@ Square RookSquare(Square startSq, Piece rook)
             {
                 'K' => RookSquare(Square.H1.Relative(c), rook),
                 'Q' => RookSquare(Square.A1.Relative(c), rook),
-                _ => char.IsBetween(token, 'A', 'H') ? new Square(Rank.Rank1.Relative(c), new File(token - 'A')) : Square.None
+                _ => char.IsBetween(token, 'A', 'H')
+                    ? new Square(Rank.Rank1.Relative(c), new File(token - 'A'))
+                    : Square.None
             };
 
             if (rsq != Square.None)
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index db687ad2..32965a85 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -178,7 +178,11 @@ private static int ParseValue(int index, in ulong value, Span<char> target)
     public string ToString(string format, IFormatProvider formatProvider)
         => string.Format(formatProvider, format, ToString());
 
-    public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null)
+    public bool TryFormat(
+        Span<char> destination,
+        out int charsWritten,
+        ReadOnlySpan<char> format = default,
+        IFormatProvider provider = null)
     {
         var index = 0;
 

From 4c9af1a7cfa348a1bfd54366ee4e1436f2225c47 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 9 Apr 2023 21:25:51 +0200
Subject: [PATCH 029/119] Optimized Board.Squares() + minor styling

---
 .../MoveGeneratorBenchmark.cs                 |  58 ++++++++++
 .../Services/MoveGeneratorService.cs          |   3 +-
 src/Rudzoft.ChessLib/Board.cs                 |   8 +-
 .../MoveGeneration/MoveGenerator.cs           |   7 +-
 src/Rudzoft.ChessLib/State.cs                 |   3 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        | 101 ++++++------------
 6 files changed, 104 insertions(+), 76 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
new file mode 100644
index 00000000..cdca3001
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
@@ -0,0 +1,58 @@
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
+
+namespace Rudzoft.ChessLib.Benchmark;
+
+[MemoryDiagnoser]
+public class MoveGeneratorBenchmark
+{
+    [Params
+        (
+            Fen.Fen.StartPositionFen,
+            // "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
+            // "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
+            // "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
+            // "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
+            "rnbqk2r/pppppppp/8/8/8/8/PPPPPPPP/RNBQK2R w KQkq - 5 25",
+            "r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w KQkq - 0 1"
+        )
+    ]
+    public string _fen;
+    
+    private IPosition _pos;
+
+    private ObjectPool<IMoveList> _objectPool;
+
+    [GlobalSetup]
+    public void Setup()
+    {
+        var board = new Board();
+        var pieceValue = new Values();
+        var state = new State();
+        var validator = new PositionValidator();
+        _objectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        _pos = new Position(board, pieceValue, validator, _objectPool);
+        _pos.Set(_fen, ChessMode.Normal, state);
+    }
+
+    [Benchmark(Baseline = true)]
+    public int GenerateMovesNoPool()
+    {
+        var moves = _pos.GenerateMoves();
+        return moves.Length;
+    }
+    
+    [Benchmark]
+    public int GenerateMovesWithPool()
+    {
+        var moves = _objectPool.Get();
+        moves.Generate(_pos);
+        var length = moves.Length;
+        _objectPool.Return(moves);
+        return length;
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
index 77047499..bdd90600 100644
--- a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
@@ -19,9 +19,8 @@ public IEnumerable<string> GenerateMoves(MoveQuery parameters)
     {
         _logger.LogInformation("Generating moves. fen={Fen},type={Type},mode={Mode}", parameters.Fen, parameters.Type, parameters.Mode);
 
-        var fd = new FenData(parameters.Fen);
         var state = new State();
-        _position.Set(in fd, parameters.Mode, state);
+        _position.Set(parameters.Fen, parameters.Mode, state);
         return _position.GenerateMoves(parameters.Type).Select(static em => em.Move.ToString());
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index 83cf41bf..b49e29b3 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -150,10 +150,12 @@ public Square Square(PieceTypes pt, Player p)
 
     public ReadOnlySpan<Square> Squares(PieceTypes pt, Player c)
     {
-        var squares = _pieceList[pt.MakePiece(c).AsInt()];
-        return squares[0] == Types.Square.None
+        var pcIndex = pt.MakePiece(c).AsInt();
+        var pcCount = _pieceCount[pcIndex];
+
+        return pcCount == 0
             ? ReadOnlySpan<Square>.Empty
-            : squares.AsSpan()[..Array.IndexOf(squares, Types.Square.None)];
+            : _pieceList[pcIndex].AsSpan()[..pcCount];
     }
 
     public int PieceCount(Piece pc) => _pieceCount[pc.AsInt()];
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 45187024..7d49edd1 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -250,9 +250,10 @@ private static int GenerateMoves(
                     !(pt.PseudoAttacks(from) & target & pos.CheckedSquares(pt))))
                 continue;
 
-            var b = checks
-                ? pos.GetAttacks(from, pt) & target & pos.CheckedSquares(pt)
-                : pos.GetAttacks(from, pt) & target;
+            var b = pos.GetAttacks(from, pt) & target;
+
+            if (checks)
+                b &= pos.CheckedSquares(pt);
 
             index = Move.Create(moves, index, from, ref b);
         }
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index ca3e51b4..c9821494 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -121,8 +121,7 @@ public State CopyTo(State other)
         other.Previous = this;
 
         // initialize the rest of the values
-        if (other.CheckedSquares == null)
-            other.CheckedSquares = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
+        other.CheckedSquares ??= new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
 
         return other;
     }
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index a55581bd..023fffea 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -58,6 +58,8 @@ private BitBoard(int value)
     public int Count => BitBoards.PopCount(in this);
 
     public bool IsEmpty => Value == 0;
+    
+    public bool IsNotEmpty => Value != 0;
 
     public static BitBoard Empty => BitBoards.EmptyBitBoard;
 
@@ -75,84 +77,64 @@ private BitBoard(int value)
     public Square this[int index] => this.Get(index);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator BitBoard(ulong value)
-        => new(value);
+    public static implicit operator BitBoard(ulong value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator BitBoard(int value)
-        => new(value);
+    public static implicit operator BitBoard(int value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator BitBoard(Square sq)
-        => new(sq.AsBb());
+    public static implicit operator BitBoard(Square sq) => new(sq.AsBb());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Create(ulong value)
-        => new(value);
+    public static BitBoard Create(ulong value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Create(uint value)
-        => new(value);
+    public static BitBoard Create(uint value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator *(BitBoard left, ulong right)
-        => new(left.Value * right);
+    public static BitBoard operator *(BitBoard left, ulong right) => new(left.Value * right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator *(ulong left, BitBoard right)
-        => new(left * right.Value);
+    public static BitBoard operator *(ulong left, BitBoard right) => new(left * right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator -(BitBoard left, int right)
-        => new(left.Value - (ulong)right);
+    public static BitBoard operator -(BitBoard left, int right) => new(left.Value - (ulong)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator >>(BitBoard left, int right)
-        => new(left.Value >> right);
+    public static BitBoard operator >>(BitBoard left, int right) => new(left.Value >> right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator <<(BitBoard left, int right)
-        => new(left.Value << right);
+    public static BitBoard operator <<(BitBoard left, int right) => new(left.Value << right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator |(BitBoard left, Square right)
-        => new(left.Value | right.AsBb());
+    public static BitBoard operator |(BitBoard left, Square right) => new(left.Value | right.AsBb());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator |(BitBoard left, BitBoard right)
-        => new(left.Value | right.Value);
+    public static BitBoard operator |(BitBoard left, BitBoard right) => new(left.Value | right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator ^(BitBoard left, BitBoard right)
-        => new(left.Value ^ right.Value);
+    public static BitBoard operator ^(BitBoard left, BitBoard right) => new(left.Value ^ right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(BitBoard left, BitBoard right)
-        => new(left.Value & right.Value);
+    public static BitBoard operator &(BitBoard left, BitBoard right) => new(left.Value & right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(ulong left, BitBoard right)
-        => new(left & right.Value);
+    public static BitBoard operator &(ulong left, BitBoard right) => new(left & right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(BitBoard left, ulong right)
-        => new(left.Value & right);
+    public static BitBoard operator &(BitBoard left, ulong right) => new(left.Value & right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(BitBoard left, Square right)
-        => left.Value & right.AsBb();
+    public static BitBoard operator &(BitBoard left, Square right) => left.Value & right.AsBb();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(BitBoard left, File right)
-        => left.Value & right.BitBoardFile();
+    public static BitBoard operator &(BitBoard left, File right) => left.Value & right.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(Square left, BitBoard right)
-        => left.AsBb() & right.Value;
+    public static BitBoard operator &(Square left, BitBoard right) => left.AsBb() & right.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator ~(BitBoard bb)
-        => new(~bb.Value);
+    public static BitBoard operator ~(BitBoard bb) => new(~bb.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard operator --(BitBoard bb)
@@ -162,56 +144,43 @@ public static BitBoard Create(uint value)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <(BitBoard left, BitBoard right)
-        => left.Count < right.Count;
+    public static bool operator <(BitBoard left, BitBoard right) => left.Count < right.Count;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >(BitBoard left, BitBoard right)
-        => left.Count > right.Count;
+    public static bool operator >(BitBoard left, BitBoard right) => left.Count > right.Count;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >=(BitBoard left, BitBoard right)
-        => left.Count >= right.Count;
+    public static bool operator >=(BitBoard left, BitBoard right) => left.Count >= right.Count;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <=(BitBoard left, BitBoard right)
-        => left.Count <= right.Count;
+    public static bool operator <=(BitBoard left, BitBoard right) => left.Count <= right.Count;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator true(BitBoard bb)
-        => bb.Value != ulong.MinValue;
+    public static bool operator true(BitBoard bb) => bb.Value != ulong.MinValue;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator false(BitBoard bb)
-        => bb.Value == 0;
+    public static bool operator false(BitBoard bb) => bb.Value == 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !(BitBoard bb)
-        => bb.Value == 0;
+    public static bool operator !(BitBoard bb) => bb.Value == 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator bool(BitBoard b)
-        => b.Value != 0;
+    public static implicit operator bool(BitBoard b) => b.Value != 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Contains(Square sq)
-        => !(this & sq).IsEmpty;
+    public bool Contains(Square sq) => !(this & sq).IsEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square FirstOrDefault()
-        => IsEmpty ? Square.None : this.Lsb();
+    public Square FirstOrDefault() => IsEmpty ? Square.None : this.Lsb();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard Xor(int pos)
-        => new(Value ^ (uint)pos);
+    public BitBoard Xor(int pos) => new(Value ^ (uint)pos);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard And(BitBoard other)
-        => Value & other;
+    public BitBoard And(in BitBoard other) => Value & other.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard Or(BitBoard other)
-        => this | other;
+    public BitBoard Or(in BitBoard other) => this | other.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard OrAll(params BitBoard[] bbs)

From cb1765549acbc5977d7112162d63c15413ac0878 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 9 Apr 2023 21:35:00 +0200
Subject: [PATCH 030/119] Optimized start-up of KpkBitbase slightly

---
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs | 62 +++++++++++--------
 1 file changed, 36 insertions(+), 26 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index 0bae64b5..aa223a61 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -60,26 +60,25 @@ public KpkBitBase()
         // changed to either wins or draws (15 cycles needed).
         var repeat = 1;
 
-        while (repeat != 0)
+        ref var dbSpace = ref MemoryMarshal.GetReference(db);
+
+        do
         {
             repeat = 1;
-            ref var dbSpace = ref MemoryMarshal.GetReference(db);
             for (var i = 0; i < db.Length; ++i)
             {
                 var kpkPosition = Unsafe.Add(ref dbSpace, i);
-                repeat = (kpkPosition.Result == Results.Unknown && kpkPosition.Classify(db) != Results.Unknown).AsByte();
+                repeat = (kpkPosition.Result == Results.Unknown
+                          && kpkPosition.Classify(db) != Results.Unknown).AsByte();
             }
-        }
+        } while (repeat != 0);
 
         // Fill the bitbase with the decisive results
-        var idx = 0;
-        ref var setDbSpace = ref MemoryMarshal.GetReference(db);
         for (var i = 0; i < db.Length; ++i)
         {
-            var kpkPosition = Unsafe.Add(ref setDbSpace, i);
+            var kpkPosition = Unsafe.Add(ref dbSpace, i);
             if (kpkPosition.Result == Results.Win)
-                _kpKbb.Set(idx, true);
-            idx++;
+                _kpKbb.Set(i, true);
         }
     }
 
@@ -176,20 +175,8 @@ public static KpkPosition Create(int idx)
         /// <returns>Result after classification</returns>
         public Results Classify(ReadOnlySpan<KpkPosition> db)
         {
-            var (good, bad) = Stm.IsWhite
-                ? (Results.Win, Results.Draw)
-                : (Results.Draw, Results.Win);
-
-            var r = Results.None;
-            var b = PieceTypes.King.PseudoAttacks(KingSquares[Stm.Side]);
-
-            while (b)
-            {
-                var (bkSq, wkSq) = Stm.IsWhite
-                    ? (KingSquares[Player.Black.Side], BitBoards.PopLsb(ref b))
-                    : (BitBoards.PopLsb(ref b), KingSquares[Player.White.Side]);
-                r |= db[Index(~Stm, bkSq, wkSq, PawnSquare)].Result;
-            }
+            var (good, bad) = InitResults();
+            var r = GetInitialKingResults(db);
 
             if (Stm.IsWhite)
             {
@@ -212,8 +199,31 @@ public Results Classify(ReadOnlySpan<KpkPosition> db)
                 return good;
 
             return (r & Results.Unknown) != 0
-                    ? Results.Unknown
-                    : bad;
+                ? Results.Unknown
+                : bad;
+        }
+
+        private (Results, Results) InitResults()
+        {
+            return Stm.IsWhite
+                ? (Results.Win, Results.Draw)
+                : (Results.Draw, Results.Win);
+        }
+
+        private Results GetInitialKingResults(ReadOnlySpan<KpkPosition> db)
+        {
+            var r = Results.None;
+            var b = PieceTypes.King.PseudoAttacks(KingSquares[Stm.Side]);
+
+            while (b)
+            {
+                var (bkSq, wkSq) = Stm.IsWhite
+                    ? (KingSquares[Player.Black.Side], BitBoards.PopLsb(ref b))
+                    : (BitBoards.PopLsb(ref b), KingSquares[Player.White.Side]);
+                r |= db[Index(~Stm, bkSq, wkSq, PawnSquare)].Result;
+            }
+
+            return r;
         }
     }
 
@@ -240,7 +250,7 @@ public bool Probe(bool stngActive, Square skSq, Square wkSq, Square spSq)
     {
         return _kpKbb[Index(stngActive, skSq, wkSq, spSq)];
     }
-    
+
     public bool IsDraw(IPosition pos)
     {
         return !Probe(

From bea50c5d391148d9a61213c7ffd0b5dce6f64178 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 10 Apr 2023 00:13:46 +0200
Subject: [PATCH 031/119] Reorder arguments for move generator internal methods

---
 .../MoveGeneration/MoveGenerator.cs           | 22 +++++++++----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 7d49edd1..fd50f81f 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -51,7 +51,7 @@ public static int Generate(
         switch (type)
         {
             case MoveGenerationType.Legal:
-                return GenerateLegal(in pos, moves, index, us);
+                return GenerateLegal(index, in pos, moves, us);
 
             case MoveGenerationType.Captures:
             case MoveGenerationType.Quiets:
@@ -59,10 +59,10 @@ public static int Generate(
                 return GenerateCapturesQuietsNonEvasions(in pos, moves, index, us, type);
 
             case MoveGenerationType.Evasions:
-                return GenerateEvasions(in pos, moves, index, us);
+                return GenerateEvasions(index, in pos, moves, us);
 
             case MoveGenerationType.QuietChecks:
-                return GenerateQuietChecks(in pos, moves, index, us);
+                return GenerateQuietChecks(index, in pos, moves, us);
 
             default:
                 Debug.Assert(false);
@@ -78,12 +78,12 @@ private static int GenerateAll(
         Player us,
         MoveGenerationType type)
     {
-        index = GeneratePawnMoves(in pos, moves, index, in target, us, type);
+        index = GeneratePawnMoves(index, in pos, moves, in target, us, type);
 
         var checks = type == MoveGenerationType.QuietChecks;
 
         for (var pt = PieceTypes.Knight; pt <= PieceTypes.Queen; ++pt)
-            index = GenerateMoves(in pos, moves, index, us, in target, pt, checks);
+            index = GenerateMoves(index, in pos, moves, us, in target, pt, checks);
 
         if (checks || type == MoveGenerationType.Evasions)
             return index;
@@ -146,9 +146,9 @@ private static int GenerateCapturesQuietsNonEvasions(
     /// <param name="us"></param>
     /// <returns></returns>
     private static int GenerateEvasions(
+        int index,
         in IPosition pos,
         Span<ValMove> moves,
-        int index,
         Player us)
     {
         Debug.Assert(pos.InCheck);
@@ -192,13 +192,13 @@ private static int GenerateEvasions(
     /// <param name="us"></param>
     /// <returns></returns>
     private static int GenerateLegal(
+        int index,
         in IPosition pos,
         Span<ValMove> moves,
-        int index,
         Player us)
     {
         var end = pos.InCheck
-            ? GenerateEvasions(in pos, moves, index, us)
+            ? GenerateEvasions(index, in pos, moves, us)
             : Generate(in pos, moves, index, us, MoveGenerationType.NonEvasions);
 
         var pinned = pos.KingBlockers(us) & pos.Pieces(us);
@@ -229,9 +229,9 @@ private static int GenerateLegal(
     /// <param name="checks">Position is in check</param>
     /// <returns>The new move index</returns>
     private static int GenerateMoves(
+        int index,
         in IPosition pos,
         Span<ValMove> moves,
-        int index,
         Player us,
         in BitBoard target,
         PieceTypes pt,
@@ -272,9 +272,9 @@ private static int GenerateMoves(
     /// <param name="type">The type of moves to generate</param>
     /// <returns>The new move index</returns>
     private static int GeneratePawnMoves(
+        int index,
         in IPosition pos,
         Span<ValMove> moves,
-        int index,
         in BitBoard target,
         Player us,
         MoveGenerationType type)
@@ -436,9 +436,9 @@ private static int GeneratePawnMoves(
     /// <param name="us"></param>
     /// <returns></returns>
     private static int GenerateQuietChecks(
+        int index,
         in IPosition pos,
         Span<ValMove> moves,
-        int index,
         Player us)
     {
         Debug.Assert(!pos.InCheck);

From 1155f9b7cbad0ff54b22b0ae4432dd8d511defe0 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 10 Apr 2023 09:11:18 +0200
Subject: [PATCH 032/119] Cleanup + move gen types are now flags for simpler
 type detection

---
 Rudzoft.ChessLib.sln                          |  42 ------
 global.json                                   |   4 +-
 .../InBetweenBenchmark.cs                     |   1 -
 .../Rudzoft.ChessLib.Benchmark.csproj         |   3 +-
 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs |   3 +-
 .../Queries/MoveQuery.cs                      |   2 +-
 .../Services/MoveGeneratorService.cs          |   4 +-
 ...nerationType.cs => MoveGenerationTypes.cs} |  31 +++-
 .../MoveGeneration/IMoveList.cs               |   2 +-
 .../MoveGeneration/MoveFactory.cs             |   4 +-
 .../MoveGeneration/MoveGenerator.cs           | 136 +++++++++---------
 .../MoveGeneration/MoveList.cs                |   8 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |   6 +-
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |   2 +-
 14 files changed, 112 insertions(+), 136 deletions(-)
 rename src/Rudzoft.ChessLib/Enums/{MoveGenerationType.cs => MoveGenerationTypes.cs} (75%)

diff --git a/Rudzoft.ChessLib.sln b/Rudzoft.ChessLib.sln
index 68246590..e1496d68 100644
--- a/Rudzoft.ChessLib.sln
+++ b/Rudzoft.ChessLib.sln
@@ -32,91 +32,49 @@ EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
-		Debug|x64 = Debug|x64
 		Release|Any CPU = Release|Any CPU
-		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Debug|x64.ActiveCfg = Debug|x64
-		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Debug|x64.Build.0 = Debug|x64
 		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Release|Any CPU.Build.0 = Release|Any CPU
-		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Release|x64.ActiveCfg = Release|x64
-		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Release|x64.Build.0 = Release|x64
 		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Debug|x64.Build.0 = Debug|Any CPU
 		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Release|Any CPU.Build.0 = Release|Any CPU
-		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Release|x64.ActiveCfg = Release|Any CPU
-		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Release|x64.Build.0 = Release|Any CPU
 		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Debug|x64.ActiveCfg = Debug|x64
-		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Debug|x64.Build.0 = Debug|x64
 		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Release|Any CPU.Build.0 = Release|Any CPU
-		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Release|x64.ActiveCfg = Release|x64
-		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Release|x64.Build.0 = Release|x64
 		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Debug|x64.Build.0 = Debug|Any CPU
 		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Release|x64.ActiveCfg = Release|Any CPU
-		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Release|x64.Build.0 = Release|Any CPU
 		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Debug|x64.Build.0 = Debug|Any CPU
 		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Release|Any CPU.Build.0 = Release|Any CPU
-		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Release|x64.ActiveCfg = Release|Any CPU
-		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Release|x64.Build.0 = Release|Any CPU
 		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Debug|x64.ActiveCfg = Debug|x64
-		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Debug|x64.Build.0 = Debug|x64
 		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Release|Any CPU.Build.0 = Release|Any CPU
-		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Release|x64.ActiveCfg = Release|x64
-		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Release|x64.Build.0 = Release|x64
 		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Debug|x64.Build.0 = Debug|Any CPU
 		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Release|Any CPU.Build.0 = Release|Any CPU
-		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Release|x64.ActiveCfg = Release|Any CPU
-		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Release|x64.Build.0 = Release|Any CPU
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Debug|x64.Build.0 = Debug|Any CPU
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|Any CPU.Build.0 = Release|Any CPU
-		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|x64.ActiveCfg = Release|Any CPU
-		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|x64.Build.0 = Release|Any CPU
 		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|x64.Build.0 = Debug|Any CPU
 		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|Any CPU.Build.0 = Release|Any CPU
-		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|x64.ActiveCfg = Release|Any CPU
-		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|x64.Build.0 = Release|Any CPU
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|x64.Build.0 = Debug|Any CPU
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.Build.0 = Release|Any CPU
-		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|x64.ActiveCfg = Release|Any CPU
-		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|x64.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/global.json b/global.json
index 87aef9f1..aaac9e04 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
 {
   "sdk": {
-    "version": "6.0.0",
-    "rollForward": "latestMajor",
+    "version": "7.0.0",
+    "rollForward": "latestMinor",
     "allowPrerelease": false
   }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs
index a29bc9c1..98206326 100644
--- a/src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs
@@ -67,5 +67,4 @@ public void IsAsciiDigit()
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static bool InBetweenTrad(int v, int min, int max) => v >= min && v <= max;
-
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 5a045d92..74196d13 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -3,8 +3,9 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
-    <Platforms>AnyCPU;x64</Platforms>
+    <Platforms>AnyCPU</Platforms>
     <LangVersion>default</LangVersion>
+    <Configurations>Debug;Release</Configurations>
   </PropertyGroup>
 
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
index 00b2fb6d..457b04df 100644
--- a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
@@ -37,8 +37,7 @@ public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationTok
         var currentGameMoves = new List<PgnMove>();
         var inMoveSection = false;
 
-        string line;
-        while ((line = streamReader.ReadLine()) != null)
+        while (await streamReader.ReadLineAsync(cancellationToken) is { } line)
         {
             if (string.IsNullOrWhiteSpace(line))
             {
diff --git a/src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs b/src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs
index eb4456a4..06be93e9 100644
--- a/src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs
@@ -2,4 +2,4 @@
 
 namespace Rudzoft.ChessLib.WebApi.Queries;
 
-public sealed record MoveQuery(string Fen = Fen.Fen.StartPositionFen, MoveGenerationType Type = MoveGenerationType.Legal, ChessMode Mode = ChessMode.Normal);
+public sealed record MoveQuery(string Fen = Fen.Fen.StartPositionFen, MoveGenerationTypes Types = MoveGenerationTypes.Legal, ChessMode Mode = ChessMode.Normal);
diff --git a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
index bdd90600..1abeb48f 100644
--- a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
@@ -17,10 +17,10 @@ public MoveGeneratorService(ILogger<IMoveGeneratorService> logger, IPosition pos
 
     public IEnumerable<string> GenerateMoves(MoveQuery parameters)
     {
-        _logger.LogInformation("Generating moves. fen={Fen},type={Type},mode={Mode}", parameters.Fen, parameters.Type, parameters.Mode);
+        _logger.LogInformation("Generating moves. fen={Fen},type={Type},mode={Mode}", parameters.Fen, parameters.Types, parameters.Mode);
 
         var state = new State();
         _position.Set(parameters.Fen, parameters.Mode, state);
-        return _position.GenerateMoves(parameters.Type).Select(static em => em.Move.ToString());
+        return _position.GenerateMoves(parameters.Types).Select(static em => em.Move.ToString());
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Enums/MoveGenerationType.cs b/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
similarity index 75%
rename from src/Rudzoft.ChessLib/Enums/MoveGenerationType.cs
rename to src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
index baa846e9..02d8602f 100644
--- a/src/Rudzoft.ChessLib/Enums/MoveGenerationType.cs
+++ b/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
@@ -24,40 +24,57 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using System.Runtime.CompilerServices;
+
 namespace Rudzoft.ChessLib.Enums;
 
 /// <summary>
 /// Move generation flag
 /// </summary>
-public enum MoveGenerationType
+[Flags]
+public enum MoveGenerationTypes
 {
+    None = 0,
+    
     /// <summary>
     /// Generate all legal moves
     /// </summary>
-    Legal,
+    Legal = 1,
 
     /// <summary>
     /// Generate only captures
     /// </summary>
-    Captures,
+    Captures = 2,
 
     /// <summary>
     /// Generate only quiet moves (non-captures)
     /// </summary>
-    Quiets,
+    Quiets = 4,
 
     /// <summary>
     /// Generate only moves which are not evasions
     /// </summary>
-    NonEvasions,
+    NonEvasions = 8,
 
     /// <summary>
     /// Generate only evasion moves (if fx in check)
     /// </summary>
-    Evasions,
+    Evasions = 16,
 
     /// <summary>
     /// Generate only moves which are not captures and gives check
     /// </summary>
-    QuietChecks
+    QuietChecks = 32,
+    
+    All = Legal | Captures | Quiets | NonEvasions | Evasions | QuietChecks
+}
+
+public static class MoveGenerationTypesExtensions
+{
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool HasFlagFast(this MoveGenerationTypes value, MoveGenerationTypes flag)
+    {
+        return (value & flag) != 0;
+    }
 }
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
index de8cc483..0151592b 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
@@ -48,6 +48,6 @@ public interface IMoveList : IReadOnlyCollection<ValMove>
     bool Contains(in ValMove item);
     bool Contains(Move move);
     bool Contains(Square from, Square to);
-    void Generate(in IPosition pos, MoveGenerationType type = MoveGenerationType.Legal);
+    void Generate(in IPosition pos, MoveGenerationTypes types = MoveGenerationTypes.Legal);
     ReadOnlySpan<ValMove> Get();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs
index 5ad0653a..64ec4a19 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs
@@ -31,10 +31,10 @@ namespace Rudzoft.ChessLib.MoveGeneration;
 
 public static class MoveFactory
 {
-    public static MoveList GenerateMoves(this IPosition pos, MoveGenerationType type = MoveGenerationType.Legal)
+    public static MoveList GenerateMoves(this IPosition pos, MoveGenerationTypes types = MoveGenerationTypes.Legal)
     {
         var ml = new MoveList();
-        ml.Generate(pos, type);
+        ml.Generate(pos, types);
         return ml;
     }
 }
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index fd50f81f..c5916d50 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -46,27 +46,26 @@ public static int Generate(
         Span<ValMove> moves,
         int index,
         Player us,
-        MoveGenerationType type)
+        MoveGenerationTypes types)
     {
-        switch (type)
-        {
-            case MoveGenerationType.Legal:
-                return GenerateLegal(index, in pos, moves, us);
+        if (types == MoveGenerationTypes.Legal)
+            return GenerateLegal(index, in pos, moves, us);
 
-            case MoveGenerationType.Captures:
-            case MoveGenerationType.Quiets:
-            case MoveGenerationType.NonEvasions:
-                return GenerateCapturesQuietsNonEvasions(in pos, moves, index, us, type);
+        const MoveGenerationTypes capturesQuietsNonEvasions =
+            MoveGenerationTypes.Captures | MoveGenerationTypes.Quiets | MoveGenerationTypes.NonEvasions;
 
-            case MoveGenerationType.Evasions:
-                return GenerateEvasions(index, in pos, moves, us);
+        if (capturesQuietsNonEvasions.HasFlagFast(types))
+            return GenerateCapturesQuietsNonEvasions(in pos, moves, index, us, types);
 
-            case MoveGenerationType.QuietChecks:
+        switch (types)
+        {
+            case MoveGenerationTypes.Evasions:
+                return GenerateEvasions(index, in pos, moves, us);
+            case MoveGenerationTypes.QuietChecks:
                 return GenerateQuietChecks(index, in pos, moves, us);
-
             default:
                 Debug.Assert(false);
-                throw new ArgumentOutOfRangeException(nameof(type), type, null);
+                throw new ArgumentOutOfRangeException(nameof(types), types, null);
         }
     }
 
@@ -76,23 +75,23 @@ private static int GenerateAll(
         int index,
         in BitBoard target,
         Player us,
-        MoveGenerationType type)
+        MoveGenerationTypes types)
     {
-        index = GeneratePawnMoves(index, in pos, moves, in target, us, type);
+        index = GeneratePawnMoves(index, in pos, moves, in target, us, types);
 
-        var checks = type == MoveGenerationType.QuietChecks;
+        var checks = types == MoveGenerationTypes.QuietChecks;
 
         for (var pt = PieceTypes.Knight; pt <= PieceTypes.Queen; ++pt)
             index = GenerateMoves(index, in pos, moves, us, in target, pt, checks);
 
-        if (checks || type == MoveGenerationType.Evasions)
+        if (checks || types == MoveGenerationTypes.Evasions)
             return index;
 
         var ksq = pos.GetKingSquare(us);
         var b = pos.GetAttacks(ksq, PieceTypes.King) & target;
         index = Move.Create(moves, index, ksq, ref b);
 
-        if (type == MoveGenerationType.Captures || !pos.CanCastle(pos.SideToMove))
+        if (types == MoveGenerationTypes.Captures || !pos.CanCastle(pos.SideToMove))
             return index;
 
         var (kingSide, queenSide) = CastleSideRights[us.Side];
@@ -113,27 +112,27 @@ private static int GenerateAll(
     /// <param name="moves"></param>
     /// <param name="index"></param>
     /// <param name="us"></param>
-    /// <param name="type"></param>
+    /// <param name="types"></param>
     /// <returns></returns>
     private static int GenerateCapturesQuietsNonEvasions(
         in IPosition pos,
         Span<ValMove> moves,
         int index,
         Player us,
-        MoveGenerationType type)
+        MoveGenerationTypes types)
     {
-        Debug.Assert(type is MoveGenerationType.Captures or MoveGenerationType.Quiets
-            or MoveGenerationType.NonEvasions);
+        Debug.Assert(types is MoveGenerationTypes.Captures or MoveGenerationTypes.Quiets
+            or MoveGenerationTypes.NonEvasions);
         Debug.Assert(!pos.InCheck);
-        var target = type switch
+        var target = types switch
         {
-            MoveGenerationType.Captures => pos.Pieces(~us),
-            MoveGenerationType.Quiets => ~pos.Pieces(),
-            MoveGenerationType.NonEvasions => ~pos.Pieces(us),
+            MoveGenerationTypes.Captures => pos.Pieces(~us),
+            MoveGenerationTypes.Quiets => ~pos.Pieces(),
+            MoveGenerationTypes.NonEvasions => ~pos.Pieces(us),
             _ => BitBoard.Empty
         };
 
-        return GenerateAll(in pos, moves, index, in target, us, type);
+        return GenerateAll(in pos, moves, index, in target, us, types);
     }
 
     /// <summary>
@@ -180,7 +179,7 @@ private static int GenerateEvasions(
         checkSquare = pos.Checkers.Lsb();
         var target = checkSquare.BitboardBetween(ksq) | checkSquare;
 
-        return GenerateAll(in pos, moves, index, in target, us, MoveGenerationType.Evasions);
+        return GenerateAll(in pos, moves, index, in target, us, MoveGenerationTypes.Evasions);
     }
 
     /// <summary>
@@ -199,7 +198,7 @@ private static int GenerateLegal(
     {
         var end = pos.InCheck
             ? GenerateEvasions(index, in pos, moves, us)
-            : Generate(in pos, moves, index, us, MoveGenerationType.NonEvasions);
+            : Generate(in pos, moves, index, us, MoveGenerationTypes.NonEvasions);
 
         var pinned = pos.KingBlockers(us) & pos.Pieces(us);
         var ksq = pos.GetKingSquare(us);
@@ -262,14 +261,14 @@ private static int GenerateMoves(
     }
 
     /// <summary>
-    /// Generate all pawn moves. The type of moves are determined by the <see cref="MoveGenerationType"/>
+    /// Generate all pawn moves. The type of moves are determined by the <see cref="MoveGenerationTypes"/>
     /// </summary>
     /// <param name="pos">The current position</param>
     /// <param name="moves">The current moves so far</param>
     /// <param name="index">The index of the current moves</param>
     /// <param name="target">The target squares</param>
     /// <param name="us">The current player</param>
-    /// <param name="type">The type of moves to generate</param>
+    /// <param name="types">The type of moves to generate</param>
     /// <returns>The new move index</returns>
     private static int GeneratePawnMoves(
         int index,
@@ -277,7 +276,7 @@ private static int GeneratePawnMoves(
         Span<ValMove> moves,
         in BitBoard target,
         Player us,
-        MoveGenerationType type)
+        MoveGenerationTypes types)
     {
         // Compute our parametrized parameters named according to the point of view of white side.
 
@@ -286,10 +285,10 @@ private static int GeneratePawnMoves(
 
         var pawns = pos.Pieces(PieceTypes.Pawn, us);
 
-        var enemies = type switch
+        var enemies = types switch
         {
-            MoveGenerationType.Evasions => pos.Pieces(them) & target,
-            MoveGenerationType.Captures => target,
+            MoveGenerationTypes.Evasions => pos.Pieces(them) & target,
+            MoveGenerationTypes.Captures => target,
             _ => pos.Pieces(them)
         };
 
@@ -301,9 +300,11 @@ private static int GeneratePawnMoves(
         BitBoard pawnTwo;
 
         // Single and double pawn pushes, no promotions
-        if (type != MoveGenerationType.Captures)
+        if (types != MoveGenerationTypes.Captures)
         {
-            emptySquares = type is MoveGenerationType.Quiets or MoveGenerationType.QuietChecks
+            const MoveGenerationTypes quiets = MoveGenerationTypes.Quiets | MoveGenerationTypes.QuietChecks;
+
+            emptySquares = quiets.HasFlagFast(types)
                 ? target
                 : ~pos.Pieces();
 
@@ -311,15 +312,15 @@ private static int GeneratePawnMoves(
             pawnTwo = (pawnOne & us.Rank3()).Shift(up) & emptySquares;
 
             // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
-            switch (type)
+            switch (types)
             {
                 // Consider only blocking squares
-                case MoveGenerationType.Evasions:
+                case MoveGenerationTypes.Evasions:
                     pawnOne &= target;
                     pawnTwo &= target;
                     break;
 
-                case MoveGenerationType.QuietChecks:
+                case MoveGenerationTypes.QuietChecks:
                 {
                     pawnOne &= ksq.PawnAttack(them);
                     pawnTwo &= ksq.PawnAttack(them);
@@ -361,13 +362,13 @@ private static int GeneratePawnMoves(
         // Promotions and under-promotions
         if (pawnsOn7)
         {
-            switch (type)
+            switch (types)
             {
-                case MoveGenerationType.Captures:
+                case MoveGenerationTypes.Captures:
                     emptySquares = ~pos.Pieces();
                     break;
 
-                case MoveGenerationType.Evasions:
+                case MoveGenerationTypes.Evasions:
                     emptySquares &= target;
                     break;
             }
@@ -377,19 +378,20 @@ private static int GeneratePawnMoves(
             var pawnPromoUp = pawnsOn7.Shift(up) & emptySquares;
 
             while (pawnPromoRight)
-                index = MakePromotions(moves, index, BitBoards.PopLsb(ref pawnPromoRight), ksq, right, type);
+                index = MakePromotions(index, moves, BitBoards.PopLsb(ref pawnPromoRight), ksq, right, types);
 
             while (pawnPromoLeft)
-                index = MakePromotions(moves, index, BitBoards.PopLsb(ref pawnPromoLeft), ksq, left, type);
+                index = MakePromotions(index, moves, BitBoards.PopLsb(ref pawnPromoLeft), ksq, left, types);
 
             while (pawnPromoUp)
-                index = MakePromotions(moves, index, BitBoards.PopLsb(ref pawnPromoUp), ksq, up, type);
+                index = MakePromotions(index, moves, BitBoards.PopLsb(ref pawnPromoUp), ksq, up, types);
         }
 
+        const MoveGenerationTypes capturesEnPassant = MoveGenerationTypes.Captures | MoveGenerationTypes.Evasions |
+                                                      MoveGenerationTypes.NonEvasions;
+
         // Standard and en-passant captures
-        if (type != MoveGenerationType.Captures
-            && type != MoveGenerationType.Evasions
-            && type != MoveGenerationType.NonEvasions)
+        if (!types.HasFlagFast(capturesEnPassant))
             return index;
 
         pawnOne = pawnsNotOn7.Shift(right) & enemies;
@@ -415,7 +417,7 @@ private static int GeneratePawnMoves(
         // An en passant capture can be an evasion only if the checking piece is the double
         // pushed pawn and so is in the target. Otherwise this is a discovery check and we are
         // forced to do otherwise.
-        if (type == MoveGenerationType.Evasions && (target & (pos.EnPassantSquare - up)).IsEmpty)
+        if (types == MoveGenerationTypes.Evasions && (target & (pos.EnPassantSquare - up)).IsEmpty)
             return index;
 
         pawnOne = pawnsNotOn7 & pos.EnPassantSquare.PawnAttack(them);
@@ -463,7 +465,7 @@ private static int GenerateQuietChecks(
             index = Move.Create(moves, index, from, ref b);
         }
 
-        return GenerateAll(in pos, moves, index, in emptySquares, us, MoveGenerationType.QuietChecks);
+        return GenerateAll(in pos, moves, index, in emptySquares, us, MoveGenerationTypes.QuietChecks);
     }
 
     /// <summary>
@@ -474,32 +476,34 @@ private static int GenerateQuietChecks(
     /// <param name="to"></param>
     /// <param name="ksq"></param>
     /// <param name="direction"></param>
-    /// <param name="type"></param>
+    /// <param name="types"></param>
     /// <returns></returns>
     private static int MakePromotions(
-        Span<ValMove> moves,
         int index,
+        Span<ValMove> moves,
         Square to,
         Square ksq,
         Direction direction,
-        MoveGenerationType type)
+        MoveGenerationTypes types)
     {
         var from = to - direction;
 
-        switch (type)
+        const MoveGenerationTypes queenPromotion = MoveGenerationTypes.Captures
+                                                   | MoveGenerationTypes.Evasions
+                                                   | MoveGenerationTypes.NonEvasions;
+
+        if (types.HasFlagFast(queenPromotion))
         {
-            case MoveGenerationType.Captures:
-            case MoveGenerationType.Evasions:
-            case MoveGenerationType.NonEvasions:
-                moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Queen);
-                if (PieceTypes.Knight.PseudoAttacks(to) & ksq)
-                    moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion);
-                break;
+            moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Queen);
+            if (PieceTypes.Knight.PseudoAttacks(to) & ksq)
+                moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion);
         }
 
-        if (type != MoveGenerationType.Quiets
-            && type != MoveGenerationType.Evasions
-            && type != MoveGenerationType.NonEvasions)
+        const MoveGenerationTypes noneQueenPromotion = MoveGenerationTypes.Quiets
+                                                       | MoveGenerationTypes.Evasions
+                                                       | MoveGenerationTypes.NonEvasions;
+
+        if (!types.HasFlagFast(noneQueenPromotion))
             return index;
 
         moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Rook);
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index a297c6d6..7b4715ef 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -116,18 +116,18 @@ public bool Contains(Square from, Square to)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void Generate(in IPosition pos, MoveGenerationType type = MoveGenerationType.Legal)
+    public void Generate(in IPosition pos, MoveGenerationTypes types = MoveGenerationTypes.Legal)
     {
         _cur = 0;
-        Length = MoveGenerator.Generate(in pos, _moves.AsSpan(), 0, pos.SideToMove, type);
+        Length = MoveGenerator.Generate(in pos, _moves.AsSpan(), 0, pos.SideToMove, types);
         _moves[Length] = ValMove.Empty;
     }
 
     [SkipLocalsInit]
-    public static int GenerateMoveCount(in IPosition pos, MoveGenerationType type = MoveGenerationType.Legal)
+    public static int GenerateMoveCount(in IPosition pos, MoveGenerationTypes types = MoveGenerationTypes.Legal)
     {
         Span<ValMove> moves = stackalloc ValMove[218];
-        return MoveGenerator.Generate(in pos, moves, 0, pos.SideToMove, type);
+        return MoveGenerator.Generate(in pos, moves, 0, pos.SideToMove, types);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index b9512e7e..9c13a0b9 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <LangVersion>default</LangVersion>
-    <Platforms>AnyCPU;x64</Platforms>
+    <Platforms>AnyCPU</Platforms>
     <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
@@ -29,6 +29,7 @@
     <RepositoryUrl>https://github.com/rudzen/ChessLib</RepositoryUrl>
     <RepositoryType>git</RepositoryType>
     <Product>Rudzoft.ChessLib</Product>
+    <Configurations>Debug;Release</Configurations>
   </PropertyGroup>
 
   <PropertyGroup Condition="'$(IsWindows)'=='true'">
@@ -47,9 +48,6 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-  </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
     <PlatformTarget>AnyCPU</PlatformTarget>
   </PropertyGroup>
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index 1fa568a2..24eda998 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
-    <Platforms>AnyCPU;x64</Platforms>
+    <Platforms>AnyCPU</Platforms>
     <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
     <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
     <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>

From 742f7b3b6e7f7f3e46c577e345b27212ae89a35c Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 10 Apr 2023 17:41:39 +0200
Subject: [PATCH 033/119] Enabled tiered pgo as default

---
 .../Rudzoft.ChessLib.Benchmark.csproj           |  1 +
 .../Rudzoft.ChessLib.PGN.Test.csproj            |  1 +
 .../Rudzoft.ChessLib.PGN.csproj                 |  1 +
 .../Rudzoft.ChessLib.Perft.Interfaces.csproj    |  1 +
 .../Rudzoft.ChessLib.Perft.csproj               |  1 +
 .../Rudzoft.ChessLib.Test.csproj                |  1 +
 .../Rudzoft.ChessLib.WebApi.csproj              |  1 +
 .../MoveGeneration/IMoveList.cs                 |  1 +
 src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs | 17 +++++++++++++++++
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj    |  1 +
 .../Rudzoft.Perft.Framework.csproj              |  1 +
 src/Rudzoft.Perft/Rudzoft.Perft.csproj          |  1 +
 12 files changed, 28 insertions(+)

diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 74196d13..de03e915 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -3,6 +3,7 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
+    <TieredPGO>true</TieredPGO>
     <Platforms>AnyCPU</Platforms>
     <LangVersion>default</LangVersion>
     <Configurations>Debug;Release</Configurations>
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index 4d4c15c0..03fccd68 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -2,6 +2,7 @@
 
     <PropertyGroup>
         <TargetFramework>net7.0</TargetFramework>
+        <TieredPGO>true</TieredPGO>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
 
diff --git a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
index 60f11f07..5a91149e 100644
--- a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
+++ b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
@@ -2,6 +2,7 @@
 
     <PropertyGroup>
         <TargetFramework>net7.0</TargetFramework>
+        <TieredPGO>true</TieredPGO>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
     </PropertyGroup>
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
index b866c2d3..9ed3c4f8 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
@@ -2,6 +2,7 @@
 
   <PropertyGroup>
     <TargetFramework>net7.0</TargetFramework>
+    <TieredPGO>true</TieredPGO>
     <LangVersion>default</LangVersion>
   </PropertyGroup>
 
diff --git a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
index 9820e964..88bbf50b 100644
--- a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
+++ b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
@@ -2,6 +2,7 @@
 
   <PropertyGroup>
     <TargetFramework>net7.0</TargetFramework>
+    <TieredPGO>true</TieredPGO>
     <LangVersion>default</LangVersion>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <Nullable>enable</Nullable>
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index b3753ce1..2d1ec991 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -2,6 +2,7 @@
 
   <PropertyGroup>
     <TargetFramework>net7.0</TargetFramework>
+    <TieredPGO>true</TieredPGO>
     <IsPackable>false</IsPackable>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <LangVersion>default</LangVersion>
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index fce18996..5fc9662e 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -2,6 +2,7 @@
 
     <PropertyGroup>
         <TargetFramework>net7.0</TargetFramework>
+        <TieredPGO>true</TieredPGO>
         <Nullable>enable</Nullable>
         <ImplicitUsings>enable</ImplicitUsings>
         <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
index 0151592b..c5ad0c3d 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
@@ -48,6 +48,7 @@ public interface IMoveList : IReadOnlyCollection<ValMove>
     bool Contains(in ValMove item);
     bool Contains(Move move);
     bool Contains(Square from, Square to);
+    bool ContainsType(MoveTypes type);
     void Generate(in IPosition pos, MoveGenerationTypes types = MoveGenerationTypes.Legal);
     ReadOnlySpan<ValMove> Get();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index 7b4715ef..fdc45908 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -115,6 +115,23 @@ public bool Contains(Square from, Square to)
         return false;
     }
 
+    public bool ContainsType(MoveTypes type)
+    {
+        var moveSpan = _moves.AsSpan()[..Length];
+        if (moveSpan.IsEmpty)
+            return false;
+
+        ref var movesSpace = ref MemoryMarshal.GetReference(moveSpan);
+        for (var i = 0; i < moveSpan.Length; ++i)
+        {
+            var m = Unsafe.Add(ref movesSpace, i).Move;
+            if (m.MoveType() == type)
+                return true;
+        }
+
+        return false;
+    }
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Generate(in IPosition pos, MoveGenerationTypes types = MoveGenerationTypes.Legal)
     {
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 9c13a0b9..12d10598 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -8,6 +8,7 @@
     <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
     <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
     <TargetFramework>net7.0</TargetFramework>
+    <TieredPGO>true</TieredPGO>
     <PackageVersion>0.0.4</PackageVersion>
     <Authors>Rudy Alex Kohn</Authors>
     <Company>None</Company>
diff --git a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
index acd99a98..24047c6a 100644
--- a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
+++ b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
@@ -2,6 +2,7 @@
 
   <PropertyGroup>
     <TargetFramework>net7.0</TargetFramework>
+    <TieredPGO>true</TieredPGO>
     <RootNamespace>Perft</RootNamespace>
     <LangVersion>default</LangVersion>
   </PropertyGroup>
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index 24eda998..8450eeeb 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -3,6 +3,7 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
+    <TieredPGO>true</TieredPGO>
     <Platforms>AnyCPU</Platforms>
     <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
     <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>

From 7d019275c5eab7fd0bd3985ba383d09c81e293e9 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Wed, 12 Apr 2023 19:44:26 +0200
Subject: [PATCH 034/119] Updated various functionalities

- Blockage
- Position Validation
---
 .../IterateBenchmark.cs                       |  10 +-
 .../Rudzoft.ChessLib.Benchmark.csproj         |   1 +
 .../Rudzoft.ChessLib.PGN.csproj               |   1 +
 .../DataTests/DataTests.cs                    |   2 +
 .../PositionTests/ValidationTests.cs          |  20 +--
 src/Rudzoft.ChessLib/Blockage.cs              | 116 ++++++------
 .../Extensions/SpanExtensions.cs              |   3 +
 src/Rudzoft.ChessLib/IPosition.cs             |   2 +-
 src/Rudzoft.ChessLib/Position.cs              |   8 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |   2 -
 src/Rudzoft.ChessLib/Types/Piece.cs           |  10 ++
 .../Types/PieceSquareEventArgs.cs             |   1 +
 .../Validation/IPositionValidator.cs          |   4 +-
 .../Validation/PositionValidationResult.cs    |   3 +
 .../Validation/PositionValidator.cs           | 168 ++++++------------
 15 files changed, 161 insertions(+), 190 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib/Validation/PositionValidationResult.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
index 8da1f5a6..3e1475d6 100644
--- a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
@@ -6,8 +7,7 @@ namespace Rudzoft.ChessLib.Benchmark;
 [MemoryDiagnoser]
 public class IterateBenchmark
 {
-    [Params(10000, 50000)]
-    public int N;
+    [Params(10000, 50000)] public int N;
 
     [Benchmark(Description = "Stackalloc")]
     public void IterateOne()
@@ -27,8 +27,10 @@ public void IterateTwo()
     {
         var res = 0;
         for (var i = 0; i < N; ++i)
-        for (var player = Players.White; player < Players.PlayerNb; ++player)
-            res += (int)player;
+        {
+            for (var player = Players.White; player < Players.PlayerNb; ++player)
+                res += (int)player;
+        }
 
         N = res;
     }
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index de03e915..70dc2bf3 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -7,6 +7,7 @@
     <Platforms>AnyCPU</Platforms>
     <LangVersion>default</LangVersion>
     <Configurations>Debug;Release</Configurations>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
diff --git a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
index 5a91149e..33d1f6d9 100644
--- a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
+++ b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
@@ -5,6 +5,7 @@
         <TieredPGO>true</TieredPGO>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
+        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     </PropertyGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs b/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
index afe2a247..1146a632 100644
--- a/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
+++ b/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
@@ -25,12 +25,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.DataTests;
 
 public sealed class DataTests
 {
+    [SkipLocalsInit]
     [Fact]
     public void SquareChars()
     {
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index acfa38d6..f59efa61 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -28,7 +28,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
@@ -61,7 +60,7 @@ public ValidationTests()
     public void ValidationKingsNegative()
     {
         const PositionValidationTypes type = PositionValidationTypes.Kings;
-        var expectedErrorMsg = $"king count for player {Player.White} was 2";
+        var expectedErrorMsg = $"king count for player {Players.White} was 2";
 
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
@@ -74,15 +73,12 @@ public void ValidationKingsNegative()
 
         pos.AddPiece(pc, Square.E4);
 
-        var validator = pos.Validate(type);
-
-        Assert.NotNull(validator.ErrorMsg);
-        Assert.NotEmpty(validator.ErrorMsg);
-
-        var actualErrorMessage = validator.ErrorMsg;
+        var (ok, actualErrorMessage) = pos.Validate(type);
 
+        Assert.NotNull(actualErrorMessage);
+        Assert.NotEmpty(actualErrorMessage);
         Assert.Equal(expectedErrorMsg, actualErrorMessage);
-        Assert.False(validator.IsOk);
+        Assert.False(ok);
     }
 
     [Fact]
@@ -101,9 +97,9 @@ public void ValidateCastleling()
 
         var validator = pos.Validate(validationType);
 
-        Assert.True(validator.IsOk);
-        Assert.NotNull(validator.ErrorMsg);
-        Assert.Empty(validator.ErrorMsg);
+        Assert.True(validator.Ok);
+        Assert.NotNull(validator.Errors);
+        Assert.Empty(validator.Errors);
     }
 
     // TODO : Add tests for the rest of the validations
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 4c9a548c..1225ffd1 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -40,10 +40,13 @@ namespace Rudzoft.ChessLib;
 /// </summary>
 public sealed class Blockage : IBlockage
 {
+    private record struct MarkedPawns(BitBoard Fixed, BitBoard Marked, BitBoard Dynamic);
+
     private static readonly BitBoard PawnFileASquares =
         File.FileA.FileBB() & ~(Rank.Rank1.RankBB() | Rank.Rank8.RankBB());
 
     /// <inheritdoc />
+    [SkipLocalsInit]
     public bool IsBlocked(in IPosition pos)
     {
         // Quick check if there is only pawns and kings on the board
@@ -51,9 +54,6 @@ public bool IsBlocked(in IPosition pos)
         if (pos.PieceCount(PieceTypes.AllPieces) > pos.PieceCount(PieceTypes.Pawn) + 2)
             return false;
 
-        // Contains the rank for each file which has a fence
-        Span<Rank> fenceRank = stackalloc Rank[File.Count];
-
         var us = pos.SideToMove;
         var them = ~us;
 
@@ -63,21 +63,20 @@ public bool IsBlocked(in IPosition pos)
 
         var up = us.PawnPushDistance();
 
-        var fixedPawn = BitBoards.EmptyBitBoard;
-        var marked = BitBoards.EmptyBitBoard;
-        var dynamicPawns = BitBoards.EmptyBitBoard;
         var processed = BitBoards.EmptyBitBoard;
 
         var fence = BitBoards.EmptyBitBoard;
 
-        MarkOurPawns(in pos, ref fixedPawn, ref marked, ref dynamicPawns);
-        MarkTheirPawns(ref marked, in theirPawns, us);
+        var (fixedPawns, markedPawns, dynamicPawns) = MarkPawns(in pos, in theirPawns);
 
-        var isFenceFormed = FormFence(in marked, ref fence, ref processed, us);
+        var isFenceFormed = FormFence(in markedPawns, ref fence, ref processed, us);
 
         if (!isFenceFormed)
             return false;
 
+        // Contains the rank for each file which has a fence
+        Span<Rank> fenceRank = stackalloc Rank[File.Count];
+
         ComputeFenceRanks(fenceRank, in fence);
 
         var ourKsq = pos.GetKingSquare(us);
@@ -86,6 +85,7 @@ public bool IsBlocked(in IPosition pos)
             return false;
 
         var theirKsq = pos.GetKingSquare(them);
+
         dynamicPawns |= ComputeDynamicFencedPawns(in pos, fenceRank, in theirPawns, us);
 
         while (dynamicPawns)
@@ -110,20 +110,9 @@ public bool IsBlocked(in IPosition pos)
                     if (theirKsq.File != f || theirKsq.RelativeRank(us) < rr)
                         return false;
 
-                    if (f != File.FileA)
-                    {
-                        if (pos.GetPiece(sq + Direction.West) != ourPawn)
-                            return false;
-
-                        if (BitBoards.PopCount(ourPawns & PreviousFile(f)) > 1)
-                            return false;
-
-                        if ((fixedPawn & (sq + Direction.West)).IsEmpty)
-                            return false;
-
-                        if ((fence & (sq + Direction.West)).IsEmpty)
-                            return false;
-                    }
+                    if (f != File.FileA
+                        && !LowerRankFenceNonFileANotBlocked(pos, sq, ourPawn, in ourPawns, f, in fixedPawns, in fence))
+                        return false;
 
                     if (f != File.FileH)
                     {
@@ -133,7 +122,7 @@ public bool IsBlocked(in IPosition pos)
                         if (BitBoards.PopCount(ourPawns & NextFile(f)) > 1)
                             return false;
 
-                        if ((fixedPawn & (sq + Direction.East)).IsEmpty)
+                        if ((fixedPawns & (sq + Direction.East)).IsEmpty)
                             return false;
 
                         if ((fence & (sq + Direction.East)).IsEmpty)
@@ -177,20 +166,10 @@ public bool IsBlocked(in IPosition pos)
                     if (theirKsq.File != f || theirKsq.RelativeRank(us) < rr)
                         return false;
 
-                    if (f != File.FileA)
-                    {
-                        if (pos.GetPiece(sq + Direction.West) != ourPawn)
-                            return false;
-
-                        if (BitBoards.PopCount(ourPawns & (f - 1)) > 1)
-                            return false;
-
-                        if ((fixedPawn & Square.Create(r, PreviousFile(f))).IsEmpty)
-                            return false;
-
-                        if ((fence & Square.Create(r, PreviousFile(f))).IsEmpty)
-                            return false;
-                    }
+                    if (f != File.FileA
+                        && !LowerRankNotFileABelowFenceRankNotBlocked(
+                            in pos, sq, ourPawn, in ourPawns, f, in fixedPawns, r, in fence))
+                        return false;
 
                     if (f != File.FileH)
                     {
@@ -200,7 +179,7 @@ public bool IsBlocked(in IPosition pos)
                         if (BitBoards.PopCount(ourPawns & (f + 1)) > 1)
                             return false;
 
-                        if ((fixedPawn & Square.Create(r, NextFile(f))).IsEmpty)
+                        if ((fixedPawns & Square.Create(r, NextFile(f))).IsEmpty)
                             return false;
 
                         if ((fence & Square.Create(r, NextFile(f))).IsEmpty)
@@ -216,10 +195,41 @@ public bool IsBlocked(in IPosition pos)
         return true;
     }
 
+    private static bool LowerRankNotFileABelowFenceRankNotBlocked(
+        in IPosition pos,
+        Square sq,
+        Piece ourPawn,
+        in BitBoard ourPawns,
+        File f,
+        in BitBoard fixedPawns,
+        Rank r,
+        in BitBoard fence)
+    {
+        return pos.GetPiece(sq + Direction.West) == ourPawn
+               && BitBoards.PopCount(ourPawns & (f - 1)) <= 1
+               && !(fixedPawns & Square.Create(r, PreviousFile(f))).IsEmpty
+               && !(fence & Square.Create(r, PreviousFile(f))).IsEmpty;
+    }
+
+    private static bool LowerRankFenceNonFileANotBlocked(
+        in IPosition pos,
+        Square sq,
+        Piece ourPawn,
+        in BitBoard ourPawns,
+        File f,
+        in BitBoard fixedPawns,
+        in BitBoard fence)
+    {
+        return pos.GetPiece(sq + Direction.West) == ourPawn
+               && BitBoards.PopCount(ourPawns & PreviousFile(f)) <= 1
+               && !(fixedPawns & (sq + Direction.West)).IsEmpty
+               && !(fence & (sq + Direction.West)).IsEmpty;
+    }
+
     /// <summary>
     /// Computes the fence ranks
     /// </summary>
-    private void ComputeFenceRanks(Span<Rank> fenceRank, in BitBoard fence)
+    private static void ComputeFenceRanks(Span<Rank> fenceRank, in BitBoard fence)
     {
         var covered = fence;
 
@@ -234,17 +244,16 @@ private void ComputeFenceRanks(Span<Rank> fenceRank, in BitBoard fence)
     /// <summary>
     /// Marks the current players pawns as either fixed and marked or dynamic
     /// </summary>
-    private static void MarkOurPawns(
-        in IPosition pos,
-        ref BitBoard fixedPawn,
-        ref BitBoard marked,
-        ref BitBoard dynamicPawns)
+    private static MarkedPawns MarkPawns(in IPosition pos, in BitBoard theirPawns)
     {
+        var fixedPawns = BitBoards.EmptyBitBoard;
+        var marked = BitBoards.EmptyBitBoard;
+        var dynamicPawns = BitBoards.EmptyBitBoard;
+
         var us = pos.SideToMove;
         var them = ~us;
         var up = us.PawnPushDistance();
         var ourPawns = pos.Pieces(PieceTypes.Pawn, us);
-        var theirPawns = pos.Pieces(PieceTypes.Pawn, them);
         var theirPawn = PieceTypes.Pawn.MakePiece(them);
 
         while (ourPawns)
@@ -252,27 +261,23 @@ private static void MarkOurPawns(
             var psq = BitBoards.PopLsb(ref ourPawns);
             var rr = psq.RelativeRank(us);
             if (rr < Rank.Rank7
-                && (pos.GetPiece(psq + up) == theirPawn || !(fixedPawn & (psq + up)).IsEmpty)
+                && (pos.GetPiece(psq + up) == theirPawn || !(fixedPawns & (psq + up)).IsEmpty)
                 && (psq.PawnAttack(us) & theirPawns).IsEmpty)
             {
-                fixedPawn |= psq;
+                fixedPawns |= psq;
                 marked |= psq;
             }
             else
                 dynamicPawns |= psq;
         }
-    }
 
-    /// <summary>
-    /// Marks the opponent pawn attacks
-    /// </summary>
-    private static void MarkTheirPawns(ref BitBoard marked, in BitBoard theirPawns, Player us)
-    {
         var (southEast, southWest) = us.IsWhite
             ? (Direction.SouthEast, Direction.SouthWest)
             : (Direction.NorthEast, Direction.NorthWest);
 
         marked |= theirPawns.Shift(southEast) | theirPawns.Shift(southWest);
+
+        return new(fixedPawns, marked, dynamicPawns);
     }
 
     /// <summary>
@@ -325,7 +330,8 @@ private static bool FormFence(in BitBoard marked, ref BitBoard fence, ref BitBoa
         while (bb)
         {
             var startSquare = BitBoards.PopLsb(ref bb);
-            if ((marked & startSquare).IsEmpty || !FormsFence(startSquare, ref processed, ref fence, in marked, us))
+            if ((marked & startSquare).IsEmpty ||
+                !FormsFence(startSquare, ref processed, ref fence, in marked, us))
                 continue;
             fence |= startSquare;
             return true;
diff --git a/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs b/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
index fe8610a8..9a7c7a16 100644
--- a/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Extensions;
 
@@ -38,6 +39,7 @@ public static class SpanExtensions
     /// <param name="targetIndex">The current index for target span</param>
     /// <param name="capacity">Max size of "value".ToString(), defaults to 3</param>
     /// <returns>New index after append</returns>
+    [SkipLocalsInit]
     public static int Append(this Span<char> target, int v, int targetIndex, int capacity = 3)
     {
         Span<char> s = stackalloc char[capacity];
@@ -55,6 +57,7 @@ public static int Append(this Span<char> target, int v, int targetIndex, int cap
     /// <param name="targetIndex">The current index for target span</param>
     /// <param name="capacity">Max size of "value".ToString(), defaults to 20</param>
     /// <returns>New index after append</returns>
+    [SkipLocalsInit]
     public static int Append(this Span<char> target, in ulong v, int targetIndex, int capacity = 20)
     {
         Span<char> s = stackalloc char[capacity];
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index 62b864e6..a0987bf0 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -218,5 +218,5 @@ public interface IPosition : IEnumerable<Piece>
     
     Value NonPawnMaterial();
     
-    IPositionValidator Validate(PositionValidationTypes type = PositionValidationTypes.Basic);
+    PositionValidationResult Validate(PositionValidationTypes type = PositionValidationTypes.Basic);
 }
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index a1f083d1..89cfdbb7 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -871,7 +871,7 @@ public void MakeNullMove(in State newState)
 
         State.Repetition = 0;
 
-        Debug.Assert(_positionValidator.Validate(this, PositionValidationTypes.Basic).IsOk);
+        Debug.Assert(_positionValidator.Validate(this, PositionValidationTypes.Basic).Ok);
     }
 
     public Piece MovedPiece(Move m)
@@ -1324,8 +1324,8 @@ public void TakeMove(Move m)
         Ply--;
 
 #if DEBUG
-        var validator = _positionValidator.Validate(this);
-        Debug.Assert(validator.IsOk);
+        var validatorResult = _positionValidator.Validate(this);
+        Debug.Assert(validatorResult.Ok);
 #endif
     }
 
@@ -1382,7 +1382,7 @@ public override string ToString()
         return result;
     }
 
-    public IPositionValidator Validate(PositionValidationTypes type = PositionValidationTypes.Basic)
+    public PositionValidationResult Validate(PositionValidationTypes type = PositionValidationTypes.Basic)
         => _positionValidator.Validate(this, type);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 12d10598..53707d7f 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -62,9 +62,7 @@
     <PackageReference Include="ZString" Version="2.5.0" />
   </ItemGroup>
   <ItemGroup>
-    <Folder Include="Pgn" />
     <Folder Include="Protocol\" />
-    <Folder Include="Validation\" />
   </ItemGroup>
   <ItemGroup>
     <None Include="..\..\README.md">
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index d15cf2c9..6ce303d0 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -125,6 +125,16 @@ private Piece(Piece pc)
         Pieces.BlackQueen,
         Pieces.BlackKing
     };
+    
+    public static PieceTypes[] AllPieceTypes { get; } =
+    {
+        PieceTypes.Pawn,
+        PieceTypes.Knight,
+        PieceTypes.Bishop,
+        PieceTypes.Rook,
+        PieceTypes.Queen,
+        PieceTypes.King
+    };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Piece(char value)
diff --git a/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs b/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
index 2f34eb0f..c02c7b65 100644
--- a/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
+++ b/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
@@ -74,6 +74,7 @@ public override int GetHashCode()
     public string ToString(string format, IFormatProvider formatProvider)
         => string.Format(formatProvider, format, Piece + Square.ToString());
 
+    [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null)
     {
diff --git a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
index b3d23e1a..b4cc8662 100644
--- a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
@@ -28,7 +28,5 @@ namespace Rudzoft.ChessLib.Validation;
 
 public interface IPositionValidator
 {
-    string ErrorMsg { get; }
-    bool IsOk { get; }
-    IPositionValidator Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All);
+    PositionValidationResult Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All);
 }
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidationResult.cs b/src/Rudzoft.ChessLib/Validation/PositionValidationResult.cs
new file mode 100644
index 00000000..200bf0c7
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidationResult.cs
@@ -0,0 +1,3 @@
+namespace Rudzoft.ChessLib.Validation;
+
+public sealed record PositionValidationResult(bool Ok, string Errors);
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index 47e8a80e..d9dbd82c 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -25,8 +25,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Collections.Generic;
 using System.Linq;
-using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Types;
 
@@ -55,71 +55,63 @@ public static bool HasFlagFast(this PositionValidationTypes @this, PositionValid
 
 public sealed class PositionValidator : IPositionValidator
 {
-    public string ErrorMsg { get; private set; }
-    public bool IsOk { get; private set; }
-
-    public IPositionValidator Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All)
+    public PositionValidationResult Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All)
     {
-        var error = string.Empty;
+        var errors = new List<string>();
 
         if (type.HasFlagFast(PositionValidationTypes.Basic))
-            error = ValidateBasic(in pos);
+            errors.AddRange(ValidateBasic(pos));
 
         if (type.HasFlagFast(PositionValidationTypes.Castle))
-            error = ValidateCastleling(in pos, error);
+            errors.AddRange(ValidateCastleling(pos));
 
         if (type.HasFlagFast(PositionValidationTypes.Kings))
-            error = ValidateKings(in pos, error);
+            errors.AddRange(ValidateKings(pos));
 
         if (type.HasFlagFast(PositionValidationTypes.Pawns))
-            error = ValidatePawns(in pos, error);
+            errors.AddRange(ValidatePawns(pos));
 
         if (type.HasFlagFast(PositionValidationTypes.PieceConsistency))
-            error = ValidatePieceConsistency(in pos, error);
+            errors.AddRange(ValidatePieceConsistency(pos));
 
         if (type.HasFlagFast(PositionValidationTypes.PieceCount))
-            error = ValidatePieceCount(pos, error);
+            errors.AddRange(ValidatePieceCount(pos));
 
         if (type.HasFlagFast(PositionValidationTypes.PieceTypes))
-            error = ValidatePieceTypes(in pos, error);
+            errors.AddRange(ValidatePieceTypes(pos));
 
         if (type.HasFlagFast(PositionValidationTypes.State))
-            error = ValidateState(in pos, error);
+            errors.AddRange(ValidateState(pos));
 
-        IsOk = error.IsNullOrEmpty();
-        ErrorMsg = error;
-        return this;
+        var ok = errors.Count == 0;
+        return new(ok, ok ? string.Empty : string.Join('\n', errors));
     }
 
-    private static string ValidateBasic(in IPosition pos)
+    private static IEnumerable<string> ValidateBasic(IPosition pos)
     {
-        var error = string.Empty;
         if (pos.SideToMove != Player.White && pos.SideToMove != Player.Black)
-            error = AddError(error, $"{nameof(pos.SideToMove)} is not a valid");
+            yield return $"{nameof(pos.SideToMove)} is not a valid";
 
         if (pos.GetPiece(pos.GetKingSquare(Player.White)) != Piece.WhiteKing)
-            error = AddError(error, "white king position is not a white king");
+            yield return "white king position is not a white king";
 
         if (pos.GetPiece(pos.GetKingSquare(Player.Black)) != Piece.BlackKing)
-            error = AddError(error, "black king position is not a black king");
+            yield return "black king position is not a black king";
 
         if (pos.EnPassantSquare != Square.None && pos.EnPassantSquare.RelativeRank(pos.SideToMove) != Ranks.Rank6)
-            error = AddError(error, $"{nameof(pos.EnPassantSquare)} square is not on rank 6");
-
-        return error;
+            yield return $"{nameof(pos.EnPassantSquare)} square is not on relative rank 6";
     }
 
-    private string ValidateCastleling(in IPosition pos, string error)
+    private IEnumerable<string> ValidateCastleling(IPosition pos)
     {
-        Span<Player> players = stackalloc Player[] { Player.White, Player.Black };
-        Span<CastleRight> crs = stackalloc CastleRight[] { CastleRight.None, CastleRight.None };
+        var crs = new[] { CastleRight.None, CastleRight.None };
 
-        foreach (var c in players)
+        foreach (var p in Player.AllPlayers)
         {
-            crs[0] = CastleRights.King.MakeCastleRights(c);
-            crs[1] = CastleRights.Queen.MakeCastleRights(c);
+            crs[0] = CastleRights.King.MakeCastleRights(p);
+            crs[1] = CastleRights.Queen.MakeCastleRights(p);
 
-            var ourRook = PieceTypes.Rook.MakePiece(c);
+            var ourRook = PieceTypes.Rook.MakePiece(p);
             foreach (var cr in crs)
             {
                 if (!pos.CanCastle(cr))
@@ -128,126 +120,84 @@ private string ValidateCastleling(in IPosition pos, string error)
                 var rookSq = pos.CastlingRookSquare(cr);
 
                 if (pos.GetPiece(rookSq) != ourRook)
-                    error = AddError(error, $"rook does not appear on its position for {c}");
+                    yield return $"rook does not appear on its position for {p}";
 
                 if (pos.GetCastleRightsMask(rookSq) != cr)
-                    error = AddError(error, $"castleling rights mask at {rookSq} does not match for player {c}");
+                    yield return $"castleling rights mask at {rookSq} does not match for player {p}";
 
-                if ((pos.GetCastleRightsMask(pos.GetKingSquare(c)) & cr) != cr)
-                    error = AddError(error,
-                        $"castleling rights mask at {pos.GetKingSquare(c)} does not match for player {c}");
+                if ((pos.GetCastleRightsMask(pos.GetKingSquare(p)) & cr) != cr)
+                    yield return $"castleling rights mask at {pos.GetKingSquare(p)} does not match for player {p}";
             }
         }
-
-        return error;
     }
 
-    private static string ValidateKings(in IPosition pos, string error)
+    private static IEnumerable<string> ValidateKings(IPosition pos)
     {
-        Span<Player> players = stackalloc Player[] { Player.White, Player.Black };
-
-        foreach (var player in players)
+        foreach (var player in Player.AllPlayers)
         {
             var count = pos.PieceCount(PieceTypes.King, player);
             if (count != 1)
-                error = AddError(error, $"king count for player {player} was {count}");
+                yield return $"king count for player {player} was {count}";
         }
 
-        if (!(pos.AttacksTo(pos.GetKingSquare(~pos.SideToMove)) & pos.Pieces(pos.SideToMove)).IsEmpty)
-            error = AddError(error, "kings appear to attack each other");
-
-        return error;
+        if ((pos.AttacksTo(pos.GetKingSquare(~pos.SideToMove)) & pos.Pieces(pos.SideToMove)).IsNotEmpty)
+            yield return "kings appear to attack each other";
     }
 
-    private static string ValidatePawns(in IPosition pos, string error)
+    private static IEnumerable<string> ValidatePawns(IPosition pos)
     {
-        if (!(pos.Pieces(PieceTypes.Pawn) & (Rank.Rank1.RankBB() | Rank.Rank8.RankBB())).IsEmpty)
-            error = AddError(error, "pawns exists on rank 1 or rank 8");
+        if ((pos.Pieces(PieceTypes.Pawn) & (Rank.Rank1.RankBB() | Rank.Rank8.RankBB())).IsNotEmpty)
+            yield return "pawns exists on rank 1 or rank 8";
 
         if (pos.PieceCount(PieceTypes.Pawn, Player.White) > 8)
-            error = AddError(error, "white side has more than 8 pawns");
+            yield return "white side has more than 8 pawns";
 
         if (pos.PieceCount(PieceTypes.Pawn, Player.Black) > 8)
-            error = AddError(error, "black side has more than 8 pawns");
-
-        return error;
+            yield return "black side has more than 8 pawns";
     }
 
-    private static string ValidatePieceConsistency(in IPosition pos, string error)
+    private static IEnumerable<string> ValidatePieceConsistency(IPosition pos)
     {
-        if (!(pos.Pieces(Player.White) & pos.Pieces(Player.Black)).IsEmpty)
-            error = AddError(error, "white and black pieces overlap");
+        if ((pos.Pieces(Player.White) & pos.Pieces(Player.Black)).IsNotEmpty)
+            yield return "white and black pieces overlap";
 
         if ((pos.Pieces(Player.White) | pos.Pieces(Player.Black)) != pos.Pieces())
-            error = AddError(error, "white and black pieces do not match all pieces");
+            yield return "white and black pieces do not match all pieces";
 
         if (pos.Pieces(Player.White).Count > 16)
-            error = AddError(error, "white side has more than 16 pieces");
+            yield return "white side has more than 16 pieces";
 
         if (pos.Pieces(Player.Black).Count > 16)
-            error = AddError(error, "black side has more than 16 pieces");
-
-        return error;
+            yield return "black side has more than 16 pieces";
     }
 
-    private static string ValidatePieceCount(IPosition pos, string error) =>
-        Piece.AllPieces
-            .Select(static pc => new { pc, pt = pc.Type() })
-            .Select(static t => new { t, c = t.pc.ColorOf() })
-            .Where(t => pos.PieceCount(t.t.pt, t.c) != pos.Pieces(t.t.pt, t.c).Count)
-            .Select(static t => t.t.pc)
-            .Aggregate(error, static (current, pc) => AddError(current, $"piece count does not match for piece {pc}"));
+    private static IEnumerable<string> ValidatePieceCount(IPosition pos)
+        => Piece.AllPieces
+            .Where(pc => pos.PieceCount(pc) != pos.Pieces(pc).Count)
+            .Select(static pc => $"piece count does not match for piece {pc}");
 
-    private static string ValidatePieceTypes(in IPosition pos, string error)
-    {
-        Span<PieceTypes> pts = stackalloc PieceTypes[]
-        {
-            PieceTypes.Pawn,
-            PieceTypes.Knight,
-            PieceTypes.Bishop,
-            PieceTypes.Rook,
-            PieceTypes.Queen,
-            PieceTypes.King
-        };
-
-        foreach (var p1 in pts)
-        {
-            foreach (var p2 in pts)
-            {
-                if (p1 == p2 || (pos.Pieces(p1) & pos.Pieces(p2)).IsEmpty)
-                    continue;
-
-                error = AddError(error, $"piece types {p1} and {p2} doesn't align");
-            }
-        }
+    private static IEnumerable<string> ValidatePieceTypes(IPosition pos)
+        => Piece.AllPieceTypes.SelectMany(p1 => Piece.AllPieceTypes
+                .Where(p2 => p1 != p2 && !(pos.Pieces(p1) & pos.Pieces(p2)).IsEmpty),
+            static (p1, p2) => $"piece types {p1} and {p2} doesn't align");
 
-        return error;
-    }
-
-    private string ValidateState(in IPosition pos, string error)
+    private static IEnumerable<string> ValidateState(IPosition pos)
     {
         var state = pos.State;
 
         if (state.Key.Key == 0 && !pos.Pieces().IsEmpty)
-            error = AddError(error, "state key is invalid");
+            yield return "state key is invalid";
 
-        if (pos.PieceCount(PieceTypes.Pawn) == 0 && state.PawnKey.Key != Zobrist.ZobristNoPawn)
-            error = AddError(error, "empty pawn key is invalid");
+        if (pos.PieceCount(PieceTypes.Pawn) == 0 && state.PawnKey != Zobrist.ZobristNoPawn)
+            yield return "empty pawn key is invalid";
 
         if (state.Repetition < 0)
-            error = AddError(error, $"{nameof(state.Repetition)} is negative");
+            yield return $"{nameof(state.Repetition)} is negative";
 
         if (state.Rule50 < 0)
-            error = AddError(error, $"{nameof(state.Rule50)} is negative");
+            yield return $"{nameof(state.Rule50)} is negative";
 
         if (state.Equals(state.Previous))
-            error = AddError(error, "state has itself as previous state");
-
-        return error;
+            yield return "state has itself as previous state";
     }
-
-    private static string AddError(string error, string message)
-        => error.IsNullOrEmpty()
-            ? message
-            : $"{error}, {message}";
 }
\ No newline at end of file

From 4916c68d91a70cab4cd0bf5b9dfc8446560b702f Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Wed, 12 Apr 2023 21:49:31 +0200
Subject: [PATCH 035/119] Better use of BitBoard.IsNotEmpty etc

---
 .../PiecesTests/PieceAttacksKingTests.cs      |   2 +-
 .../PiecesTests/PieceAttacksKnightTests.cs    |   6 +-
 .../PositionTests/ValidationTests.cs          |   2 +-
 src/Rudzoft.ChessLib/Blockage.cs              |  12 +-
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |   2 +-
 .../MoveGeneration/MoveGenerator.cs           |   4 +-
 src/Rudzoft.ChessLib/Position.cs              | 114 +++++------
 src/Rudzoft.ChessLib/State.cs                 |  15 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |   2 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 192 +++++++-----------
 src/Rudzoft.ChessLib/Types/CastleRight.cs     |  75 +++----
 src/Rudzoft.ChessLib/Types/Square.cs          | 156 +++++---------
 .../Validation/PositionValidator.cs           |   4 +-
 13 files changed, 217 insertions(+), 369 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
index 3af43c83..42c2429d 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
@@ -51,7 +51,7 @@ public void AlphaPattern()
         {
             var sq = BitBoards.PopLsb(ref bb);
             var attacks = _fixture.RegAttacks[attackIndex](sq);
-            var isCorner = !(_fixture.BoardCorners & sq).IsEmpty;
+            var isCorner = (_fixture.BoardCorners & sq).IsNotEmpty;
             var expected = _fixture.KingExpected[index];
             if (isCorner)
                 expected -= 2; /* for corners */
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs
index aeb665f4..46a5b43a 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs
@@ -50,9 +50,9 @@ public void AlphaPattern()
         {
             var sq = BitBoards.PopLsb(ref bb);
             var attacks = _fixture.RegAttacks[attackIndex](sq);
-            var expected = !(_fixture.BoardCorners & sq).IsEmpty
+            var expected = (_fixture.BoardCorners & sq).IsNotEmpty
                 ? _fixture.KnightExpected[index] >> 1 /* for corners */
-                : !(narrowLocations & sq).IsEmpty
+                : (narrowLocations & sq).IsNotEmpty
                     ? _fixture.KnightExpected[index] - 1 /* narrowLocations */
                     : _fixture.KnightExpected[index];
             var actual = attacks.Count;
@@ -73,7 +73,7 @@ public void BetaPattern()
         {
             var sq = BitBoards.PopLsb(ref bb);
             var attacks = _fixture.RegAttacks[attackIndex](sq);
-            var expected = !(narrowLocations & sq).IsEmpty
+            var expected = (narrowLocations & sq).IsNotEmpty
                 ? _fixture.KnightExpected[index] - 2
                 : _fixture.KnightExpected[index];
             var actual = attacks.Count;
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index f59efa61..a2df935b 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -60,7 +60,7 @@ public ValidationTests()
     public void ValidationKingsNegative()
     {
         const PositionValidationTypes type = PositionValidationTypes.Kings;
-        var expectedErrorMsg = $"king count for player {Players.White} was 2";
+        var expectedErrorMsg = $"king count for player {Player.White} was 2";
 
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 1225ffd1..640742eb 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -207,8 +207,8 @@ private static bool LowerRankNotFileABelowFenceRankNotBlocked(
     {
         return pos.GetPiece(sq + Direction.West) == ourPawn
                && BitBoards.PopCount(ourPawns & (f - 1)) <= 1
-               && !(fixedPawns & Square.Create(r, PreviousFile(f))).IsEmpty
-               && !(fence & Square.Create(r, PreviousFile(f))).IsEmpty;
+               && (fixedPawns & Square.Create(r, PreviousFile(f))).IsNotEmpty
+               && (fence & Square.Create(r, PreviousFile(f))).IsNotEmpty;
     }
 
     private static bool LowerRankFenceNonFileANotBlocked(
@@ -222,8 +222,8 @@ private static bool LowerRankFenceNonFileANotBlocked(
     {
         return pos.GetPiece(sq + Direction.West) == ourPawn
                && BitBoards.PopCount(ourPawns & PreviousFile(f)) <= 1
-               && !(fixedPawns & (sq + Direction.West)).IsEmpty
-               && !(fence & (sq + Direction.West)).IsEmpty;
+               && (fixedPawns & (sq + Direction.West)).IsNotEmpty
+               && (fence & (sq + Direction.West)).IsNotEmpty;
     }
 
     /// <summary>
@@ -261,7 +261,7 @@ private static MarkedPawns MarkPawns(in IPosition pos, in BitBoard theirPawns)
             var psq = BitBoards.PopLsb(ref ourPawns);
             var rr = psq.RelativeRank(us);
             if (rr < Rank.Rank7
-                && (pos.GetPiece(psq + up) == theirPawn || !(fixedPawns & (psq + up)).IsEmpty)
+                && (pos.GetPiece(psq + up) == theirPawn || (fixedPawns & (psq + up)).IsNotEmpty)
                 && (psq.PawnAttack(us) & theirPawns).IsEmpty)
             {
                 fixedPawns |= psq;
@@ -311,7 +311,7 @@ private static bool FormsFence(Square sq, ref BitBoard processed, ref BitBoard f
         {
             var direction = Unsafe.Add(ref directionSpace, i);
             var s = sq + direction;
-            if ((marked & s).IsEmpty || !(processed & s).IsEmpty ||
+            if ((marked & s).IsEmpty || (processed & s).IsNotEmpty ||
                 !FormsFence(s, ref processed, ref fence, in marked, us))
                 continue;
             fence |= s;
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index aa223a61..5f4b7c26 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -129,7 +129,7 @@ public static KpkPosition Create(int idx)
             if (ksq[Player.White.Side].Distance(ksq[Player.Black.Side]) <= 1
                 || ksq[Player.White.Side] == psq
                 || ksq[Player.Black.Side] == psq
-                || (stm.IsWhite && !(psq.PawnAttack(Player.White) & ksq[Player.Black.Side]).IsEmpty))
+                || (stm.IsWhite && (psq.PawnAttack(Player.White) & ksq[Player.Black.Side]).IsNotEmpty))
                 results = Results.None;
 
             // Win if the pawn can be promoted without getting captured
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index c5916d50..57ef0319 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -245,8 +245,8 @@ private static int GenerateMoves(
         {
             var from = Unsafe.Add(ref squaresSpace, i);
             if (checks
-                && (!(pos.KingBlockers(~us) & from).IsEmpty ||
-                    !(pt.PseudoAttacks(from) & target & pos.CheckedSquares(pt))))
+                && ((pos.KingBlockers(~us) & from).IsNotEmpty ||
+                    (pt.PseudoAttacks(from) & target & pos.CheckedSquares(pt)).IsNotEmpty))
                 continue;
 
             var b = pos.GetAttacks(from, pt) & target;
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 89cfdbb7..6dc4f41d 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -101,7 +101,7 @@ public Position(
 
     public string FenNotation => GenerateFen().ToString();
 
-    public bool InCheck => State.Checkers;
+    public bool InCheck => State.Checkers.IsNotEmpty;
 
     public bool IsMate => !HasMoves();
 
@@ -136,13 +136,13 @@ public void AddPiece(Piece pc, Square sq)
     }
 
     public bool AttackedByKing(Square sq, Player p)
-        => !(GetAttacks(sq, PieceTypes.King) & GetKingSquare(p)).IsEmpty;
+        => (GetAttacks(sq, PieceTypes.King) & GetKingSquare(p)).IsNotEmpty;
 
     public bool AttackedByKnight(Square sq, Player p)
-        => !(Board.Pieces(p, PieceTypes.Knight) & GetAttacks(sq, PieceTypes.Knight)).IsEmpty;
+        => (Board.Pieces(p, PieceTypes.Knight) & GetAttacks(sq, PieceTypes.Knight)).IsNotEmpty;
 
     public bool AttackedByPawn(Square sq, Player p) =>
-        !(Board.Pieces(p, PieceTypes.Pawn) & sq.PawnAttack(~p)).IsEmpty;
+        (Board.Pieces(p, PieceTypes.Pawn) & sq.PawnAttack(~p)).IsNotEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool AttackedBySlider(Square sq, Player p)
@@ -156,7 +156,7 @@ public bool AttackedBySlider(Square sq, Player p)
         if (Board.Pieces(p, PieceTypes.Bishop) & bishopAttacks)
             return true;
 
-        return !(Board.Pieces(p, PieceTypes.Queen) & (bishopAttacks | rookAttacks)).IsEmpty;
+        return (Board.Pieces(p, PieceTypes.Queen) & (bishopAttacks | rookAttacks)).IsNotEmpty;
     }
 
     public BitBoard AttacksBy(PieceTypes pt, Player p)
@@ -207,14 +207,11 @@ public BitBoard AttacksTo(Square sq, in BitBoard occ)
                | (GetAttacks(sq, PieceTypes.King) & Board.Pieces(PieceTypes.King));
     }
 
-    public BitBoard AttacksTo(Square sq)
-        => AttacksTo(sq, Board.Pieces());
+    public BitBoard AttacksTo(Square sq) => AttacksTo(sq, Board.Pieces());
 
-    public BitBoard KingBlockers(Player p)
-        => State.BlockersForKing[p.Side];
+    public BitBoard KingBlockers(Player p) => State.BlockersForKing[p.Side];
 
-    public bool IsKingBlocker(Player p, Square sq)
-        => KingBlockers(p).Contains(sq);
+    public bool IsKingBlocker(Player p, Square sq) => KingBlockers(p).Contains(sq);
 
     public BitBoard SliderBlockerOn(Square sq, BitBoard attackers, ref BitBoard pinners, ref BitBoard hidders)
     {
@@ -239,7 +236,7 @@ public BitBoard SliderBlockerOn(Square sq, BitBoard attackers, ref BitBoard pinn
 
             blockers |= b;
 
-            if (!(b & defenders).IsEmpty)
+            if ((b & defenders).IsNotEmpty)
                 pinners |= sniperSq;
             else
                 hidders |= sniperSq;
@@ -248,20 +245,17 @@ public BitBoard SliderBlockerOn(Square sq, BitBoard attackers, ref BitBoard pinn
         return blockers;
     }
 
-    public bool CanCastle(CastleRight cr)
-        => State.CastlelingRights.Has(cr);
+    public bool CanCastle(CastleRight cr) => State.CastlelingRights.Has(cr);
 
-    public bool CanCastle(Player p)
-        => State.CastlelingRights.Has(p);
+    public bool CanCastle(Player p) => State.CastlelingRights.Has(p);
 
-    public ref BitBoard CastleKingPath(CastleRight cr)
-        => ref _castleKingPath[cr.AsInt()];
+    public ref BitBoard CastleKingPath(CastleRight cr) => ref _castleKingPath[cr.AsInt()];
 
     public bool CastlingImpeded(CastleRight cr)
     {
         Debug.Assert(cr.Rights is CastleRights.WhiteKing or CastleRights.WhiteQueen or CastleRights.BlackKing
             or CastleRights.BlackQueen);
-        return !(Board.Pieces() & _castleRookPath[cr.Rights.AsInt()]).IsEmpty;
+        return (Board.Pieces() & _castleRookPath[cr.Rights.AsInt()]).IsNotEmpty;
     }
 
     public Square CastlingRookSquare(CastleRight cr)
@@ -271,8 +265,7 @@ public Square CastlingRookSquare(CastleRight cr)
         return _castlingRookSquare[cr.Rights.AsInt()];
     }
 
-    public BitBoard CheckedSquares(PieceTypes pt)
-        => State.CheckedSquares[pt.AsInt()];
+    public BitBoard CheckedSquares(PieceTypes pt) => State.CheckedSquares[pt.AsInt()];
 
     public void Clear()
     {
@@ -450,14 +443,14 @@ public bool GivesCheck(Move m)
         var pt = pc.Type();
 
         // Is there a direct check?
-        if (!(State.CheckedSquares[pt.AsInt()] & to).IsEmpty)
+        if ((State.CheckedSquares[pt.AsInt()] & to).IsNotEmpty)
             return true;
 
         var us = _sideToMove;
         var them = ~us;
 
         // Is there a discovered check?
-        if (!(State.BlockersForKing[them.Side] & from).IsEmpty
+        if ((State.BlockersForKing[them.Side] & from).IsNotEmpty
             && !from.Aligned(to, GetKingSquare(them)))
             return true;
 
@@ -467,7 +460,7 @@ public bool GivesCheck(Move m)
                 return false;
 
             case MoveTypes.Promotion:
-                return !(GetAttacks(to, m.PromotedPieceType(), Board.Pieces() ^ from) & GetKingSquare(them)).IsEmpty;
+                return (GetAttacks(to, m.PromotedPieceType(), Board.Pieces() ^ from) & GetKingSquare(them)).IsNotEmpty;
 
             // En passant capture with check? We have already handled the case of direct checks
             // and ordinary discovered check, so the only case we need to handle is the unusual
@@ -482,7 +475,7 @@ public bool GivesCheck(Move m)
                                Board.Pieces(us, PieceTypes.Rook, PieceTypes.Queen))
                               | (GetAttacks(ksq, PieceTypes.Bishop, in b) &
                                  Board.Pieces(us, PieceTypes.Bishop, PieceTypes.Queen));
-                return !attacks.IsEmpty;
+                return attacks.IsNotEmpty;
             }
             case MoveTypes.Castling:
             {
@@ -494,8 +487,9 @@ public bool GivesCheck(Move m)
                 var rookTo = (rookFrom > kingFrom ? Square.F1 : Square.D1).Relative(us);
                 var ksq = GetKingSquare(them);
 
-                return !(PieceTypes.Rook.PseudoAttacks(rookTo) & ksq).IsEmpty && !(GetAttacks(rookTo, PieceTypes.Rook,
-                    (Board.Pieces() ^ kingFrom ^ rookFrom) | rookTo | kingTo) & ksq).IsEmpty;
+                return (PieceTypes.Rook.PseudoAttacks(rookTo) & ksq).IsNotEmpty
+                       && (GetAttacks(rookTo, PieceTypes.Rook,
+                           (Board.Pieces() ^ kingFrom ^ rookFrom) | rookTo | kingTo) & ksq).IsNotEmpty;
             }
             default:
                 Debug.Assert(false);
@@ -608,8 +602,7 @@ private bool IsEnPassantMoveLegal(Square to, Player us, Square from, Square ksq)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsOccupied(Square sq)
-        => !Board.IsEmpty(sq);
+    public bool IsOccupied(Square sq) => !Board.IsEmpty(sq);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool IsPieceTypeOnSquare(Square sq, PieceTypes pt) => Board.PieceAt(sq).Type() == pt;
@@ -643,7 +636,7 @@ public bool IsPseudoLegal(Move m)
             return false;
 
         // The destination square cannot be occupied by a friendly piece
-        if (!(Pieces(us) & to).IsEmpty)
+        if ((Pieces(us) & to).IsNotEmpty)
             return false;
 
         // Handle the special case of a pawn move
@@ -684,14 +677,13 @@ public bool IsPseudoLegal(Move m)
         }
         // In case of king moves under check we have to remove king so to catch as invalid moves
         // like b1a1 when opposite queen is on c1.
-        else if (!(AttacksTo(to, Pieces() ^ from) & Pieces(~us)).IsEmpty)
+        else if ((AttacksTo(to, Pieces() ^ from) & Pieces(~us)).IsNotEmpty)
             return false;
 
         return true;
     }
 
-    public void MakeMove(Move m, in State newState)
-        => MakeMove(m, in newState, GivesCheck(m));
+    public void MakeMove(Move m, in State newState) => MakeMove(m, in newState, GivesCheck(m));
 
     public void MakeMove(Move m, in State newState, bool givesCheck)
     {
@@ -874,8 +866,8 @@ public void MakeNullMove(in State newState)
         Debug.Assert(_positionValidator.Validate(this, PositionValidationTypes.Basic).Ok);
     }
 
-    public Piece MovedPiece(Move m)
-        => Board.MovedPiece(m);
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public Piece MovedPiece(Move m) => Board.MovedPiece(m);
 
     /// <summary>
     /// Converts a move data type to move notation string format which chess engines understand.
@@ -943,29 +935,21 @@ public bool PassedPawn(Square sq)
     public bool PawnIsolated(Square sq, Player p)
         => ((sq.PawnAttackSpan(p) | sq.PawnAttackSpan(~p)) & Board.Pieces(p, PieceTypes.Pawn)).IsEmpty;
 
-    public bool PieceOnFile(Square sq, Player p, PieceTypes pt)
-        => !(Board.Pieces(p, pt) & sq).IsEmpty;
+    public bool PieceOnFile(Square sq, Player p, PieceTypes pt) => (Board.Pieces(p, pt) & sq).IsNotEmpty;
 
-    public BitBoard Pieces()
-        => Board.Pieces();
+    public BitBoard Pieces() => Board.Pieces();
 
-    public BitBoard Pieces(Player p)
-        => Board.Pieces(p);
+    public BitBoard Pieces(Player p) => Board.Pieces(p);
 
-    public BitBoard Pieces(Piece pc)
-        => Board.Pieces(pc.ColorOf(), pc.Type());
+    public BitBoard Pieces(Piece pc) => Board.Pieces(pc.ColorOf(), pc.Type());
 
-    public BitBoard Pieces(PieceTypes pt)
-        => Board.Pieces(pt);
+    public BitBoard Pieces(PieceTypes pt) => Board.Pieces(pt);
 
-    public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2)
-        => Board.Pieces(pt1, pt2);
+    public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2) => Board.Pieces(pt1, pt2);
 
-    public BitBoard Pieces(PieceTypes pt, Player p)
-        => Board.Pieces(p, pt);
+    public BitBoard Pieces(PieceTypes pt, Player p) => Board.Pieces(p, pt);
 
-    public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2, Player p)
-        => Board.Pieces(p, pt1, pt2);
+    public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2, Player p) => Board.Pieces(p, pt1, pt2);
 
     public int PieceCount() => Board.PieceCount();
 
@@ -982,8 +966,8 @@ public bool SemiOpenFileOn(Player p, Square sq)
 
     public bool BishopPaired(Player p) =>
         Board.PieceCount(PieceTypes.Bishop, p) >= 2
-        && !(Board.Pieces(p, PieceTypes.Bishop) & Player.White.ColorBB()).IsEmpty
-        && !(Board.Pieces(p, PieceTypes.Bishop) & Player.Black.ColorBB()).IsEmpty;
+        && (Board.Pieces(p, PieceTypes.Bishop) & Player.White.ColorBB()).IsNotEmpty
+        && (Board.Pieces(p, PieceTypes.Bishop) & Player.Black.ColorBB()).IsNotEmpty;
 
     public bool BishopOpposed() =>
         Board.PieceCount(Piece.WhiteBishop) == 1
@@ -1051,7 +1035,7 @@ public bool SeeGe(Move m, Value threshold)
             // Locate and remove the next least valuable attacker, and add to the bitboard
             // 'attackers' any X-ray attackers behind it.
             var bb = stmAttackers & Board.Pieces(PieceTypes.Pawn);
-            if (!bb.IsEmpty)
+            if (bb.IsNotEmpty)
             {
                 if ((swap = Values.PawnValueMg - swap) < res)
                     break;
@@ -1060,14 +1044,14 @@ public bool SeeGe(Move m, Value threshold)
                 attackers |= GetAttacks(to, PieceTypes.Bishop, in occupied) &
                              Board.Pieces(PieceTypes.Bishop, PieceTypes.Queen);
             }
-            else if (!(bb = stmAttackers & Board.Pieces(PieceTypes.Knight)).IsEmpty)
+            else if ((bb = stmAttackers & Board.Pieces(PieceTypes.Knight)).IsNotEmpty)
             {
                 if ((swap = Values.KnightValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
             }
-            else if (!(bb = stmAttackers & Board.Pieces(PieceTypes.Bishop)).IsEmpty)
+            else if ((bb = stmAttackers & Board.Pieces(PieceTypes.Bishop)).IsNotEmpty)
             {
                 if ((swap = Values.BishopValueMg - swap) < res)
                     break;
@@ -1076,7 +1060,7 @@ public bool SeeGe(Move m, Value threshold)
                 attackers |= GetAttacks(to, PieceTypes.Bishop, in occupied) &
                              Board.Pieces(PieceTypes.Bishop, PieceTypes.Queen);
             }
-            else if (!(bb = stmAttackers & Board.Pieces(PieceTypes.Rook)).IsEmpty)
+            else if ((bb = stmAttackers & Board.Pieces(PieceTypes.Rook)).IsNotEmpty)
             {
                 if ((swap = Values.RookValueMg - swap) < res)
                     break;
@@ -1085,7 +1069,7 @@ public bool SeeGe(Move m, Value threshold)
                 attackers |= GetAttacks(to, PieceTypes.Rook, in occupied) &
                              Board.Pieces(PieceTypes.Rook, PieceTypes.Queen);
             }
-            else if (!(bb = stmAttackers & Board.Pieces(PieceTypes.Queen)).IsEmpty)
+            else if ((bb = stmAttackers & Board.Pieces(PieceTypes.Queen)).IsNotEmpty)
             {
                 if ((swap = Values.QueenValueMg - swap) < res)
                     break;
@@ -1149,8 +1133,7 @@ private void SetupPieces(ReadOnlySpan<char> fenChunk)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private void SetupPlayer(ReadOnlySpan<char> fenChunk)
-        => _sideToMove = (fenChunk[0] != 'w').AsByte();
+    private void SetupPlayer(ReadOnlySpan<char> fenChunk) => _sideToMove = (fenChunk[0] != 'w').AsByte();
 
     private void SetupEnPassant(ReadOnlySpan<char> fenChunk)
     {
@@ -1253,8 +1236,7 @@ public IPosition Set(ReadOnlySpan<char> code, Player p, State state)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ReadOnlySpan<Square> Squares(PieceTypes pt, Player p)
-        => Board.Squares(pt, p);
+    public ReadOnlySpan<Square> Squares(PieceTypes pt, Player p) => Board.Squares(pt, p);
 
     public void TakeMove(Move m)
     {
@@ -1390,11 +1372,7 @@ private static CastleRights OrCastlingRight(Player c, bool isKingSide)
         => (CastleRights)((int)CastleRights.WhiteKing << ((!isKingSide).AsByte() + 2 * c.Side));
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    private (Square, Square) DoCastleling(
-        Player us,
-        Square from,
-        ref Square to,
-        CastlePerform castlePerform)
+    private (Square, Square) DoCastleling(Player us, Square from, ref Square to, CastlePerform castlePerform)
     {
         var kingSide = to > from;
         var doCastleling = castlePerform == CastlePerform.Do;
@@ -1486,7 +1464,7 @@ private void SetState()
         SetCheckInfo(State);
 
         // compute hash keys
-        for (var b = Board.Pieces(); !b.IsEmpty;)
+        for (var b = Board.Pieces(); b.IsNotEmpty;)
         {
             var sq = BitBoards.PopLsb(ref b);
             var pc = GetPiece(sq);
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index c9821494..bfb0b029 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -36,11 +36,11 @@ namespace Rudzoft.ChessLib;
 public sealed class State : IEquatable<State>
 {
     public HashKey MaterialKey { get; set; }
-    
+
     public HashKey PawnKey { get; set; }
 
     public int Rule50 { get; set; }
-    
+
     public int PliesFromNull { get; set; }
 
     public CastleRight CastlelingRights { get; set; }
@@ -110,7 +110,7 @@ public State()
     public State CopyTo(State other)
     {
         other ??= new State();
-        
+
         // copy over preserved values
         other.MaterialKey = MaterialKey;
         other.PawnKey = PawnKey;
@@ -144,7 +144,7 @@ public void Clear()
     public void UpdateRepetition()
     {
         Repetition = 0;
-        
+
         var end = End();
 
         if (end < 4)
@@ -180,8 +180,7 @@ public bool Equals(State other)
                && Equals(Previous, other.Previous);
     }
 
-    public override bool Equals(object obj)
-        => ReferenceEquals(this, obj) || obj is State other && Equals(other);
+    public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is State other && Equals(other);
 
     public override int GetHashCode()
     {
@@ -197,8 +196,8 @@ public override int GetHashCode()
         hashCode.Add(Checkers);
         hashCode.Add(Previous);
         hashCode.Add(CapturedPiece);
-        hashCode.Add(Pinners.Where(static p => !p.IsEmpty));
-        hashCode.Add(CheckedSquares.Where(static csq => !csq.IsEmpty));
+        hashCode.Add(Pinners.Where(static p => p.IsNotEmpty));
+        hashCode.Add(CheckedSquares.Where(static csq => csq.IsNotEmpty));
         return hashCode.ToHashCode();
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index 023fffea..ffe551ad 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -168,7 +168,7 @@ private BitBoard(int value)
     public static implicit operator bool(BitBoard b) => b.Value != 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Contains(Square sq) => !(this & sq).IsEmpty;
+    public bool Contains(Square sq) => (this & sq).IsNotEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Square FirstOrDefault() => IsEmpty ? Square.None : this.Lsb();
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index b16883a0..33889187 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -318,7 +318,7 @@ static BitBoard ComputeKnightAttack(in BitBoard bb)
 
             pt = PieceTypes.King.AsInt();
             PseudoAttacksBB[pt][sq] = b.NorthOne() | b.SouthOne() | b.EastOne() | b.WestOne()
-                                    | b.NorthEastOne() | b.NorthWestOne() | b.SouthEastOne() | b.SouthWestOne();
+                                      | b.NorthEastOne() | b.NorthWestOne() | b.SouthEastOne() | b.SouthWestOne();
 
             // Compute lines and betweens
             ref var magicPiecesSpace = ref MemoryMarshal.GetReference(validMagicPieces);
@@ -335,20 +335,22 @@ static BitBoard ComputeKnightAttack(in BitBoard bb)
 
                     var sq2 = s2.AsInt();
 
-                    LineBB[sq][sq2] = (GetAttacks(s1, validMagicPiece, EmptyBitBoard) & GetAttacks(s2, validMagicPiece, EmptyBitBoard)) | s1 | s2;
-                    BetweenBB[sq][sq2] = GetAttacks(s1, validMagicPiece, BbSquares[sq2]) & GetAttacks(s2, validMagicPiece, BbSquares[sq]);
+                    LineBB[sq][sq2] = (GetAttacks(s1, validMagicPiece, EmptyBitBoard) &
+                                       GetAttacks(s2, validMagicPiece, EmptyBitBoard)) | s1 | s2;
+                    BetweenBB[sq][sq2] = GetAttacks(s1, validMagicPiece, BbSquares[sq2]) &
+                                         GetAttacks(s2, validMagicPiece, BbSquares[sq]);
                 }
             }
 
             // Compute KingRings
             InitializeKingRing(s1, sq, file);
         }
-        
+
         SlotFileBB = new[]
         {
             File.FileE.FileBB() | File.FileF.FileBB() | File.FileG.FileBB() | File.FileH.FileBB(), // King
             File.FileA.FileBB() | File.FileB.FileBB() | File.FileC.FileBB() | File.FileD.FileBB(), // Queen
-            File.FileC.FileBB() | File.FileD.FileBB() | File.FileE.FileBB() | File.FileF.FileBB()  // Center
+            File.FileC.FileBB() | File.FileD.FileBB() | File.FileE.FileBB() | File.FileF.FileBB() // Center
         };
     }
 
@@ -374,48 +376,37 @@ private static void InitializeKingRing(Square s1, int sq, File file)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard FileBB(this File f)
-        => FilesBB[f.AsInt()];
+    public static BitBoard FileBB(this File f) => FilesBB[f.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard RankBB(this Rank r)
-        => RanksBB[r.AsInt()];
+    public static BitBoard RankBB(this Rank r) => RanksBB[r.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ColorBB(this Player p)
-        => ColorsBB[p.Side];
+    public static BitBoard ColorBB(this Player p) => ColorsBB[p.Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard FirstRank(Player p)
-        => Ranks1[p.Side];
+    public static BitBoard FirstRank(Player p) => Ranks1[p.Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ThirdRank(Player p)
-        => Ranks3BB[p.Side];
+    public static BitBoard ThirdRank(Player p) => Ranks3BB[p.Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SeventhRank(Player p)
-        => Ranks7BB[p.Side];
+    public static BitBoard SeventhRank(Player p) => Ranks7BB[p.Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SixthAndSeventhRank(Player p)
-        => Ranks6And7BB[p.Side];
+    public static BitBoard SixthAndSeventhRank(Player p) => Ranks6And7BB[p.Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SeventhAndEightsRank(Player p)
-        => Ranks7And8BB[p.Side];
+    public static BitBoard SeventhAndEightsRank(Player p) => Ranks7And8BB[p.Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PseudoAttacks(this PieceTypes pt, Square sq)
-        => PseudoAttacksBB[pt.AsInt()][sq.AsInt()];
+    public static BitBoard PseudoAttacks(this PieceTypes pt, Square sq) => PseudoAttacksBB[pt.AsInt()][sq.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KnightAttacks(this Square sq)
-        => PseudoAttacksBB[PieceTypes.Knight.AsInt()][sq.AsInt()];
+    public static BitBoard KnightAttacks(this Square sq) => PseudoAttacksBB[PieceTypes.Knight.AsInt()][sq.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KingAttacks(this Square sq)
-        => PseudoAttacksBB[PieceTypes.King.AsInt()][sq.AsInt()];
+    public static BitBoard KingAttacks(this Square sq) => PseudoAttacksBB[PieceTypes.King.AsInt()][sq.AsInt()];
 
     /// <summary>
     /// Attack for pawn.
@@ -424,8 +415,7 @@ public static BitBoard KingAttacks(this Square sq)
     /// <param name="p">The player side</param>
     /// <returns>ref to bitboard of attack</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnAttack(this Square sq, Player p)
-        => PseudoAttacksBB[p.Side][sq.AsInt()];
+    public static BitBoard PawnAttack(this Square sq, Player p) => PseudoAttacksBB[p.Side][sq.AsInt()];
 
     /// <summary>
     /// Returns the bitboard representation of the rank of which the square is located.
@@ -433,8 +423,7 @@ public static BitBoard PawnAttack(this Square sq, Player p)
     /// <param name="sq">The square</param>
     /// <returns>The bitboard of square rank</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard BitBoardRank(this Square sq)
-        => sq.Rank.BitBoardRank();
+    public static BitBoard BitBoardRank(this Square sq) => sq.Rank.BitBoardRank();
 
     /// <summary>
     /// Returns the bitboard representation of a rank.
@@ -442,8 +431,7 @@ public static BitBoard BitBoardRank(this Square sq)
     /// <param name="r">The rank</param>
     /// <returns>The bitboard of square rank</returns>
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard BitBoardRank(this Rank r)
-        => Rank1BB << (8 * r.AsInt());
+    public static BitBoard BitBoardRank(this Rank r) => Rank1BB << (8 * r.AsInt());
 
     /// <summary>
     /// Returns the bitboard representation of the file of which the square is located.
@@ -451,8 +439,7 @@ public static BitBoard BitBoardRank(this Rank r)
     /// <param name="sq">The square</param>
     /// <returns>The bitboard of square file</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard BitBoardFile(this Square sq)
-        => sq.File.BitBoardFile();
+    public static BitBoard BitBoardFile(this Square sq) => sq.File.BitBoardFile();
 
     /// <summary>
     /// Returns the bitboard representation of the file.
@@ -460,8 +447,7 @@ public static BitBoard BitBoardFile(this Square sq)
     /// <param name="f">The file</param>
     /// <returns>The bitboard of file</returns>
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard BitBoardFile(this File f)
-        => FileABB << f.AsInt();
+    public static BitBoard BitBoardFile(this File f) => FileABB << f.AsInt();
 
     /// <summary>
     /// Returns all squares in front of the square in the same file as bitboard
@@ -470,8 +456,7 @@ public static BitBoard BitBoardFile(this File f)
     /// <param name="p">The side, white is north and black is south</param>
     /// <returns>The bitboard of all forward file squares</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ForwardFile(this Square sq, Player p)
-        => ForwardFileBB[p.Side][sq.AsInt()];
+    public static BitBoard ForwardFile(this Square sq, Player p) => ForwardFileBB[p.Side][sq.AsInt()];
 
     /// <summary>
     /// Returns all squares in pawn attack pattern in front of the square.
@@ -480,8 +465,7 @@ public static BitBoard ForwardFile(this Square sq, Player p)
     /// <param name="p">White = north, Black = south</param>
     /// <returns>The bitboard representation</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnAttackSpan(this Square sq, Player p)
-        => PawnAttackSpanBB[p.Side][sq.AsInt()];
+    public static BitBoard PawnAttackSpan(this Square sq, Player p) => PawnAttackSpanBB[p.Side][sq.AsInt()];
 
     /// <summary>
     /// Returns all square of both file and pawn attack pattern in front of square. This is the
@@ -491,74 +475,59 @@ public static BitBoard PawnAttackSpan(this Square sq, Player p)
     /// <param name="p">White = north, Black = south</param>
     /// <returns>The bitboard representation</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PassedPawnFrontAttackSpan(this Square sq, Player p)
-        => PassedPawnMaskBB[p.Side][sq.AsInt()];
+    public static BitBoard PassedPawnFrontAttackSpan(this Square sq, Player p) => PassedPawnMaskBB[p.Side][sq.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ForwardRanks(this Square sq, Player p)
-        => ForwardRanksBB[p.Side][sq.AsInt()];
+    public static BitBoard ForwardRanks(this Square sq, Player p) => ForwardRanksBB[p.Side][sq.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard BitboardBetween(this Square sq1, Square sq2)
-        => BetweenBB[sq1.AsInt()][sq2.AsInt()];
+    public static BitBoard BitboardBetween(this Square sq1, Square sq2) => BetweenBB[sq1.AsInt()][sq2.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square Get(this in BitBoard bb, int pos)
-        => (int)(bb.Value >> pos) & 0x1;
+    public static Square Get(this in BitBoard bb, int pos) => (int)(bb.Value >> pos) & 0x1;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool IsSet(this in BitBoard bb, int pos)
-        => (bb.Value & (One << pos)) != ulong.MinValue;
+    public static bool IsSet(this in BitBoard bb, int pos) => (bb.Value & (One << pos)) != ulong.MinValue;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square First(this in BitBoard bb)
-        => bb.Lsb();
+    public static Square First(this in BitBoard bb) => bb.Lsb();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square Last(this in BitBoard bb)
-        => bb.Msb();
+    public static Square Last(this in BitBoard bb) => bb.Msb();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Line(this Square sq1, Square sq2)
-        => LineBB[sq1.AsInt()][sq2.AsInt()];
+    public static BitBoard Line(this Square sq1, Square sq2) => LineBB[sq1.AsInt()][sq2.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SlotFile(CastleSides cs)
-        => SlotFileBB[cs.AsInt()];
+    public static BitBoard SlotFile(CastleSides cs) => SlotFileBB[cs.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard AdjacentFiles(File f)
-        => AdjacentFilesBB[f.AsInt()];
+    public static BitBoard AdjacentFiles(File f) => AdjacentFilesBB[f.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static bool Aligned(this Square sq1, Square sq2, Square sq3)
-        => !(Line(sq1, sq2) & sq3).IsEmpty;
+    public static bool Aligned(this Square sq1, Square sq2, Square sq3) => (Line(sq1, sq2) & sq3).IsNotEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard FrontSquares(this Player p, Square sq)
         => ForwardRanksBB[p.Side][sq.AsInt()] & sq.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KingRing(this Square sq, Player p)
-        => KingRingBB[p.Side][sq.AsInt()];
+    public static BitBoard KingRing(this Square sq, Player p) => KingRingBB[p.Side][sq.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int Distance(this Square sq1, Square sq2)
-        => SquareDistance[sq1.AsInt()][sq2.AsInt()];
+    public static int Distance(this Square sq1, Square sq2) => SquareDistance[sq1.AsInt()][sq2.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard DistanceRing(this Square sq, int length)
-        => DistanceRingBB[sq.AsInt()][length];
+    public static BitBoard DistanceRing(this Square sq, int length) => DistanceRingBB[sq.AsInt()][length];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PromotionRank(this Player p)
-        => PromotionRanks[p.Side];
+    public static BitBoard PromotionRank(this Player p) => PromotionRanks[p.Side];
 
     [SkipLocalsInit]
     public static string PrintBitBoard(in BitBoard bb, string title = "")
     {
         const string line = "+---+---+---+---+---+---+---+---+---+";
-        const string buttom = "|   | A | B | C | D | E | F | G | H |";
+        const string bottom = "|   | A | B | C | D | E | F | G | H |";
         Span<char> span = stackalloc char[768];
         var idx = 0;
         foreach (var c in line)
@@ -588,7 +557,7 @@ public static string PrintBitBoard(in BitBoard bb, string title = "")
             span[idx++] = '\n';
         }
 
-        foreach (var c in buttom)
+        foreach (var c in bottom)
             span[idx++] = c;
 
         span[idx++] = '\n';
@@ -607,8 +576,7 @@ public static string PrintBitBoard(in BitBoard bb, string title = "")
     /// <param name="bb">The word to get lsb from</param>
     /// <returns>The index of the found bit</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square Lsb(this in BitBoard bb)
-        => new(BitOperations.TrailingZeroCount(bb.Value));
+    public static Square Lsb(this in BitBoard bb) => new(BitOperations.TrailingZeroCount(bb.Value));
 
     /// <summary>
     /// Retrieves the least significant bit in an int word.
@@ -616,48 +584,37 @@ public static Square Lsb(this in BitBoard bb)
     /// <param name="v">The word to get lsb from</param>
     /// <returns>The index of the found bit</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int Lsb(this int v)
-        => BitOperations.TrailingZeroCount(v);
+    public static int Lsb(this int v) => BitOperations.TrailingZeroCount(v);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square Msb(this in BitBoard bb)
-        => new(63 ^ BitOperations.LeadingZeroCount(bb.Value));
+    public static Square Msb(this in BitBoard bb) => new(63 ^ BitOperations.LeadingZeroCount(bb.Value));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square FrontMostSquare(in BitBoard bb, Player p)
-        => p.IsWhite ? Lsb(in bb) : Msb(in bb);
+    public static Square FrontMostSquare(in BitBoard bb, Player p) => p.IsWhite ? Lsb(in bb) : Msb(in bb);
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard NorthOne(this BitBoard bb)
-        => bb << 8;
+    public static BitBoard NorthOne(this BitBoard bb) => bb << 8;
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard SouthOne(this BitBoard bb)
-        => bb >> 8;
+    public static BitBoard SouthOne(this BitBoard bb) => bb >> 8;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard EastOne(this in BitBoard bb)
-        => (bb & ~FileHBB) << 1;
+    public static BitBoard EastOne(this in BitBoard bb) => (bb & ~FileHBB) << 1;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard WestOne(this in BitBoard bb)
-        => (bb & ~FileABB) >> 1;
+    public static BitBoard WestOne(this in BitBoard bb) => (bb & ~FileABB) >> 1;
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard SouthEastOne(this in BitBoard bb)
-        => (bb & ~FileHBB) >> 7;
+    public static BitBoard SouthEastOne(this in BitBoard bb) => (bb & ~FileHBB) >> 7;
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard SouthWestOne(this in BitBoard bb)
-        => (bb & ~FileABB) >> 9;
+    public static BitBoard SouthWestOne(this in BitBoard bb) => (bb & ~FileABB) >> 9;
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard NorthWestOne(this in BitBoard bb)
-        => (bb & ~FileABB) << 7;
+    public static BitBoard NorthWestOne(this in BitBoard bb) => (bb & ~FileABB) << 7;
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard NorthEastOne(this in BitBoard bb)
-        => (bb & ~FileHBB) << 9;
+    public static BitBoard NorthEastOne(this in BitBoard bb) => (bb & ~FileHBB) << 9;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard NorthFill(this BitBoard bb)
@@ -684,8 +641,7 @@ public static BitBoard SouthFill(this BitBoard bb)
     /// <param name="p">The direction to fill in, white = north, black = south</param>
     /// <returns>Filled bitboard</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Fill(this in BitBoard bb, Player p)
-        => FillFuncs[p.Side](bb);
+    public static BitBoard Fill(this in BitBoard bb, Player p) => FillFuncs[p.Side](bb);
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     public static BitBoard Shift(this in BitBoard bb, Direction d)
@@ -697,12 +653,10 @@ public static BitBoard Shift(this in BitBoard bb, Direction d)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnEastAttack(this in BitBoard bb, Player p)
-        => Shift(in bb, p.PawnEastAttackDistance());
+    public static BitBoard PawnEastAttack(this in BitBoard bb, Player p) => Shift(in bb, p.PawnEastAttackDistance());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnWestAttack(this in BitBoard bb, Player p)
-        => Shift(in bb, p.PawnWestAttackDistance());
+    public static BitBoard PawnWestAttack(this in BitBoard bb, Player p) => Shift(in bb, p.PawnWestAttackDistance());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard PawnAttacks(this in BitBoard bb, Player p)
@@ -723,16 +677,14 @@ public static BitBoard PawnDoubleAttacks(this in BitBoard bb, Player p)
     /// </summary>
     /// <param name="bb">The bitboard as reference</param>
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static void ResetLsb(ref BitBoard bb)
-        => bb &= bb - 1;
+    public static void ResetLsb(ref BitBoard bb) => bb &= bb - 1;
 
     /// <summary>
     /// Reset the least significant bit in-place
     /// </summary>
     /// <param name="v">The integer as reference</param>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static void ResetLsb(ref int v)
-        => v &= v - 1;
+    public static void ResetLsb(ref int v) => v &= v - 1;
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     public static Square PopLsb(ref BitBoard bb)
@@ -756,16 +708,13 @@ public static int PopLsb(ref int v)
     /// <param name="bb">The ulong bit representation to count</param>
     /// <returns>The number of bits found</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int PopCount(in BitBoard bb)
-        => BitOperations.PopCount(bb.Value);
+    public static int PopCount(in BitBoard bb) => BitOperations.PopCount(bb.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Rank7(this Player p)
-        => Ranks7BB[p.Side];
+    public static BitBoard Rank7(this Player p) => Ranks7BB[p.Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Rank3(this Player p)
-        => Ranks3BB[p.Side];
+    public static BitBoard Rank3(this Player p) => Ranks3BB[p.Side];
 
     /// <summary>
     /// Generate a bitboard based on a square.
@@ -773,8 +722,7 @@ public static BitBoard Rank3(this Player p)
     /// <param name="sq">The square to generate bitboard from</param>
     /// <returns>The generated bitboard</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard MakeBitboard(Square sq)
-        => sq.AsBb();
+    public static BitBoard MakeBitboard(Square sq) => sq.AsBb();
 
     /// <summary>
     /// Generate a bitboard based on two squares.
@@ -783,8 +731,7 @@ public static BitBoard MakeBitboard(Square sq)
     /// <param name="sq2">The second square to generate bitboard from</param>
     /// <returns>The generated bitboard</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard MakeBitboard(Square sq, Square sq2)
-        => sq.AsBb() | sq2.AsBb();
+    public static BitBoard MakeBitboard(Square sq, Square sq2) => sq.AsBb() | sq2.AsBb();
 
     /// <summary>
     /// Generate a bitboard based on a variadic amount of squares.
@@ -801,8 +748,7 @@ public static BitBoard MakeBitboard(params Square[] sqs)
     /// <param name="bb">The bitboard to set</param>
     /// <returns>true if more than one bit set, otherwise false</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool MoreThanOne(in BitBoard bb)
-        => (bb.Value & (bb.Value - 1)) != 0;
+    public static bool MoreThanOne(in BitBoard bb) => (bb.Value & (bb.Value - 1)) != 0;
 
     /// <summary>
     /// Helper method to generate shift function dictionary for all directions.
@@ -826,8 +772,7 @@ private static IDictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
             { Direction.West, static board => board.WestOne() }
         };
 
-    private static Func<BitBoard, BitBoard>[] MakeFillFuncs()
-        => new[] { NorthFill, SouthFill };
+    private static Func<BitBoard, BitBoard>[] MakeFillFuncs() => new[] { NorthFill, SouthFill };
 
     private static BitBoard GetAttacks(this in Square sq, PieceTypes pt, in BitBoard occ = default)
     {
@@ -843,6 +788,5 @@ private static BitBoard GetAttacks(this in Square sq, PieceTypes pt, in BitBoard
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static BitBoard PseudoAttack(this in Square sq, PieceTypes pt)
-        => PseudoAttacksBB[pt.AsInt()][sq.AsInt()];
-}
+    private static BitBoard PseudoAttack(this in Square sq, PieceTypes pt) => PseudoAttacksBB[pt.AsInt()][sq.AsInt()];
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index 5603ad24..04ab304e 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -74,20 +74,16 @@ public enum CastlePerform
 public static class CastleExtensions
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string GetCastleString(Square toSquare, Square fromSquare)
-        => toSquare < fromSquare ? "O-O-O" : "O-O";
+    public static string GetCastleString(Square toSquare, Square fromSquare) => toSquare < fromSquare ? "O-O-O" : "O-O";
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool HasFlagFast(this CastleRights value, CastleRights flag)
-        => (value & flag) != 0;
+    public static bool HasFlagFast(this CastleRights value, CastleRights flag) => (value & flag) != 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int AsInt(this CastleRights value)
-        => (int)value;
+    public static int AsInt(this CastleRights value) => (int)value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRights Without(this CastleRights @this, CastleRights remove)
-        => @this & ~remove;
+    public static CastleRights Without(this CastleRights @this, CastleRights remove) => @this & ~remove;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static CastleRights MakeCastleRights(this CastleRights cs, Player p)
@@ -102,7 +98,9 @@ public static CastleRights MakeCastleRights(this CastleRights cs, Player p)
 
 public readonly record struct CastleRight(CastleRights Rights)
 {
-    private CastleRight(int cr) : this((CastleRights)cr) { }
+    private CastleRight(int cr) : this((CastleRights)cr)
+    {
+    }
 
     public bool IsNone => Rights == CastleRights.None;
 
@@ -114,84 +112,65 @@ private CastleRight(int cr) : this((CastleRights)cr) { }
     public static CastleRight King { get; } = new(CastleRights.King);
     public static CastleRight Queen { get; } = new(CastleRights.Queen);
     public static CastleRight White { get; } = new(CastleRights.White);
-    public static CastleRight Black  { get; }= new(CastleRights.Black);
+    public static CastleRight Black { get; } = new(CastleRights.Black);
     public static CastleRight Any { get; } = new(CastleRights.Any);
 
     public const int Count = (int)CastleRights.Count;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator CastleRight(CastleRights cr)
-        => new(cr);
+    public static implicit operator CastleRight(CastleRights cr) => new(cr);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator CastleRight(Player p)
-        => Create(p);
+    public static implicit operator CastleRight(Player p) => Create(p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight Create(Player p)
-        => new((CastleRights)((int)CastleRights.White << (p.Side << 1)));
+    public static CastleRight Create(Player p) => new((CastleRights)((int)CastleRights.White << (p.Side << 1)));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(CastleRight other)
-        => Rights == other.Rights;
+    public bool Equals(CastleRight other) => Rights == other.Rights;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => (int)Rights;
+    public override int GetHashCode() => (int)Rights;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator true(CastleRight cr)
-        => cr.Rights != CastleRights.None;
+    public static bool operator true(CastleRight cr) => cr.Rights != CastleRights.None;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator false(CastleRight cr)
-        => cr.Rights == CastleRights.None;
+    public static bool operator false(CastleRight cr) => cr.Rights == CastleRights.None;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight operator |(CastleRight cr1, CastleRight cr2)
-        => new(cr1.Rights | cr2.Rights);
+    public static CastleRight operator |(CastleRight cr1, CastleRight cr2) => new(cr1.Rights | cr2.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight operator |(CastleRight cr1, CastleRights cr2)
-        => new(cr1.Rights | cr2);
+    public static CastleRight operator |(CastleRight cr1, CastleRights cr2) => new(cr1.Rights | cr2);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight operator ^(CastleRight cr1, CastleRight cr2)
-        => new(cr1.Rights ^ cr2.Rights);
+    public static CastleRight operator ^(CastleRight cr1, CastleRight cr2) => new(cr1.Rights ^ cr2.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight operator ^(CastleRight cr1, CastleRights cr2)
-        => new(cr1.Rights ^ cr2);
+    public static CastleRight operator ^(CastleRight cr1, CastleRights cr2) => new(cr1.Rights ^ cr2);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight operator &(CastleRight cr1, CastleRight cr2)
-        => new(cr1.Rights & cr2.Rights);
+    public static CastleRight operator &(CastleRight cr1, CastleRight cr2) => new(cr1.Rights & cr2.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight operator &(CastleRight cr1, CastleRights cr2)
-        => new(cr1.Rights & cr2);
+    public static CastleRight operator &(CastleRight cr1, CastleRights cr2) => new(cr1.Rights & cr2);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight operator ~(CastleRight cr)
-        => new(~cr.Rights);
+    public static CastleRight operator ~(CastleRight cr) => new(~cr.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public HashKey Key()
-        => Rights.GetZobristCastleling();
+    public HashKey Key() => Rights.GetZobristCastleling();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Has(CastleRights cr)
-        => Rights.HasFlagFast(cr);
+    public bool Has(CastleRights cr) => Rights.HasFlagFast(cr);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Has(CastleRight cr)
-        => Rights.HasFlagFast(cr.Rights);
+    public bool Has(CastleRight cr) => Rights.HasFlagFast(cr.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public CastleRight Not(CastleRights cr)
-        => new(Rights & ~cr);
+    public CastleRight Not(CastleRights cr) => new(Rights & ~cr);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int AsInt()
-        => Rights.AsInt();
+    public int AsInt() => Rights.AsInt();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index 496faa33..429eece9 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -84,22 +84,17 @@ public Square(int square) : this((Squares)square) { }
     public Square(Square sq) : this(sq.Value) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square(int rank, int file)
-        : this((Squares)(rank << 3) + (byte)file) { }
+    public Square(int rank, int file) : this((Squares)(rank << 3) + (byte)file) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square(Ranks r, Files f)
-        : this((int)r, (int)f) { }
+    public Square(Ranks r, Files f) : this((int)r, (int)f) { }
 
-    public Square(Rank r, File f)
-        : this(r.AsInt(), f.AsInt()) { }
+    public Square(Rank r, File f) : this(r.AsInt(), f.AsInt()) { }
 
-    public Square((Rank, File) rankFile)
-        : this(rankFile.Item1, rankFile.Item2) { }
+    public Square((Rank, File) rankFile) : this(rankFile.Item1, rankFile.Item2) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Square(string value)
-        => new(new Square(value[1] - '1', value[0] - 'a'));
+    public static implicit operator Square(string value) => new(new Square(value[1] - '1', value[0] - 'a'));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Deconstruct(out Rank r, out File f)
@@ -108,26 +103,19 @@ public void Deconstruct(out Rank r, out File f)
         f = File;
     }
 
-    public Rank Rank
-        => new(AsInt() >> 3);
+    public Rank Rank => new(AsInt() >> 3);
 
-    public char RankChar
-        => Rank.Char;
+    public char RankChar => Rank.Char;
 
-    public File File
-        => new(AsInt() & 7);
+    public File File => new(AsInt() & 7);
 
-    public char FileChar
-        => File.Char;
+    public char FileChar => File.Char;
 
-    public bool IsOk
-        => ((int)Value).InBetween((int)Squares.a1, (int)Squares.h8);
+    public bool IsOk => ((int)Value).InBetween((int)Squares.a1, (int)Squares.h8);
 
-    public bool IsPromotionRank
-        => !(BitBoards.PromotionRanksBB & this).IsEmpty;
+    public bool IsPromotionRank => (BitBoards.PromotionRanksBB & this).IsNotEmpty;
 
-    public bool IsDark
-        => !(Player.Black.ColorBB() & this).IsEmpty;
+    public bool IsDark => (Player.Black.ColorBB() & this).IsNotEmpty;
 
     public static Square None { get; } = new(Squares.none);
 
@@ -209,132 +197,100 @@ public static Square Create(Rank r, File f)
         => new(r, f);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Square(int value)
-        => new(value);
+    public static implicit operator Square(int value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Square(Squares sq)
-        => new(sq);
+    public static implicit operator Square(Squares sq) => new(sq);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Square((Rank, File) value)
-        => new(value);
+    public static implicit operator Square((Rank, File) value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(Square left, Squares right)
-        => left.Value == right;
+    public static bool operator ==(Square left, Squares right) => left.Value == right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(Square left, Squares right)
-        => left.Value != right;
+    public static bool operator !=(Square left, Squares right) => left.Value != right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator +(Square left, Square right)
-        => new(left.Value + (byte)right.AsInt());
+    public static Square operator +(Square left, Square right) => new(left.Value + (byte)right.AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator +(Square left, int right)
-        => new(left.Value + (byte)right);
+    public static Square operator +(Square left, int right) => new(left.Value + (byte)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator +(Square left, Direction right)
-        => new(left.Value + (byte)right.Value);
+    public static Square operator +(Square left, Direction right) => new(left.Value + (byte)right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator +(Square left, Directions right)
-        => new(left.Value + (byte)right);
+    public static Square operator +(Square left, Directions right) => new(left.Value + (byte)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator -(Square left, Square right)
-        => new(left.Value - right.Value);
+    public static Square operator -(Square left, Square right) => new(left.Value - right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator -(Square left, int right)
-        => new(left.Value - (byte)right);
+    public static Square operator -(Square left, int right) => new(left.Value - (byte)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator -(Square left, Direction right)
-        => new(left.Value - (byte)right.Value);
+    public static Square operator -(Square left, Direction right) => new(left.Value - (byte)right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator -(Square left, Directions right)
-        => new(left.Value - (byte)right);
+    public static Square operator -(Square left, Directions right) => new(left.Value - (byte)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator ++(Square sq)
-        => new(sq.Value + 1);
+    public static Square operator ++(Square sq) => new(sq.Value + 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator --(Square sq)
-        => new(sq.Value - 1);
+    public static Square operator --(Square sq) => new(sq.Value - 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(Square left, ulong right)
-        => new(left.AsBb().Value & right);
+    public static BitBoard operator &(Square left, ulong right) => new(left.AsBb().Value & right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator &(ulong left, Square right)
-        => left & right.AsBb();
+    public static BitBoard operator &(ulong left, Square right) => left & right.AsBb();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator |(Square left, Square right)
-        => new(left.AsBb().Value | right.AsBb().Value);
+    public static BitBoard operator |(Square left, Square right) => new(left.AsBb().Value | right.AsBb().Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator |(ulong left, Square right)
-        => new(left | right.AsBb().Value);
+    public static BitBoard operator |(ulong left, Square right) => new(left | right.AsBb().Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator |(Square left, int right)
-        => left.AsInt() | right;
+    public static int operator |(Square left, int right) => left.AsInt() | right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator ~(Square left)
-        => ~left.AsBb();
+    public static BitBoard operator ~(Square left) => ~left.AsBb();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator >> (Square left, int right)
-        => left.AsInt() >> right;
+    public static int operator >> (Square left, int right) => left.AsInt() >> right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >(Square left, Square right)
-        => left.Value > right.Value;
+    public static bool operator >(Square left, Square right) => left.Value > right.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <(Square left, Square right)
-        => left.Value < right.Value;
+    public static bool operator <(Square left, Square right) => left.Value < right.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <=(Square left, Square right)
-        => left.Value <= right.Value;
+    public static bool operator <=(Square left, Square right) => left.Value <= right.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >=(Square left, Square right)
-        => left.Value >= right.Value;
+    public static bool operator >=(Square left, Square right) => left.Value >= right.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator true(Square sq)
-        => sq.IsOk;
+    public static bool operator true(Square sq) => sq.IsOk;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator false(Square sq)
-        => !sq.IsOk;
+    public static bool operator false(Square sq) => !sq.IsOk;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square Relative(Player p)
-        => (int)Value ^ (p.Side * 56);
+    public Square Relative(Player p) => (int)Value ^ (p.Side * 56);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square Max(Square other)
-        => Value > other.Value ? this : other;
+    public Square Max(Square other) => Value > other.Value ? this : other;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square Min(Square other)
-        => Value <= other.Value ? this : other;
+    public Square Min(Square other) => Value <= other.Value ? this : other;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string ToString()
-        => SquareStrings[AsInt()];
+    public override string ToString() => SquareStrings[AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string ToString(string format, IFormatProvider formatProvider)
@@ -353,24 +309,19 @@ public bool TryFormat(
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(Square other)
-        => Value == other.Value;
+    public bool Equals(Square other) => Value == other.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => AsInt();
+    public override int GetHashCode() => AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int AsInt()
-        => (int)Value;
+    public int AsInt() => (int)Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard AsBb()
-        => BitBoards.BbSquares[AsInt()];
+    public BitBoard AsBb() => BitBoards.BbSquares[AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank RelativeRank(Player p)
-        => Rank.Relative(p);
+    public Rank RelativeRank(Player p) => Rank.Relative(p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool IsOppositeColor(Square other)
@@ -388,16 +339,13 @@ public int CompareTo(Square other)
     /// Swap A1 &lt;-&gt; H1
     /// </summary>
     /// <returns>Flipped square by File</returns>
-    public Square FlipFile()
-        => AsInt() ^ Squares.h1.AsInt();
+    public Square FlipFile() => AsInt() ^ Squares.h1.AsInt();
 
     /// <summary>
     /// Swap A1 &lt;-&gt; A8
     /// </summary>
     /// <returns>Flipped square by Rank</returns>
-    public Square FlipRank()
-        => AsInt() ^ Squares.a8.AsInt();
+    public Square FlipRank() => AsInt() ^ Squares.a8.AsInt();
 
-    public Player Color()
-        => ((AsInt() + Rank.AsInt()) ^ 1) & 1;
+    public Player Color() => ((AsInt() + Rank.AsInt()) ^ 1) & 1;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index d9dbd82c..8e5d0d1e 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -178,14 +178,14 @@ private static IEnumerable<string> ValidatePieceCount(IPosition pos)
 
     private static IEnumerable<string> ValidatePieceTypes(IPosition pos)
         => Piece.AllPieceTypes.SelectMany(p1 => Piece.AllPieceTypes
-                .Where(p2 => p1 != p2 && !(pos.Pieces(p1) & pos.Pieces(p2)).IsEmpty),
+                .Where(p2 => p1 != p2 && (pos.Pieces(p1) & pos.Pieces(p2)).IsNotEmpty),
             static (p1, p2) => $"piece types {p1} and {p2} doesn't align");
 
     private static IEnumerable<string> ValidateState(IPosition pos)
     {
         var state = pos.State;
 
-        if (state.Key.Key == 0 && !pos.Pieces().IsEmpty)
+        if (state.Key.Key == 0 && pos.PieceCount() != 0)
             yield return "state key is invalid";
 
         if (pos.PieceCount(PieceTypes.Pawn) == 0 && state.PawnKey != Zobrist.ZobristNoPawn)

From 542a90ce3d67b65f75609c0280a4d09e2aaeaad8 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 16 Apr 2023 10:04:13 +0200
Subject: [PATCH 036/119] Minor updates

- Added proper KillerMove factory
- Adjusted PolyglotBook factory
- Removed null-check for array fill
- Removed redundant initializer in KpkBitBase
- Removed some MathExtensions methods as they were just indirections of Math
- Minor formatting update for some types
- Updated readme
---
 README.md                                     |  25 +++-
 Rudzoft.ChessLib.sln                          |   5 +
 .../PiecesTests/PieceAttacksBishopTests.cs    |   2 +-
 .../PiecesTests/SliderMobilityFixture.cs      |  15 +-
 .../PiecesTests/SliderMobilityTests.cs        |   4 +-
 src/Rudzoft.ChessLib/Cuckoo.cs                |   4 +-
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |   2 +-
 .../Extensions/ArrayExtensions.cs             |   4 +-
 .../ChessLibServiceCollectionExtensions.cs    |   2 +-
 .../Extensions/MathExtensions.cs              |  20 ---
 .../Factories/IKillerMovesFactory.cs          |  36 +++++
 .../Factories/IPolyglotBookFactory.cs         |   3 +-
 .../Factories/KillerMovesFactory.cs           |  42 ++++++
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          |   4 +-
 src/Rudzoft.ChessLib/Position.cs              |  14 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |   2 +-
 .../Tables/KillerMoves/KillerMoves.cs         |   3 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |  24 +---
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 110 +++++++--------
 src/Rudzoft.ChessLib/Types/CastleRight.cs     |  15 +-
 src/Rudzoft.ChessLib/Types/Depth.cs           |  39 ++----
 src/Rudzoft.ChessLib/Types/Direction.cs       |  45 ++----
 src/Rudzoft.ChessLib/Types/File.cs            |   2 +-
 src/Rudzoft.ChessLib/Types/HashKey.cs         |  42 ++----
 src/Rudzoft.ChessLib/Types/MagicBB.cs         |  54 +++-----
 src/Rudzoft.ChessLib/Types/Move.cs            |  48 +++----
 src/Rudzoft.ChessLib/Types/Piece.cs           | 131 +++++++-----------
 src/Rudzoft.ChessLib/Types/Player.cs          |  60 +++-----
 src/Rudzoft.ChessLib/Types/Rank.cs            |   2 +-
 src/Rudzoft.ChessLib/Types/RootMove.cs        |  24 +---
 src/Rudzoft.ChessLib/Types/Score.cs           |  77 ++++------
 src/Rudzoft.ChessLib/Types/ValMove.cs         |  27 ++--
 src/Rudzoft.ChessLib/Types/Value.cs           | 102 +++++---------
 .../Validation/PositionValidator.cs           |   4 +-
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |   2 +-
 35 files changed, 434 insertions(+), 561 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib/Factories/IKillerMovesFactory.cs
 create mode 100644 src/Rudzoft.ChessLib/Factories/KillerMovesFactory.cs

diff --git a/README.md b/README.md
index 82d1a816..cb181633 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ A C# chess data library with complete move generation and all needed custom type
 
 ## Requirements
 
-* .NET 6.0+
+* .NET 7.0+
 
 ## What is this for?
 
@@ -73,6 +73,7 @@ Yes you can, it is designed with that in mind.
 * Pawn blockage algorithm
 * Cuckoo repetition algorithm
 * Polyglot book support
+* Basic PGN file import support
 * Plenty of unit tests to see how it works
 
 ### Perft
@@ -80,7 +81,7 @@ Yes you can, it is designed with that in mind.
 Perft console test program approximate timings to depth 6 for normal start position
 
 * AMD-FX 8350 = ~12.5 seconds. (without TT) (earlier version)
-* Intel i7-8086k = ~3.3 seconds
+* Intel i7-8086k = ~2.3 seconds
 
 ### Transposition Table
 
@@ -88,7 +89,7 @@ ph
 
 ### Move Generator
 
-Example
+#### Example
 
 ```c#
 // generate all legal moves for current position
@@ -99,6 +100,24 @@ var moveList = game.Pos.GenerateMoves();
 // ..
 ```
 
+#### MoveList example
+
+By using the MoveListPool you can avoid allocations and reuse the same MoveList instance.
+
+```c#
+var moveList = _moveListPool.Get();
+moveList.Generate(position, MoveType.Legal);
+var moves = moveList.Get();
+
+foreach (var move in moves)
+{
+    // do something
+}
+
+_moveListPool.Return(moveList);
+```
+
+
 ## What is not included?
 
 * Evaluation (except KPK)
diff --git a/Rudzoft.ChessLib.sln b/Rudzoft.ChessLib.sln
index e1496d68..403cdb89 100644
--- a/Rudzoft.ChessLib.sln
+++ b/Rudzoft.ChessLib.sln
@@ -29,6 +29,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN", "src
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN.Test", "src\Rudzoft.ChessLib.PGN.Test\Rudzoft.ChessLib.PGN.Test.csproj", "{556ADC0D-074D-4B8F-9E45-1275342FCB74}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solutionitems", "solutionitems", "{5B4D0B75-CE2C-45E2-92D3-987704C20001}"
+	ProjectSection(SolutionItems) = preProject
+		README.md = README.md
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs
index 96e9c8d9..8ce4ec79 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs
@@ -62,7 +62,7 @@ public void BishopBorderBlocked()
         while (border)
         {
             var sq = BitBoards.PopLsb(ref border);
-            var attacks = sq.BishopAttacks(borderInner);
+            var attacks = sq.BishopAttacks(in borderInner);
             Assert.False(attacks.IsEmpty);
             var expected = corners & sq ? expectedCorner : expectedSide;
             var actual = attacks.Count;
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
index fa30b72d..02997d82 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
@@ -36,8 +35,18 @@ public sealed class SliderMobilityFixture
 
     public int[] RookExpected { get; } = { 14, 14, 14, 14 };
 
-    public Func<Square, BitBoard, BitBoard>[] SliderAttacks { get; } = { MagicBB.BishopAttacks, MagicBB.RookAttacks, MagicBB.QueenAttacks };
+    public BitBoard SliderAttacks(PieceTypes pt, Square sq, in BitBoard occ)
+    {
+        var index = SliderIndex(pt);
+        return index switch
 
-    public int SliderIndex(PieceTypes pt)
+        {
+            0 => sq.BishopAttacks(in occ),
+            1 => sq.RookAttacks(in occ),
+            _ => sq.QueenAttacks(in occ)
+        };
+    }
+
+    private static int SliderIndex(PieceTypes pt)
         => pt.AsInt() - 3;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
index 776b6f94..e3b174ca 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
@@ -53,11 +53,11 @@ public SliderMobilityTests(SliderMobilityFixture fixture)
     [InlineData(Delta, PieceTypes.Queen, 27)]
     public void BishopMobility(ulong pattern, PieceTypes pt, int expectedMobility)
     {
-        var sliderIndex = _fixture.SliderIndex(pt);
+        var empty = BitBoard.Empty;
         var bb = new BitBoard(pattern);
 
         var expected = bb.Count * expectedMobility;
-        var actual = bb.Select(x => _fixture.SliderAttacks[sliderIndex](x, BitBoard.Empty).Count).Sum();
+        var actual = bb.Select(x => _fixture.SliderAttacks(pt, x, in empty).Count).Sum();
 
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 67e38ebf..55bf3f91 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -46,8 +46,8 @@ public static class Cuckoo
     static Cuckoo()
     {
         var count = 0;
-        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.AllPieces);
-        for (var i = 0; i < Piece.AllPieces.Length; i++) {
+        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.All);
+        for (var i = 0; i < Piece.All.Length; i++) {
             var pc = Unsafe.Add(ref piecesSpace, i);
             var bb = BitBoards.AllSquares;
             while (bb)
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index 5f4b7c26..f46d9842 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -58,7 +58,7 @@ public KpkBitBase()
 
         // Iterate through the positions until none of the unknown positions can be
         // changed to either wins or draws (15 cycles needed).
-        var repeat = 1;
+        int repeat;
 
         ref var dbSpace = ref MemoryMarshal.GetReference(db);
 
diff --git a/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
index f5d97340..0ccce26b 100644
--- a/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Diagnostics;
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Extensions;
@@ -34,8 +35,7 @@ public static class ArrayExtensions
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static void Fill<T>(this T[] arr, in T value)
     {
-        if (arr == null)
-            throw new ArgumentNullException(nameof(arr));
+        Debug.Assert(arr != null);
         Array.Fill(arr, value);
     }
 
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index cbfbf895..13c02cf1 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -85,7 +85,7 @@ public static IServiceCollection AddChessLib(
                 uci.Initialize();
                 return uci;
             })
-            .AddTransient(static _ => KillerMoves.Create(64))
+            .AddTransient<IKillerMovesFactory, KillerMovesFactory>()
             .AddSingleton<ISearchParameters, SearchParameters>()
             .AddSingleton<IValues, Values>()
             .AddSingleton<IKpkBitBase, KpkBitBase>()
diff --git a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
index 2efe716b..e49f0c56 100644
--- a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
@@ -50,26 +50,6 @@ public static bool InBetween(this char v, char min, char max)
     public static bool InBetween(this uint v, int min, int max)
         => v - (uint)min <= (uint)max - (uint)min;
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int Clamp(this int v, int min, int max)
-        => v < min ? min : v > max ? max : v;
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static double Clamp(this double v, double min, double max)
-        => v < min ? min : v > max ? max : v;
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int Abs(this int @this)
-        => Math.Abs(@this);
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int Max(this int @this, int value)
-        => Math.Max(@this, value);
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static double Round(this double @this, int digits)
-        => Math.Round(@this, digits);
-
     /// <summary>
     /// Converts a bool to a byte (0 or 1)
     ///
diff --git a/src/Rudzoft.ChessLib/Factories/IKillerMovesFactory.cs b/src/Rudzoft.ChessLib/Factories/IKillerMovesFactory.cs
new file mode 100644
index 00000000..9bdb9d25
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Factories/IKillerMovesFactory.cs
@@ -0,0 +1,36 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Rudzoft.ChessLib.Tables.KillerMoves;
+
+namespace Rudzoft.ChessLib.Factories;
+
+public interface IKillerMovesFactory : IServiceFactory<IKillerMoves>
+{
+    const int DefaultDepth = 64;
+    
+    IKillerMoves Create(int depth = DefaultDepth);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs b/src/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs
index 6a353d4e..f62a002d 100644
--- a/src/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs
@@ -28,8 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Factories;
 
-public interface IPolyglotBookFactory
+public interface IPolyglotBookFactory : IServiceFactory<IPolyglotBook>
 {
     IPolyglotBook Create(string bookFile);
-    IPolyglotBook Create();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Factories/KillerMovesFactory.cs b/src/Rudzoft.ChessLib/Factories/KillerMovesFactory.cs
new file mode 100644
index 00000000..96bc996e
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Factories/KillerMovesFactory.cs
@@ -0,0 +1,42 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Rudzoft.ChessLib.Tables.KillerMoves;
+
+namespace Rudzoft.ChessLib.Factories;
+
+public sealed class KillerMovesFactory : IKillerMovesFactory
+{
+    public IKillerMoves Create()
+    {
+        return Create(IKillerMovesFactory.DefaultDepth);
+    }
+
+    public IKillerMoves Create(int depth)
+    {
+        return KillerMoves.Create(depth);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index 272b08c3..39fdaac5 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -97,8 +97,8 @@ private static void InitializePst(in IRKiss rnd)
         for (var i = 0; i < ZobristPst.Length; i++)
             ZobristPst[i] = new HashKey[Square.Count];
 
-        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.AllPieces);
-        for (var i = 0; i < Piece.AllPieces.Length; ++i)
+        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.All);
+        for (var i = 0; i < Piece.All.Length; ++i)
         {
             var pc = Unsafe.Add(ref piecesSpace, i);
             var bb = BitBoards.AllSquares;
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 6dc4f41d..9b486da2 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -148,11 +148,11 @@ public bool AttackedByPawn(Square sq, Player p) =>
     public bool AttackedBySlider(Square sq, Player p)
     {
         var occupied = Board.Pieces();
-        var rookAttacks = sq.RookAttacks(occupied);
+        var rookAttacks = sq.RookAttacks(in occupied);
         if (Board.Pieces(p, PieceTypes.Rook) & rookAttacks)
             return true;
 
-        var bishopAttacks = sq.BishopAttacks(occupied);
+        var bishopAttacks = sq.BishopAttacks(in occupied);
         if (Board.Pieces(p, PieceTypes.Bishop) & bishopAttacks)
             return true;
 
@@ -377,9 +377,9 @@ public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
         {
             PieceTypes.Knight => pt.PseudoAttacks(sq),
             PieceTypes.King => pt.PseudoAttacks(sq),
-            PieceTypes.Bishop => sq.BishopAttacks(occ),
-            PieceTypes.Rook => sq.RookAttacks(occ),
-            PieceTypes.Queen => sq.QueenAttacks(occ),
+            PieceTypes.Bishop => sq.BishopAttacks(in occ),
+            PieceTypes.Rook => sq.RookAttacks(in occ),
+            PieceTypes.Queen => sq.QueenAttacks(in occ),
             _ => BitBoard.Empty
         };
     }
@@ -1487,8 +1487,8 @@ private void SetState()
         key ^= State.CastlelingRights.Key();
 
         var materialKey = HashKey.Empty;
-        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.AllPieces);
-        for (var i = 0; i < Piece.AllPieces.Length; i++)
+        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.All);
+        for (var i = 0; i < Piece.All.Length; i++)
         {
             var pc = Unsafe.Add(ref piecesSpace, i);
             for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 53707d7f..62d8e1f8 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -57,7 +57,7 @@
     <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.4" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
     <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
     <PackageReference Include="ZString" Version="2.5.0" />
   </ItemGroup>
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
index 0f1ca575..4a4b7303 100644
--- a/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
@@ -37,8 +37,7 @@ public sealed class KillerMoves : IKillerMoves
     private readonly PieceSquare[][] _killerMoves;
     private readonly int _maxDepth;
 
-    public static IKillerMoves Create(int maxDepth)
-        => new KillerMoves(maxDepth).Initialize();
+    public static IKillerMoves Create(int maxDepth) => new KillerMoves(maxDepth).Initialize();
 
     private KillerMoves(int maxDepth)
     {
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index ffe551ad..3ac6d45e 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -220,31 +220,17 @@ public IEnumerator<Square> GetEnumerator()
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    IEnumerator IEnumerable.GetEnumerator()
-        => GetEnumerator();
+    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string ToString()
-        => BitBoards.PrintBitBoard(in this, Value.ToString());
+    public override string ToString() => BitBoards.PrintBitBoard(in this, Value.ToString());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void ToString(TextWriter textWriter)
-    {
-        try
-        {
-            textWriter.WriteLine(ToString());
-        }
-        catch (IOException ioe)
-        {
-            throw new IOException("Writer is closed", ioe);
-        }
-    }
+    public void ToString(TextWriter textWriter) => textWriter.WriteLine(ToString());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(BitBoard other)
-        => Value == other.Value;
+    public bool Equals(BitBoard other) => Value == other.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => Value.GetHashCode();
+    public override int GetHashCode() => Value.GetHashCode();
 }
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 33889187..0ec350ab 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -153,21 +153,21 @@ public static class BitBoards
     /// are a special case, as index range 0,sq are for White and 1,sq are for Black. This is
     /// possible because index 0 is NoPiece type.
     /// </summary>
-    private static readonly BitBoard[][] PseudoAttacksBB;
+    private static readonly BitBoard[][] PseudoAttacksBB = new BitBoard[PieceTypes.PieceTypeNb.AsInt()][];
 
-    private static readonly BitBoard[][] PawnAttackSpanBB;
+    private static readonly BitBoard[][] PawnAttackSpanBB = new BitBoard[Player.Count][];
 
-    private static readonly BitBoard[][] PassedPawnMaskBB;
+    private static readonly BitBoard[][] PassedPawnMaskBB = new BitBoard[Player.Count][];
 
-    private static readonly BitBoard[][] ForwardRanksBB;
+    private static readonly BitBoard[][] ForwardRanksBB = new BitBoard[Player.Count][];
 
-    private static readonly BitBoard[][] ForwardFileBB;
+    private static readonly BitBoard[][] ForwardFileBB = new BitBoard[Player.Count][];
 
-    private static readonly BitBoard[][] KingRingBB;
+    private static readonly BitBoard[][] KingRingBB = new BitBoard[Player.Count][];
 
-    private static readonly BitBoard[][] BetweenBB;
+    private static readonly BitBoard[][] BetweenBB = new BitBoard[Square.Count][];
 
-    private static readonly BitBoard[][] LineBB;
+    private static readonly BitBoard[][] LineBB = new BitBoard[Square.Count][];
 
     private static readonly BitBoard[] AdjacentFilesBB =
     {
@@ -175,9 +175,9 @@ public static class BitBoards
         FileFBB | FileHBB, FileGBB
     };
 
-    private static readonly int[][] SquareDistance; // chebyshev distance
+    private static readonly int[][] SquareDistance = new int[Square.Count][]; // chebyshev distance
 
-    private static readonly BitBoard[][] DistanceRingBB;
+    private static readonly BitBoard[][] DistanceRingBB = new BitBoard[Square.Count][];
 
     private static readonly BitBoard[] SlotFileBB;
 
@@ -189,43 +189,33 @@ public static class BitBoards
 
     static BitBoards()
     {
-        PseudoAttacksBB = new BitBoard[PieceTypes.PieceTypeNb.AsInt()][];
         for (var i = 0; i < PseudoAttacksBB.Length; i++)
             PseudoAttacksBB[i] = new BitBoard[Square.Count];
 
-        PawnAttackSpanBB = new BitBoard[Player.Count][];
         PawnAttackSpanBB[0] = new BitBoard[Square.Count];
         PawnAttackSpanBB[1] = new BitBoard[Square.Count];
 
-        PassedPawnMaskBB = new BitBoard[Player.Count][];
         PassedPawnMaskBB[0] = new BitBoard[Square.Count];
         PassedPawnMaskBB[1] = new BitBoard[Square.Count];
 
-        ForwardRanksBB = new BitBoard[Player.Count][];
         ForwardRanksBB[0] = new BitBoard[Square.Count];
         ForwardRanksBB[1] = new BitBoard[Square.Count];
 
-        ForwardFileBB = new BitBoard[Player.Count][];
         ForwardFileBB[0] = new BitBoard[Square.Count];
         ForwardFileBB[1] = new BitBoard[Square.Count];
 
-        KingRingBB = new BitBoard[Player.Count][];
         KingRingBB[0] = new BitBoard[Square.Count];
         KingRingBB[1] = new BitBoard[Square.Count];
 
-        BetweenBB = new BitBoard[Square.Count][];
         for (var i = 0; i < BetweenBB.Length; i++)
             BetweenBB[i] = new BitBoard[Square.Count];
 
-        LineBB = new BitBoard[Square.Count][];
         for (var i = 0; i < LineBB.Length; i++)
             LineBB[i] = new BitBoard[Square.Count];
 
-        SquareDistance = new int[Square.Count][];
         for (var i = 0; i < SquareDistance.Length; i++)
             SquareDistance[i] = new int[Square.Count];
 
-        DistanceRingBB = new BitBoard[Square.Count][];
         for (var i = 0; i < SquareDistance.Length; i++)
             DistanceRingBB[i] = new BitBoard[8];
 
@@ -263,18 +253,6 @@ static BitBoards()
         }
 
         // mini local helpers
-        static BitBoard ComputeKnightAttack(in BitBoard bb)
-        {
-            var res = (bb & ~(FileABB | FileBBB)) << 6;
-            res |= (bb & ~FileABB) << 15;
-            res |= (bb & ~FileHBB) << 17;
-            res |= (bb & ~(FileGBB | FileHBB)) << 10;
-            res |= (bb & ~(FileGBB | FileHBB)) >> 6;
-            res |= (bb & ~FileHBB) >> 15;
-            res |= (bb & ~FileABB) >> 17;
-            res |= (bb & ~(FileABB | FileBBB)) >> 10;
-            return res;
-        }
 
         Span<PieceTypes> validMagicPieces = stackalloc PieceTypes[] { PieceTypes.Bishop, PieceTypes.Rook };
 
@@ -284,7 +262,6 @@ static BitBoard ComputeKnightAttack(in BitBoard bb)
         {
             var s1 = PopLsb(ref bb);
             var sq = s1.AsInt();
-            var b = s1.AsBb();
 
             var file = s1.File;
 
@@ -293,39 +270,19 @@ static BitBoard ComputeKnightAttack(in BitBoard bb)
             while (bb2)
             {
                 var s2 = PopLsb(ref bb2);
-                var dist = (byte)distanceFile(s1, s2).Max(distanceRank(s1, s2));
+                var dist = Math.Max(distanceFile(s1, s2), distanceRank(s1, s2));
                 SquareDistance[sq][s2.AsInt()] = dist;
                 DistanceRingBB[sq][dist] |= s2;
             }
 
-            PseudoAttacksBB[0][sq] = b.NorthEastOne() | b.NorthWestOne();
-            PseudoAttacksBB[1][sq] = b.SouthWestOne() | b.SouthEastOne();
-
-            var pt = PieceTypes.Knight.AsInt();
-            PseudoAttacksBB[pt][sq] = ComputeKnightAttack(in b);
-
-            var bishopAttacks = s1.BishopAttacks(EmptyBitBoard);
-            var rookAttacks = s1.RookAttacks(EmptyBitBoard);
-
-            pt = PieceTypes.Bishop.AsInt();
-            PseudoAttacksBB[pt][sq] = bishopAttacks;
-
-            pt = PieceTypes.Rook.AsInt();
-            PseudoAttacksBB[pt][sq] = rookAttacks;
-
-            pt = PieceTypes.Queen.AsInt();
-            PseudoAttacksBB[pt][sq] = bishopAttacks | rookAttacks;
-
-            pt = PieceTypes.King.AsInt();
-            PseudoAttacksBB[pt][sq] = b.NorthOne() | b.SouthOne() | b.EastOne() | b.WestOne()
-                                      | b.NorthEastOne() | b.NorthWestOne() | b.SouthEastOne() | b.SouthWestOne();
+            InitializePseudoAttacks(s1);
 
             // Compute lines and betweens
             ref var magicPiecesSpace = ref MemoryMarshal.GetReference(validMagicPieces);
             for (var i = 0; i < validMagicPieces.Length; i++)
             {
                 var validMagicPiece = Unsafe.Add(ref magicPiecesSpace, i);
-                pt = validMagicPiece.AsInt();
+                var pt = validMagicPiece.AsInt();
                 var bb3 = AllSquares;
                 while (bb3)
                 {
@@ -350,10 +307,43 @@ static BitBoard ComputeKnightAttack(in BitBoard bb)
         {
             File.FileE.FileBB() | File.FileF.FileBB() | File.FileG.FileBB() | File.FileH.FileBB(), // King
             File.FileA.FileBB() | File.FileB.FileBB() | File.FileC.FileBB() | File.FileD.FileBB(), // Queen
-            File.FileC.FileBB() | File.FileD.FileBB() | File.FileE.FileBB() | File.FileF.FileBB() // Center
+            File.FileC.FileBB() | File.FileD.FileBB() | File.FileE.FileBB() | File.FileF.FileBB()  // Center
         };
     }
 
+    private static BitBoard ComputeKnightAttack(in BitBoard bb)
+    {
+        var res = (bb & ~(FileABB | FileBBB)) << 6;
+        res |= (bb & ~FileABB) << 15;
+        res |= (bb & ~FileHBB) << 17;
+        res |= (bb & ~(FileGBB | FileHBB)) << 10;
+        res |= (bb & ~(FileGBB | FileHBB)) >> 6;
+        res |= (bb & ~FileHBB) >> 15;
+        res |= (bb & ~FileABB) >> 17;
+        res |= (bb & ~(FileABB | FileBBB)) >> 10;
+        return res;
+    }
+
+    private static void InitializePseudoAttacks(Square sq)
+    {
+        var s = sq.AsInt();
+        var b = sq.AsBb();
+        var bishopAttacks = sq.BishopAttacks(EmptyBitBoard);
+        var rookAttacks = sq.RookAttacks(EmptyBitBoard);
+
+        // Pawns
+        PseudoAttacksBB[0][s] = b.NorthEastOne() | b.NorthWestOne();
+        PseudoAttacksBB[1][s] = b.SouthWestOne() | b.SouthEastOne();
+
+        PseudoAttacksBB[PieceTypes.Knight.AsInt()][s] = ComputeKnightAttack(in b);
+        PseudoAttacksBB[PieceTypes.Bishop.AsInt()][s] = bishopAttacks;
+        PseudoAttacksBB[PieceTypes.Rook.AsInt()][s] = rookAttacks;
+        PseudoAttacksBB[PieceTypes.Queen.AsInt()][s] = bishopAttacks | rookAttacks;
+        PseudoAttacksBB[PieceTypes.King.AsInt()][s] = b.NorthOne()       | b.SouthOne()     | b.EastOne()
+                                                      | b.WestOne()      | b.NorthEastOne() | b.NorthWestOne()
+                                                      | b.SouthEastOne() | b.SouthWestOne();
+    }
+
     private static void InitializeKingRing(Square s1, int sq, File file)
     {
         const int pt = (int)PieceTypes.King;
@@ -780,9 +770,9 @@ private static BitBoard GetAttacks(this in Square sq, PieceTypes pt, in BitBoard
         {
             PieceTypes.Knight => PseudoAttacksBB[pt.AsInt()][sq.AsInt()],
             PieceTypes.King => PseudoAttacksBB[pt.AsInt()][sq.AsInt()],
-            PieceTypes.Bishop => sq.BishopAttacks(occ),
-            PieceTypes.Rook => sq.RookAttacks(occ),
-            PieceTypes.Queen => sq.QueenAttacks(occ),
+            PieceTypes.Bishop => sq.BishopAttacks(in occ),
+            PieceTypes.Rook => sq.RookAttacks(in occ),
+            PieceTypes.Queen => sq.QueenAttacks(in occ),
             _ => EmptyBitBoard
         };
     }
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index 04ab304e..6dc72257 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -87,20 +87,21 @@ public static class CastleExtensions
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static CastleRights MakeCastleRights(this CastleRights cs, Player p)
-        => p.IsWhite
-            ? cs == CastleRights.Queen
+    {
+        if (p.IsWhite)
+            return cs == CastleRights.Queen
                 ? CastleRights.WhiteQueen
-                : CastleRights.WhiteKing
-            : cs == CastleRights.Queen
+                : CastleRights.WhiteKing;
+        else
+            return cs == CastleRights.Queen
                 ? CastleRights.BlackQueen
                 : CastleRights.BlackKing;
+    }
 }
 
 public readonly record struct CastleRight(CastleRights Rights)
 {
-    private CastleRight(int cr) : this((CastleRights)cr)
-    {
-    }
+    private CastleRight(int cr) : this((CastleRights)cr) { }
 
     public bool IsNone => Rights == CastleRights.None;
 
diff --git a/src/Rudzoft.ChessLib/Types/Depth.cs b/src/Rudzoft.ChessLib/Types/Depth.cs
index 3562a8a3..af27e288 100644
--- a/src/Rudzoft.ChessLib/Types/Depth.cs
+++ b/src/Rudzoft.ChessLib/Types/Depth.cs
@@ -72,28 +72,22 @@ public struct Depth : IEquatable<Depth>
     public static Depth Offset => new(Depths.Offset);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Depth(string value)
-        => new(Maths.ToIntegral(value));
+    public static implicit operator Depth(string value) => new(Maths.ToIntegral(value));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Depth(Depths value)
-        => new(value);
+    public static implicit operator Depth(Depths value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Depth(int value)
-        => new(value);
+    public static implicit operator Depth(int value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Depth(byte value)
-        => new(value);
+    public static implicit operator Depth(byte value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(Depth left, Depth right)
-        => left.Value == right.Value;
+    public static bool operator ==(Depth left, Depth right) => left.Value == right.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(Depth left, Depth right)
-        => left.Value != right.Value;
+    public static bool operator !=(Depth left, Depth right) => left.Value != right.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool operator <=(Depth left, Depth right) => left.Value <= right.Value;
@@ -117,30 +111,23 @@ public static implicit operator Depth(byte value)
     public static Depth operator -(Depth left, Depth right) => new(left.Value - right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(Depth left, int right)
-        => left.Value == right;
+    public static bool operator ==(Depth left, int right) => left.Value == right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(Depth left, int right)
-        => left.Value != right;
+    public static bool operator !=(Depth left, int right) => left.Value != right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(Depth left, Depths right)
-        => left.Value == right.AsInt();
+    public static bool operator ==(Depth left, Depths right) => left.Value == right.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(Depth left, Depths right)
-        => left.Value != right.AsInt();
+    public static bool operator !=(Depth left, Depths right) => left.Value != right.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(Depth other)
-        => Value == other.Value;
+    public bool Equals(Depth other) => Value == other.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override bool Equals(object obj)
-        => obj is Depth other && Equals(other);
+    public override bool Equals(object obj) => obj is Depth other && Equals(other);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => Value;
+    public override int GetHashCode() => Value;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Direction.cs b/src/Rudzoft.ChessLib/Types/Direction.cs
index f2bfaa5b..ddd347c3 100644
--- a/src/Rudzoft.ChessLib/Types/Direction.cs
+++ b/src/Rudzoft.ChessLib/Types/Direction.cs
@@ -76,61 +76,46 @@ public readonly record struct Direction(Directions Value)
     public static Direction None { get; } = new(Directions.NoDirection);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private Direction(int d)
-        : this((Directions)d) { }
+    private Direction(int d) : this((Directions)d) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Direction(int value)
-        => new(value);
+    public static implicit operator Direction(int value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Direction(Directions value)
-        => new(value);
+    public static implicit operator Direction(Directions value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Direction operator +(Direction left, Direction right)
-        => new(left.Value + (int)right.Value);
+    public static Direction operator +(Direction left, Direction right) => new(left.Value + (int)right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Direction operator +(Direction left, Directions right)
-        => new(left.Value + (int)right);
+    public static Direction operator +(Direction left, Directions right) => new(left.Value + (int)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Direction operator +(Direction left, int right)
-        => new(left.Value + right);
+    public static Direction operator +(Direction left, int right) => new(left.Value + right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Direction operator -(Direction left, Direction right)
-        => new(left.Value - (int)right.Value);
+    public static Direction operator -(Direction left, Direction right) => new(left.Value - (int)right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Direction operator -(Direction left, Directions right)
-        => new(left.Value - (int)right);
+    public static Direction operator -(Direction left, Directions right) => new(left.Value - (int)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Direction operator -(Direction left, int right)
-        => new(left.Value - right);
+    public static Direction operator -(Direction left, int right) => new(left.Value - right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Direction operator *(Direction left, Direction right)
-        => new((int)left.Value * (int)right.Value);
+    public static Direction operator *(Direction left, Direction right) => new((int)left.Value * (int)right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Direction operator *(Direction left, Directions right)
-        => new((int)left.Value * (int)right);
+    public static Direction operator *(Direction left, Directions right) => new((int)left.Value * (int)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Direction operator *(Direction left, int right)
-        => new((int)left.Value * right);
+    public static Direction operator *(Direction left, int right) => new((int)left.Value * right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(Direction other)
-        => Value == other.Value;
+    public bool Equals(Direction other) => Value == other.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => AsInt();
+    public override int GetHashCode() => AsInt();
 
-    private int AsInt()
-        => (int)Value;
+    private int AsInt() => (int)Value;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index 708df2e8..8c79b4c4 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -191,7 +191,7 @@ public bool TryFormat(
     public int EdgeDistance() => Math.Min(AsInt() - FileA.AsInt(), FileH.AsInt() - AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public File Clamp(File min, File max) => new(Value.AsInt().Clamp(min.AsInt(), max.AsInt()));
+    public File Clamp(File min, File max) => new(Math.Clamp(Value.AsInt(), min.AsInt(), max.AsInt()));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int Distance(File other) => Math.Abs(AsInt() - other.AsInt());
diff --git a/src/Rudzoft.ChessLib/Types/HashKey.cs b/src/Rudzoft.ChessLib/Types/HashKey.cs
index 1d4e40ad..672e9c01 100644
--- a/src/Rudzoft.ChessLib/Types/HashKey.cs
+++ b/src/Rudzoft.ChessLib/Types/HashKey.cs
@@ -60,64 +60,50 @@ private HashKey(uint key32)
     public static HashKey Empty => new();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator HashKey(ulong value)
-        => new(value);
+    public static implicit operator HashKey(ulong value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator HashKey(uint value)
-        => new(value);
+    public static implicit operator HashKey(uint value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(HashKey left, HashKey right)
-        => left.Key == right.Key;
+    public static bool operator ==(HashKey left, HashKey right) => left.Key == right.Key;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(HashKey left, HashKey right)
-        => left.Key != right.Key;
+    public static bool operator !=(HashKey left, HashKey right) => left.Key != right.Key;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey operator >> (HashKey left, int right)
-        => new(left.Key >> right);
+    public static HashKey operator >> (HashKey left, int right) => new(left.Key >> right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey operator <<(HashKey left, int right)
-        => new(left.Key << right);
+    public static HashKey operator <<(HashKey left, int right) => new(left.Key << right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey operator &(HashKey left, int right)
-        => new(left.Key & (ulong)right);
+    public static HashKey operator &(HashKey left, int right) => new(left.Key & (ulong)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey operator ^(HashKey left, int right)
-        => new(left.Key ^ (ulong)right);
+    public static HashKey operator ^(HashKey left, int right) => new(left.Key ^ (ulong)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey operator ^(HashKey left, HashKey right)
-        => new(left.Key ^ right.Key);
+    public static HashKey operator ^(HashKey left, HashKey right) => new(left.Key ^ right.Key);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey operator ^(HashKey left, ulong right)
-        => new(left.Key ^ right);
+    public static HashKey operator ^(HashKey left, ulong right) => new(left.Key ^ right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static HashKey operator ^(HashKey left, CastleRights right)
         => new(left.Key ^ right.GetZobristCastleling().Key);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey operator ^(HashKey left, File right)
-        => new(left.Key ^ right.GetZobristEnPassant().Key);
+    public static HashKey operator ^(HashKey left, File right) => new(left.Key ^ right.GetZobristEnPassant().Key);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(HashKey other)
-        => Key == other.Key;
+    public bool Equals(HashKey other) => Key == other.Key;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override bool Equals(object obj)
-        => obj is HashKey other && Equals(other);
+    public override bool Equals(object obj) => obj is HashKey other && Equals(other);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => Key.GetHashCode();
+    public override int GetHashCode() => Key.GetHashCode();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string ToString() => $"0x{Key:X}";
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index dd60f11f..2a84970a 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -47,7 +47,7 @@ public static class MagicBB
 
     private static readonly BitBoard[][] MagicBishopDb = new BitBoard[Square.Count][];
 
-    private static readonly ulong[] BishopMagics =
+    private static readonly Memory<ulong> BishopMagics = new[]
     {
         0x0002020202020200UL, 0x0002020202020000UL, 0x0004010202000000UL, 0x0004040080000000UL,
         0x0001104000000000UL, 0x0000821040000000UL, 0x0000410410400000UL, 0x0000104104104000UL,
@@ -67,7 +67,7 @@ public static class MagicBB
         0x0000000010020200UL, 0x0000000404080200UL, 0x0000040404040400UL, 0x0002020202020200UL
     };
 
-    private static readonly ulong[] BishopMask =
+    private static readonly Memory<ulong> BishopMask = new[]
     {
         0x0040201008040200UL, 0x0000402010080400UL, 0x0000004020100A00UL, 0x0000000040221400UL,
         0x0000000002442800UL, 0x0000000204085000UL, 0x0000020408102000UL, 0x0002040810204000UL,
@@ -89,7 +89,7 @@ public static class MagicBB
 
     private static readonly BitBoard[][] MagicRookDb = new BitBoard[Square.Count][];
 
-    private static readonly ulong[] RookMagics =
+    private static readonly Memory<ulong> RookMagics = new[]
     {
         0x0080001020400080UL, 0x0040001000200040UL, 0x0080081000200080UL, 0x0080040800100080UL,
         0x0080020400080080UL, 0x0080010200040080UL, 0x0080008001000200UL, 0x0080002040800100UL,
@@ -109,7 +109,7 @@ public static class MagicBB
         0x0001000204080011UL, 0x0001000204000801UL, 0x0001000082000401UL, 0x0000002040810402UL
     };
 
-    private static readonly ulong[] RookMask =
+    private static readonly Memory<ulong> RookMask = new[]
     {
         0x000101010101017EUL, 0x000202020202027CUL, 0x000404040404047AUL, 0x0008080808080876UL,
         0x001010101010106EUL, 0x002020202020205EUL, 0x004040404040403EUL, 0x008080808080807EUL,
@@ -155,18 +155,18 @@ static MagicBB()
 
         for (var i = 0; i < squares.Length; ++i)
         {
-            var numSquares = InitSquares(squares, BishopMask[i], initMagicMovesDb);
+            var numSquares = InitSquares(squares, BishopMask.Span[i], initMagicMovesDb);
             for (var temp = ulong.MinValue; temp < One << numSquares; ++temp)
             {
                 var occ = InitMagicMovesOccupancy(squares[..numSquares], in temp);
-                MagicBishopDb[i][occ * BishopMagics[i] >> 55] = InitmagicmovesBmoves(i, in occ);
+                MagicBishopDb[i][occ * BishopMagics.Span[i] >> 55] = InitmagicmovesBmoves(i, in occ);
             }
 
-            numSquares = InitSquares(squares, RookMask[i], initMagicMovesDb);
+            numSquares = InitSquares(squares, RookMask.Span[i], initMagicMovesDb);
             for (var temp = ulong.MinValue; temp < One << numSquares; ++temp)
             {
                 var occ = InitMagicMovesOccupancy(squares[..numSquares], in temp);
-                MagicRookDb[i][occ * RookMagics[i] >> 52] = InitmagicmovesRmoves(i, in occ);
+                MagicRookDb[i][occ * RookMagics.Span[i] >> 52] = InitmagicmovesRmoves(i, in occ);
             }
         }
     }
@@ -196,14 +196,14 @@ private static ulong InitMagicMovesOccupancy(ReadOnlySpan<int> squares, in ulong
         return ret;
     }
 
-    public static BitBoard BishopAttacks(this Square square, BitBoard occupied)
-        => MagicBishopDb[square.AsInt()][(occupied.Value & BishopMask[square.AsInt()]) * BishopMagics[square.AsInt()] >> 55];
+    public static BitBoard BishopAttacks(this Square square, in BitBoard occupied)
+        => MagicBishopDb[square.AsInt()][(occupied.Value & BishopMask.Span[square.AsInt()]) * BishopMagics.Span[square.AsInt()] >> 55];
 
-    public static BitBoard RookAttacks(this Square square, BitBoard occupied)
-        => MagicRookDb[square.AsInt()][(occupied.Value & RookMask[square.AsInt()]) * RookMagics[square.AsInt()] >> 52];
+    public static BitBoard RookAttacks(this Square square, in BitBoard occupied)
+        => MagicRookDb[square.AsInt()][(occupied.Value & RookMask.Span[square.AsInt()]) * RookMagics.Span[square.AsInt()] >> 52];
 
-    public static BitBoard QueenAttacks(this Square square, BitBoard occupied)
-        => square.BishopAttacks(occupied) | square.RookAttacks(occupied);
+    public static BitBoard QueenAttacks(this Square square, in BitBoard occupied)
+        => square.BishopAttacks(in occupied) | square.RookAttacks(in occupied);
 
     private static ulong InitmagicmovesRmoves(int square, in ulong occ)
     {
@@ -215,16 +215,14 @@ private static ulong InitmagicmovesRmoves(int square, in ulong occ)
         {
             bit <<= 8;
             ret |= bit;
-        }
-        while (bit != 0 && (bit & occ) == ulong.MinValue);
+        } while (bit != 0 && (bit & occ) == ulong.MinValue);
 
         bit = One << square;
         do
         {
             bit >>= 8;
             ret |= bit;
-        }
-        while (bit != 0 && (bit & occ) == ulong.MinValue);
+        } while (bit != 0 && (bit & occ) == ulong.MinValue);
 
         bit = One << square;
         do
@@ -234,8 +232,7 @@ private static ulong InitmagicmovesRmoves(int square, in ulong occ)
                 ret |= bit;
             else
                 break;
-        }
-        while ((bit & occ) == ulong.MinValue);
+        } while ((bit & occ) == ulong.MinValue);
 
         bit = One << square;
         do
@@ -245,8 +242,7 @@ private static ulong InitmagicmovesRmoves(int square, in ulong occ)
                 ret |= bit;
             else
                 break;
-        }
-        while ((bit & occ) == ulong.MinValue);
+        } while ((bit & occ) == ulong.MinValue);
 
         return ret;
     }
@@ -266,8 +262,7 @@ private static ulong InitmagicmovesBmoves(int square, in ulong occ)
                 ret |= bit;
             else
                 break;
-        }
-        while (bit != 0 && (bit & occ) == ulong.MinValue);
+        } while (bit != 0 && (bit & occ) == ulong.MinValue);
 
         bit = One << square;
         bit2 = bit;
@@ -279,8 +274,7 @@ private static ulong InitmagicmovesBmoves(int square, in ulong occ)
                 ret |= bit;
             else
                 break;
-        }
-        while (bit != 0 && (bit & occ) == ulong.MinValue);
+        } while (bit != 0 && (bit & occ) == ulong.MinValue);
 
         bit = One << square;
         bit2 = bit;
@@ -292,8 +286,7 @@ private static ulong InitmagicmovesBmoves(int square, in ulong occ)
                 ret |= bit;
             else
                 break;
-        }
-        while (bit != 0 && (bit & occ) == ulong.MinValue);
+        } while (bit != 0 && (bit & occ) == ulong.MinValue);
 
         bit = One << square;
         bit2 = bit;
@@ -305,9 +298,8 @@ private static ulong InitmagicmovesBmoves(int square, in ulong occ)
                 ret |= bit;
             else
                 break;
-        }
-        while (bit != 0 && (bit & occ) == ulong.MinValue);
+        } while (bit != 0 && (bit & occ) == ulong.MinValue);
 
         return ret;
     }
-}
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index 8f66d904..9ccabaaf 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -83,16 +83,13 @@ public static implicit operator Move(string value)
         => new(new Square(value[1] - '1', value[0] - 'a'), new Square(value[3] - '1', value[2] - 'a'));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Move(ValMove valMove)
-        => valMove.Move;
+    public static implicit operator Move(ValMove valMove) => valMove.Move;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Move(ushort value)
-        => new(value);
+    public static implicit operator Move(ushort value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Move Create(Square from, Square to)
-        => new(from, to);
+    public static Move Create(Square from, Square to) => new(from, to);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static int Create(Span<ValMove> moves, int index, Square from, ref BitBoard to)
@@ -107,44 +104,34 @@ public static Move Create(Square from, Square to, MoveTypes moveType, PieceTypes
         => new(from, to, moveType, promoPt);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square FromSquare()
-        => new((Data >> 6) & 0x3F);
+    public Square FromSquare() => new((Data >> 6) & 0x3F);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square ToSquare()
-        => new(Data & 0x3F);
+    public Square ToSquare() => new(Data & 0x3F);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public PieceTypes PromotedPieceType()
-        => (PieceTypes)(((Data >> 12) & 3) + 2);
+    public PieceTypes PromotedPieceType() => (PieceTypes)(((Data >> 12) & 3) + 2);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsQueenPromotion()
-        => PromotedPieceType() == PieceTypes.Queen;
+    public bool IsQueenPromotion() => PromotedPieceType() == PieceTypes.Queen;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public MoveTypes MoveType()
-        => (MoveTypes)(Data & (3 << 14));
+    public MoveTypes MoveType() => (MoveTypes)(Data & (3 << 14));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsType(MoveTypes moveType)
-        => MoveType() == moveType;
+    public bool IsType(MoveTypes moveType) => MoveType() == moveType;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsEnPassantMove()
-        => MoveType() == MoveTypes.Enpassant;
+    public bool IsEnPassantMove() => MoveType() == MoveTypes.Enpassant;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsCastleMove()
-        => MoveType() == MoveTypes.Castling;
+    public bool IsCastleMove() => MoveType() == MoveTypes.Castling;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsPromotionMove()
-        => MoveType() == MoveTypes.Promotion;
+    public bool IsPromotionMove() => MoveType() == MoveTypes.Promotion;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsNullMove()
-        => Data == 0;
+    public bool IsNullMove() => Data == 0;
 
     /// <summary>
     /// Makes no assumption of the legality of the move,
@@ -152,16 +139,13 @@ public bool IsNullMove()
     /// </summary>
     /// <returns>true if not the same from and to square</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsValidMove()
-        => FromSquare() != ToSquare();
+    public bool IsValidMove() => FromSquare() != ToSquare();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(Move other)
-        => Data == other.Data;
+    public bool Equals(Move other) => Data == other.Data;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => Data;
+    public override int GetHashCode() => Data;
 
     [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 6ce303d0..2370d99c 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -83,12 +83,10 @@ public static bool InBetween(this PieceTypes v, PieceTypes min, PieceTypes max)
 public readonly record struct Piece(Pieces Value)
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private Piece(int pc)
-        : this((Pieces)pc) { }
+    private Piece(int pc) : this((Pieces)pc) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private Piece(Piece pc)
-        : this(pc.Value) { }
+    private Piece(Piece pc) : this(pc.Value) { }
 
     public const int Count = (int)Pieces.PieceNb + 1;
 
@@ -110,23 +108,23 @@ private Piece(Piece pc)
     public static Piece BlackQueen { get; } = new(Pieces.BlackQueen);
     public static Piece BlackKing { get; } = new(Pieces.BlackKing);
 
-    public static Piece[] AllPieces { get; } =
+    public static Piece[] All { get; } =
     {
-        Pieces.WhitePawn,
-        Pieces.WhiteKnight,
-        Pieces.WhiteBishop,
-        Pieces.WhiteRook,
-        Pieces.WhiteQueen,
-        Pieces.WhiteKing,
-        Pieces.BlackPawn,
-        Pieces.BlackKnight,
-        Pieces.BlackBishop,
-        Pieces.BlackRook,
-        Pieces.BlackQueen,
-        Pieces.BlackKing
+        WhitePawn,
+        WhiteKnight,
+        WhiteBishop,
+        WhiteRook,
+        WhiteQueen,
+        WhiteKing,
+        BlackPawn,
+        BlackKnight,
+        BlackBishop,
+        BlackRook,
+        BlackQueen,
+        BlackKing
     };
-    
-    public static PieceTypes[] AllPieceTypes { get; } =
+
+    public static PieceTypes[] AllTypes { get; } =
     {
         PieceTypes.Pawn,
         PieceTypes.Knight,
@@ -137,106 +135,85 @@ private Piece(Piece pc)
     };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Piece(char value)
-        => new(GetPiece(value));
+    public static implicit operator Piece(char value) => new(GetPiece(value));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Piece(int value)
-        => new(value);
+    public static implicit operator Piece(int value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Piece(Pieces pc)
-        => new(pc);
+    public static implicit operator Piece(Pieces pc) => new(pc);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece operator ~(Piece pc)
-        => new(pc.AsInt() ^ 8);
+    public static Piece operator ~(Piece pc) => new(pc.AsInt() ^ 8);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece operator +(Piece left, Player right)
-        => new(left.Value + (byte)(right << 3));
+    public static Piece operator +(Piece left, Player right) => new(left.Value + (byte)(right << 3));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece operator >> (Piece left, int right)
-        => new((int)left.Value >> right);
+    public static Piece operator >> (Piece left, int right) => new((int)left.Value >> right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece operator <<(Piece left, int right)
-        => new((int)left.Value << right);
+    public static Piece operator <<(Piece left, int right) => new((int)left.Value << right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator ==(Piece left, Pieces right)
-        => left.Value == right;
+    public static bool operator ==(Piece left, Pieces right) => left.Value == right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator !=(Piece left, Pieces right)
-        => left.Value != right;
+    public static bool operator !=(Piece left, Pieces right) => left.Value != right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <=(Piece left, Pieces right)
-        => left.Value <= right;
+    public static bool operator <=(Piece left, Pieces right) => left.Value <= right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >=(Piece left, Pieces right)
-        => left.Value >= right;
+    public static bool operator >=(Piece left, Pieces right) => left.Value >= right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator <(Piece left, Pieces right)
-        => left.Value < right;
+    public static bool operator <(Piece left, Pieces right) => left.Value < right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator >(Piece left, Pieces right)
-        => left.Value > right;
+    public static bool operator >(Piece left, Pieces right) => left.Value > right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece operator ++(Piece left)
-        => new(left.Value + 1);
+    public static Piece operator ++(Piece left) => new(left.Value + 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece operator --(Piece left)
-        => new(left.Value - 1);
+    public static Piece operator --(Piece left) => new(left.Value - 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator true(Piece pc)
-        => pc.Value.AsInt() != EmptyPiece.AsInt();
+    public static bool operator true(Piece pc) => pc.Value.AsInt() != EmptyPiece.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator false(Piece pc)
-        => pc.Value.AsInt() == EmptyPiece.AsInt();
+    public static bool operator false(Piece pc) => pc.Value.AsInt() == EmptyPiece.AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Player ColorOf()
-        => new((int)Value >> 3);
+    public Player ColorOf() => new((int)Value >> 3);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(Piece other)
-        => Value == other.Value;
+    public bool Equals(Piece other) => Value == other.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override int GetHashCode()
-        => (int)Value << 16;
+    public override int GetHashCode() => (int)Value << 16;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string ToString()
-        => this.GetPieceString();
+    public override string ToString() => this.GetPieceString();
 
     private static Piece GetPiece(char character)
     {
         return character switch
         {
-            'P' => Pieces.WhitePawn,
-            'p' => Pieces.BlackPawn,
-            'N' => Pieces.WhiteKnight,
-            'B' => Pieces.WhiteBishop,
-            'R' => Pieces.WhiteRook,
-            'Q' => Pieces.WhiteQueen,
-            'K' => Pieces.WhiteKing,
-            'n' => Pieces.BlackKnight,
-            'b' => Pieces.BlackBishop,
-            'r' => Pieces.BlackRook,
-            'q' => Pieces.BlackQueen,
-            'k' => Pieces.BlackKing,
-            _ => Pieces.NoPiece
+            'P' => WhitePawn,
+            'p' => BlackPawn,
+            'N' => WhiteKnight,
+            'B' => WhiteBishop,
+            'R' => WhiteRook,
+            'Q' => WhiteQueen,
+            'K' => WhiteKing,
+            'n' => BlackKnight,
+            'b' => BlackBishop,
+            'r' => BlackRook,
+            'q' => BlackQueen,
+            'k' => BlackKing,
+            _ => EmptyPiece
         };
     }
 
@@ -248,10 +225,8 @@ public void Deconstruct(out PieceTypes pt, out Player p)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int AsInt()
-        => (int)Value;
+    public int AsInt() => (int)Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public PieceTypes Type()
-        => (PieceTypes)(AsInt() & 0x7);
+    public PieceTypes Type() => (PieceTypes)(AsInt() & 0x7);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Player.cs
index 81cc9aaf..6b6513c5 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Player.cs
@@ -66,8 +66,7 @@ public Player(Players p)
         : this((byte)p) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void Deconstruct(out byte side)
-        => side = Side;
+    public void Deconstruct(out byte side) => side = Side;
 
     public bool IsWhite => Side == byte.MinValue;
 
@@ -84,44 +83,34 @@ public void Deconstruct(out byte side)
     public const int Count = (int)Players.PlayerNb;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(int value)
-        => new((byte)value);
+    public static implicit operator Player(int value) => new((byte)value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(byte value)
-        => new(value);
+    public static implicit operator Player(byte value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(uint value)
-        => new((byte)value);
+    public static implicit operator Player(uint value) => new((byte)value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(Players p)
-        => new(p);
+    public static implicit operator Player(Players p) => new(p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(bool value)
-        => new(value.AsByte());
+    public static implicit operator Player(bool value) => new(value.AsByte());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Player Create(Players p)
-        => new(p);
+    public static Player Create(Players p) => new(p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Player operator ~(Player p)
-        => new(p.Side ^ 1);
+    public static Player operator ~(Player p) => new(p.Side ^ 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator <<(Player left, int right)
-        => left.Side << right;
+    public static int operator <<(Player left, int right) => left.Side << right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator >>(Player left, int right)
-        => left.Side >> right;
+    public static int operator >>(Player left, int right) => left.Side >> right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Pieces operator +(PieceTypes pieceType, Player side)
-        => (Pieces)pieceType + (byte)(side.Side << 3);
+    public static Pieces operator +(PieceTypes pieceType, Player side) => (Pieces)pieceType + (byte)(side.Side << 3);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Equals(Player other) => Side == other.Side;
@@ -130,12 +119,10 @@ public static Player Create(Players p)
     public override int GetHashCode() => Side << 24;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string ToString()
-        => PlayerColors[Side];
+    public override string ToString() => PlayerColors[Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public string ToString(string format, IFormatProvider formatProvider)
-        => ToString();
+    public string ToString(string format, IFormatProvider formatProvider) => ToString();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider)
@@ -146,30 +133,23 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsOk()
-        => Side.InBetween(White.Side, Black.Side);
+    public bool IsOk() => Side.InBetween(White.Side, Black.Side);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public string GetName()
-        => PlayerColors[Side];
+    public string GetName() => PlayerColors[Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Direction PawnPushDistance()
-        => PawnPushDist[Side];
+    public Direction PawnPushDistance() => PawnPushDist[Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Direction PawnDoublePushDistance()
-        => PawnDoublePushDist[Side];
+    public Direction PawnDoublePushDistance() => PawnDoublePushDist[Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Direction PawnWestAttackDistance()
-        => PawnWestAttackDist[Side];
+    public Direction PawnWestAttackDistance() => PawnWestAttackDist[Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Direction PawnEastAttackDistance()
-        => PawnEastAttackDist[Side];
+    public Direction PawnEastAttackDistance() => PawnEastAttackDist[Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard PawnPush(BitBoard bb)
-        => PawnPushModifiers[Side](bb);
+    public BitBoard PawnPush(BitBoard bb) => PawnPushModifiers[Side](bb);
 }
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index 4cab4ccf..727ca7ea 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -203,7 +203,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
     public int EdgeDistance() => Math.Min(AsInt() - Rank1.AsInt(), Rank8.AsInt() - AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank Clamp(Rank min, Rank max) => new(Value.AsInt().Clamp(min.AsInt(), max.AsInt()));
+    public Rank Clamp(Rank min, Rank max) => new(Math.Clamp(Value.AsInt(), min.AsInt(), max.AsInt()));
     
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int Distance(Rank other) => Math.Abs(AsInt() - other.AsInt());
diff --git a/src/Rudzoft.ChessLib/Types/RootMove.cs b/src/Rudzoft.ChessLib/Types/RootMove.cs
index 53cc618e..f02bdf88 100644
--- a/src/Rudzoft.ChessLib/Types/RootMove.cs
+++ b/src/Rudzoft.ChessLib/Types/RootMove.cs
@@ -22,14 +22,11 @@ public RootMove(Move m)
     public Value TbValue { get; set; }
     
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator RootMove(Move m)
-        => new(m);
+    public static implicit operator RootMove(Move m) => new(m);
 
-    public static bool operator !=(RootMove left, Move right)
-        => left.FirstOrDefault() != right;
+    public static bool operator !=(RootMove left, Move right) => left.FirstOrDefault() != right;
 
-    public static bool operator ==(RootMove left, Move right)
-        => left.FirstOrDefault() == right;
+    public static bool operator ==(RootMove left, Move right) => left.FirstOrDefault() == right;
     
     public static bool operator <(RootMove left, RootMove right)
         => left.NewValue > right.NewValue || left.NewValue == right.NewValue && left.OldValue > right.OldValue;
@@ -37,20 +34,11 @@ public static implicit operator RootMove(Move m)
     public static bool operator >(RootMove left, RootMove right)
         => left.NewValue < right.NewValue || left.NewValue == right.NewValue && left.OldValue < right.OldValue;
     
-    private bool Equals(RootMove other)
-    {
-        return this.FirstOrDefault().Equals(other.FirstOrDefault());
-    }
+    private bool Equals(RootMove other) => this.FirstOrDefault().Equals(other.FirstOrDefault());
 
-    public override bool Equals(object obj)
-    {
-        return ReferenceEquals(this, obj) || obj is RootMove other && Equals(other);
-    }
+    public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is RootMove other && Equals(other);
 
-    public override int GetHashCode()
-    {
-        return this.FirstOrDefault().GetHashCode();
-    }
+    public override int GetHashCode() => this.FirstOrDefault().GetHashCode();
 }
 
 public sealed class RootMoves : List<RootMove>
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index 2353968d..1fcb0902 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -39,28 +39,22 @@ public struct Score : IEquatable<Score>
     private Vector2 _data;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private Score(Vector2 value)
-        => _data = value;
+    private Score(Vector2 value) => _data = value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private Score(int value)
-        => _data = new Vector2(value, 0);
+    private Score(int value) => _data = new Vector2(value, 0);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Score(float mg, float eg)
-        => _data = new Vector2(mg, eg);
+    public Score(float mg, float eg) => _data = new Vector2(mg, eg);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Score(int mg, int eg)
-        => _data = new Vector2(mg, eg);
+    public Score(int mg, int eg) => _data = new Vector2(mg, eg);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Score(Value mg, Value eg)
-        => _data = new Vector2((int)mg.Raw, (int)eg.Raw);
+    public Score(Value mg, Value eg) => _data = new Vector2((int)mg.Raw, (int)eg.Raw);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Score(Score s)
-        => _data = s._data;
+    public Score(Score s) => _data = s._data;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Score Create(int mg, int eg) => new(mg, eg);
@@ -69,35 +63,27 @@ public Score(Score s)
     public static Score Create(in Vector2 v) => new(v);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Score(int v)
-        => new(v);
+    public static implicit operator Score(int v) => new(v);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Score(Vector2 v)
-        => new(v);
+    public static implicit operator Score(Vector2 v) => new(v);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Score operator *(Score s, int v)
-        => new(Vector2.Multiply(s._data, v));
+    public static Score operator *(Score s, int v) => new(Vector2.Multiply(s._data, v));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Score operator /(Score s, int v)
-        => new(Vector2.Divide(s._data, v));
+    public static Score operator /(Score s, int v) => new(Vector2.Divide(s._data, v));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Score operator *(Score s, bool b)
-        => b ? s : Zero;
+    public static Score operator *(Score s, bool b) => b ? s : Zero;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Score operator +(Score s1, Score s2)
-        => new(Vector2.Add(s1._data, s2._data));
+    public static Score operator +(Score s1, Score s2) => new(Vector2.Add(s1._data, s2._data));
 
-    public static Score operator -(Score s1, Score s2)
-        => new(Vector2.Subtract(s1._data, s2._data));
+    public static Score operator -(Score s1, Score s2) => new(Vector2.Subtract(s1._data, s2._data));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Score operator +(Score s, int v)
-        => new(Vector2.Add(s._data, new Vector2(v, v)));
+    public static Score operator +(Score s, int v) => new(Vector2.Add(s._data, new Vector2(v, v)));
 
     public static readonly Score Zero = new();
 
@@ -109,43 +95,30 @@ public readonly void Deconstruct(out float mg, out float eg)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void SetMg(int v)
-        => _data.X = v;
+    public void SetMg(int v) => _data.X = v;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void SetEg(int v)
-        => _data.Y = v;
+    public void SetEg(int v) => _data.Y = v;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public readonly float Eg()
-        => _data.Y;
+    public readonly float Eg() => _data.Y;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public readonly float Mg()
-        => _data.X;
+    public readonly float Mg() => _data.X;
 
-    public readonly override string ToString()
-        => $"Mg:{_data.X}, Eg:{_data.Y}";
+    public readonly override string ToString() => $"Mg:{_data.X}, Eg:{_data.Y}";
 
-    public bool Equals(Score other)
-        => _data.Equals(other._data);
+    public bool Equals(Score other) => _data.Equals(other._data);
 
-    public override bool Equals(object obj)
-        => obj is Score other && Equals(other);
+    public override bool Equals(object obj) => obj is Score other && Equals(other);
 
 #pragma warning disable S2328 // "GetHashCode" should not reference mutable fields
-    public readonly override int GetHashCode()
+    public readonly override int GetHashCode() => _data.GetHashCode();
 #pragma warning restore S2328 // "GetHashCode" should not reference mutable fields
-        => _data.GetHashCode();
 
-    public static bool operator ==(Score left, Score right)
-        => left._data.Equals(right._data);
+    public static bool operator ==(Score left, Score right) => left._data.Equals(right._data);
 
-    public static bool operator !=(Score left, Score right)
-        => !(left == right);
+    public static bool operator !=(Score left, Score right) => !(left == right);
 
-    public Score Negate()
-    {
-        return -_data;
-    }
+    public Score Negate() => -_data;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/ValMove.cs b/src/Rudzoft.ChessLib/Types/ValMove.cs
index 727466dd..47ef4078 100644
--- a/src/Rudzoft.ChessLib/Types/ValMove.cs
+++ b/src/Rudzoft.ChessLib/Types/ValMove.cs
@@ -47,31 +47,22 @@ private ValMove(Move m, int s)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator ValMove(Move m)
-        => new(m, 0);
+    public static implicit operator ValMove(Move m) => new(m, 0);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator ValMove(int s)
-        => new(Move.EmptyMove, s);
+    public static implicit operator ValMove(int s) => new(Move.EmptyMove, s);
 
-    public static bool operator !=(ValMove left, ValMove right)
-        => !(left.Move == right.Move);
+    public static bool operator !=(ValMove left, ValMove right) => !(left.Move == right.Move);
 
-    public static bool operator ==(ValMove left, ValMove right)
-        => left.Move == right.Move;
+    public static bool operator ==(ValMove left, ValMove right) => left.Move == right.Move;
 
-    public override bool Equals(object obj)
-        => obj is ValMove other && Equals(other);
+    public override bool Equals(object obj) => obj is ValMove other && Equals(other);
 
-    public bool Equals(ValMove other)
-        => Move.Equals(other.Move);
+    public bool Equals(ValMove other) => Move.Equals(other.Move);
 
-    public readonly int CompareTo(ValMove other)
-        => other.Score.CompareTo(Score);
+    public readonly int CompareTo(ValMove other) => other.Score.CompareTo(Score);
     
-    public readonly override int GetHashCode()
-        => Move.GetHashCode();
+    public readonly override int GetHashCode() => Move.GetHashCode();
 
-    public readonly override string ToString()
-        => $"{Move}, {Score}";
+    public readonly override string ToString()=> $"{Move}, {Score}";
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Value.cs b/src/Rudzoft.ChessLib/Types/Value.cs
index f94b6dfd..5a6500de 100644
--- a/src/Rudzoft.ChessLib/Types/Value.cs
+++ b/src/Rudzoft.ChessLib/Types/Value.cs
@@ -35,11 +35,9 @@ namespace Rudzoft.ChessLib.Types;
 {
     public readonly DefaultPieceValues Raw;
 
-    public Value(Value value)
-        : this(value.Raw) { }
+    public Value(Value value) : this(value.Raw) { }
 
-    public Value(int value)
-        : this((DefaultPieceValues)value) { }
+    public Value(int value) : this((DefaultPieceValues)value) { }
 
     private Value(DefaultPieceValues value) => Raw = value;
 
@@ -51,99 +49,67 @@ public Value(int value)
 
     public static Value Of(int v) => v;
 
-    public static implicit operator Value(int value)
-        => new(value);
+    public static implicit operator Value(int value) => new(value);
 
-    public static implicit operator Value(DefaultPieceValues value)
-        => new(value);
+    public static implicit operator Value(DefaultPieceValues value) => new(value);
 
-    public static bool operator ==(Value left, Value right)
-        => left.Equals(right);
+    public static bool operator ==(Value left, Value right) => left.Equals(right);
 
-    public static bool operator !=(Value left, Value right)
-        => !left.Equals(right);
+    public static bool operator !=(Value left, Value right) => !left.Equals(right);
 
-    public static Value operator +(Value left, Value right)
-        => new((int)left.Raw + (int)right.Raw);
+    public static Value operator +(Value left, Value right) => new((int)left.Raw + (int)right.Raw);
 
-    public static Value operator +(Value left, int right)
-        => new((int)left.Raw + right);
+    public static Value operator +(Value left, int right) => new((int)left.Raw + right);
 
-    public static Value operator +(Value left, DefaultPieceValues right)
-        => new((int)left.Raw + (int)right);
+    public static Value operator +(Value left, DefaultPieceValues right) => new((int)left.Raw + (int)right);
 
-    public static Value operator +(int left, Value right)
-        => new(left + (int)right.Raw);
+    public static Value operator +(int left, Value right) => new(left + (int)right.Raw);
 
-    public static Value operator -(Value left, Value right)
-        => new(left.Raw - right.Raw);
+    public static Value operator -(Value left, Value right) => new(left.Raw - right.Raw);
 
-    public static Value operator -(Value left, int right)
-        => new((int)left.Raw - right);
+    public static Value operator -(Value left, int right) => new((int)left.Raw - right);
 
-    public static Value operator -(DefaultPieceValues left, Value right)
-        => new((int)left - right.Raw);
+    public static Value operator -(DefaultPieceValues left, Value right) => new((int)left - right.Raw);
 
-    public static Value operator -(Value left, DefaultPieceValues right)
-        => new(left.Raw - (int)right);
+    public static Value operator -(Value left, DefaultPieceValues right) => new(left.Raw - (int)right);
 
-    public static Value operator -(int left, Value right)
-        => new(left - (int)right.Raw);
+    public static Value operator -(int left, Value right) => new(left - (int)right.Raw);
 
-    public static Value operator ++(Value value)
-        => new((int)value.Raw + 1);
+    public static Value operator ++(Value value) => new((int)value.Raw + 1);
 
-    public static Value operator --(Value value)
-        => new((int)value.Raw - 1);
+    public static Value operator --(Value value) => new((int)value.Raw - 1);
 
-    public static Value operator *(Value left, int right)
-        => new((int)left.Raw * right);
+    public static Value operator *(Value left, int right) => new((int)left.Raw * right);
 
-    public static Value operator *(int left, Value right)
-        => new(left * right.Raw.AsInt());
+    public static Value operator *(int left, Value right) => new(left * right.Raw.AsInt());
 
-    public static bool operator >(Value left, Value right)
-        => left.Raw > right.Raw;
+    public static bool operator >(Value left, Value right) => left.Raw > right.Raw;
 
-    public static bool operator <(Value left, Value right)
-        => left.Raw < right.Raw;
+    public static bool operator <(Value left, Value right) => left.Raw < right.Raw;
 
-    public static bool operator <=(Value left, Value right)
-        => left.Raw <= right.Raw;
+    public static bool operator <=(Value left, Value right) => left.Raw <= right.Raw;
 
-    public static bool operator >=(Value left, Value right)
-        => left.Raw >= right.Raw;
+    public static bool operator >=(Value left, Value right) => left.Raw >= right.Raw;
 
-    public static bool operator >(Value left, DefaultPieceValues right)
-        => left.Raw > right;
+    public static bool operator >(Value left, DefaultPieceValues right) => left.Raw > right;
 
-    public static bool operator <(Value left, DefaultPieceValues right)
-        => left.Raw < right;
+    public static bool operator <(Value left, DefaultPieceValues right) => left.Raw < right;
 
-    public static bool operator <=(Value left, DefaultPieceValues right)
-        => left.Raw <= right;
+    public static bool operator <=(Value left, DefaultPieceValues right) => left.Raw <= right;
 
-    public static bool operator >=(Value left, DefaultPieceValues right)
-        => left.Raw >= right;
+    public static bool operator >=(Value left, DefaultPieceValues right) => left.Raw >= right;
 
-    public static bool operator true(Value value)
-        => value.Raw > 0;
+    public static bool operator true(Value value) => value.Raw > 0;
 
-    public static bool operator false(Value value)
-        => value.Raw <= 0;
+    public static bool operator false(Value value) => value.Raw <= 0;
 
-    public Value ForColor(Player p)
-        => p.IsWhite ? this : new Value(-(int)Raw);
+    public Value ForColor(Player p) => p.IsWhite ? this : new Value(-(int)Raw);
 
-    public bool Equals(Value other)
-        => Raw == other.Raw;
+    public bool Equals(Value other) => Raw == other.Raw;
 
-    public override bool Equals(object obj)
-        => obj is Value other && Equals(other);
+    public override bool Equals(object obj) => obj is Value other && Equals(other);
 
-    public override int GetHashCode()
-        => (int)Raw;
+    public override int GetHashCode() => (int)Raw;
 
-    public override string ToString()
-        => Raw.ToString();
+    public override string ToString() => Raw.ToString();
 }
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index 8e5d0d1e..39367031 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -172,12 +172,12 @@ private static IEnumerable<string> ValidatePieceConsistency(IPosition pos)
     }
 
     private static IEnumerable<string> ValidatePieceCount(IPosition pos)
-        => Piece.AllPieces
+        => Piece.All
             .Where(pc => pos.PieceCount(pc) != pos.Pieces(pc).Count)
             .Select(static pc => $"piece count does not match for piece {pc}");
 
     private static IEnumerable<string> ValidatePieceTypes(IPosition pos)
-        => Piece.AllPieceTypes.SelectMany(p1 => Piece.AllPieceTypes
+        => Piece.AllTypes.SelectMany(p1 => Piece.AllTypes
                 .Where(p2 => p1 != p2 && (pos.Pieces(p1) & pos.Pieces(p2)).IsNotEmpty),
             static (p1, p2) => $"piece types {p1} and {p2} doesn't align");
 
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index 8450eeeb..eaa5dc7c 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -38,7 +38,7 @@
     <PackageReference Include="CommandLineParser" Version="2.9.1" />
     <PackageReference Include="ConsoleGUI" Version="1.4.1" />
     <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.4" />
+    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
     <PackageReference Include="Serilog" Version="2.12.0" />
     <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
     <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />

From 3a2e85659cbbd7c19a17db709d042b955cf49829 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 24 Apr 2023 21:02:48 +0200
Subject: [PATCH 037/119] Minor improvements

- Aligned InBetween with dotnet naming of IsBetween
- Simplified MoveList
- Spelling fix in HiResTimer
- SearchParameter depth now int + added missing method to interface
- Encapsulated UCI options for simpler use
- Added bitboard debugger display
- Some types now implement IMinMaxValue<T>
- Added some static square properties with tests
- Minor formatting
---
 .../IsNumericBenchmark.cs                     |  2 +-
 .../SquareTests/SquareRangeTests.cs           | 86 +++++++++++++++++++
 src/Rudzoft.ChessLib/Blockage.cs              |  6 +-
 .../ChessLibServiceCollectionExtensions.cs    |  1 -
 .../Extensions/MathExtensions.cs              | 39 ++++-----
 src/Rudzoft.ChessLib/Extensions/Maths.cs      |  2 +-
 src/Rudzoft.ChessLib/Game.cs                  |  3 +-
 .../MoveGeneration/MoveList.cs                | 42 +++------
 src/Rudzoft.ChessLib/Position.cs              |  2 +-
 .../Protocol/UCI/HiResTimer.cs                |  8 +-
 .../Protocol/UCI/ISearchParameters.cs         |  4 +-
 src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs     |  3 +-
 .../Protocol/UCI/SearchParameters.cs          |  2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      | 46 ++++++----
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  2 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        | 25 +++---
 src/Rudzoft.ChessLib/Types/BitBoards.cs       |  4 +-
 src/Rudzoft.ChessLib/Types/File.cs            | 24 ++++--
 src/Rudzoft.ChessLib/Types/MagicBB.cs         |  6 +-
 src/Rudzoft.ChessLib/Types/Piece.cs           | 27 +++---
 src/Rudzoft.ChessLib/Types/Player.cs          | 15 ++--
 src/Rudzoft.ChessLib/Types/Rank.cs            |  2 +-
 src/Rudzoft.ChessLib/Types/Square.cs          | 32 ++++---
 23 files changed, 246 insertions(+), 137 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
index 3d344143..d5a60918 100644
--- a/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
@@ -23,7 +23,7 @@ public void MathExtInBetween()
     {
         foreach (var c in _s.AsSpan())
         {
-            var b = c.InBetween('0', '9');
+            var b = c.IsBetween('0', '9');
         }
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs b/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
new file mode 100644
index 00000000..8e9d131a
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
@@ -0,0 +1,86 @@
+using System;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Test.SquareTests;
+
+public sealed class SquareRangeTests
+{
+    [Theory]
+    [InlineData(Squares.a1, true, false)]
+    [InlineData(Squares.a2, true, false)]
+    [InlineData(Squares.a3, true, false)]
+    [InlineData(Squares.a4, true, false)]
+    [InlineData(Squares.a5, true, false)]
+    [InlineData(Squares.a6, true, false)]
+    [InlineData(Squares.a7, true, false)]
+    [InlineData(Squares.a8, true, false)]
+    [InlineData(Squares.b1, true, false)]
+    [InlineData(Squares.b2, true, false)]
+    [InlineData(Squares.b3, true, false)]
+    [InlineData(Squares.b4, true, false)]
+    [InlineData(Squares.b5, true, false)]
+    [InlineData(Squares.b6, true, false)]
+    [InlineData(Squares.b7, true, false)]
+    [InlineData(Squares.b8, true, false)]
+    [InlineData(Squares.c1, true, false)]
+    [InlineData(Squares.c2, true, false)]
+    [InlineData(Squares.c3, true, false)]
+    [InlineData(Squares.c4, true, false)]
+    [InlineData(Squares.c5, true, false)]
+    [InlineData(Squares.c6, true, false)]
+    [InlineData(Squares.c7, true, false)]
+    [InlineData(Squares.c8, true, false)]
+    [InlineData(Squares.d1, true, false)]
+    [InlineData(Squares.d2, true, false)]
+    [InlineData(Squares.d3, true, false)]
+    [InlineData(Squares.d4, true, false)]
+    [InlineData(Squares.d5, true, false)]
+    [InlineData(Squares.d6, true, false)]
+    [InlineData(Squares.d7, true, false)]
+    [InlineData(Squares.d8, true, false)]
+    [InlineData(Squares.e1, false, true)]
+    [InlineData(Squares.e2, false, true)]
+    [InlineData(Squares.e3, false, true)]
+    [InlineData(Squares.e4, false, true)]
+    [InlineData(Squares.e5, false, true)]
+    [InlineData(Squares.e6, false, true)]
+    [InlineData(Squares.e7, false, true)]
+    [InlineData(Squares.e8, false, true)]
+    [InlineData(Squares.f1, false, true)]
+    [InlineData(Squares.f2, false, true)]
+    [InlineData(Squares.f3, false, true)]
+    [InlineData(Squares.f4, false, true)]
+    [InlineData(Squares.f5, false, true)]
+    [InlineData(Squares.f6, false, true)]
+    [InlineData(Squares.f7, false, true)]
+    [InlineData(Squares.f8, false, true)]
+    [InlineData(Squares.g1, false, true)]
+    [InlineData(Squares.g2, false, true)]
+    [InlineData(Squares.g3, false, true)]
+    [InlineData(Squares.g4, false, true)]
+    [InlineData(Squares.g5, false, true)]
+    [InlineData(Squares.g6, false, true)]
+    [InlineData(Squares.g7, false, true)]
+    [InlineData(Squares.g8, false, true)]
+    [InlineData(Squares.h1, false, true)]
+    [InlineData(Squares.h2, false, true)]
+    [InlineData(Squares.h3, false, true)]
+    [InlineData(Squares.h4, false, true)]
+    [InlineData(Squares.h5, false, true)]
+    [InlineData(Squares.h6, false, true)]
+    [InlineData(Squares.h7, false, true)]
+    [InlineData(Squares.h8, false, true)]
+    [InlineData(Squares.none, false, false)]
+    public void InWhiteSquaresRange(Squares sq, bool expectedInWhiteRange, bool expectedInBlackRange)
+    {
+        Square s = sq;
+        var whiteSquares = Square.All.AsSpan(Square.WhiteSide);
+        var blackSquares = Square.All.AsSpan(Square.BlackSide);
+
+        var actualInWhite = whiteSquares.Contains(s);
+        var actualInBlack = blackSquares.Contains(s);
+        
+        Assert.Equal(expectedInWhiteRange, actualInWhite);
+        Assert.Equal(expectedInBlackRange, actualInBlack);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 640742eb..0748bfab 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -370,9 +370,7 @@ private static BitBoard ComputeDynamicFencedPawns(
         return result;
     }
 
-    private static File NextFile(File f)
-        => f + 1;
+    private static File NextFile(File f) => f + 1;
 
-    private static File PreviousFile(File f)
-        => f - 1;
+    private static File PreviousFile(File f) => f - 1;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 13c02cf1..762c1db4 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -36,7 +36,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
-using Rudzoft.ChessLib.Tables.KillerMoves;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
diff --git a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
index e49f0c56..e82da5f9 100644
--- a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 // ReSharper disable MemberCanBeInternal
@@ -35,20 +34,21 @@ namespace Rudzoft.ChessLib.Extensions;
 public static class MathExtensions
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool InBetween(this int v, int min, int max)
-        => ((v - min) | (max - v)) >= 0;
+    public static bool IsBetween(this int v, int min, int max) => ((v - min) | (max - v)) >= 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool InBetween(this byte v, byte min, byte max)
-        => (((int)v - (int)min) | ((int)max - (int)v)) >= 0;
+    public static bool IsBetween(this byte v, byte min, byte max) => (((int)v - (int)min) | ((int)max - (int)v)) >= 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool InBetween(this char v, char min, char max)
-        => (((int)v - (int)min) | ((int)max - (int)v)) >= 0;
+    public static bool IsBetween(this char v, char min, char max) => (((int)v - (int)min) | ((int)max - (int)v)) >= 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool InBetween(this uint v, int min, int max)
-        => v - (uint)min <= (uint)max - (uint)min;
+    public static bool IsBetween(this uint v, int min, int max) => v - (uint)min <= (uint)max - (uint)min;
+
+#if !NET7_0_OR_GREATER
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool IsAsciiDigit(this char c) => IsBetween(c, '0', '9');
+#endif
 
     /// <summary>
     /// Converts a bool to a byte (0 or 1)
@@ -63,33 +63,26 @@ public static bool InBetween(this uint v, int min, int max)
     /// <param name="b"></param>
     /// <returns></returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static unsafe byte AsByte(this bool b)
-        => *(byte*)&b;
+    public static unsafe byte AsByte(this bool b) => *(byte*)&b;
 
     /// <summary>
     /// Modulo for pow^2 values...
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int ModPow2(int input, int ceil)
-        => input & (ceil - 1);
+    public static int ModPow2(int input, int ceil) => input & (ceil - 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int Pow2(this int value)
-        => 1 << BitBoards.Msb(value).AsInt();
+    public static int Pow2(this int value) => 1 << BitBoards.Msb(value).AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool IsEven(this int value)
-        => (value & 1) == 0;
+    public static bool IsEven(this int value) => (value & 1) == 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool IsOdd(this int value)
-        => !value.IsEven();
+    public static bool IsOdd(this int value) => !value.IsEven();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static long MidPoint(this long @this, long that)
-        => (@this + that) >> 1;
+    public static long MidPoint(this long @this, long that) => (@this + that) >> 1;
     
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Value Min(this Value @this, Value other)
-        => @this < other ? @this : other;
+    public static Value Min(this Value @this, Value other) => @this < other ? @this : other;
 }
diff --git a/src/Rudzoft.ChessLib/Extensions/Maths.cs b/src/Rudzoft.ChessLib/Extensions/Maths.cs
index 1ed50875..e57afad3 100644
--- a/src/Rudzoft.ChessLib/Extensions/Maths.cs
+++ b/src/Rudzoft.ChessLib/Extensions/Maths.cs
@@ -105,5 +105,5 @@ public static bool ToIntegral(ReadOnlySpan<char> str, out int result)
         result = x;
         return true;
     }
-
+    
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 606da1ab..73f5f11a 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -146,6 +146,8 @@ public ulong Perft(int depth, bool root = true)
         var ml = _moveListPool.Get();
         ml.Generate(in _pos);
 
+        var state = new State();
+        
         var moves = ml.Get();
         ref var movesSpace = ref MemoryMarshal.GetReference(moves);
         for (var i = 0; i < moves.Length; ++i)
@@ -156,7 +158,6 @@ public ulong Perft(int depth, bool root = true)
             else
             {
                 var m = valMove.Move;
-                var state = new State();
                 _pos.MakeMove(m, in state);
 
                 if (depth <= 2)
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index fdc45908..a74b356d 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -35,6 +35,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.MoveGeneration;
 
+/***
+ * Holds a list of moves.
+ * It works through an index pointer,
+ * which means that re-use is never actually clearing any data, just resetting the index.
+ */
 public sealed class MoveList : IMoveList
 {
     private readonly ValMove[] _moves;
@@ -83,39 +88,16 @@ public bool Contains(in ValMove item)
         => Contains(item.Move);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Contains(Move move)
-    {
-        var moveSpan = _moves.AsSpan()[..Length];
-        if (moveSpan.IsEmpty)
-            return false;
-
-        ref var movesSpace = ref MemoryMarshal.GetReference(moveSpan);
-        for (var i = 0; i < moveSpan.Length; ++i)
-            if (move == Unsafe.Add(ref movesSpace, i).Move)
-                return true;
-
-        return false;
-    }
+    public bool Contains(Move move) => Contains(m => m == move);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Contains(Square from, Square to)
-    {
-        var moveSpan = _moves.AsSpan()[..Length];
-        if (moveSpan.IsEmpty)
-            return false;
+    public bool Contains(Square from, Square to) => Contains(m => m.FromSquare() == from && m.ToSquare() == to);
 
-        ref var movesSpace = ref MemoryMarshal.GetReference(moveSpan);
-        for (var i = 0; i < moveSpan.Length; ++i)
-        {
-            var m = Unsafe.Add(ref movesSpace, i).Move;
-            if (m.FromSquare() == from && m.ToSquare() == to)
-                return true;
-        }
-
-        return false;
-    }
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool ContainsType(MoveTypes type) => Contains(m => m.MoveType() == type);
 
-    public bool ContainsType(MoveTypes type)
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool Contains(Predicate<Move> predicate)
     {
         var moveSpan = _moves.AsSpan()[..Length];
         if (moveSpan.IsEmpty)
@@ -125,7 +107,7 @@ public bool ContainsType(MoveTypes type)
         for (var i = 0; i < moveSpan.Length; ++i)
         {
             var m = Unsafe.Add(ref movesSpace, i).Move;
-            if (m.MoveType() == type)
+            if (predicate(m))
                 return true;
         }
 
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 9b486da2..71a37b4d 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1222,7 +1222,7 @@ public IPosition Set(string fen, ChessMode chessMode, State state, bool validate
     public IPosition Set(ReadOnlySpan<char> code, Player p, State state)
     {
         Debug.Assert(code[0] == 'K' && code[1..].IndexOf('K') != -1);
-        Debug.Assert(code.Length.InBetween(0, 8));
+        Debug.Assert(code.Length.IsBetween(0, 8));
         Debug.Assert(code[0] == 'K');
 
         var kingPos = code.LastIndexOf('K');
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
index 863c6ade..4e061ec6 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
@@ -41,6 +41,8 @@ public sealed class HiResTimer : IHiResTimer, IEquatable<HiResTimer>
 
     public static readonly double Frequency = Stopwatch.Frequency;
 
+    private static readonly TimeSpan RestartThreshold = TimeSpan.FromHours(1);
+    
     private readonly object _intervalLock;
 
     private float _interval;
@@ -88,7 +90,7 @@ public float Interval
         }
     }
 
-    public bool IsRunning => _cancellationTokenSource != null && !_cancellationTokenSource.Token.IsCancellationRequested && _cancellationTokenSource.Token.CanBeCanceled;
+    public bool IsRunning => _cancellationTokenSource is { Token: { IsCancellationRequested: false, CanBeCanceled: true } };
 
     public bool UseHighPriorityThread { get; set; } = true;
 
@@ -207,8 +209,8 @@ private void ExecuteTimer(CancellationToken cancellationToken)
             if (cancellationToken.IsCancellationRequested)
                 return;
 
-            // restarting the timer in every hour to prevent precision problems
-            if (Stopwatch.GetElapsedTime(timeStamp).TotalHours < 1d)
+            // restarting the timer in every day to hour precision problems
+            if (Stopwatch.GetElapsedTime(timeStamp) < RestartThreshold)
                 continue;
             timeStamp = Stopwatch.GetTimestamp();
             nextTrigger = 0f;
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
index 07198e96..ff3c254f 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
@@ -43,7 +43,7 @@ public interface ISearchParameters : ISpanFormattable
 
     ulong MoveTime { get; set; }
 
-    ulong Depth { get; set; }
+    int Depth { get; set; }
 
     ulong Nodes { get; set; }
 
@@ -62,4 +62,6 @@ public interface ISearchParameters : ISpanFormattable
     ulong Time(Player p);
 
     bool DecreaseMovesToGo();
+
+    bool UseTimeManagement();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
index c672db55..a92fa317 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
@@ -33,7 +33,6 @@ namespace Rudzoft.ChessLib.Protocol.UCI;
 public interface IUci
 {
     public int MaxThreads { get; set; }
-    public IDictionary<string, IOption> O { get; set; }
     Action<IOption> OnLogger { get; set; }
     Action<IOption> OnEval { get; set; }
     Action<IOption> OnThreads { get; set; }
@@ -45,6 +44,8 @@ public interface IUci
 
     void AddOption(string name, IOption option);
 
+    bool TryGetOption(string name, out IOption option);
+    
     ulong Nps(in ulong nodes, in TimeSpan time);
 
     Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove);
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index 32965a85..4f778233 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -98,7 +98,7 @@ public ulong MovesToGo
         set => _movesToGo = value;
     }
 
-    public ulong Depth { get; set; }
+    public int Depth { get; set; }
 
     public ulong Nodes { get; set; }
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index 6a0c6e39..be5c7299 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -44,17 +44,17 @@ public class Uci : IUci
     private static readonly string[] OptionTypeStrings = Enum.GetNames(typeof(UciOptionType));
 
     private readonly ObjectPool<StringBuilder> _pvPool;
+    private readonly Dictionary<string, IOption> _options;
 
     public Uci()
     {
         var policy = new StringBuilderPooledObjectPolicy();
         _pvPool = new DefaultObjectPool<StringBuilder>(policy, 128);
-        O = new Dictionary<string, IOption>();
+        _options = new Dictionary<string, IOption>();
     }
 
     public int MaxThreads { get; set; }
 
-    public IDictionary<string, IOption> O { get; set; }
 
     public Action<IOption> OnLogger { get; set; }
 
@@ -70,21 +70,23 @@ public Uci()
 
     public void Initialize(int maxThreads = 128)
     {
-        O["Write Debug Log"] = new Option("Write Debug Log", O.Count, false, OnLogger);
-        O["Write Search Log"] = new Option("Write Search Log", O.Count, false);
-        O["Search Log Filename"] = new Option("Search Log Filename", O.Count);
-        O["Book File"] = new Option("Book File", O.Count);
-        O["Best Book Move"] = new Option("Best Book Move", O.Count, false);
-        O["Threads"] = new Option("Threads", O.Count, 1, 1, maxThreads, OnThreads);
-        O["Hash"] = new Option("Hash", O.Count, 32, 1, 16384, OnHashSize);
-        O["Clear Hash"] = new Option("Clear Hash", O.Count, OnClearHash);
-        O["Ponder"] = new Option("Ponder", O.Count, true);
-        O["OwnBook"] = new Option("OwnBook", O.Count, false);
-        O["MultiPV"] = new Option("MultiPV", O.Count, 1, 1, 500);
-        O["UCI_Chess960"] = new Option("UCI_Chess960", O.Count, false);
+        _options["Write Debug Log"] = new Option("Write Debug Log", _options.Count, false, OnLogger);
+        _options["Write Search Log"] = new Option("Write Search Log", _options.Count, false);
+        _options["Search Log Filename"] = new Option("Search Log Filename", _options.Count);
+        _options["Book File"] = new Option("Book File", _options.Count);
+        _options["Best Book Move"] = new Option("Best Book Move", _options.Count, false);
+        _options["Threads"] = new Option("Threads", _options.Count, 1, 1, maxThreads, OnThreads);
+        _options["Hash"] = new Option("Hash", _options.Count, 32, 1, 16384, OnHashSize);
+        _options["Clear Hash"] = new Option("Clear Hash", _options.Count, OnClearHash);
+        _options["Ponder"] = new Option("Ponder", _options.Count, true);
+        _options["OwnBook"] = new Option("OwnBook", _options.Count, false);
+        _options["MultiPV"] = new Option("MultiPV", _options.Count, 1, 1, 500);
+        _options["UCI_Chess960"] = new Option("UCI_Chess960", _options.Count, false);
     }
 
-    public void AddOption(string name, IOption option) => O[name] = option;
+    public void AddOption(string name, IOption option) => _options[name] = option;
+    
+    public bool TryGetOption(string name, out IOption option) => _options.TryGetValue(name, out option);
 
     public ulong Nps(in ulong nodes, in TimeSpan time)
         => (ulong)(nodes * 1000.0 / time.Milliseconds);
@@ -137,9 +139,15 @@ public string CurrentMoveNum(int moveNumber, Move move, in ulong visitedNodes, i
         => $"info currmovenumber {moveNumber} currmove {move} nodes {visitedNodes} time {time.Milliseconds}";
 
     public string Score(int value, int mateInMaxPly, int valueMate)
-        => Math.Abs(value) >= mateInMaxPly
-            ? $"mate {(value > 0 ? valueMate - value + 1 : -valueMate - value) / 2}"
-            : $"cp {ToCenti(value)}";
+    {
+        if (Math.Abs(value) >= mateInMaxPly)
+        {
+            var s = (value > 0 ? valueMate - value + 1 : -valueMate - value) / 2;
+            return $"mate {s}";
+        }
+        else
+            return $"cp {ToCenti(value)}";
+    }
 
     public string ScoreCp(int value)
         => $"info score cp {ToCenti(value)}";
@@ -213,7 +221,7 @@ public string MoveToString(Move m, ChessMode chessMode = ChessMode.Normal)
     /// <returns>the current UCI options as string</returns>
     public new string ToString()
     {
-        var list = new List<IOption>(O.Values);
+        var list = new List<IOption>(_options.Values);
         list.Sort(OptionComparer);
         var sb = _pvPool.Get();
 
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 62d8e1f8..28f4101d 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -7,7 +7,6 @@
     <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
     <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
     <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
-    <TargetFramework>net7.0</TargetFramework>
     <TieredPGO>true</TieredPGO>
     <PackageVersion>0.0.4</PackageVersion>
     <Authors>Rudy Alex Kohn</Authors>
@@ -31,6 +30,7 @@
     <RepositoryType>git</RepositoryType>
     <Product>Rudzoft.ChessLib</Product>
     <Configurations>Debug;Release</Configurations>
+    <TargetFramework>net7.0</TargetFramework>
   </PropertyGroup>
 
   <PropertyGroup Condition="'$(IsWindows)'=='true'">
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index 3ac6d45e..ac38e124 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Numerics;
@@ -41,24 +42,28 @@ namespace Rudzoft.ChessLib.Types;
 /// Enumeration will yield each set bit as a Square struct.
 /// <para>For more information - please see https://github.com/rudzen/ChessLib/wiki/BitBoard</para>
 /// </summary>
+[DebuggerDisplay("{BitBoards.StringifyRaw(Value)}")]
 public readonly record struct BitBoard(ulong Value) : IEnumerable<Square>, IMinMaxValue<BitBoard>
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard(BitBoard value)
-        : this(value.Value) { }
+    public BitBoard(BitBoard value) : this(value.Value)
+    {
+    }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard(Square sq)
-        : this(sq.AsBb()) { }
+    public BitBoard(Square sq) : this(sq.AsBb())
+    {
+    }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private BitBoard(int value)
-        : this((ulong)value) { }
+    private BitBoard(int value) : this((ulong)value)
+    {
+    }
 
     public int Count => BitBoards.PopCount(in this);
 
     public bool IsEmpty => Value == 0;
-    
+
     public bool IsNotEmpty => Value != 0;
 
     public static BitBoard Empty => BitBoards.EmptyBitBoard;
@@ -101,7 +106,7 @@ private BitBoard(int value)
     public static BitBoard operator -(BitBoard left, int right) => new(left.Value - (ulong)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard operator >>(BitBoard left, int right) => new(left.Value >> right);
+    public static BitBoard operator >> (BitBoard left, int right) => new(left.Value >> right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard operator <<(BitBoard left, int right) => new(left.Value << right);
@@ -223,7 +228,7 @@ public IEnumerator<Square> GetEnumerator()
     IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string ToString() => BitBoards.PrintBitBoard(in this, Value.ToString());
+    public override string ToString() => BitBoards.Stringify(in this, Value.ToString());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void ToString(TextWriter textWriter) => textWriter.WriteLine(ToString());
@@ -233,4 +238,4 @@ public IEnumerator<Square> GetEnumerator()
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override int GetHashCode() => Value.GetHashCode();
-}
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 0ec350ab..277f863c 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -513,8 +513,10 @@ public static BitBoard FrontSquares(this Player p, Square sq)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard PromotionRank(this Player p) => PromotionRanks[p.Side];
 
+    public static string StringifyRaw(in ulong bb, string title = "") => Stringify(BitBoard.Create(bb), title);
+    
     [SkipLocalsInit]
-    public static string PrintBitBoard(in BitBoard bb, string title = "")
+    public static string Stringify(in BitBoard bb, string title = "")
     {
         const string line = "+---+---+---+---+---+---+---+---+---+";
         const string bottom = "|   | A | B | C | D | E | F | G | H |";
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index 8c79b4c4..7eeba7cc 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using Rudzoft.ChessLib.Extensions;
 using System;
+using System.Numerics;
 using System.Runtime.CompilerServices;
 
 // ReSharper disable UnusedMember.Global
@@ -52,7 +53,11 @@ public static class FilesExtensions
     public static int AsInt(this Files f) => (int)f;
 }
 
-public readonly record struct File(Files Value) : IComparable<File>, ISpanFormattable, IValidationType
+public readonly record struct File(Files Value)
+    : IComparable<File>, ISpanFormattable, IValidationType
+#if NET7_0_OR_GREATER 
+        , IMinMaxValue<File>
+#endif
 {
     private static readonly string[] FileStrings = { "a", "b", "c", "d", "e", "f", "g", "h" };
 
@@ -65,16 +70,22 @@ public readonly record struct File(Files Value) : IComparable<File>, ISpanFormat
     public static File FileG { get; } = new(Files.FileG);
     public static File FileH { get; } = new(Files.FileH);
     public static File[] AllFiles { get; } = { FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH };
+    public static File MaxValue => FileH;
+    public static File MinValue => FileA;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public File(int file) : this((Files)file) { }
+    public File(int file) : this((Files)file)
+    {
+    }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public File(File f) : this(f.Value) { }
+    public File(File f) : this(f.Value)
+    {
+    }
 
     public char Char => (char)('a' + Value);
 
-    public bool IsOk => Value.AsInt().InBetween(Files.FileA.AsInt(), Files.FileH.AsInt());
+    public bool IsOk => Value.AsInt().IsBetween(Files.FileA.AsInt(), Files.FileH.AsInt());
 
     public const int Count = (int)Files.FileNb;
 
@@ -117,6 +128,9 @@ public File(File f) : this(f.Value) { }
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static File operator ++(File f) => new(f.Value + 1);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static File operator --(File f) => new(f.Value - 1);
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard operator &(File left, ulong right) => left.BitBoardFile() & right;
 
@@ -136,7 +150,7 @@ public File(File f) : this(f.Value) { }
     public static BitBoard operator ~(File left) => ~left.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator >>(File left, int right) => left.AsInt() >> right;
+    public static int operator >> (File left, int right) => left.AsInt() >> right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool operator >(File left, File right) => left.AsInt() > right.AsInt();
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index 2a84970a..28e4a79b 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -197,10 +197,12 @@ private static ulong InitMagicMovesOccupancy(ReadOnlySpan<int> squares, in ulong
     }
 
     public static BitBoard BishopAttacks(this Square square, in BitBoard occupied)
-        => MagicBishopDb[square.AsInt()][(occupied.Value & BishopMask.Span[square.AsInt()]) * BishopMagics.Span[square.AsInt()] >> 55];
+        => MagicBishopDb[square.AsInt()][
+            (occupied.Value & BishopMask.Span[square.AsInt()]) * BishopMagics.Span[square.AsInt()] >> 55];
 
     public static BitBoard RookAttacks(this Square square, in BitBoard occupied)
-        => MagicRookDb[square.AsInt()][(occupied.Value & RookMask.Span[square.AsInt()]) * RookMagics.Span[square.AsInt()] >> 52];
+        => MagicRookDb[square.AsInt()][
+            (occupied.Value & RookMask.Span[square.AsInt()]) * RookMagics.Span[square.AsInt()] >> 52];
 
     public static BitBoard QueenAttacks(this Square square, in BitBoard occupied)
         => square.BishopAttacks(in occupied) | square.RookAttacks(in occupied);
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 2370d99c..b35da68d 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 
@@ -76,11 +77,13 @@ public static bool InBetween(this PieceTypes v, PieceTypes min, PieceTypes max)
         (uint)v - (uint)min <= (uint)max - (uint)min;
 }
 
-/// <inheritdoc />
 /// <summary>
 /// Piece. Contains the piece type which indicate what type and color the piece is
 /// </summary>
 public readonly record struct Piece(Pieces Value)
+#if net7_0
+    : IMinMaxValue<Piece>
+#endif
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private Piece(int pc) : this((Pieces)pc) { }
@@ -110,20 +113,18 @@ private Piece(Piece pc) : this(pc.Value) { }
 
     public static Piece[] All { get; } =
     {
-        WhitePawn,
-        WhiteKnight,
-        WhiteBishop,
-        WhiteRook,
-        WhiteQueen,
-        WhiteKing,
-        BlackPawn,
-        BlackKnight,
-        BlackBishop,
-        BlackRook,
-        BlackQueen,
-        BlackKing
+        WhitePawn, WhiteKnight, WhiteBishop, WhiteRook, WhiteQueen, WhiteKing,
+        BlackPawn, BlackKnight, BlackBishop, BlackRook, BlackQueen, BlackKing
     };
 
+    public static Piece MaxValue => WhitePawn;
+
+    public static Piece MinValue => BlackKing;
+
+    public static Range WhitePieces => new(0, 5);
+    
+    public static Range BlackPieces => new(6, 11);
+    
     public static PieceTypes[] AllTypes { get; } =
     {
         PieceTypes.Pawn,
diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Player.cs
index 6b6513c5..72254459 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Player.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Numerics;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 
@@ -44,6 +45,9 @@ public enum PlayerTypes
 }
 
 public readonly record struct Player(byte Side) : ISpanFormattable
+#if NET7_0
+    , IMinMaxValue<Player>
+#endif
 {
     private static readonly Direction[] PawnPushDist = { Direction.North, Direction.South };
 
@@ -67,18 +71,15 @@ public Player(Players p)
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Deconstruct(out byte side) => side = Side;
-
+    
     public bool IsWhite => Side == byte.MinValue;
-
     public bool IsBlack => Side != byte.MinValue;
-
     public char Fen => PlayerFen[Side];
-
     public static Player White { get; } = new(Players.White);
-
     public static Player Black { get; } = new(Players.Black);
-
     public static Player[] AllPlayers { get; } = { White, Black };
+    public static Player MaxValue => White;
+    public static Player MinValue => Black;
 
     public const int Count = (int)Players.PlayerNb;
 
@@ -133,7 +134,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsOk() => Side.InBetween(White.Side, Black.Side);
+    public bool IsOk() => Side.IsBetween(White.Side, Black.Side);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string GetName() => PlayerColors[Side];
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index 727ca7ea..99d9b8fa 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -77,7 +77,7 @@ public Rank(Rank r) : this(r.Value) { }
 
     public char Char => (char)('1' + Value.AsInt());
 
-    public bool IsOk => Value.AsInt().InBetween(Ranks.Rank1.AsInt(), Ranks.Rank8.AsInt());
+    public bool IsOk => Value.AsInt().IsBetween(Ranks.Rank1.AsInt(), Ranks.Rank8.AsInt());
 
     public const int Count = (int)Ranks.RankNb;
 
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index 429eece9..bb3cf35a 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -51,15 +51,12 @@ public enum Squares : byte
 public static class SquaresExtensions
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard BitBoardSquare(this Squares sq)
-        => BitBoards.BbSquares[sq.AsInt()];
+    public static BitBoard BitBoardSquare(this Squares sq) => BitBoards.BbSquares[sq.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square RelativeSquare(this Squares sq, Player p)
-        => sq.AsInt() ^ (p.Side * 56);
+    public static Square RelativeSquare(this Squares sq, Player p) => sq.AsInt() ^ (p.Side * 56);
 
-    public static int AsInt(this Squares sq)
-        => (int)sq;
+    public static int AsInt(this Squares sq) => (int)sq;
 }
 
 /// <summary>
@@ -111,12 +108,12 @@ public void Deconstruct(out Rank r, out File f)
 
     public char FileChar => File.Char;
 
-    public bool IsOk => ((int)Value).InBetween((int)Squares.a1, (int)Squares.h8);
+    public bool IsOk => ((int)Value).IsBetween((int)Squares.a1, (int)Squares.h8);
 
     public bool IsPromotionRank => (BitBoards.PromotionRanksBB & this).IsNotEmpty;
 
     public bool IsDark => (Player.Black.ColorBB() & this).IsNotEmpty;
-
+    
     public static Square None { get; } = new(Squares.none);
 
     public static Square A1 { get; } = new(Squares.a1);
@@ -193,8 +190,23 @@ public void Deconstruct(out Rank r, out File f)
 
     public const int Count = 64;
 
-    public static Square Create(Rank r, File f)
-        => new(r, f);
+    public static Square[] All => new[]
+    {
+        A1, A2, A3, A4, A5, A6, A7, A8,
+        B1, B2, B3, B4, B5, B6, B7, B8,
+        C1, C2, C3, C4, C5, C6, C7, C8,
+        D1, D2, D3, D4, D5, D6, D7, D8,
+        E1, E2, E3, E4, E5, E6, E7, E8,
+        F1, F2, F3, F4, F5, F6, F7, F8,
+        G1, G2, G3, G4, G5, G6, G7, G8,
+        H1, H2, H3, H4, H5, H6, H7, H8,
+    };
+
+    public static readonly Range WhiteSide = new(0, 32);
+
+    public static readonly Range BlackSide = new(32, 64);
+
+    public static Square Create(Rank r, File f) => new(r, f);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Square(int value) => new(value);

From c3d2c41817a9f7b549bf5db383d060ae7ae59736 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 24 Apr 2023 21:04:18 +0200
Subject: [PATCH 038/119] Updated RKiss method arguments

---
 src/Rudzoft.ChessLib/Hash/RKiss.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Hash/RKiss.cs b/src/Rudzoft.ChessLib/Hash/RKiss.cs
index 5d1cb104..81830993 100644
--- a/src/Rudzoft.ChessLib/Hash/RKiss.cs
+++ b/src/Rudzoft.ChessLib/Hash/RKiss.cs
@@ -34,12 +34,12 @@ public sealed class RKiss : IRKiss
     private ulong _s;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private RKiss(ulong s)
+    private RKiss(in ulong s)
     {
         _s = s;
     }
 
-    public static IRKiss Create(ulong seed) => new RKiss(seed);
+    public static IRKiss Create(in ulong seed) => new RKiss(seed);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public IEnumerable<ulong> Get(int count)

From 185465f9e4fdd300098a7e3d0234091eb3e7e3ee Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 24 Apr 2023 21:07:34 +0200
Subject: [PATCH 039/119] HashKeys now retrieved with ref from Zobrist
 (reminder: don't modify assignments directly!)

---
 src/Rudzoft.ChessLib/Hash/Zobrist.cs      | 6 +++---
 src/Rudzoft.ChessLib/Types/CastleRight.cs | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index 39fdaac5..b8aaae67 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -117,14 +117,14 @@ private static void InitializeRandomArray(Span<HashKey> array, IRKiss rnd)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey GetZobristPst(this Piece piece, Square square) => ZobristPst[piece.AsInt()][square.AsInt()];
+    public static ref HashKey GetZobristPst(this Piece piece, Square square) => ref ZobristPst[piece.AsInt()][square.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey GetZobristCastleling(this CastleRights index) => ZobristCastling[index.AsInt()];
+    public static ref HashKey GetZobristCastleling(this CastleRights index) => ref ZobristCastling[index.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static HashKey GetZobristSide() => ZobristSide;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey GetZobristEnPassant(this File file) => ZobristEpFile[file.AsInt()];
+    public static ref HashKey GetZobristEnPassant(this File file) => ref ZobristEpFile[file.AsInt()];
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index 6dc72257..1c1a7946 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -161,7 +161,7 @@ private CastleRight(int cr) : this((CastleRights)cr) { }
     public static CastleRight operator ~(CastleRight cr) => new(~cr.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public HashKey Key() => Rights.GetZobristCastleling();
+    public ref HashKey Key() => ref Rights.GetZobristCastleling();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Has(CastleRights cr) => Rights.HasFlagFast(cr);

From c05d910dfab1ba7c0c1900b16ae01e8f2e568b0d Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Wed, 26 Apr 2023 07:54:19 +0200
Subject: [PATCH 040/119] Added Sign for Player (1 = White, -1 = Black)

---
 src/Rudzoft.ChessLib/Types/Player.cs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Player.cs
index 72254459..c3847130 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Player.cs
@@ -63,6 +63,8 @@ public readonly record struct Player(byte Side) : ISpanFormattable
 
     private static readonly Func<BitBoard, BitBoard>[] PawnPushModifiers = { BitBoards.NorthOne, BitBoards.SouthOne };
 
+    private static readonly int[] ScoreSign = { 1, -1 };
+
     public Player(Player p)
         : this(p.Side) { }
 
@@ -74,6 +76,7 @@ public Player(Players p)
     
     public bool IsWhite => Side == byte.MinValue;
     public bool IsBlack => Side != byte.MinValue;
+    public int Sign => ScoreSign[Side];
     public char Fen => PlayerFen[Side];
     public static Player White { get; } = new(Players.White);
     public static Player Black { get; } = new(Players.Black);

From ad6ee5bc406c258991ae011e9ccbb25ed0477f74 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Apr 2023 17:02:38 +0200
Subject: [PATCH 041/119] Slightly improved startup-time + position init-time

---
 .../SquareIterateBenchmark.cs                 | 72 +++++++++++++++++++
 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs | 22 +++---
 src/Rudzoft.ChessLib/Blockage.cs              |  6 +-
 src/Rudzoft.ChessLib/Cuckoo.cs                |  6 +-
 .../ChessLibServiceCollectionExtensions.cs    |  2 +
 .../Extensions/PieceExtensions.cs             |  2 +-
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          | 12 +---
 src/Rudzoft.ChessLib/Position.cs              |  7 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 21 ++----
 src/Rudzoft.ChessLib/Types/Piece.cs           |  2 +-
 src/Rudzoft.ChessLib/Types/Score.cs           | 12 ++++
 11 files changed, 113 insertions(+), 51 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
new file mode 100644
index 00000000..cf2d2f15
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Benchmark;
+
+[MemoryDiagnoser]
+public class SquareIterateBenchmark
+{
+
+    [Benchmark(Baseline = true)]
+    public int SimpleLoopBaseType()
+    {
+        var sum = 0;
+        for (var i = Squares.a1; i <= Squares.h8; i++)
+            sum += i.AsInt();
+        return sum;
+    }
+
+    [Benchmark]
+    public int SimpleLoop()
+    {
+        var sum = 0;
+        for (var i = Square.A1; i <= Square.H8; i++)
+            sum += i.AsInt();
+        return sum;
+    }
+
+    [Benchmark]
+    public int ForEachLoop()
+    {
+        var sum = 0;
+        foreach (var sq in Square.All)
+            sum += sq.AsInt();
+        return sum;
+    }
+
+    [Benchmark]
+    public int ForEachLoop_AsSpan()
+    {
+        var sum = 0;
+        var span = Square.All.AsSpan();
+        foreach (var sq in span)
+            sum += sq.AsInt();
+        return sum;
+    }
+
+    [Benchmark]
+    public int ForLoop_AsSpan_Marshal()
+    {
+        var sum = 0;
+        ref var space = ref MemoryMarshal.GetArrayDataReference(Square.All);
+        for (var i = 0; i < Square.All.Length; i++)
+            sum += Unsafe.Add(ref space, i).AsInt();
+        return sum;
+    }
+    
+    [Benchmark]
+    public int BitBoard_WhileLoop()
+    {
+        var sum = 0;
+        var all = BitBoards.AllSquares;
+        while (all)
+        {
+            var sq = BitBoards.PopLsb(ref all);
+            sum += sq.AsInt();
+        }
+
+        return sum;
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
index 457b04df..1b62726a 100644
--- a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
@@ -56,16 +56,12 @@ public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationTok
                     if (word.Contains('.'))
                     {
                         if (int.TryParse(word.TrimEnd('.'), out var moveNumber))
-                        {
                             currentMoveNumber = moveNumber;
-                        }
                     }
                     else if (char.IsLetter(word[0]))
                     {
                         if (currentGameMoves.Count == 0 || !string.IsNullOrEmpty(currentGameMoves[^1].BlackMove))
-                        {
                             currentGameMoves.Add(new PgnMove(currentMoveNumber, word, string.Empty));
-                        }
                         else
                         {
                             var lastMove = currentGameMoves[^1];
@@ -90,22 +86,20 @@ public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationTok
                     var firstQuoteIndex = line.IndexOf('"');
                     var lastQuoteIndex = line.LastIndexOf('"');
 
-                    if (firstSpaceIndex > 0 && firstQuoteIndex > firstSpaceIndex &&
-                        lastQuoteIndex > firstQuoteIndex)
-                    {
-                        var tagName = line.Substring(1, firstSpaceIndex - 1).Trim();
-                        var tagValue = line.Substring(firstQuoteIndex + 1, lastQuoteIndex - firstQuoteIndex - 1)
-                            .Trim();
+                    if (firstSpaceIndex <= 0 || firstQuoteIndex <= firstSpaceIndex
+                                             || lastQuoteIndex <= firstQuoteIndex)
+                        continue;
+                    
+                    var tagName = line.Substring(1, firstSpaceIndex - 1).Trim();
+                    var tagValue = line.Substring(firstQuoteIndex + 1, lastQuoteIndex - firstQuoteIndex - 1)
+                        .Trim();
 
-                        currentGameTags[tagName] = tagValue;
-                    }
+                    currentGameTags[tagName] = tagValue;
                 }
             }
         }
 
         if (currentGameTags.Count > 0 && currentGameMoves.Count > 0)
-        {
             yield return new PgnGame(currentGameTags, currentGameMoves);
-        }
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 0748bfab..d1272425 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -353,10 +353,8 @@ private static BitBoard ComputeDynamicFencedPawns(
         var ourPawn = PieceTypes.Pawn.MakePiece(us);
         var result = BitBoard.Empty;
 
-        ref var fileSpace = ref MemoryMarshal.GetArrayDataReference(File.AllFiles);
-        for (var i = 0; i < File.AllFiles.Length; ++i)
+        foreach (var f in File.AllFiles)
         {
-            var f = Unsafe.Add(ref fileSpace, i);
             var sq = NextFenceRankSquare(fenceRank, f, them);
             var b = sq.ForwardFile(them) & theirPawns;
             while (b)
@@ -366,7 +364,7 @@ private static BitBoard ComputeDynamicFencedPawns(
                     result |= sq;
             }
         }
-
+        
         return result;
     }
 
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 55bf3f91..4dce9981 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -46,9 +46,9 @@ public static class Cuckoo
     static Cuckoo()
     {
         var count = 0;
-        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.All);
-        for (var i = 0; i < Piece.All.Length; i++) {
-            var pc = Unsafe.Add(ref piecesSpace, i);
+        
+        foreach (var pc in Piece.All)
+        {
             var bb = BitBoards.AllSquares;
             while (bb)
             {
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 762c1db4..17b94ace 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -57,12 +57,14 @@ public static IServiceCollection AddChessLib(
             serviceCollection.AddSingleton(configuration);
         }
 
+        // TODO : Add configuration "manually" to avoid IL2026 warning
         serviceCollection.AddOptions<TranspositionTableConfiguration>().Configure<IConfiguration>(
             static (settings, configuration)
                 => configuration
                     .GetSection(TranspositionTableConfiguration.Section)
                     .Bind(settings));
 
+        // TODO : Add configuration "manually" to avoid IL2026 warning
         serviceCollection.AddOptions<PolyglotBookConfiguration>().Configure<IConfiguration>(
             static (settings, configuration)
                 => configuration
diff --git a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
index 04903a6f..620405a5 100644
--- a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
@@ -33,7 +33,7 @@ public static class PieceExtensions
 {
     private const string PgnPieceChars = " PNBRQK";
 
-    internal const string PieceChars = PgnPieceChars + "  pnbrqk";
+    public const string PieceChars = PgnPieceChars + "  pnbrqk";
 
     private const string PromotionPieceNotation = "  nbrq";
 
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index b8aaae67..23e91a26 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -97,16 +97,10 @@ private static void InitializePst(in IRKiss rnd)
         for (var i = 0; i < ZobristPst.Length; i++)
             ZobristPst[i] = new HashKey[Square.Count];
 
-        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.All);
-        for (var i = 0; i < Piece.All.Length; ++i)
+        foreach (var pc in Piece.All)
         {
-            var pc = Unsafe.Add(ref piecesSpace, i);
-            var bb = BitBoards.AllSquares;
-            while (bb)
-            {
-                var sq = BitBoards.PopLsb(ref bb).AsInt();
-                ZobristPst[pc.AsInt()][sq] = rnd.Rand();
-            }
+            for (var sq = Squares.a1; sq <= Squares.h8; sq++)
+                ZobristPst[pc.AsInt()][sq.AsInt()] = rnd.Rand();
         }
     }
 
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 71a37b4d..30342457 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1487,13 +1487,10 @@ private void SetState()
         key ^= State.CastlelingRights.Key();
 
         var materialKey = HashKey.Empty;
-        ref var piecesSpace = ref MemoryMarshal.GetArrayDataReference(Piece.All);
-        for (var i = 0; i < Piece.All.Length; i++)
-        {
-            var pc = Unsafe.Add(ref piecesSpace, i);
+        
+        foreach (var pc in Piece.All)
             for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
                 materialKey ^= pc.GetZobristPst(cnt);
-        }
 
         State.Key = key;
         State.PawnKey = pawnKey;
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 277f863c..70cfa81e 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -225,24 +225,18 @@ static BitBoards()
         static int distanceRank(Square x, Square y) => distance(x.Rank.AsInt(), y.Rank.AsInt());
 
         // ForwardRanksBB population loop idea from sf
-        ref var rankSpace = ref MemoryMarshal.GetArrayDataReference(Rank.All);
-        for (var i = 0; i < Rank.All.Length; i++)
+        for (var r = Rank.Rank1; r <= Rank.Rank8; r++)
         {
-            var r = Unsafe.Add(ref rankSpace, i);
             var rank = r.AsInt();
             ForwardRanksBB[0][rank] = ~(ForwardRanksBB[1][rank + 1] = ForwardRanksBB[1][rank] | r.BitBoardRank());
         }
 
         BitBoard bb;
 
-        ref var playerSpace = ref MemoryMarshal.GetArrayDataReference(Player.AllPlayers);
-        for (var i = 0; i < Player.AllPlayers.Length; ++i)
+        foreach (var player in Player.AllPlayers)
         {
-            var player = Unsafe.Add(ref playerSpace, i);
-            bb = AllSquares;
-            while (bb)
+            foreach (var square in Square.All)
             {
-                var square = PopLsb(ref bb);
                 var s = square.AsInt();
                 var file = square.File;
                 var rank = square.Rank.AsInt();
@@ -251,7 +245,7 @@ static BitBoards()
                 PassedPawnMaskBB[player.Side][s] = ForwardFileBB[player.Side][s] | PawnAttackSpanBB[player.Side][s];
             }
         }
-
+        
         // mini local helpers
 
         Span<PieceTypes> validMagicPieces = stackalloc PieceTypes[] { PieceTypes.Bishop, PieceTypes.Rook };
@@ -348,10 +342,9 @@ private static void InitializeKingRing(Square s1, int sq, File file)
     {
         const int pt = (int)PieceTypes.King;
 
-        ref var playerSpace = ref MemoryMarshal.GetArrayDataReference(Player.AllPlayers);
-        for (var i = 0; i < Player.AllPlayers.Length; ++i)
+        // TODO : Change to basic for-loop
+        foreach (var player in Player.AllPlayers)
         {
-            var player = Unsafe.Add(ref playerSpace, i);
             KingRingBB[player.Side][sq] = PseudoAttacksBB[pt][sq];
             if (s1.RelativeRank(player) == Ranks.Rank1)
                 KingRingBB[player.Side][sq] |= KingRingBB[player.Side][sq].Shift(PawnPushDirections[player.Side]);
@@ -362,7 +355,7 @@ private static void InitializeKingRing(Square s1, int sq, File file)
                 KingRingBB[player.Side][sq] |= KingRingBB[player.Side][sq].EastOne();
 
             Debug.Assert(!KingRingBB[player.Side][sq].IsEmpty);
-        }
+        }        
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index b35da68d..8b8576f2 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -198,7 +198,7 @@ private Piece(Piece pc) : this(pc.Value) { }
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string ToString() => this.GetPieceString();
 
-    private static Piece GetPiece(char character)
+    public static Piece GetPiece(char character)
     {
         return character switch
         {
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index 1fcb0902..dbd18c4c 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -93,6 +93,18 @@ public readonly void Deconstruct(out float mg, out float eg)
         mg = _data.X;
         eg = _data.Y;
     }
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public void AddMg(int v) => _data.X += v;
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public void AddEg(int v) => _data.Y += v;
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public void SubtractMg(int v) => _data.X -= v;
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public void SubtractEg(int v) => _data.Y -= v;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void SetMg(int v) => _data.X = v;

From 063772cd34889579a0f39c81ce8ff34ee6a6670a Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Wed, 3 May 2023 20:51:47 +0200
Subject: [PATCH 042/119] Code update

- Improved performance for various lookups
- Improved performance for various loops
- Relaxed unsafe code use (sizeof(T) -> Unsafe.SizeOf<T>())
---
 .../Rudzoft.ChessLib.Benchmark.csproj         |  1 -
 .../ShiftFuncBench.cs                         | 28 ++++++++++++++++---
 .../Rudzoft.ChessLib.PGN.csproj               |  1 -
 src/Rudzoft.ChessLib.Perft/PerftTable.cs      | 10 +++----
 .../Rudzoft.ChessLib.Perft.csproj             |  1 -
 .../DataTests/RecordHashTests.cs              | 21 ++++++++++++++
 .../SizesTests/BaseTypesSizeTests.cs          | 21 +++++++-------
 src/Rudzoft.ChessLib/Blockage.cs              |  2 +-
 src/Rudzoft.ChessLib/Cuckoo.cs                |  3 +-
 .../Transposition/TranspositionTable.cs       | 10 +------
 .../TranspositionTableConfiguration.cs        |  2 +-
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          |  2 +-
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |  4 +--
 src/Rudzoft.ChessLib/Position.cs              |  2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      | 25 ++++++++++-------
 .../Tables/Perft/PerftTable.cs                | 11 ++------
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 27 ++++++++++--------
 src/Rudzoft.ChessLib/Types/HashKey.cs         |  7 +----
 .../Environment/FrameworkEnvironment.cs       |  8 ------
 19 files changed, 102 insertions(+), 84 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 70dc2bf3..de03e915 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -7,7 +7,6 @@
     <Platforms>AnyCPU</Platforms>
     <LangVersion>default</LangVersion>
     <Configurations>Debug;Release</Configurations>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
diff --git a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
index 7fa89b0f..4140c5e3 100644
--- a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
@@ -1,5 +1,7 @@
 using System;
 using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
@@ -8,8 +10,7 @@ namespace Rudzoft.ChessLib.Benchmark;
 // ReSharper disable once ClassCanBeSealed.Global
 public class ShiftFuncBench
 {
-
-    private static readonly IDictionary<Direction, Func<BitBoard, BitBoard>> ShiftFuncs = MakeShiftFuncs();
+    private static readonly Dictionary<Direction, Func<BitBoard, BitBoard>> ShiftFuncs = MakeShiftFuncs();
 
     private static readonly Direction[] AllDirections = {
         Direction.North, Direction.South, Direction.East, Direction.West,
@@ -28,6 +29,16 @@ public BitBoard ShiftFuncLookup()
         return bb;
     }
 
+    [Benchmark]
+    public BitBoard ShiftFunc2Lookup()
+    {
+        var bb = BitBoards.EmptyBitBoard;
+        foreach (var direction in AllDirections)
+            bb = ShiftF2(BitBoards.Center, direction);
+
+        return bb;
+    }
+
     [Benchmark]
     public BitBoard BasicLookup()
     {
@@ -46,6 +57,16 @@ private static BitBoard ShiftF(in BitBoard bb, Direction direction)
         throw new ArgumentException("Invalid shift argument.", nameof(direction));
     }
 
+    private static BitBoard ShiftF2(in BitBoard bb, Direction direction)
+    {
+        ref var func = ref CollectionsMarshal.GetValueRefOrNullRef(ShiftFuncs, direction);
+        
+        if (Unsafe.IsNullRef(ref func))
+            throw new ArgumentException("Invalid shift argument.", nameof(direction));
+            
+        return func(bb);
+    }
+
     private static BitBoard Shift(in BitBoard bb, Direction direction)
     {
         if (direction == Direction.North)
@@ -78,7 +99,7 @@ private static BitBoard Shift(in BitBoard bb, Direction direction)
             throw new ArgumentException("Invalid shift argument.", nameof(direction));
     }
 
-    private static IDictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
+    private static Dictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
     {
         return new Dictionary<Direction, Func<BitBoard, BitBoard>>(13)
         {
@@ -97,5 +118,4 @@ private static IDictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
             { Direction.West, static board => board.WestOne() }
         };
     }
-
 }
diff --git a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
index 33d1f6d9..5a91149e 100644
--- a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
+++ b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
@@ -5,7 +5,6 @@
         <TieredPGO>true</TieredPGO>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
-        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     </PropertyGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib.Perft/PerftTable.cs b/src/Rudzoft.ChessLib.Perft/PerftTable.cs
index ce19cb59..529628c8 100644
--- a/src/Rudzoft.ChessLib.Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib.Perft/PerftTable.cs
@@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using Rudzoft.ChessLib.Types;
 using System;
+using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Perft;
 
@@ -37,12 +38,9 @@ internal static class PerftTable
 
     static PerftTable()
     {
-        unsafe
-        {
-            var entrySize = sizeof(PerftHashEntry);
-            TtSize = (ulong)(HashMemory * 1024 * 1024 / entrySize);
-            Table = new PerftHashEntry[TtSize];
-        }
+        var entrySize = Unsafe.SizeOf<PerftHashEntry>();
+        TtSize = (ulong)(HashMemory * 1024 * 1024 / entrySize);
+        Table = new PerftHashEntry[TtSize];
     }
     
     private struct PerftHashEntry
diff --git a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
index 88bbf50b..fe983558 100644
--- a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
+++ b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
@@ -4,7 +4,6 @@
     <TargetFramework>net7.0</TargetFramework>
     <TieredPGO>true</TieredPGO>
     <LangVersion>default</LangVersion>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <Nullable>enable</Nullable>
   </PropertyGroup>
 
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs b/src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs
new file mode 100644
index 00000000..ee9f5ce3
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Rudzoft.ChessLib.Test.DataTests;
+
+public sealed class RecordHashTests
+{
+    private sealed record C(string One, string Two, DateOnly Date);
+    
+    [Fact]
+    public void SwappedRecordValuesProduceVarianceInHashCodes()
+    {
+        var date = new DateOnly(2021, 1, 1);
+        var c1 = new C("one", "two", date);
+        var c2 = new C("two", "one", date);
+
+        var h1 = c1.GetHashCode();
+        var h2 = c2.GetHashCode();
+        
+        Assert.NotEqual(h1, h2);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
index 685e8559..5a693985 100644
--- a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.SizesTests;
@@ -31,18 +32,18 @@ namespace Rudzoft.ChessLib.Test.SizesTests;
 public sealed class BaseTypesSizeTests
 {
     [Fact]
-    public unsafe void MoveSize()
+    public void MoveSize()
     {
         const int expected = 2;
-        var actual = sizeof(Move);
+        var actual = Unsafe.SizeOf<Move>();
         Assert.Equal(expected, actual);
     }
 
     [Fact]
-    public unsafe void PieceSize()
+    public void PieceSize()
     {
         const int expected = 1;
-        var actual = sizeof(Piece);
+        var actual = Unsafe.SizeOf<Piece>();
         Assert.Equal(expected, actual);
     }
 
@@ -50,7 +51,7 @@ public unsafe void PieceSize()
     public unsafe void SquareSize()
     {
         const int expected = 1;
-        var actual = sizeof(Square);
+        var actual = Unsafe.SizeOf<Square>();
         Assert.Equal(expected, actual);
     }
 
@@ -58,23 +59,23 @@ public unsafe void SquareSize()
     public unsafe void RankSize()
     {
         const int expected = 4;
-        var actual = sizeof(Rank);
+        var actual = Unsafe.SizeOf<Rank>();
         Assert.Equal(expected, actual);
     }
 
     [Fact]
-    public unsafe void FileSize()
+    public void FileSize()
     {
         const int expected = 4;
-        var actual = sizeof(File);
+        var actual = Unsafe.SizeOf<File>();
         Assert.Equal(expected, actual);
     }
 
     [Fact]
-    public unsafe void DepthSize()
+    public void DepthSize()
     {
         const int expected = 4;
-        var actual = sizeof(Depth);
+        var actual = Unsafe.SizeOf<Depth>();
         Assert.Equal(expected, actual);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index d1272425..50d73d0b 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -353,7 +353,7 @@ private static BitBoard ComputeDynamicFencedPawns(
         var ourPawn = PieceTypes.Pawn.MakePiece(us);
         var result = BitBoard.Empty;
 
-        foreach (var f in File.AllFiles)
+        foreach (var f in File.AllFiles.AsSpan())
         {
             var sq = NextFenceRankSquare(fenceRank, f, them);
             var b = sq.ForwardFile(them) & theirPawns;
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 4dce9981..3f873222 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -47,7 +48,7 @@ static Cuckoo()
     {
         var count = 0;
         
-        foreach (var pc in Piece.All)
+        foreach (var pc in Piece.All.AsSpan())
         {
             var bb = BitBoards.AllSquares;
             while (bb)
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index c9d17a16..6ea3b5d2 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -35,21 +35,13 @@ namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 
 public sealed class TranspositionTable : ITranspositionTable
 {
-    private static readonly int ClusterSize;
+    private static readonly int ClusterSize = Unsafe.SizeOf<TranspositionTableEntry>() * 4;
 
     private Cluster[] _table;
     private ulong _elements;
     private int _fullnessElements;
     private sbyte _generation;
 
-    static TranspositionTable()
-    {
-        unsafe
-        {
-            ClusterSize = sizeof(TranspositionTableEntry) * 4;
-        }
-    }
-
     public TranspositionTable(IOptions<TranspositionTableConfiguration> options)
     {
         _table = Array.Empty<Cluster>();
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
index e065c8d8..da7f2de5 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
@@ -32,6 +32,6 @@ public sealed class TranspositionTableConfiguration
 {
     public const string Section = "TranspositionTable";
     
-    [Range(0, 1 << 31, ErrorMessage = "Default size for TT: {0} must be between {1} and {2}.")]
+    [Range(1, int.MaxValue, ErrorMessage = "Default size for TT: {0} must be between {1} and {2}.")]
     public int DefaultSize { get; init; }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index 23e91a26..e067cf7d 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -97,7 +97,7 @@ private static void InitializePst(in IRKiss rnd)
         for (var i = 0; i < ZobristPst.Length; i++)
             ZobristPst[i] = new HashKey[Square.Count];
 
-        foreach (var pc in Piece.All)
+        foreach (var pc in Piece.All.AsSpan())
         {
             for (var sq = Squares.a1; sq <= Squares.h8; sq++)
                 ZobristPst[pc.AsInt()][sq.AsInt()] = rnd.Rand();
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index b72cd3c9..acf698cb 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -56,9 +56,9 @@ public sealed class PolyglotBook : IPolyglotBook
     private readonly Random _rnd;
     private readonly ObjectPool<IMoveList> _moveListPool;
 
-    private unsafe PolyglotBook(ObjectPool<IMoveList> pool)
+    private PolyglotBook(ObjectPool<IMoveList> pool)
     {
-        _entrySize = sizeof(PolyglotBookEntry);
+        _entrySize = Unsafe.SizeOf<PolyglotBookEntry>();
         _rnd = new Random(DateTime.Now.Millisecond);
         _moveListPool = pool;
     }
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 30342457..38c11dda 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1488,7 +1488,7 @@ private void SetState()
 
         var materialKey = HashKey.Empty;
         
-        foreach (var pc in Piece.All)
+        foreach (var pc in Piece.All.AsSpan())
             for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
                 materialKey ^= pc.GetZobristPst(cnt);
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index be5c7299..66b86a09 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -54,18 +54,11 @@ public Uci()
     }
 
     public int MaxThreads { get; set; }
-
-
     public Action<IOption> OnLogger { get; set; }
-
     public Action<IOption> OnEval { get; set; }
-
     public Action<IOption> OnThreads { get; set; }
-
     public Action<IOption> OnHashSize { get; set; }
-
     public Action<IOption> OnClearHash { get; set; }
-
     public bool IsDebugModeEnabled { get; set; }
 
     public void Initialize(int maxThreads = 128)
@@ -86,7 +79,19 @@ public void Initialize(int maxThreads = 128)
 
     public void AddOption(string name, IOption option) => _options[name] = option;
     
-    public bool TryGetOption(string name, out IOption option) => _options.TryGetValue(name, out option);
+    public bool TryGetOption(string name, out IOption option)
+    {
+        ref var opt = ref CollectionsMarshal.GetValueRefOrNullRef(_options, name);
+        
+        if (Unsafe.IsNullRef(ref opt))
+        {
+            option = default;
+            return false;
+        }
+        
+        option = opt;
+        return true;
+    }
 
     public ulong Nps(in ulong nodes, in TimeSpan time)
         => (ulong)(nodes * 1000.0 / time.Milliseconds);
@@ -145,8 +150,8 @@ public string Score(int value, int mateInMaxPly, int valueMate)
             var s = (value > 0 ? valueMate - value + 1 : -valueMate - value) / 2;
             return $"mate {s}";
         }
-        else
-            return $"cp {ToCenti(value)}";
+
+        return $"cp {ToCenti(value)}";
     }
 
     public string ScoreCp(int value)
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
index 860c094d..5781f8a2 100644
--- a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Tables.Perft;
@@ -46,18 +47,10 @@ public Score Evaluate(IPosition pos)
 public sealed class PerftTable : HashTable<IPerftTableEntry>
 {
     private const int HashMemory = 4;
-    private static readonly int ElementSize;
+    private static readonly int ElementSize = Unsafe.SizeOf<PerftTableEntry>();
 
     private readonly int _mask;
 
-    static PerftTable()
-    {
-        unsafe
-        {
-            ElementSize = sizeof(PerftTableEntry);
-        }
-    }
-    
     public PerftTable()
     {
         Initialize(ElementSize, HashMemory, static () => new PerftTableEntry { Key = 0, Count = ulong.MinValue, Depth = -1 });
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 70cfa81e..e0639038 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -183,7 +183,7 @@ public static class BitBoards
 
     private static readonly Direction[] PawnPushDirections = { Direction.North, Direction.South };
 
-    private static readonly IDictionary<Direction, Func<BitBoard, BitBoard>> ShiftFuncs = MakeShiftFuncs();
+    private static readonly Dictionary<Direction, Func<BitBoard, BitBoard>> ShiftFuncs = MakeShiftFuncs();
 
     private static readonly Func<BitBoard, BitBoard>[] FillFuncs = MakeFillFuncs();
 
@@ -233,16 +233,17 @@ static BitBoards()
 
         BitBoard bb;
 
-        foreach (var player in Player.AllPlayers)
+        foreach (var player in Player.AllPlayers.AsSpan())
         {
-            foreach (var square in Square.All)
+            foreach (var square in Square.All.AsSpan())
             {
+                var side = player.Side;
                 var s = square.AsInt();
                 var file = square.File;
                 var rank = square.Rank.AsInt();
-                ForwardFileBB[player.Side][s] = ForwardRanksBB[player.Side][rank] & file.BitBoardFile();
-                PawnAttackSpanBB[player.Side][s] = ForwardRanksBB[player.Side][rank] & AdjacentFilesBB[file.AsInt()];
-                PassedPawnMaskBB[player.Side][s] = ForwardFileBB[player.Side][s] | PawnAttackSpanBB[player.Side][s];
+                ForwardFileBB[side][s] = ForwardRanksBB[side][rank] & file.BitBoardFile();
+                PawnAttackSpanBB[side][s] = ForwardRanksBB[side][rank] & AdjacentFilesBB[file.AsInt()];
+                PassedPawnMaskBB[side][s] = ForwardFileBB[side][s] | PawnAttackSpanBB[side][s];
             }
         }
         
@@ -631,10 +632,12 @@ public static BitBoard SouthFill(this BitBoard bb)
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     public static BitBoard Shift(this in BitBoard bb, Direction d)
     {
-        if (ShiftFuncs.TryGetValue(d, out var func))
-            return func(bb);
-
-        throw new ArgumentException("Invalid shift argument.", nameof(d));
+        ref var func = ref CollectionsMarshal.GetValueRefOrNullRef(ShiftFuncs, d);
+        
+        if (Unsafe.IsNullRef(ref func))
+            throw new ArgumentException("Invalid shift argument.", nameof(d));
+            
+        return func(bb);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -739,8 +742,8 @@ public static BitBoard MakeBitboard(params Square[] sqs)
     /// Helper method to generate shift function dictionary for all directions.
     /// </summary>
     /// <returns>The generated shift dictionary</returns>
-    private static IDictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
-        => new Dictionary<Direction, Func<BitBoard, BitBoard>>(13)
+    private static Dictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
+        => new(13)
         {
             { Direction.None, static board => board },
             { Direction.North, static board => board.NorthOne() },
diff --git a/src/Rudzoft.ChessLib/Types/HashKey.cs b/src/Rudzoft.ChessLib/Types/HashKey.cs
index 672e9c01..5b534a05 100644
--- a/src/Rudzoft.ChessLib/Types/HashKey.cs
+++ b/src/Rudzoft.ChessLib/Types/HashKey.cs
@@ -36,17 +36,12 @@ namespace Rudzoft.ChessLib.Types;
 {
     private HashKey(ulong key)
     {
-        LowerKey = UpperKey = 0;
-        Key16 = 0;
         Key = key;
     }
 
     private HashKey(uint key32)
     {
-        UpperKey = 0;
-        Key = 0;
-        Key16 = 0;
-        LowerKey = key32;
+        Key = key32;
     }
 
     [field: FieldOffset(0)] public ushort Key16 { get; }
diff --git a/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs b/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
index 6822671c..35de0b6c 100644
--- a/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
+++ b/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
@@ -39,8 +39,6 @@ public sealed class FrameworkEnvironment : IFrameworkEnvironment
     // ReSharper disable once IdentifierTypo
     public static extern uint getuid();
 
-    #region Public Properties
-
     private static readonly string FrameWork = Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
 
     public bool IsDevelopment { get; set; }
@@ -55,10 +53,6 @@ public sealed class FrameworkEnvironment : IFrameworkEnvironment
 
     public bool HighresTimer => Stopwatch.IsHighResolution;
 
-    #endregion Public Properties
-
-    #region Constructor
-
     public FrameworkEnvironment()
     {
 #if RELEASE
@@ -66,8 +60,6 @@ public FrameworkEnvironment()
 #endif
     }
 
-    #endregion Constructor
-
     private static bool CheckIsAdmin()
     {
         try

From b8d0b3dfcdccee952961ada1e9d6d75c94068bc7 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 13 May 2023 09:24:28 +0200
Subject: [PATCH 043/119] Couple of updates

- Renamed InvalidFen -> InvalidFenException
- Renamed State.Key -> State.PositionKey
- Simplified BitBoards init
- Improved BitBoards shift func lookup
- Fixed an issue with incoming states not being handled correctly
- Improved handling of incoming state objects
- Added Position.MovePositionKey(Move)
- Work towards fixing some zobrist instability
- Added position key check in perft tests
- Added basic zobrist piece move hash key checks
---
 .../FENTests/FenTests.cs                      |  6 +-
 .../PerftTests/PerftVerify.cs                 |  6 ++
 .../ZobristTests/ZobristHashTests.cs          | 67 +++++++++++++
 src/Rudzoft.ChessLib.WebApi/Program.cs        |  1 +
 .../Rudzoft.ChessLib.WebApi.csproj            |  1 +
 src/Rudzoft.ChessLib/Cuckoo.cs                |  4 +-
 .../{InvalidFen.cs => InvalidFenException.cs} | 10 +-
 src/Rudzoft.ChessLib/Fen/Fen.cs               | 30 +++---
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          | 73 +++++++++++++--
 src/Rudzoft.ChessLib/IPosition.cs             |  8 +-
 src/Rudzoft.ChessLib/Position.cs              | 93 ++++++++++---------
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |  6 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  2 +-
 src/Rudzoft.ChessLib/State.cs                 | 10 +-
 .../Tables/Perft/PerftTable.cs                |  2 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 28 +++---
 .../Validation/PositionValidator.cs           |  2 +-
 17 files changed, 241 insertions(+), 108 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
 rename src/Rudzoft.ChessLib/Exceptions/{InvalidFen.cs => InvalidFenException.cs} (82%)

diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index a614b6d7..77a6bd94 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -77,9 +77,9 @@ public void GetFen(string fen)
     }
 
     [Theory]
-    [InlineData("z3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", typeof(InvalidFen))]
-    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/ip2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", typeof(InvalidFen))]
-    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/lPPBBPPP/R3K2R w KQkq - 0 1", typeof(InvalidFen))]
+    [InlineData("z3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", typeof(InvalidFenException))]
+    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/ip2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", typeof(InvalidFenException))]
+    [InlineData("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/lPPBBPPP/R3K2R w KQkq - 0 1", typeof(InvalidFenException))]
     public void Validate(string fen, Type expectedException)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
index 484b6ddf..cfc33baa 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
@@ -44,8 +44,14 @@ protected void AssertPerft(string fen, int depth, in ulong expected)
     {
         var g = _serviceProvider.GetRequiredService<IGame>();
         g.NewGame(fen);
+
+        var initialZobrist = g.Pos.State.PositionKey;
         
         var actual = g.Perft(depth);
+
+        var afterZobrist = g.Pos.State.PositionKey;
+        
         Assert.Equal(expected, actual);
+        Assert.Equal(initialZobrist, afterZobrist);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
new file mode 100644
index 00000000..5173fa35
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
+
+namespace Rudzoft.ChessLib.Test.ZobristTests;
+
+public sealed class ZobristHashTests
+{
+    
+    private readonly IServiceProvider _serviceProvider;
+
+    public ZobristHashTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
+            .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
+            .BuildServiceProvider();
+    }
+
+    [Theory]
+    [InlineData(Fen.Fen.StartPositionFen, Squares.a2, Squares.a4)]
+    [InlineData(Fen.Fen.StartPositionFen, Squares.a2, Squares.a3)]
+    [InlineData(Fen.Fen.StartPositionFen, Squares.b1, Squares.c3)]
+    public void BackAndForth(string fen, Squares from, Squares to)
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var stateIndex = 0;
+        var states = new List<State> { new() };
+
+        pos.Set(fen, ChessMode.Normal, states[stateIndex]);
+
+        var startKey = pos.State.PositionKey;
+
+        var move = new Move(from, to);
+
+        stateIndex++;
+        states.Add(new State());
+
+        pos.MakeMove(move, states[stateIndex]);
+
+        var moveKey = pos.State.PositionKey;
+        
+        pos.TakeMove(move);
+
+        var finalKey = pos.State.PositionKey;
+        
+        Assert.NotEqual(startKey, moveKey);
+        Assert.Equal(startKey, states[0].PositionKey);
+        Assert.NotEqual(startKey, states[1].PositionKey);
+        Assert.Equal(startKey, finalKey);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.WebApi/Program.cs b/src/Rudzoft.ChessLib.WebApi/Program.cs
index d58907cb..cf8973fe 100644
--- a/src/Rudzoft.ChessLib.WebApi/Program.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Program.cs
@@ -5,6 +5,7 @@
 
 // Add services to the container.
 
+builder.Services.AddHealthChecks();
 builder.Services.AddControllers();
 // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
 builder.Services.AddEndpointsApiExplorer();
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index 5fc9662e..b6944f51 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -15,6 +15,7 @@
           <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
         <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
+        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.4" />
         <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
         <PackageReference Include="Microsoft.OpenApi" Version="1.6.3" />
         <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 3f873222..19617b53 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -91,13 +91,13 @@ public static bool HashCuckooCycle(in IPosition pos, int end, int ply)
             return false;
 
         var state = pos.State;
-        var originalKey = state.Key;
+        var originalKey = state.PositionKey;
         var statePrevious = state.Previous;
 
         for (var i = 3; i <= end; i += 2)
         {
             statePrevious = statePrevious.Previous.Previous;
-            var moveKey = originalKey ^ statePrevious.Key;
+            var moveKey = originalKey ^ statePrevious.PositionKey;
 
             var j = CuckooHashOne(in moveKey);
             var found = CuckooKeys[j] == moveKey;
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidFen.cs b/src/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
similarity index 82%
rename from src/Rudzoft.ChessLib/Exceptions/InvalidFen.cs
rename to src/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
index ac8f4eac..675cbbbb 100644
--- a/src/Rudzoft.ChessLib/Exceptions/InvalidFen.cs
+++ b/src/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
@@ -29,17 +29,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Exceptions;
 
-public sealed class InvalidFen : ArgumentException
+public sealed class InvalidFenException : ArgumentException
 {
-    public InvalidFen()
+    public InvalidFenException()
     { }
 
-    public InvalidFen(string message)
+    public InvalidFenException(string message)
         : base(message) { }
 
-    public InvalidFen(string message, Exception innerException)
+    public InvalidFenException(string message, Exception innerException)
         : base(message, innerException) { }
 
-    public InvalidFen(SerializationInfo info, StreamingContext context)
+    public InvalidFenException(SerializationInfo info, StreamingContext context)
         : base(info, context) { }
 }
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index 7a0436b0..edbecbaf 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -71,22 +71,22 @@ public static void Validate(string fen)
         var f = fen.AsSpan().Trim();
 
         if (f.IsEmpty)
-            throw new InvalidFen("Fen is empty.");
+            throw new InvalidFenException("Fen is empty.");
 
         var invalidCharIndex = f.IndexOfAnyExcept(ValidChars.AsSpan());
 
         if (invalidCharIndex > -1)
-            throw new InvalidFen($"Invalid char detected in fen. fen={f}, pos={invalidCharIndex}");
+            throw new InvalidFenException($"Invalid char detected in fen. fen={f}, pos={invalidCharIndex}");
 
         if (f.Length >= MaxFenLen)
-            throw new InvalidFen($"Invalid length for fen {fen}.");
+            throw new InvalidFenException($"Invalid length for fen {fen}.");
 
         if (!ValidFenRegex.Value.IsMatch(fen))
-            throw new InvalidFen($"Invalid format for fen {fen}.");
+            throw new InvalidFenException($"Invalid format for fen {fen}.");
 
         CountValidity(f);
         if (!CountPieceValidity(f))
-            throw new InvalidFen($"Invalid piece validity for fen {fen}");
+            throw new InvalidFenException($"Invalid piece validity for fen {fen}");
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -98,7 +98,7 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
     {
         var spaceIndex = s.IndexOf(Space);
         if (spaceIndex == -1)
-            throw new InvalidFen($"Invalid fen {s.ToString()}");
+            throw new InvalidFenException($"Invalid fen {s.ToString()}");
 
         var mainSection = s[..spaceIndex];
 
@@ -116,21 +116,21 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
             if (t == '/')
             {
                 if (++pieceCount[0] > SeparatorCount)
-                    throw new InvalidFen($"Invalid fen (too many separators) {s.ToString()}");
+                    throw new InvalidFenException($"Invalid fen (too many separators) {s.ToString()}");
                 continue;
             }
 
             if (char.IsNumber(t))
             {
                 if (!char.IsBetween(t, '1', '8'))
-                    throw new InvalidFen($"Invalid fen (not a valid square jump) {s.ToString()}");
+                    throw new InvalidFenException($"Invalid fen (not a valid square jump) {s.ToString()}");
                 continue;
             }
 
             var pieceIndex = PieceExtensions.PieceChars.IndexOf(t);
 
             if (pieceIndex == -1)
-                throw new InvalidFen($"Invalid fen (unknown piece) {s.ToString()}");
+                throw new InvalidFenException($"Invalid fen (unknown piece) {s.ToString()}");
 
             var pc = new Piece((Pieces)pieceIndex);
             var pt = pc.Type();
@@ -140,7 +140,7 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
             var limit = limits[pt.AsInt()];
 
             if (pieceCount[pc.AsInt()] > limit)
-                throw new InvalidFen(
+                throw new InvalidFenException(
                     $"Invalid fen (piece limit exceeded for {pc}. index={i},limit={limit},count={pieceCount[pc.AsInt()]}) {s.ToString()}");
         }
 
@@ -161,20 +161,20 @@ static bool GetSpanSum(ReadOnlySpan<int> span, int limit)
         var valid = GetSpanSum(whitePieces, 15);
 
         if (!valid)
-            throw new InvalidFen($"Invalid fen (white piece count exceeds limit) {s.ToString()}");
+            throw new InvalidFenException($"Invalid fen (white piece count exceeds limit) {s.ToString()}");
 
         var blackPieces = pieceCount.Slice(9, 5);
 
         valid = GetSpanSum(blackPieces, 15);
 
         if (!valid)
-            throw new InvalidFen($"Invalid fen (black piece count exceeds limit) {s.ToString()}");
+            throw new InvalidFenException($"Invalid fen (black piece count exceeds limit) {s.ToString()}");
 
         spaceIndex = s.LastIndexOf(' ');
         var endSection = s[spaceIndex..];
 
         if (Maths.ToIntegral(endSection) >= 2048)
-            throw new InvalidFen($"Invalid half move count for fen {s.ToString()}");
+            throw new InvalidFenException($"Invalid half move count for fen {s.ToString()}");
 
         return true;
     }
@@ -193,12 +193,12 @@ private static void CountValidity(ReadOnlySpan<char> str)
         var valid = spaceCount >= SpaceCount;
 
         if (!valid)
-            throw new InvalidFen($"Invalid space character count in fen {str.ToString()}");
+            throw new InvalidFenException($"Invalid space character count in fen {str.ToString()}");
 
         valid = separatorCount == SeparatorCount;
 
         if (!valid)
-            throw new InvalidFen($"Invalid separator count in fen {str.ToString()}");
+            throw new InvalidFenException($"Invalid separator count in fen {str.ToString()}");
     }
 
     private static (int, int) CountSpacesAndSeparators(ReadOnlySpan<char> str)
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index e067cf7d..e8934d06 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -60,17 +60,17 @@ public static class Zobrist
     /// <summary>
     /// Represents the piece index (as in EPieces), with each a square of the board value to match.
     /// </summary>
-    private static readonly HashKey[][] ZobristPst = new HashKey[16][];
+    private static readonly HashKey[][] ZobristPst = new HashKey[Square.Count][];
 
     /// <summary>
     /// Represents the castleling rights.
     /// </summary>
-    private static readonly HashKey[] ZobristCastling = new HashKey[16];
+    private static readonly HashKey[] ZobristCastling = new HashKey[CastleRight.Count];
 
     /// <summary>
     /// En-Passant is only required to have 8 entries, one for each possible file where the En-Passant square can occur.
     /// </summary>
-    private static readonly HashKey[] ZobristEpFile = new HashKey[8];
+    private static readonly HashKey[] ZobristEpFile = new HashKey[File.Count];
 
     /// <summary>
     /// This is used if the side to move is black, if the side is white, no hashing will occur.
@@ -82,6 +82,9 @@ public static class Zobrist
     /// </summary>
     public static readonly HashKey ZobristNoPawn;
 
+
+    private static readonly HashKey Empty = HashKey.Empty;
+    
     static Zobrist()
     {
         var rnd = RKiss.Create(DefaultRandomSeed);
@@ -95,12 +98,13 @@ static Zobrist()
     private static void InitializePst(in IRKiss rnd)
     {
         for (var i = 0; i < ZobristPst.Length; i++)
-            ZobristPst[i] = new HashKey[Square.Count];
+            ZobristPst[i] = new HashKey[Piece.Count];
 
-        foreach (var pc in Piece.All.AsSpan())
+        for (var sq = Squares.a1; sq <= Squares.h8; sq++)
         {
-            for (var sq = Squares.a1; sq <= Squares.h8; sq++)
-                ZobristPst[pc.AsInt()][sq.AsInt()] = rnd.Rand();
+            var s = sq.AsInt();
+            foreach (var pc in Piece.All.AsSpan())
+                ZobristPst[s][pc.AsInt()] = rnd.Rand();
         }
     }
 
@@ -110,8 +114,55 @@ private static void InitializeRandomArray(Span<HashKey> array, IRKiss rnd)
             array[i] = rnd.Rand();
     }
 
+    public static HashKey ComputeMaterialKey(IPosition pos)
+    {
+        var key = HashKey.Empty;
+        foreach (var pc in Piece.All.AsSpan())
+            for (var count = 0; count < pos.Board.PieceCount(pc); count++)
+                key ^= GetZobristPst(pc, count);
+        
+        return key;
+    }
+
+    public static HashKey ComputePawnKey(IPosition pos)
+    {
+        var key = ZobristNoPawn;
+
+        var pawns = pos.Pieces(Piece.WhitePawn);
+
+        while (pawns)
+            key ^= GetZobristPst(Piece.WhitePawn, BitBoards.PopLsb(ref pawns));
+
+        pawns = pos.Pieces(Piece.BlackPawn);
+        
+        while (pawns)
+            key ^= GetZobristPst(Piece.BlackPawn, BitBoards.PopLsb(ref pawns));
+        
+        return key; 
+    }
+
+    public static HashKey ComputePositionKey(IPosition pos)
+    {
+        var key = HashKey.Empty;
+
+        foreach (var pc in Piece.All.AsSpan())
+        {
+            var bb = pos.Pieces(pc);
+            while (bb)
+                key ^= GetZobristPst(pc, BitBoards.PopLsb(ref bb));
+        }
+
+        if (pos.SideToMove.IsWhite)
+            key ^= ZobristSide;
+
+        key ^= GetZobristCastleling(pos.State.CastlelingRights.Rights);
+        key ^= GetZobristEnPassant(pos.EnPassantSquare.File);
+        
+        return key;
+    }
+    
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static ref HashKey GetZobristPst(this Piece piece, Square square) => ref ZobristPst[piece.AsInt()][square.AsInt()];
+    public static ref HashKey GetZobristPst(this Piece piece, Square square) => ref ZobristPst[square.AsInt()][piece.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static ref HashKey GetZobristCastleling(this CastleRights index) => ref ZobristCastling[index.AsInt()];
@@ -121,4 +172,10 @@ private static void InitializeRandomArray(Span<HashKey> array, IRKiss rnd)
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static ref HashKey GetZobristEnPassant(this File file) => ref ZobristEpFile[file.AsInt()];
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static HashKey GetZobristEnPassant(this Square sq)
+    {
+        return sq == Square.None ? HashKey.Empty : GetZobristEnPassant(sq.File);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index a0987bf0..2235a5d3 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -190,11 +190,11 @@ public interface IPosition : IEnumerable<Piece>
 
     FenData GenerateFen();
 
-    IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool validate = false, int searcher = 0);
+    IPosition Set(in FenData fenData, ChessMode chessMode, in State state, bool validate = false, int searcher = 0);
 
-    IPosition Set(string fen, ChessMode chessMode, State state, bool validate = false, int searcher = 0);
+    IPosition Set(string fen, ChessMode chessMode, in State state, bool validate = false, int searcher = 0);
 
-    IPosition Set(ReadOnlySpan<char> code, Player p, State state);
+    IPosition Set(ReadOnlySpan<char> code, Player p, in State state);
     
     HashKey GetPiecesKey();
 
@@ -217,6 +217,8 @@ public interface IPosition : IEnumerable<Piece>
     Value NonPawnMaterial(Player p);
     
     Value NonPawnMaterial();
+
+    HashKey MovePositionKey(Move m);
     
     PositionValidationResult Validate(PositionValidationTypes type = PositionValidationTypes.Basic);
 }
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 38c11dda..0a385c11 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -349,7 +349,7 @@ public FenData GenerateFen()
                     fen[length++] = CastlingRookSquare(CastleRight.BlackQueen).FileChar;
             }
             else
-                throw new Exception($"Invalid chess mode. mode={ChessMode}");
+                throw new InvalidFenException($"Invalid chess mode. mode={ChessMode}");
         }
 
         fen[length++] = space;
@@ -691,7 +691,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         State = State.CopyTo(newState);
         var state = State;
 
-        var k = state.Key ^ Zobrist.GetZobristSide();
+        var k = state.PositionKey ^ Zobrist.GetZobristSide();
 
         Ply++;
         state.Rule50++;
@@ -717,7 +717,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             Debug.Assert(pc == PieceTypes.King.MakePiece(us));
             Debug.Assert(capturedPiece == PieceTypes.Rook.MakePiece(us));
 
-            var (rookFrom, rookTo) = DoCastleling(us, from, ref to, CastlePerform.Do);
+            var (rookFrom, rookTo) = DoCastle(us, from, ref to, CastlePerform.Do);
 
             k ^= capturedPiece.GetZobristPst(rookFrom) ^ capturedPiece.GetZobristPst(rookTo);
 
@@ -826,7 +826,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         Debug.Assert(GetKingSquare(them).IsOk);
 
         // Update state properties
-        state.Key = k;
+        state.PositionKey = k;
         state.CapturedPiece = capturedPiece.Type();
 
         state.Checkers = givesCheck ? AttacksTo(GetKingSquare(them)) & Board.Pieces(us) : BitBoard.Empty;
@@ -843,16 +843,16 @@ public void MakeNullMove(in State newState)
     {
         Debug.Assert(!InCheck);
 
-        State = State.CopyTo(newState);
+        CopyState(in newState);
 
         if (State.EnPassantSquare != Square.None)
         {
             var enPassantFile = State.EnPassantSquare.File;
-            State.Key ^= enPassantFile.GetZobristEnPassant();
+            State.PositionKey ^= enPassantFile.GetZobristEnPassant();
             State.EnPassantSquare = Square.None;
         }
 
-        State.Key ^= Zobrist.GetZobristSide();
+        State.PositionKey ^= Zobrist.GetZobristSide();
 
         ++State.Rule50;
         State.PliesFromNull = 0;
@@ -1000,10 +1000,12 @@ public bool SeeGe(Move m, Value threshold)
         var (from, to) = m;
 
         var swap = Values.GetPieceValue(GetPiece(to), Phases.Mg) - threshold;
+        
         if (swap < Value.ValueZero)
             return false;
 
         swap = Values.GetPieceValue(GetPiece(from), Phases.Mg) - swap;
+        
         if (swap <= Value.ValueZero)
             return true;
 
@@ -1118,7 +1120,7 @@ private void SetupPieces(ReadOnlySpan<char> fenChunk)
                 var pieceIndex = PieceExtensions.PieceChars.IndexOf(c);
 
                 if (pieceIndex == -1)
-                    throw new InvalidFen("Invalid char detected");
+                    throw new InvalidFenException("Invalid char detected");
 
                 Player p = new(char.IsLower(PieceExtensions.PieceChars[pieceIndex]));
 
@@ -1193,7 +1195,7 @@ private void SetupMoveNumber(IFenData fenData)
     /// <param name="state">State reference to use. Allows to keep track of states if pre-created (i.e. in a stack) before engine search start</param>
     /// <param name="validate">If true, the fen should be validated, otherwise not</param>
     /// <param name="searcher">Searcher index, to help point to a specific search index in thread-based search array</param>
-    public IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool validate = false, int searcher = 0)
+    public IPosition Set(in FenData fenData, ChessMode chessMode, in State state, bool validate = false, int searcher = 0)
     {
         if (validate)
             Fen.Fen.Validate(fenData.Fen.ToString());
@@ -1203,11 +1205,11 @@ public IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool
         ChessMode = chessMode;
         Searcher = searcher;
 
-        State = state.CopyTo(State);
+        CopyState(in state);
 
         SetupPieces(fenData.Chunk());
         SetupPlayer(fenData.Chunk());
-        SetupCastleling(fenData.Chunk());
+        SetupCastle(fenData.Chunk());
         SetupEnPassant(fenData.Chunk());
         SetupMoveNumber(fenData);
 
@@ -1216,10 +1218,10 @@ public IPosition Set(in FenData fenData, ChessMode chessMode, State state, bool
         return this;
     }
 
-    public IPosition Set(string fen, ChessMode chessMode, State state, bool validate = false, int searcher = 0)
+    public IPosition Set(string fen, ChessMode chessMode, in State state, bool validate = false, int searcher = 0)
         => Set(new FenData(fen), chessMode, state, validate, searcher);
 
-    public IPosition Set(ReadOnlySpan<char> code, Player p, State state)
+    public IPosition Set(ReadOnlySpan<char> code, Player p, in State state)
     {
         Debug.Assert(code[0] == 'K' && code[1..].IndexOf('K') != -1);
         Debug.Assert(code.Length.IsBetween(0, 8));
@@ -1232,7 +1234,7 @@ public IPosition Set(ReadOnlySpan<char> code, Player p, State state)
 
         var fenStr = $"{sides[0]}{8 - sides[0].Length}/8/8/8/8/8/8/{sides[1]}{8 - sides[1].Length} w - - 0 10";
 
-        return Set(fenStr, ChessMode.Normal, state);
+        return Set(fenStr, ChessMode.Normal, in state);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -1265,7 +1267,7 @@ public void TakeMove(Move m)
         }
 
         if (type == MoveTypes.Castling)
-            DoCastleling(us, from, ref to, CastlePerform.Undo);
+            DoCastle(us, from, ref to, CastlePerform.Undo);
         else
         {
             // Note: The parameters are reversed, since we move the piece "back"
@@ -1358,12 +1360,29 @@ public override string ToString()
         }
 
         output.AppendLine("    a   b   c   d   e   f   g   h");
-        output.AppendLine($"Zobrist : 0x{State.Key.Key:X}");
+        output.AppendLine($"Zobrist : 0x{State.PositionKey.Key:X}");
         var result = output.ToString();
         _outputObjectPool.Return(output);
         return result;
     }
 
+    public HashKey MovePositionKey(Move m)
+    {
+        Debug.Assert(m.IsValidMove());
+
+        var movePositionKey = State.PositionKey
+                              ^ Zobrist.GetZobristSide()
+                              ^ Board.MovedPiece(m).GetZobristPst(m.FromSquare())
+                              ^ Board.MovedPiece(m).GetZobristPst(m.ToSquare());
+
+        if (!Board.IsEmpty(m.ToSquare()))
+            movePositionKey ^= Board.PieceAt(m.ToSquare()).GetZobristPst(m.ToSquare());
+
+        movePositionKey ^= State.EnPassantSquare.GetZobristEnPassant();
+
+        return movePositionKey;
+    }
+
     public PositionValidationResult Validate(PositionValidationTypes type = PositionValidationTypes.Basic)
         => _positionValidator.Validate(this, type);
 
@@ -1372,7 +1391,7 @@ private static CastleRights OrCastlingRight(Player c, bool isKingSide)
         => (CastleRights)((int)CastleRights.WhiteKing << ((!isKingSide).AsByte() + 2 * c.Side));
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    private (Square, Square) DoCastleling(Player us, Square from, ref Square to, CastlePerform castlePerform)
+    private (Square, Square) DoCastle(Player us, Square from, ref Square to, CastlePerform castlePerform)
     {
         var kingSide = to > from;
         var doCastleling = castlePerform == CastlePerform.Do;
@@ -1454,11 +1473,13 @@ private void SetCheckInfo(in State state)
         state.CheckedSquares[PieceTypes.King.AsInt()] = BitBoard.Empty;
     }
 
+    private void CopyState(in State newState)
+    {
+        State = State.CopyTo(newState);
+    }
+    
     private void SetState()
     {
-        var key = HashKey.Empty;
-        var pawnKey = Zobrist.ZobristNoPawn;
-
         State.Checkers = AttacksTo(GetKingSquare(_sideToMove)) & Board.Pieces(~_sideToMove);
         _nonPawnMaterial[Player.White.Side] = _nonPawnMaterial[Player.Black.Side] = Value.ValueZero;
         SetCheckInfo(State);
@@ -1470,34 +1491,16 @@ private void SetState()
             var pc = GetPiece(sq);
             var pt = pc.Type();
 
-            key ^= pc.GetZobristPst(sq);
-
-            if (pt == PieceTypes.Pawn)
-                pawnKey ^= pc.GetZobristPst(sq);
-            else if (pt != PieceTypes.King)
+            if (pt != PieceTypes.King)
                 _nonPawnMaterial[pc.ColorOf().Side] += Values.GetPieceValue(pc, Phases.Mg);
         }
 
-        if (State.EnPassantSquare != Square.None)
-            key ^= State.EnPassantSquare.File.GetZobristEnPassant();
-
-        if (_sideToMove.IsBlack)
-            key ^= Zobrist.GetZobristSide();
-
-        key ^= State.CastlelingRights.Key();
-
-        var materialKey = HashKey.Empty;
-        
-        foreach (var pc in Piece.All.AsSpan())
-            for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
-                materialKey ^= pc.GetZobristPst(cnt);
-
-        State.Key = key;
-        State.PawnKey = pawnKey;
-        State.MaterialKey = materialKey;
+        State.MaterialKey = Zobrist.ComputeMaterialKey(this);
+        State.PawnKey = Zobrist.ComputePawnKey(this);
+        State.PositionKey = Zobrist.ComputePositionKey(this);
     }
 
-    private void SetupCastleling(ReadOnlySpan<char> castleling)
+    private void SetupCastle(ReadOnlySpan<char> castle)
     {
         Square RookSquare(Square startSq, Piece rook)
         {
@@ -1507,9 +1510,9 @@ Square RookSquare(Square startSq, Piece rook)
             return targetSq;
         }
 
-        ref var castleSpace = ref MemoryMarshal.GetReference(castleling);
+        ref var castleSpace = ref MemoryMarshal.GetReference(castle);
 
-        for (var i = 0; i < castleling.Length; ++i)
+        for (var i = 0; i < castle.Length; ++i)
         {
             var ca = Unsafe.Add(ref castleSpace, i);
             Player c = char.IsLower(ca);
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index 66b86a09..c837faa3 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -101,15 +101,13 @@ public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
         var moveList = pos.GenerateMoves();
 
         var moves = moveList.Get();
-        ref var movesSpace = ref MemoryMarshal.GetReference(moves);
 
-        for (var i = 0; i < moves.Length; ++i)
+        foreach (var move in moves)
         {
-            var move = Unsafe.Add(ref movesSpace, i);
             if (uciMove.Equals(move.Move.ToString(), StringComparison.InvariantCultureIgnoreCase))
                 return move.Move;
         }
-
+        
         return Move.EmptyMove;
     }
 
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 28f4101d..1bc88c4c 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -13,7 +13,7 @@
     <Company>None</Company>
     <Version>0.0.4</Version>
     <Description>Chess library with data structures and move generation.</Description>
-    <Copyright>(C) 2017-2022 Rudy Alex Kohn</Copyright>
+    <Copyright>(C) 2017-2023 Rudy Alex Kohn</Copyright>
     <AssemblyVersion>0.0.4</AssemblyVersion>
     <FileVersion>0.0.4</FileVersion>
     <PackageProjectUrl>https://github.com/rudzen/ChessLib</PackageProjectUrl>
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index bfb0b029..96781f4a 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -53,7 +53,7 @@ public sealed class State : IEquatable<State>
     // since they are always recomputed
     // -----------------------------
 
-    public HashKey Key { get; set; }
+    public HashKey PositionKey { get; set; }
 
     /// <summary>
     /// Represents checked squares for side to move
@@ -129,7 +129,7 @@ public State CopyTo(State other)
     public void Clear()
     {
         LastMove = Move.EmptyMove;
-        PawnKey = Key = MaterialKey = HashKey.Empty;
+        PawnKey = PositionKey = MaterialKey = HashKey.Empty;
         PliesFromNull = 0;
         Repetition = 0;
         CastlelingRights = CastleRight.None;
@@ -154,7 +154,7 @@ public void UpdateRepetition()
         for (var i = 4; i <= end; i += 2)
         {
             statePrevious = statePrevious.Previous.Previous;
-            if (statePrevious.Key != Key)
+            if (statePrevious.PositionKey != PositionKey)
                 continue;
             Repetition = statePrevious.Repetition != 0 ? -i : i;
             break;
@@ -168,7 +168,7 @@ public bool Equals(State other)
     {
         if (other is null) return false;
         return LastMove.Equals(other.LastMove)
-               && Key.Equals(other.Key)
+               && PositionKey.Equals(other.PositionKey)
                && PawnKey.Equals(other.PawnKey)
                && EnPassantSquare.Equals(other.EnPassantSquare)
                && CastlelingRights == other.CastlelingRights
@@ -190,7 +190,7 @@ public override int GetHashCode()
         hashCode.Add(MaterialKey);
         hashCode.Add(PliesFromNull);
         hashCode.Add(Rule50);
-        hashCode.Add(Key);
+        hashCode.Add(PositionKey);
         hashCode.Add(CastlelingRights.Rights.AsInt());
         hashCode.Add(EnPassantSquare);
         hashCode.Add(Checkers);
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
index 5781f8a2..b06e94f7 100644
--- a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
@@ -59,7 +59,7 @@ public PerftTable()
     
     public ref IPerftTableEntry TryGet(in IPosition pos, int depth, out bool found)
     {
-        var posKey = pos.State.Key;
+        var posKey = pos.State.PositionKey;
         var entryKey = posKey & _mask ^ depth;
         ref var entry = ref this[entryKey];
         found = entry.Key == entryKey && entry.Depth == depth;
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index e0639038..db77fb6e 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -246,7 +246,7 @@ static BitBoards()
                 PassedPawnMaskBB[side][s] = ForwardFileBB[side][s] | PawnAttackSpanBB[side][s];
             }
         }
-        
+
         // mini local helpers
 
         Span<PieceTypes> validMagicPieces = stackalloc PieceTypes[] { PieceTypes.Bishop, PieceTypes.Rook };
@@ -273,10 +273,8 @@ static BitBoards()
             InitializePseudoAttacks(s1);
 
             // Compute lines and betweens
-            ref var magicPiecesSpace = ref MemoryMarshal.GetReference(validMagicPieces);
-            for (var i = 0; i < validMagicPieces.Length; i++)
+            foreach (var validMagicPiece in validMagicPieces)
             {
-                var validMagicPiece = Unsafe.Add(ref magicPiecesSpace, i);
                 var pt = validMagicPiece.AsInt();
                 var bb3 = AllSquares;
                 while (bb3)
@@ -293,16 +291,16 @@ static BitBoards()
                                          GetAttacks(s2, validMagicPiece, BbSquares[sq]);
                 }
             }
-
+            
             // Compute KingRings
             InitializeKingRing(s1, sq, file);
         }
 
         SlotFileBB = new[]
         {
-            File.FileE.FileBB() | File.FileF.FileBB() | File.FileG.FileBB() | File.FileH.FileBB(), // King
-            File.FileA.FileBB() | File.FileB.FileBB() | File.FileC.FileBB() | File.FileD.FileBB(), // Queen
-            File.FileC.FileBB() | File.FileD.FileBB() | File.FileE.FileBB() | File.FileF.FileBB()  // Center
+            FileEBB | FileFBB | FileGBB | FileHBB, // King
+            FileABB | FileBBB | FileCBB | FileDBB, // Queen
+            FileCBB | FileDBB | FileEBB | FileFBB // Center
         };
     }
 
@@ -334,8 +332,8 @@ private static void InitializePseudoAttacks(Square sq)
         PseudoAttacksBB[PieceTypes.Bishop.AsInt()][s] = bishopAttacks;
         PseudoAttacksBB[PieceTypes.Rook.AsInt()][s] = rookAttacks;
         PseudoAttacksBB[PieceTypes.Queen.AsInt()][s] = bishopAttacks | rookAttacks;
-        PseudoAttacksBB[PieceTypes.King.AsInt()][s] = b.NorthOne()       | b.SouthOne()     | b.EastOne()
-                                                      | b.WestOne()      | b.NorthEastOne() | b.NorthWestOne()
+        PseudoAttacksBB[PieceTypes.King.AsInt()][s] = b.NorthOne() | b.SouthOne() | b.EastOne()
+                                                      | b.WestOne() | b.NorthEastOne() | b.NorthWestOne()
                                                       | b.SouthEastOne() | b.SouthWestOne();
     }
 
@@ -356,7 +354,7 @@ private static void InitializeKingRing(Square s1, int sq, File file)
                 KingRingBB[player.Side][sq] |= KingRingBB[player.Side][sq].EastOne();
 
             Debug.Assert(!KingRingBB[player.Side][sq].IsEmpty);
-        }        
+        }
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -508,11 +506,11 @@ public static BitBoard FrontSquares(this Player p, Square sq)
     public static BitBoard PromotionRank(this Player p) => PromotionRanks[p.Side];
 
     public static string StringifyRaw(in ulong bb, string title = "") => Stringify(BitBoard.Create(bb), title);
-    
+
     [SkipLocalsInit]
     public static string Stringify(in BitBoard bb, string title = "")
     {
-        const string line = "+---+---+---+---+---+---+---+---+---+";
+        const string line   = "+---+---+---+---+---+---+---+---+---+";
         const string bottom = "|   | A | B | C | D | E | F | G | H |";
         Span<char> span = stackalloc char[768];
         var idx = 0;
@@ -633,10 +631,10 @@ public static BitBoard SouthFill(this BitBoard bb)
     public static BitBoard Shift(this in BitBoard bb, Direction d)
     {
         ref var func = ref CollectionsMarshal.GetValueRefOrNullRef(ShiftFuncs, d);
-        
+
         if (Unsafe.IsNullRef(ref func))
             throw new ArgumentException("Invalid shift argument.", nameof(d));
-            
+
         return func(bb);
     }
 
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index 39367031..14f531df 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -185,7 +185,7 @@ private static IEnumerable<string> ValidateState(IPosition pos)
     {
         var state = pos.State;
 
-        if (state.Key.Key == 0 && pos.PieceCount() != 0)
+        if (state.PositionKey.Key == 0 && pos.PieceCount() != 0)
             yield return "state key is invalid";
 
         if (pos.PieceCount(PieceTypes.Pawn) == 0 && state.PawnKey != Zobrist.ZobristNoPawn)

From 865774fbd104385500b991102313fefcdc2d7dea Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 13 May 2023 15:25:54 +0200
Subject: [PATCH 044/119] Update perft a bit

---
 src/Rudzoft.ChessLib/Game.cs | 48 +++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 23 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 73f5f11a..c881358d 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -78,7 +78,7 @@ public Game(
     public ISearchParameters SearchParameters { get; }
 
     public IUci Uci { get; }
-    
+
     public ICpu Cpu { get; }
 
     public bool IsRepetition => _pos.IsRepetition;
@@ -109,9 +109,9 @@ public void UpdateDrawTypes()
 
         if (moves.IsEmpty)
             gameEndType |= GameEndTypes.Pat;
-        
+
         _moveListPool.Return(moveList);
-        
+
         GameEndType = gameEndType;
     }
 
@@ -142,39 +142,41 @@ public ulong Perft(int depth, bool root = true)
         // entry.Key = _pos.State.Key;
 
         var tot = ulong.MinValue;
-        
+
         var ml = _moveListPool.Get();
         ml.Generate(in _pos);
 
         var state = new State();
-        
+
         var moves = ml.Get();
         ref var movesSpace = ref MemoryMarshal.GetReference(moves);
         for (var i = 0; i < moves.Length; ++i)
         {
-            var valMove = Unsafe.Add(ref movesSpace, i);
             if (root && depth <= 1)
+            {
                 tot++;
+                continue;
+            }
+
+            var valMove = Unsafe.Add(ref movesSpace, i);
+            var m = valMove.Move;
+
+            _pos.MakeMove(m, in state);
+
+            if (depth <= 2)
+            {
+                var ml2 = _moveListPool.Get();
+                ml2.Generate(in _pos);
+                tot += (ulong)ml2.Length;
+                _moveListPool.Return(ml2);
+            }
             else
             {
-                var m = valMove.Move;
-                _pos.MakeMove(m, in state);
-
-                if (depth <= 2)
-                {
-                    var ml2 = _moveListPool.Get();
-                    ml2.Generate(in _pos);
-                    tot += (ulong)ml2.Length;
-                    _moveListPool.Return(ml2);
-                }
-                else
-                {
-                    var next = Perft(depth - 1, false);
-                    tot += next;
-                }
-
-                _pos.TakeMove(m);
+                var next = Perft(depth - 1, false);
+                tot += next;
             }
+
+            _pos.TakeMove(m);
         }
 
         // if (!root)

From 947e87463843c3f4abe42d91dd9b5ceedcf4f293 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 14 May 2023 08:57:01 +0200
Subject: [PATCH 045/119] Updated code design

- Zobrist no longer static class
- Cuckoo no longer static class
---
 .../FenBenchmark.cs                           |  9 +-
 .../MoveGeneratorBenchmark.cs                 |  7 +-
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |  7 +-
 .../PgnMoveNotationTests/SanToMoveTests.cs    |  9 +-
 src/Rudzoft.ChessLib.Perft/Perft.cs           |  7 +-
 .../BoardTests/BoardTests.cs                  |  5 +-
 .../BookTests/PolyglotTests.cs                |  7 +-
 .../CastleTests/BasicCastleTests.cs           |  3 +
 .../EvaluationTests/KpkBitBaseTests.cs        |  3 +
 .../FENTests/FenTests.cs                      |  3 +
 .../FenceTests/FenceTests.cs                  |  3 +
 .../GameplayTests/FoolsCheckMateTests.cs      |  5 +-
 .../MoveTests/MoveGen_49.cs                   | 32 +++++--
 .../MoveTests/MoveGeneratorTests.cs           |  5 +-
 .../MoveTests/MoveTests.cs                    |  3 +
 .../NotationTests/FanTests.cs                 |  3 +
 .../NotationTests/IccfTests.cs                |  3 +
 .../NotationTests/RanTests.cs                 |  3 +
 .../NotationTests/SanTests.cs                 |  3 +
 .../PerftTests/PerftVerify.cs                 |  3 +
 .../PiecesTests/PawnDoubleAttackTests.cs      |  3 +
 .../PiecesTests/PieceAttacksRookTests.cs      | 32 +++++--
 .../PositionTests/EnPassantFenTests.cs        |  3 +
 .../PositionTests/PositionTests.cs            |  5 +-
 .../PositionTests/ValidationTests.cs          |  3 +
 .../ProtocolTests/UciTests.cs                 |  3 +
 .../ZobristTests/ZobristHashTests.cs          | 84 ++++++++++++++++++-
 src/Rudzoft.ChessLib/Cuckoo.cs                | 12 +--
 .../ChessLibServiceCollectionExtensions.cs    |  3 +
 src/Rudzoft.ChessLib/Game.cs                  | 10 ++-
 src/Rudzoft.ChessLib/Hash/IZobrist.cs         | 21 +++++
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          | 51 ++++++-----
 src/Rudzoft.ChessLib/ICuckoo.cs               |  6 ++
 src/Rudzoft.ChessLib/Position.cs              | 76 ++++++++---------
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      | 17 ++--
 src/Rudzoft.ChessLib/State.cs                 |  4 +-
 src/Rudzoft.ChessLib/Types/CastleRight.cs     |  3 -
 src/Rudzoft.ChessLib/Types/HashKey.cs         |  8 --
 .../Validation/PositionValidator.cs           | 11 ++-
 39 files changed, 340 insertions(+), 138 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib/Hash/IZobrist.cs
 create mode 100644 src/Rudzoft.ChessLib/ICuckoo.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
index 89d35e9a..cd8d5f51 100644
--- a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
@@ -1,6 +1,7 @@
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
@@ -25,10 +26,12 @@ public void Setup()
         var pieceValue = new Values();
         var fp = new FenData(F);
         var state = new State();
-        var validator = new PositionValidator();
+        var zobrist = new Zobrist();
+        var cuckoo = new Cuckoo(zobrist);
+        var validator = new PositionValidator(zobrist);
         var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
-        _pos = new Position(board, pieceValue, validator, moveListObjectPool);
-        _pos.Set(in fp, ChessMode.Normal, state);
+        _pos = new Position(board, pieceValue, zobrist, cuckoo, validator, moveListObjectPool);
+        _pos.Set(in fp, ChessMode.Normal, in state);
     }
 
     [Benchmark(Description = "StringBuilder - NOT PRESENT")]
diff --git a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
index cdca3001..7a1137ef 100644
--- a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
@@ -1,5 +1,6 @@
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
@@ -33,9 +34,11 @@ public void Setup()
         var board = new Board();
         var pieceValue = new Values();
         var state = new State();
-        var validator = new PositionValidator();
+        var zobrist = new Zobrist();
+        var cuckoo = new Cuckoo(zobrist);
+        var validator = new PositionValidator(zobrist);
         _objectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
-        _pos = new Position(board, pieceValue, validator, _objectPool);
+        _pos = new Position(board, pieceValue, zobrist, cuckoo, validator, _objectPool);
         _pos.Set(_fen, ChessMode.Normal, state);
     }
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index 6085f1ad..ff221400 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Threading.Tasks;
 using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
@@ -80,9 +81,11 @@ public void Setup()
         
         var board = new Board();
         var values = new Values();
-        var validator = new PositionValidator();
+        var zobrist = new Zobrist();
+        var cuckoo = new Cuckoo(zobrist);
+        var validator = new PositionValidator(zobrist);
 
-        var pos = new Position(board, values, validator, moveListObjectPool);
+        var pos = new Position(board, values, zobrist, cuckoo, validator, moveListObjectPool);
         
         var game = new Game(tt, uci, cpu, sp, pos, moveListObjectPool);
         _perft = new Perft.Perft(game, new []{ pp });
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
index 9937acff..62cda8df 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
@@ -28,6 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
@@ -48,6 +49,8 @@ public SanToMoveTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
@@ -68,7 +71,7 @@ public async Task BasicSanConvert()
         var pos = _serviceProvider.GetRequiredService<IPosition>();
         var fenData = new FenData(Fen.Fen.StartPositionFen);
         var state = new State();
-        pos.Set(in fenData, ChessMode.Normal, state);
+        pos.Set(in fenData, ChessMode.Normal, in state);
         
         var games = new List<PgnGame>();
         var parser = _serviceProvider.GetRequiredService<IPgnParser>();
@@ -94,7 +97,7 @@ public async Task BasicSanConvert()
             .TakeWhile(static move => move != Move.EmptyMove);
 
         foreach (var move in chessMoves)
-            pos.MakeMove(move, state);
+            pos.MakeMove(move, in state);
         
         Assert.Equal(ExpectedGameCount, games.Count);
         Assert.NotEmpty(games);
@@ -107,7 +110,7 @@ public async Task AllAtOnceConvert()
         var pos = _serviceProvider.GetRequiredService<IPosition>();
         var fenData = new FenData(Fen.Fen.StartPositionFen);
         var state = new State();
-        pos.Set(in fenData, ChessMode.Normal, state);
+        pos.Set(in fenData, ChessMode.Normal, in state);
         
         var games = new List<PgnGame>();
         var parser = _serviceProvider.GetRequiredService<IPgnParser>();
diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/Rudzoft.ChessLib.Perft/Perft.cs
index 3590f37c..aaddb48d 100644
--- a/src/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/Rudzoft.ChessLib.Perft/Perft.cs
@@ -81,10 +81,11 @@ public async IAsyncEnumerable<ulong> DoPerft(int depth)
         if (Positions.Count == 0)
             yield break;
 
+        var state = new State();
+        
         foreach (var fd in Positions.Select(static p => new FenData(p.Fen)))
         {
-            var state = new State();
-            Game.Pos.Set(in fd, ChessMode.Normal, state);
+            Game.Pos.Set(in fd, ChessMode.Normal, in state);
             var result = Game.Perft(depth);
             yield return result;
         }
@@ -101,7 +102,7 @@ public void SetGamePosition(PerftPosition pp)
     {
         var fp = new FenData(pp.Fen);
         var state = new State();
-        Game.Pos.Set(in fp, ChessMode.Normal, state);
+        Game.Pos.Set(in fp, ChessMode.Normal, in state);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 91f6c440..1a7e4c95 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -32,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -47,6 +48,8 @@ public BoardTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
@@ -149,7 +152,7 @@ public void BoardPieceCount(string fen, PieceTypes pt, Player p, int expected)
         var fenData = new FenData(fen);
         var state = new State();
 
-        pos.Set(in fenData, ChessMode.Normal, state);
+        pos.Set(in fenData, ChessMode.Normal, in state);
 
         var board = pos.Board;
 
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index 9dcc095e..2a24b9a5 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -34,6 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Factories;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
@@ -57,6 +58,8 @@ public PolyglotTests(BookFixture fixture)
             .AddSingleton(polyOptions)
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>()
@@ -86,7 +89,7 @@ public void UnsetBookFileYieldsEmptyMove()
         var fenData = new FenData(fen);
         var state = new State();
 
-        pos.Set(in fenData, ChessMode.Normal, state);
+        pos.Set(in fenData, ChessMode.Normal, in state);
 
         var book = _serviceProvider
             .GetRequiredService<IPolyglotBookFactory>()
@@ -183,7 +186,7 @@ public void HashKeyFromInitialPositionIsComputedCorrectly(string[] uciMoves, ulo
         foreach (var m in uciMoves.Select(uciMove => uci.MoveFromUci(pos, uciMove)))
         {
             Assert.False(m.IsNullMove());
-            pos.MakeMove(m, state);
+            pos.MakeMove(m, in state);
         }
 
         var actualKey = book.ComputePolyglotKey(pos).Key;
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
index db049843..20142870 100644
--- a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
+++ b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -44,6 +45,8 @@ public BasicCastleTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
index 15cb31c1..64fb1bbb 100644
--- a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
+++ b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
@@ -30,6 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Evaluation;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -45,6 +46,8 @@ public KpkBitBaseTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<IKpkBitBase, KpkBitBase>()
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index 77a6bd94..7a72460a 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -30,6 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -45,6 +46,8 @@ public FenTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
index 54cd2843..3370879a 100644
--- a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -44,6 +45,8 @@ public FenceTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<IBlockage, Blockage>()
diff --git a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
index 4542586b..a9bf9618 100644
--- a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
+++ b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
@@ -45,6 +46,8 @@ public FoolsCheckMateTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
@@ -76,7 +79,7 @@ public void FoolsCheckMate()
 
         // make the moves necessary to create a mate
         foreach (var move in moves)
-            pos.MakeMove(move, state);
+            pos.MakeMove(move, in state);
 
         // verify in check is actually true
         Assert.True(pos.InCheck);
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
index 1ac3cf9b..35f59bfa 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
@@ -24,9 +24,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
@@ -36,17 +39,34 @@ namespace Rudzoft.ChessLib.Test.MoveTests;
 
 public sealed class MoveGen_49
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public MoveGen_49()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddSingleton<ICuckoo, Cuckoo>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
+            .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
+            .BuildServiceProvider();
+    }
+    
     [Fact]
     public void MoveListContainsMismatchedElement()
     {
         const string fen = "r3kb1r/p3pppp/p1n2n2/2pp1Q2/3P1B2/2P1PN2/Pq3PPP/RN2K2R w KQkq - 0 9";
 
-        var board = new Board();
-        var pieceValue = new Values();
-        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
-        var validator = new PositionValidator();
-
-        var pos = new Position(board, pieceValue, validator, moveListObjectPool);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+        
         var fd = new FenData(fen);
         var state = new State();
 
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
index dc52ccb0..f67c176a 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
@@ -45,6 +46,8 @@ public MoveGeneratorTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<ICuckoo, Cuckoo>()
+            .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
@@ -66,7 +69,7 @@ public void InCheckMoveGeneration()
         var pos = _serviceProvider.GetRequiredService<IPosition>();
         var fenData = new FenData(fen);
         var state = new State();
-        pos.Set(in fenData, ChessMode.Normal, state);
+        pos.Set(in fenData, ChessMode.Normal, in state);
 
         // make sure black is in check
         Assert.True(pos.InCheck);
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index ab7010f7..6bced788 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -32,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -47,6 +48,8 @@ public MoveTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index 6fbe7848..d09777a8 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -30,6 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
@@ -47,6 +48,8 @@ public FanTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index 730b2844..d1418872 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
@@ -46,6 +47,8 @@ public IccfTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index 832a72a0..0bff04c6 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -30,6 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
@@ -47,6 +48,8 @@ public RanTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index b233b50e..8de99062 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -31,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
@@ -49,6 +50,8 @@ public SanTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
index cfc33baa..368b7318 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
@@ -2,6 +2,7 @@
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
@@ -23,6 +24,8 @@ protected PerftVerify()
             .AddSingleton(ttOptions)
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ISearchParameters, SearchParameters>()
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
index 7b390669..5c1f4682 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -44,6 +45,8 @@ public PawnDoubleAttackTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
index 8d36f8fc..8fab256b 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
@@ -24,8 +24,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System;
+using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
-using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -34,6 +36,27 @@ namespace Rudzoft.ChessLib.Test.PiecesTests;
 
 public sealed class PieceAttacksRookTests : PieceAttacks
 {
+    private readonly IServiceProvider _serviceProvider;
+
+    public PieceAttacksRookTests()
+    {
+        _serviceProvider = new ServiceCollection()
+            .AddTransient<IBoard, Board>()
+            .AddSingleton<IValues, Values>()
+            .AddSingleton<ICuckoo, Cuckoo>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<IPositionValidator, PositionValidator>()
+            .AddTransient<IPosition, Position>()
+            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+            .AddSingleton(static serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = new MoveListPolicy();
+                return provider.Create(policy);
+            })
+            .BuildServiceProvider();
+    }
+    
     /// <summary>
     /// Testing results of blocked rook attacks, they should always return 7 on the sides, and 14 in
     /// the corner
@@ -66,12 +89,7 @@ public void RookBorderBlocked()
          */
 
         // just to get the attacks
-        var board = new Board();
-        var pieceValue = new Values();
-        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
-        var validator = new PositionValidator();
-
-        var pos = new Position(board, pieceValue, validator, moveListObjectPool);
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         while (border)
         {
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
index b9182e3f..6f1d047c 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -44,6 +45,8 @@ public EnPassantFenTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 35530151..5278cd99 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -44,6 +45,8 @@ public PositionTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
@@ -122,7 +125,7 @@ public void SetByCodeCreatesSameMaterialKey(string code, string fen)
 
         var materialKey = pos.State.MaterialKey;
 
-        var posCode = pos.Set(code, Player.White, state);
+        var posCode = pos.Set(code, Player.White, in state);
         var codeMaterialKey = posCode.State.MaterialKey;
         
         Assert.Equal(materialKey, codeMaterialKey);
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index a2df935b..91a62087 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -29,6 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -44,6 +45,8 @@ public ValidationTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
index 5b5730bd..23727bd7 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
@@ -30,6 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
@@ -50,6 +51,8 @@ public UciTests()
         _serviceProvider = new ServiceCollection()
             .AddSingleton(options)
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IBoard, Board>()
             .AddTransient<IPosition, Position>()
diff --git a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
index 5173fa35..303952a5 100644
--- a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
@@ -1,8 +1,35 @@
-using System;
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System;
 using System.Collections.Generic;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -11,7 +38,6 @@ namespace Rudzoft.ChessLib.Test.ZobristTests;
 
 public sealed class ZobristHashTests
 {
-    
     private readonly IServiceProvider _serviceProvider;
 
     public ZobristHashTests()
@@ -19,6 +45,8 @@ public ZobristHashTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IZobrist, Zobrist>()
+            .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
@@ -54,14 +82,62 @@ public void BackAndForth(string fen, Squares from, Squares to)
         pos.MakeMove(move, states[stateIndex]);
 
         var moveKey = pos.State.PositionKey;
-        
+
         pos.TakeMove(move);
 
         var finalKey = pos.State.PositionKey;
-        
+
         Assert.NotEqual(startKey, moveKey);
         Assert.Equal(startKey, states[0].PositionKey);
         Assert.NotEqual(startKey, states[1].PositionKey);
         Assert.Equal(startKey, finalKey);
     }
+
+    [Fact]
+    public void OnlyUniqueZobristHashKeys()
+    {
+        var zobrist = new Zobrist();
+
+        var set = new HashSet<HashKey> { HashKey.Empty };
+
+        var added = false;
+
+        foreach (var sq in Square.All.AsSpan())
+        {
+            foreach (var pc in Piece.All.AsSpan())
+            {
+                added = set.Add(zobrist.GetZobristPst(sq, pc));
+                Assert.True(added);
+            }
+        }
+
+        foreach (var f in File.AllFiles.AsSpan())
+        {
+            added = set.Add(zobrist.GetZobristEnPassant(f));
+            Assert.True(added);
+        }
+
+        var castleRights = new[]
+        {
+            CastleRight.None,
+            CastleRight.WhiteKing,
+            CastleRight.BlackKing,
+            CastleRight.WhiteQueen,
+            CastleRight.BlackQueen,
+            CastleRight.King,
+            CastleRight.Queen,
+            CastleRight.White,
+            CastleRight.Black,
+            CastleRight.Any
+        };
+
+        foreach (var cr in castleRights)
+        {
+            added = set.Add(zobrist.GetZobristCastleling(cr));
+            Assert.True(added);
+        }
+
+        added = set.Add(zobrist.GetZobristSide());
+        Assert.True(added);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 19617b53..240cc9e8 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -38,13 +38,13 @@ namespace Rudzoft.ChessLib;
 /// situations. https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
 /// TODO : Unit tests
 /// </summary>
-public static class Cuckoo
+public sealed class Cuckoo : ICuckoo
 {
-    private static readonly HashKey[] CuckooKeys = new HashKey[8192];
-    private static readonly Move[] CuckooMoves = new Move[8192];
+    private readonly HashKey[] CuckooKeys = new HashKey[8192];
+    private readonly Move[] CuckooMoves = new Move[8192];
 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Minor Code Smell", "S3963:\"static\" fields should be initialized inline", Justification = "Multiple arrays in one go")]
-    static Cuckoo()
+    public Cuckoo(IZobrist zobrist)
     {
         var count = 0;
         
@@ -60,7 +60,7 @@ static Cuckoo()
                         continue;
 
                     var move = Move.Create(sq1, sq2);
-                    var key = pc.GetZobristPst(sq1) ^ pc.GetZobristPst(sq2) ^ Zobrist.GetZobristSide();
+                    var key = zobrist.GetZobristPst(sq1, pc) ^ zobrist.GetZobristPst(sq2, pc) ^ zobrist.GetZobristSide();
                     var j = CuckooHashOne(in key);
                     do
                     {
@@ -85,7 +85,7 @@ static Cuckoo()
         Debug.Assert(count == 3668);
     }
 
-    public static bool HashCuckooCycle(in IPosition pos, int end, int ply)
+    public bool HashCuckooCycle(in IPosition pos, int end, int ply)
     {
         if (end < 3)
             return false;
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 17b94ace..6cd11843 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -32,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Evaluation;
 using Rudzoft.ChessLib.Factories;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
@@ -86,6 +87,8 @@ public static IServiceCollection AddChessLib(
                 uci.Initialize();
                 return uci;
             })
+            .AddSingleton<ICuckoo, Cuckoo>()
+            .AddSingleton<IZobrist, Zobrist>()
             .AddTransient<IKillerMovesFactory, KillerMovesFactory>()
             .AddSingleton<ISearchParameters, SearchParameters>()
             .AddSingleton<IValues, Values>()
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index c881358d..ed529402 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -45,6 +45,7 @@ public sealed class Game : IGame
 {
     private readonly ObjectPool<IMoveList> _moveListPool;
     private readonly IPosition _pos;
+    private readonly PerftTable _perftTable;
 
     public Game(
         ITranspositionTable transpositionTable,
@@ -56,7 +57,8 @@ public Game(
     {
         _moveListPool = moveListPool;
         _pos = pos;
-        
+        _perftTable = new PerftTable();
+
         Table = transpositionTable;
         SearchParameters = searchParameters;
         Uci = uci;
@@ -139,7 +141,7 @@ public ulong Perft(int depth, bool root = true)
         //
         // entry.Count = ulong.MinValue;
         // entry.Depth = depth;
-        // entry.Key = _pos.State.Key;
+        // entry.Key = _pos.State.PositionKey;
 
         var tot = ulong.MinValue;
 
@@ -180,8 +182,8 @@ public ulong Perft(int depth, bool root = true)
         }
 
         // if (!root)
-        //     _perftTable.Store(_pos.State.Key, depth, tot);
-        
+        //     _perftTable.Store(_pos.State.PositionKey, depth, tot);
+
         _moveListPool.Return(ml);
 
         return tot;
diff --git a/src/Rudzoft.ChessLib/Hash/IZobrist.cs b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
new file mode 100644
index 00000000..c6da9cfd
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
@@ -0,0 +1,21 @@
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Hash;
+
+public interface IZobrist
+{
+    /// <summary>
+    /// To use as base for pawn hash table
+    /// </summary>
+    HashKey ZobristNoPawn { get; }
+
+    HashKey ComputeMaterialKey(IPosition pos);
+    HashKey ComputePawnKey(IPosition pos);
+    HashKey ComputePositionKey(IPosition pos);
+    ref HashKey GetZobristPst(Square square, Piece piece);
+    ref HashKey GetZobristCastleling(CastleRights index);
+    ref HashKey GetZobristCastleling(CastleRight index);
+    HashKey GetZobristSide();
+    ref HashKey GetZobristEnPassant(File file);
+    HashKey GetZobristEnPassant(Square sq);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index e8934d06..d2b9de3e 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Hash;
@@ -50,7 +49,7 @@ namespace Rudzoft.ChessLib.Hash;
 /// Collision information from
 /// https://chessprogramming.wikispaces.com/Zobrist+Hashing
 /// </summary>
-public static class Zobrist
+public sealed class Zobrist : IZobrist
 {
     /// <summary>
     /// The default value for random seed for improved consistency
@@ -60,32 +59,29 @@ public static class Zobrist
     /// <summary>
     /// Represents the piece index (as in EPieces), with each a square of the board value to match.
     /// </summary>
-    private static readonly HashKey[][] ZobristPst = new HashKey[Square.Count][];
+    private readonly HashKey[][] ZobristPst = new HashKey[Square.Count][];
 
     /// <summary>
     /// Represents the castleling rights.
     /// </summary>
-    private static readonly HashKey[] ZobristCastling = new HashKey[CastleRight.Count];
+    private readonly HashKey[] ZobristCastling = new HashKey[CastleRight.Count];
 
     /// <summary>
     /// En-Passant is only required to have 8 entries, one for each possible file where the En-Passant square can occur.
     /// </summary>
-    private static readonly HashKey[] ZobristEpFile = new HashKey[File.Count];
+    private readonly HashKey[] ZobristEpFile = new HashKey[File.Count];
 
     /// <summary>
     /// This is used if the side to move is black, if the side is white, no hashing will occur.
     /// </summary>
-    private static readonly HashKey ZobristSide;
+    private readonly HashKey ZobristSide;
 
     /// <summary>
     /// To use as base for pawn hash table
     /// </summary>
-    public static readonly HashKey ZobristNoPawn;
+    public HashKey ZobristNoPawn { get; }
 
-
-    private static readonly HashKey Empty = HashKey.Empty;
-    
-    static Zobrist()
+    public Zobrist()
     {
         var rnd = RKiss.Create(DefaultRandomSeed);
         InitializePst(in rnd);
@@ -95,14 +91,12 @@ static Zobrist()
         ZobristNoPawn = rnd.Rand();
     }
 
-    private static void InitializePst(in IRKiss rnd)
+    private void InitializePst(in IRKiss rnd)
     {
-        for (var i = 0; i < ZobristPst.Length; i++)
-            ZobristPst[i] = new HashKey[Piece.Count];
-
         for (var sq = Squares.a1; sq <= Squares.h8; sq++)
         {
             var s = sq.AsInt();
+            ZobristPst[s] = new HashKey[Piece.Count];
             foreach (var pc in Piece.All.AsSpan())
                 ZobristPst[s][pc.AsInt()] = rnd.Rand();
         }
@@ -114,34 +108,34 @@ private static void InitializeRandomArray(Span<HashKey> array, IRKiss rnd)
             array[i] = rnd.Rand();
     }
 
-    public static HashKey ComputeMaterialKey(IPosition pos)
+    public HashKey ComputeMaterialKey(IPosition pos)
     {
         var key = HashKey.Empty;
         foreach (var pc in Piece.All.AsSpan())
             for (var count = 0; count < pos.Board.PieceCount(pc); count++)
-                key ^= GetZobristPst(pc, count);
+                key ^= GetZobristPst(count, pc);
         
         return key;
     }
 
-    public static HashKey ComputePawnKey(IPosition pos)
+    public HashKey ComputePawnKey(IPosition pos)
     {
         var key = ZobristNoPawn;
 
         var pawns = pos.Pieces(Piece.WhitePawn);
 
         while (pawns)
-            key ^= GetZobristPst(Piece.WhitePawn, BitBoards.PopLsb(ref pawns));
+            key ^= GetZobristPst(BitBoards.PopLsb(ref pawns), Piece.WhitePawn);
 
         pawns = pos.Pieces(Piece.BlackPawn);
         
         while (pawns)
-            key ^= GetZobristPst(Piece.BlackPawn, BitBoards.PopLsb(ref pawns));
+            key ^= GetZobristPst(BitBoards.PopLsb(ref pawns), Piece.BlackPawn);
         
         return key; 
     }
 
-    public static HashKey ComputePositionKey(IPosition pos)
+    public HashKey ComputePositionKey(IPosition pos)
     {
         var key = HashKey.Empty;
 
@@ -149,7 +143,7 @@ public static HashKey ComputePositionKey(IPosition pos)
         {
             var bb = pos.Pieces(pc);
             while (bb)
-                key ^= GetZobristPst(pc, BitBoards.PopLsb(ref bb));
+                key ^= GetZobristPst(BitBoards.PopLsb(ref bb), pc);
         }
 
         if (pos.SideToMove.IsWhite)
@@ -162,19 +156,22 @@ public static HashKey ComputePositionKey(IPosition pos)
     }
     
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static ref HashKey GetZobristPst(this Piece piece, Square square) => ref ZobristPst[square.AsInt()][piece.AsInt()];
+    public ref HashKey GetZobristPst(Square square, Piece piece) => ref ZobristPst[square.AsInt()][piece.AsInt()];
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public ref HashKey GetZobristCastleling(CastleRights index) => ref ZobristCastling[index.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static ref HashKey GetZobristCastleling(this CastleRights index) => ref ZobristCastling[index.AsInt()];
+    public ref HashKey GetZobristCastleling(CastleRight index) => ref GetZobristCastleling(index.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey GetZobristSide() => ZobristSide;
+    public HashKey GetZobristSide() => ZobristSide;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static ref HashKey GetZobristEnPassant(this File file) => ref ZobristEpFile[file.AsInt()];
+    public ref HashKey GetZobristEnPassant(File file) => ref ZobristEpFile[file.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey GetZobristEnPassant(this Square sq)
+    public HashKey GetZobristEnPassant(Square sq)
     {
         return sq == Square.None ? HashKey.Empty : GetZobristEnPassant(sq.File);
     }
diff --git a/src/Rudzoft.ChessLib/ICuckoo.cs b/src/Rudzoft.ChessLib/ICuckoo.cs
new file mode 100644
index 00000000..139127c6
--- /dev/null
+++ b/src/Rudzoft.ChessLib/ICuckoo.cs
@@ -0,0 +1,6 @@
+namespace Rudzoft.ChessLib;
+
+public interface ICuckoo
+{
+    bool HashCuckooCycle(in IPosition pos, int end, int ply);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 0a385c11..3d582275 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -38,7 +38,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -56,22 +55,18 @@ public sealed class Position : IPosition
     private readonly CastleRight[] _castlingRightsMask;
     private readonly Square[] _castlingRookSquare;
     private readonly Value[] _nonPawnMaterial;
+    private readonly IZobrist _zobrist;
+    private readonly ICuckoo _cuckoo;
     private readonly ObjectPool<StringBuilder> _outputObjectPool;
     private readonly ObjectPool<IMoveList> _moveListPool;
     private readonly IPositionValidator _positionValidator;
     private Player _sideToMove;
 
-    public Position() : this(
-        new Board(),
-        new Values(),
-        new PositionValidator(),
-        new DefaultObjectPool<IMoveList>(new MoveListPolicy()))
-    {
-    }
-
     public Position(
         IBoard board,
         IValues values,
+        IZobrist zobrist,
+        ICuckoo cuckoo,
         IPositionValidator positionValidator,
         ObjectPool<IMoveList> moveListPool)
     {
@@ -87,6 +82,8 @@ public Position(
         Board = board;
         IsProbing = true;
         Values = values;
+        _zobrist = zobrist;
+        _cuckoo = cuckoo;
         State = new State();
         Clear();
     }
@@ -397,13 +394,13 @@ public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public HashKey GetPawnKey()
     {
-        var result = Zobrist.ZobristNoPawn;
+        var result = _zobrist.ZobristNoPawn;
         var pieces = Board.Pieces(PieceTypes.Pawn);
         while (pieces)
         {
             var sq = BitBoards.PopLsb(ref pieces);
             var pc = GetPiece(sq);
-            result ^= pc.GetZobristPst(sq);
+            result ^= _zobrist.GetZobristPst(sq, pc);
         }
 
         return result;
@@ -421,7 +418,7 @@ public HashKey GetPiecesKey()
         {
             var sq = BitBoards.PopLsb(ref pieces);
             var pc = Board.PieceAt(sq);
-            result ^= pc.GetZobristPst(sq);
+            result ^= _zobrist.GetZobristPst(sq, pc);
         }
 
         return result;
@@ -500,7 +497,7 @@ public bool GivesCheck(Move m)
     public bool HasGameCycle(int ply)
     {
         var end = State.End();
-        return end >= 3 && Cuckoo.HashCuckooCycle(this, end, ply);
+        return end >= 3 && _cuckoo.HashCuckooCycle(this, end, ply);
     }
 
     public bool HasRepetition()
@@ -691,7 +688,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         State = State.CopyTo(newState);
         var state = State;
 
-        var k = state.PositionKey ^ Zobrist.GetZobristSide();
+        var k = state.PositionKey ^ _zobrist.GetZobristSide();
 
         Ply++;
         state.Rule50++;
@@ -719,7 +716,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
 
             var (rookFrom, rookTo) = DoCastle(us, from, ref to, CastlePerform.Do);
 
-            k ^= capturedPiece.GetZobristPst(rookFrom) ^ capturedPiece.GetZobristPst(rookTo);
+            k ^= _zobrist.GetZobristPst(rookFrom, capturedPiece) ^ _zobrist.GetZobristPst(rookTo, capturedPiece);
 
             // reset captured piece type as castleling is "king-captures-rook"
             capturedPiece = Piece.EmptyPiece;
@@ -742,7 +739,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                     Debug.Assert(GetPiece(captureSquare) == pt.MakePiece(them));
                 }
 
-                state.PawnKey ^= capturedPiece.GetZobristPst(captureSquare);
+                state.PawnKey ^= _zobrist.GetZobristPst(captureSquare, capturedPiece);
             }
             else
             {
@@ -755,7 +752,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             if (type == MoveTypes.Enpassant)
                 Board.ClearPiece(captureSquare);
 
-            k ^= capturedPiece.GetZobristPst(captureSquare);
+            k ^= _zobrist.GetZobristPst(captureSquare, capturedPiece);
 
             // TODO : Update other depending keys and psq values here
 
@@ -764,22 +761,21 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         }
 
         // update key with moved piece
-        k ^= pc.GetZobristPst(from) ^ pc.GetZobristPst(to);
+        k ^= _zobrist.GetZobristPst(from, pc) ^ _zobrist.GetZobristPst(to, pc);
 
+        k ^= _zobrist.GetZobristEnPassant(state.EnPassantSquare);
+        
         // reset en-passant square if it is set
         if (state.EnPassantSquare != Square.None)
-        {
-            k ^= state.EnPassantSquare.File.GetZobristEnPassant();
             state.EnPassantSquare = Square.None;
-        }
 
         // Update castling rights if needed
         if (state.CastlelingRights != CastleRight.None &&
             (_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]) != CastleRight.None)
         {
-            k ^= state.CastlelingRights.Key();
+            k ^= _zobrist.GetZobristCastleling(state.CastlelingRights);
             state.CastlelingRights &= ~(_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]);
-            k ^= state.CastlelingRights.Key();
+            k ^= _zobrist.GetZobristCastleling(state.CastlelingRights);
         }
 
         // Move the piece. The tricky Chess960 castle is handled earlier
@@ -794,7 +790,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 && !((to - us.PawnPushDistance()).PawnAttack(us) & Pieces(PieceTypes.Pawn, them)).IsEmpty)
             {
                 state.EnPassantSquare = to - us.PawnPushDistance();
-                k ^= state.EnPassantSquare.File.GetZobristEnPassant();
+                k ^= _zobrist.GetZobristEnPassant(state.EnPassantSquare.File);
             }
             else if (type == MoveTypes.Promotion)
             {
@@ -807,14 +803,16 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 AddPiece(promotionPiece, to);
 
                 // Update hash keys
-                k ^= pc.GetZobristPst(to) ^ promotionPiece.GetZobristPst(to);
-                state.PawnKey ^= pc.GetZobristPst(to);
+                ref var toKey = ref _zobrist.GetZobristPst(to, pc);
+                
+                k ^= toKey ^ _zobrist.GetZobristPst(to, promotionPiece);
+                state.PawnKey ^= toKey;
 
                 _nonPawnMaterial[us.Side] += Values.GetPieceValue(promotionPiece, Phases.Mg);
             }
 
             // Update pawn hash key
-            state.PawnKey ^= pc.GetZobristPst(from) ^ pc.GetZobristPst(to);
+            state.PawnKey ^= _zobrist.GetZobristPst(from, pc) ^ _zobrist.GetZobristPst(to, pc);
 
             // Reset rule 50 draw counter
             state.Rule50 = 0;
@@ -845,14 +843,12 @@ public void MakeNullMove(in State newState)
 
         CopyState(in newState);
 
+        State.PositionKey ^= _zobrist.GetZobristEnPassant(State.EnPassantSquare);
+        
         if (State.EnPassantSquare != Square.None)
-        {
-            var enPassantFile = State.EnPassantSquare.File;
-            State.PositionKey ^= enPassantFile.GetZobristEnPassant();
             State.EnPassantSquare = Square.None;
-        }
 
-        State.PositionKey ^= Zobrist.GetZobristSide();
+        State.PositionKey ^= _zobrist.GetZobristSide();
 
         ++State.Rule50;
         State.PliesFromNull = 0;
@@ -1371,14 +1367,14 @@ public HashKey MovePositionKey(Move m)
         Debug.Assert(m.IsValidMove());
 
         var movePositionKey = State.PositionKey
-                              ^ Zobrist.GetZobristSide()
-                              ^ Board.MovedPiece(m).GetZobristPst(m.FromSquare())
-                              ^ Board.MovedPiece(m).GetZobristPst(m.ToSquare());
+                              ^ _zobrist.GetZobristSide()
+                              ^ _zobrist.GetZobristPst(m.FromSquare(), Board.MovedPiece(m))
+                              ^ _zobrist.GetZobristPst(m.ToSquare(), Board.MovedPiece(m));
 
         if (!Board.IsEmpty(m.ToSquare()))
-            movePositionKey ^= Board.PieceAt(m.ToSquare()).GetZobristPst(m.ToSquare());
+            movePositionKey ^= _zobrist.GetZobristPst(m.ToSquare(), Board.PieceAt(m.ToSquare()));
 
-        movePositionKey ^= State.EnPassantSquare.GetZobristEnPassant();
+        movePositionKey ^= _zobrist.GetZobristEnPassant(State.EnPassantSquare);
 
         return movePositionKey;
     }
@@ -1495,9 +1491,9 @@ private void SetState()
                 _nonPawnMaterial[pc.ColorOf().Side] += Values.GetPieceValue(pc, Phases.Mg);
         }
 
-        State.MaterialKey = Zobrist.ComputeMaterialKey(this);
-        State.PawnKey = Zobrist.ComputePawnKey(this);
-        State.PositionKey = Zobrist.ComputePositionKey(this);
+        State.MaterialKey = _zobrist.ComputeMaterialKey(this);
+        State.PawnKey = GetPawnKey();
+        State.PositionKey = GetPiecesKey();
     }
 
     private void SetupCastle(ReadOnlySpan<char> castle)
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index c837faa3..f8280369 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -25,7 +25,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using System.Collections;
 using System.Collections.Generic;
+using System.Linq;
 using System.Runtime.InteropServices;
 using System.Text;
 using Microsoft.Extensions.ObjectPool;
@@ -39,7 +41,7 @@ namespace Rudzoft.ChessLib.Protocol.UCI;
 
 public class Uci : IUci
 {
-    private static readonly OptionComparer OptionComparer = new();
+    private static readonly IComparer<IOption> OptionComparer = new OptionComparer();
 
     private static readonly string[] OptionTypeStrings = Enum.GetNames(typeof(UciOptionType));
 
@@ -63,6 +65,7 @@ public Uci()
 
     public void Initialize(int maxThreads = 128)
     {
+        _options.Clear();
         _options["Write Debug Log"] = new Option("Write Debug Log", _options.Count, false, OnLogger);
         _options["Write Search Log"] = new Option("Write Search Log", _options.Count, false);
         _options["Search Log Filename"] = new Option("Search Log Filename", _options.Count);
@@ -113,9 +116,8 @@ public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
 
     public IEnumerable<Move> MovesFromUci(IPosition pos, Stack<State> states, IEnumerable<string> moves)
     {
-        foreach (var uciMove in moves)
+        foreach (var move in moves.Select(x => MoveFromUci(pos, x)))
         {
-            var move = MoveFromUci(pos, uciMove);
             if (move.IsNullMove())
                 continue;
             
@@ -224,18 +226,13 @@ public string MoveToString(Move m, ChessMode chessMode = ChessMode.Normal)
     /// <returns>the current UCI options as string</returns>
     public new string ToString()
     {
-        var list = new List<IOption>(_options.Values);
-        list.Sort(OptionComparer);
         var sb = _pvPool.Get();
 
-        var listSpan = CollectionsMarshal.AsSpan(list);
-        ref var listSpace = ref MemoryMarshal.GetReference(listSpan);
-
-        for (var i = 0; i < list.Count; ++i)
+        foreach (var opt in _options.Values.Order(OptionComparer))
         {
-            var opt = Unsafe.Add(ref listSpace, i);
             sb.AppendLine();
             sb.Append("option name ").Append(opt.Name).Append(" type ").Append(OptionTypeStrings[(int)opt.Type]);
+            
             if (opt.Type != UciOptionType.Button)
                 sb.Append(" default ").Append(opt.DefaultValue);
 
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index 96781f4a..96558970 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -196,8 +196,8 @@ public override int GetHashCode()
         hashCode.Add(Checkers);
         hashCode.Add(Previous);
         hashCode.Add(CapturedPiece);
-        hashCode.Add(Pinners.Where(static p => p.IsNotEmpty));
-        hashCode.Add(CheckedSquares.Where(static csq => csq.IsNotEmpty));
+        hashCode.Add(Pinners);
+        hashCode.Add(CheckedSquares);
         return hashCode.ToHashCode();
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index 1c1a7946..8d831966 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -160,9 +160,6 @@ private CastleRight(int cr) : this((CastleRights)cr) { }
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static CastleRight operator ~(CastleRight cr) => new(~cr.Rights);
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey Key() => ref Rights.GetZobristCastleling();
-
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Has(CastleRights cr) => Rights.HasFlagFast(cr);
 
diff --git a/src/Rudzoft.ChessLib/Types/HashKey.cs b/src/Rudzoft.ChessLib/Types/HashKey.cs
index 5b534a05..fe85ae7e 100644
--- a/src/Rudzoft.ChessLib/Types/HashKey.cs
+++ b/src/Rudzoft.ChessLib/Types/HashKey.cs
@@ -27,7 +27,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
-using Rudzoft.ChessLib.Hash;
 
 namespace Rudzoft.ChessLib.Types;
 
@@ -84,13 +83,6 @@ private HashKey(uint key32)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static HashKey operator ^(HashKey left, ulong right) => new(left.Key ^ right);
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey operator ^(HashKey left, CastleRights right)
-        => new(left.Key ^ right.GetZobristCastleling().Key);
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static HashKey operator ^(HashKey left, File right) => new(left.Key ^ right.GetZobristEnPassant().Key);
-
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Equals(HashKey other) => Key == other.Key;
 
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index 14f531df..e0bd6faa 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -55,6 +55,13 @@ public static bool HasFlagFast(this PositionValidationTypes @this, PositionValid
 
 public sealed class PositionValidator : IPositionValidator
 {
+    private readonly IZobrist _zobrist;
+
+    public PositionValidator(IZobrist zobrist)
+    {
+        _zobrist = zobrist;
+    }
+
     public PositionValidationResult Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All)
     {
         var errors = new List<string>();
@@ -181,14 +188,14 @@ private static IEnumerable<string> ValidatePieceTypes(IPosition pos)
                 .Where(p2 => p1 != p2 && (pos.Pieces(p1) & pos.Pieces(p2)).IsNotEmpty),
             static (p1, p2) => $"piece types {p1} and {p2} doesn't align");
 
-    private static IEnumerable<string> ValidateState(IPosition pos)
+    private IEnumerable<string> ValidateState(IPosition pos)
     {
         var state = pos.State;
 
         if (state.PositionKey.Key == 0 && pos.PieceCount() != 0)
             yield return "state key is invalid";
 
-        if (pos.PieceCount(PieceTypes.Pawn) == 0 && state.PawnKey != Zobrist.ZobristNoPawn)
+        if (pos.PieceCount(PieceTypes.Pawn) == 0 && state.PawnKey != _zobrist.ZobristNoPawn)
             yield return "empty pawn key is invalid";
 
         if (state.Repetition < 0)

From 65ead6c264f9d216fa33e37cf250b3d0ed90c683 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Tue, 15 Aug 2023 23:06:08 +0200
Subject: [PATCH 046/119] Update to progress towards stabilizing transposition
 table hashing

---
 .../FenBenchmark.cs                           |   3 +-
 .../KeyBenchmarks.cs                          | 135 +++++
 .../MagicBBInitBenchmark.cs                   |  19 +
 .../MoveGeneratorBenchmark.cs                 |   3 +-
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |  16 +-
 .../Rudzoft.ChessLib.Benchmark.csproj         |   1 +
 .../BitboardTests/BitboardTests.cs            |   4 +-
 .../BitboardTests/MbbTests.cs                 |  18 +
 .../BoardTests/BoardTests.cs                  |  17 +-
 .../BookTests/PolyglotTests.cs                |   1 +
 .../CastleTests/BasicCastleTests.cs           |   1 +
 .../EvaluationTests/KpkBitBaseTests.cs        |   1 +
 .../FENTests/FenTests.cs                      |   1 +
 .../FenceTests/FenceTests.cs                  |   1 +
 .../GameplayTests/FoolsCheckMateTests.cs      |   1 +
 .../MateTests/MateTests.cs                    |  44 ++
 .../MoveTests/MoveGen_49.cs                   |   1 +
 .../MoveTests/MoveGeneratorTests.cs           |   1 +
 .../MoveTests/MoveTests.cs                    |   1 +
 .../NotationTests/FanTests.cs                 |   1 +
 .../NotationTests/IccfTests.cs                |   1 +
 .../NotationTests/RanTests.cs                 |   1 +
 .../NotationTests/SanTests.cs                 |   1 +
 .../PerftTests/PerftVerify.cs                 |   1 +
 .../PiecesTests/PawnDoubleAttackTests.cs      |   1 +
 .../PiecesTests/PieceAttacksRookTests.cs      |   3 +-
 .../PositionTests/EnPassantFenTests.cs        |   1 +
 .../PositionTests/PositionTests.cs            |   1 +
 .../PositionTests/ValidationTests.cs          |   1 +
 .../ProtocolTests/UciTests.cs                 |   1 +
 .../ZobristTests/ZobristHashTests.cs          |  50 +-
 src/Rudzoft.ChessLib/Cuckoo.cs                |  19 +-
 .../ChessLibServiceCollectionExtensions.cs    |   1 +
 src/Rudzoft.ChessLib/Game.cs                  |  18 +-
 src/Rudzoft.ChessLib/Hash/IZobrist.cs         |  12 +-
 src/Rudzoft.ChessLib/Hash/RKiss.cs            |  21 +-
 .../Hash/Tables/Transposition/Bound.cs        |   2 +-
 .../Hash/Tables/Transposition/Cluster.cs      |  67 ---
 .../Transposition/ITranspositionTable.cs      | 120 +---
 .../Transposition/TranspositionTable.cs       | 537 ++++++++++++------
 .../Transposition/TranspositionTableEntry.cs  |  97 ++--
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          | 103 ++--
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |   6 +-
 .../Polyglot/PolyglotBookZobrist.cs           |   8 +
 src/Rudzoft.ChessLib/Position.cs              | 145 +++--
 src/Rudzoft.ChessLib/Tables/HashTable.cs      |   9 +-
 src/Rudzoft.ChessLib/Tables/IHashTable.cs     |   2 +-
 .../Tables/Perft/PerftTable.cs                |   8 +-
 src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs     | 322 +++++++++++
 src/Rudzoft.ChessLib/Types/BitBoards.cs       |  13 +-
 src/Rudzoft.ChessLib/Types/HashKey.cs         |   2 +
 src/Rudzoft.Perft/Options/TTOptions.cs        |   2 +-
 src/Rudzoft.Perft/Perft/PerftRunner.cs        |  55 +-
 53 files changed, 1275 insertions(+), 626 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
 create mode 100644 src/Rudzoft.ChessLib.Benchmark/MagicBBInitBenchmark.cs
 create mode 100644 src/Rudzoft.ChessLib.Test/BitboardTests/MbbTests.cs
 create mode 100644 src/Rudzoft.ChessLib.Test/MateTests/MateTests.cs
 delete mode 100644 src/Rudzoft.ChessLib/Hash/Tables/Transposition/Cluster.cs
 create mode 100644 src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
index cd8d5f51..35f9e2b4 100644
--- a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
@@ -26,7 +26,8 @@ public void Setup()
         var pieceValue = new Values();
         var fp = new FenData(F);
         var state = new State();
-        var zobrist = new Zobrist();
+        var rKiss = new RKiss();
+        var zobrist = new Zobrist(rKiss);
         var cuckoo = new Cuckoo(zobrist);
         var validator = new PositionValidator(zobrist);
         var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
diff --git a/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs b/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
new file mode 100644
index 00000000..b4386df8
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace Rudzoft.ChessLib.Benchmark;
+
+[MemoryDiagnoser]
+public class KeyBenchmarks
+{
+    private sealed record Affe(string Cvr, string Se, DateOnly Ksl);
+
+    private sealed record Affe2(Guid Key, DateOnly Ksl);
+
+    private const string CvrN = "12345678";
+    private const string SeN = "87654321";
+
+    private DateOnly _ksl;
+    
+    [GlobalSetup]
+    public void Setup()
+    {
+        _ksl = DateOnly.FromDateTime(DateTime.Now);
+        var doSize = Unsafe.SizeOf<DateOnly>();
+        var dtSize = Unsafe.SizeOf<DateTime>();
+
+        var a = 0;
+    }
+    
+    [Benchmark]
+    public int StringToGuid()
+    {
+        return A(CvrN, SeN, in _ksl).GetHashCode();
+    }
+
+    [Benchmark]
+    public int StringToGuid2()
+    {
+        return A2(CvrN, SeN, in _ksl).GetHashCode();
+    }
+    
+    [Benchmark(Baseline = true)]
+    public int BaseRecord()
+    {
+        return B(CvrN, SeN, in _ksl).GetHashCode();
+    }
+    
+    [SkipLocalsInit]
+    private static Affe2 A(string s1, string s2, in DateOnly dateOnly)
+    {
+        var s1Span = s1.AsSpan();
+        var s2Span = s2.AsSpan();
+        
+        var s1Bytes = MemoryMarshal.AsBytes(s1Span);
+        var s2Bytes = MemoryMarshal.AsBytes(s2Span);
+
+        Span<byte> finalBytes = stackalloc byte[16];
+        
+        s1Bytes.CopyTo(finalBytes);
+        finalBytes[8] = s2Bytes[0];
+        finalBytes[9] = s2Bytes[1];
+        finalBytes[10] = s2Bytes[2];
+        finalBytes[11] = s2Bytes[3];
+        finalBytes[12] = s2Bytes[4];
+        finalBytes[13] = s2Bytes[5];
+        finalBytes[14] = s2Bytes[6];
+        finalBytes[15] = s2Bytes[7];
+
+        return new(new Guid(finalBytes), dateOnly);
+        
+        //var h = MemoryMarshal.TryRead(finalBytes, out ulong v);
+    }
+
+    [SkipLocalsInit]
+    private static Affe2 A2(string s1, string s2, in DateOnly dateOnly)
+    {
+        var s1Span = s1.AsSpan();
+        var s2Span = s2.AsSpan();
+        
+        var s1Bytes = MemoryMarshal.AsBytes(s1Span);
+        var s2Bytes = MemoryMarshal.AsBytes(s2Span);
+
+        Span<byte> finalBytes = stackalloc byte[s1Bytes.Length + s2Bytes.Length];
+        
+        if (Sse2.IsSupported)
+        {
+            var s1Vector = MemoryMarshal.Cast<byte, Vector128<byte>>(s1Bytes)[0];
+            var s2Vector = MemoryMarshal.Cast<byte, Vector128<byte>>(s2Bytes)[0];
+
+            MemoryMarshal.Cast<byte, Vector128<byte>>(finalBytes)[0] = s1Vector;
+            MemoryMarshal.Cast<byte, Vector128<byte>>(finalBytes[16..])[0] = s2Vector;
+        }
+        else
+        {
+            // Fall back to non-SIMD code if not supported.
+            s1Bytes.CopyTo(finalBytes);
+            s2Bytes.CopyTo(finalBytes[16..]);
+        }
+
+        return new(new Guid(finalBytes), dateOnly);
+        
+        //var h = MemoryMarshal.TryRead(finalBytes, out ulong v);
+    }
+
+    // private static Affe2 AVector(string cvr, string se, in DateOnly now)
+    // {
+    //     var cvrSpan = cvr.AsSpan();
+    //     var seSpan = se.AsSpan();
+    //     
+    //     var cvrBytes = MemoryMarshal.AsBytes(cvrSpan);
+    //     var seBytes = MemoryMarshal.AsBytes(seSpan);
+    //
+    //     Span<byte> finalBytes = stackalloc byte[16];
+    //     
+    //     cvrBytes.CopyTo(finalBytes);
+    //     finalBytes[8] = seBytes[0];
+    //     finalBytes[9] = seBytes[1];
+    //     finalBytes[10] = seBytes[2];
+    //     finalBytes[11] = seBytes[3];
+    //     finalBytes[12] = seBytes[4];
+    //     finalBytes[13] = seBytes[5];
+    //     finalBytes[14] = seBytes[6];
+    //     finalBytes[15] = seBytes[7];
+    //
+    //     
+    //             
+    //
+    // }
+
+    private static Affe B(string cvr, string se, in DateOnly now)
+    {
+        return new(cvr, se, now);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Benchmark/MagicBBInitBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/MagicBBInitBenchmark.cs
new file mode 100644
index 00000000..ed3c2ecb
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Benchmark/MagicBBInitBenchmark.cs
@@ -0,0 +1,19 @@
+// using Rudzoft.ChessLib.Types;
+//
+// namespace Rudzoft.ChessLib.Benchmark;
+//
+// [MemoryDiagnoser]
+// public class MagicBBInitBenchmark
+// {
+//     [Benchmark]
+//     public BitBoard V1()
+//     {
+//         return MagicBB.BishopAttacks(Square.A1, BitBoard.Empty);
+//     }
+//     
+//         [Benchmark]
+//     public BitBoard V2()
+//     {
+//         return MagicBB2.BishopAttacks(Square.A1, BitBoard.Empty);
+//     }
+// }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
index 7a1137ef..04ef1225 100644
--- a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
@@ -34,7 +34,8 @@ public void Setup()
         var board = new Board();
         var pieceValue = new Values();
         var state = new State();
-        var zobrist = new Zobrist();
+        var rKiss = new RKiss();
+        var zobrist = new Zobrist(rKiss);
         var cuckoo = new Cuckoo(zobrist);
         var validator = new PositionValidator(zobrist);
         _objectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index ff221400..b03b1b87 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -53,7 +53,6 @@ public class PerftBench
     [GlobalSetup]
     public void Setup()
     {
-        
         var pp = PerftPositionFactory.Create(
             Guid.NewGuid().ToString(),
             Fen.Fen.StartPositionFen,
@@ -66,7 +65,7 @@ public void Setup()
                 new(5, 4865609),
                 new(6, 119060324)
             });
-        
+
         var ttConfig = new TranspositionTableConfiguration { DefaultSize = 1 };
         var options = Options.Create(ttConfig);
         var tt = new TranspositionTable(options);
@@ -76,19 +75,20 @@ public void Setup()
         var cpu = new Cpu();
 
         var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
-        
+
         var sp = new SearchParameters();
-        
+
         var board = new Board();
         var values = new Values();
-        var zobrist = new Zobrist();
+        var rKiss = new RKiss();
+        var zobrist = new Zobrist(rKiss);
         var cuckoo = new Cuckoo(zobrist);
         var validator = new PositionValidator(zobrist);
 
         var pos = new Position(board, values, zobrist, cuckoo, validator, moveListObjectPool);
-        
+
         var game = new Game(tt, uci, cpu, sp, pos, moveListObjectPool);
-        _perft = new Perft.Perft(game, new []{ pp });
+        _perft = new Perft.Perft(game, new[] { pp });
     }
 
     [Benchmark]
@@ -110,4 +110,4 @@ public async Task<ulong> Perft()
 
         return total;
     }
-}
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index de03e915..70dc2bf3 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -7,6 +7,7 @@
     <Platforms>AnyCPU</Platforms>
     <LangVersion>default</LangVersion>
     <Configurations>Debug;Release</Configurations>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
index af42a80a..2271528e 100644
--- a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
@@ -129,8 +129,8 @@ public void RankSquaresCount(Ranks ranks, int expected)
     public void EmptyCount()
     {
         const int expected = 0;
-        var actual = BitBoards.EmptyBitBoard.Count;
-        Assert.Equal(expected, actual);
+        var actual = BitBoards.EmptyBitBoard;
+        Assert.Equal(expected, actual.Count);
         Assert.True(BitBoards.EmptyBitBoard.IsEmpty);
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/MbbTests.cs b/src/Rudzoft.ChessLib.Test/BitboardTests/MbbTests.cs
new file mode 100644
index 00000000..ef28d9b8
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Test/BitboardTests/MbbTests.cs
@@ -0,0 +1,18 @@
+// using Rudzoft.ChessLib.Types;
+// using Rudzoft.ChessLib.Types.Attacks;
+//
+// namespace Rudzoft.ChessLib.Test.BitboardTests;
+//
+// public class MbbTests
+// {
+//     [Fact]
+//     public void Test()
+//     {
+//         var sq = Square.A1;
+//         var empty = BitBoard.Empty;
+//
+//         var m = Mbb.GetQueenAttacks(sq, in empty);
+//         
+//         Assert.False(m.IsEmpty);
+//     }
+// }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 1a7e4c95..4d569cab 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -25,7 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
-using System.Diagnostics.Contracts;
+using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Microsoft.Extensions.DependencyInjection;
@@ -48,6 +48,7 @@ public BoardTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
@@ -66,13 +67,13 @@ public sealed class BoardTestsTheoryData : TheoryData<string, PieceTypes, Player
     {
         public BoardTestsTheoryData(string[] fens, PieceTypes[] pts, Player[] players, int[] expectedCounts)
         {
-            Contract.Assert(fens != null);
-            Contract.Assert(pts != null);
-            Contract.Assert(players != null);
-            Contract.Assert(expectedCounts != null);
-            Contract.Assert(fens.Length == pts.Length);
-            Contract.Assert(fens.Length == players.Length);
-            Contract.Assert(fens.Length == expectedCounts.Length);
+            Debug.Assert(fens != null);
+            Debug.Assert(pts != null);
+            Debug.Assert(players != null);
+            Debug.Assert(expectedCounts != null);
+            Debug.Assert(fens.Length == pts.Length);
+            Debug.Assert(fens.Length == players.Length);
+            Debug.Assert(fens.Length == expectedCounts.Length);
 
             var fensSpan = fens.AsSpan();
             var ptsSpan = pts.AsSpan();
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index 2a24b9a5..9fbde89a 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -58,6 +58,7 @@ public PolyglotTests(BookFixture fixture)
             .AddSingleton(polyOptions)
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
index 20142870..619f1c51 100644
--- a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
+++ b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
@@ -45,6 +45,7 @@ public BasicCastleTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
index 64fb1bbb..d806c9d9 100644
--- a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
+++ b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
@@ -46,6 +46,7 @@ public KpkBitBaseTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index 7a72460a..fc22700b 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -46,6 +46,7 @@ public FenTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
index 3370879a..dfddd260 100644
--- a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
@@ -45,6 +45,7 @@ public FenceTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
index a9bf9618..824707bc 100644
--- a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
+++ b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
@@ -46,6 +46,7 @@ public FoolsCheckMateTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/MateTests/MateTests.cs b/src/Rudzoft.ChessLib.Test/MateTests/MateTests.cs
new file mode 100644
index 00000000..30011dfe
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Test/MateTests/MateTests.cs
@@ -0,0 +1,44 @@
+// using System.Linq;
+// using Rudzoft.ChessLib.Factories;
+// using Rudzoft.ChessLib.MoveGeneration;
+// using Rudzoft.ChessLib.Protocol.UCI;
+// using Rudzoft.ChessLib.Types;
+//
+// namespace Rudzoft.ChessLib.Test.MateTests;
+//
+// public sealed class MateTests
+// {
+//
+//     [Fact]
+//     public void IsMateTest()
+//     {
+//         const string fen = "8/6pk/pb5p/8/1P2qP2/P7/2r2pNP/1QR4K b - - 1 2";
+//         var game = GameFactory.Create(fen);
+//         
+//         /*
+//          * e3f2 g1h1 f2f1q
+//          */
+//     
+//         var uciMoves = new string[]
+//         {
+//             "e3f2", "g1h1", "f2f1q"
+//         };
+//     
+//         var pos = game.Pos;
+//         var uci = new Uci();
+//         var s = new State();
+//     
+//         foreach (var uciMove in uciMoves)
+//         {
+//             var move = uci.MoveFromUci(pos, uciMove);
+//             pos.MakeMove(move, s);
+//         }
+//     
+//         var moves = pos.GenerateMoves();
+//     
+//         var empty = moves.Get().IsEmpty;
+//         
+//         Assert.True(empty);
+//     }
+//     
+// }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
index 35f59bfa..b6abbd1f 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
@@ -45,6 +45,7 @@ public MoveGen_49()
     {
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IValues, Values>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IZobrist, Zobrist>()
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
index f67c176a..9230221f 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
@@ -47,6 +47,7 @@ public MoveGeneratorTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddSingleton<ICuckoo, Cuckoo>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index 6bced788..10ec5cd6 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -48,6 +48,7 @@ public MoveTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index d09777a8..1d99938c 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -48,6 +48,7 @@ public FanTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index d1418872..085e55c4 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -47,6 +47,7 @@ public IccfTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index 0bff04c6..ebce82a7 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -48,6 +48,7 @@ public RanTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index 8de99062..4158e5c1 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -50,6 +50,7 @@ public SanTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
index 368b7318..71f7ca5e 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
@@ -24,6 +24,7 @@ protected PerftVerify()
             .AddSingleton(ttOptions)
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
index 5c1f4682..5133f5c4 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
@@ -45,6 +45,7 @@ public PawnDoubleAttackTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
index 8fab256b..3ba1db11 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
@@ -44,6 +44,7 @@ public PieceAttacksRookTests()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddSingleton<ICuckoo, Cuckoo>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
@@ -58,7 +59,7 @@ public PieceAttacksRookTests()
     }
     
     /// <summary>
-    /// Testing results of blocked rook attacks, they should always return 7 on the sides, and 14 in
+    /// Testing results of blocked rook attacks, they should always return 8 on the sides, and 14 in
     /// the corner
     /// </summary>
     [Fact]
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
index 6f1d047c..f59c6bb1 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
@@ -45,6 +45,7 @@ public EnPassantFenTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 5278cd99..152fcde8 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -45,6 +45,7 @@ public PositionTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index 91a62087..603223cf 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -45,6 +45,7 @@ public ValidationTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
index 23727bd7..acd73b8f 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
@@ -51,6 +51,7 @@ public UciTests()
         _serviceProvider = new ServiceCollection()
             .AddSingleton(options)
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
diff --git a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
index 303952a5..3f6c01aa 100644
--- a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
@@ -45,6 +45,7 @@ public ZobristHashTests()
         _serviceProvider = new ServiceCollection()
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
             .AddSingleton<IPositionValidator, PositionValidator>()
@@ -63,7 +64,7 @@ public ZobristHashTests()
     [InlineData(Fen.Fen.StartPositionFen, Squares.a2, Squares.a4)]
     [InlineData(Fen.Fen.StartPositionFen, Squares.a2, Squares.a3)]
     [InlineData(Fen.Fen.StartPositionFen, Squares.b1, Squares.c3)]
-    public void BackAndForth(string fen, Squares from, Squares to)
+    public void BackAndForthBasicMoves(string fen, Squares from, Squares to)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
@@ -93,33 +94,66 @@ public void BackAndForth(string fen, Squares from, Squares to)
         Assert.Equal(startKey, finalKey);
     }
 
+    [Theory]
+    [InlineData("rnbqkb1r/pppp1ppp/7n/3Pp3/8/8/PPP1PPPP/RNBQKBNR w KQkq e6 0 1", Squares.d5, Squares.e6, MoveTypes.Enpassant)]
+    [InlineData("r3kb1r/p1pp1p1p/bpn2qpn/3Pp3/1P6/2P1BNP1/P3PPBP/RN1QK2R w KQkq - 0 1", Squares.e1, Squares.h1, MoveTypes.Castling)]
+    [InlineData("r3kb1r/p1pp1p1p/bpn2qpn/3Pp3/1P6/2P1BNP1/P3PPBP/RN1QK2R b KQkq - 0 1", Squares.e8, Squares.a8, MoveTypes.Castling)]
+    public void AdvancedMoves(string fen, Squares from, Squares to, MoveTypes moveType)
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var stateIndex = 0;
+        var states = new List<State> { new() };
+
+        pos.Set(fen, ChessMode.Normal, states[stateIndex]);
+        
+        var startKey = pos.State.PositionKey;
+
+        var move = new Move(from, to, moveType);
+
+        stateIndex++;
+        states.Add(new State());
+
+        pos.MakeMove(move, states[stateIndex]);
+
+        var moveKey = pos.State.PositionKey;
+
+        pos.TakeMove(move);
+
+        var finalKey = pos.State.PositionKey;
+
+        Assert.NotEqual(startKey, moveKey);
+        Assert.Equal(startKey, states[0].PositionKey);
+        Assert.NotEqual(startKey, states[1].PositionKey);
+        Assert.Equal(startKey, finalKey);
+    }
+
     [Fact]
     public void OnlyUniqueZobristHashKeys()
     {
-        var zobrist = new Zobrist();
+        var zobrist = _serviceProvider.GetRequiredService<IZobrist>();
 
         var set = new HashSet<HashKey> { HashKey.Empty };
 
-        var added = false;
+        bool added;
 
         foreach (var sq in Square.All.AsSpan())
         {
             foreach (var pc in Piece.All.AsSpan())
             {
-                added = set.Add(zobrist.GetZobristPst(sq, pc));
+                added = set.Add(zobrist.Psq(sq, pc));
                 Assert.True(added);
             }
         }
 
         foreach (var f in File.AllFiles.AsSpan())
         {
-            added = set.Add(zobrist.GetZobristEnPassant(f));
+            added = set.Add(zobrist.EnPassant(f));
             Assert.True(added);
         }
 
         var castleRights = new[]
         {
-            CastleRight.None,
             CastleRight.WhiteKing,
             CastleRight.BlackKing,
             CastleRight.WhiteQueen,
@@ -133,11 +167,11 @@ public void OnlyUniqueZobristHashKeys()
 
         foreach (var cr in castleRights)
         {
-            added = set.Add(zobrist.GetZobristCastleling(cr));
+            added = set.Add(zobrist.Castleling(cr));
             Assert.True(added);
         }
 
-        added = set.Add(zobrist.GetZobristSide());
+        added = set.Add(zobrist.Side());
         Assert.True(added);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 240cc9e8..297eb3e5 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -40,12 +40,13 @@ namespace Rudzoft.ChessLib;
 /// </summary>
 public sealed class Cuckoo : ICuckoo
 {
-    private readonly HashKey[] CuckooKeys = new HashKey[8192];
-    private readonly Move[] CuckooMoves = new Move[8192];
+    private readonly HashKey[] _cuckooKeys;
+    private readonly Move[] _cuckooMoves;
 
-    [System.Diagnostics.CodeAnalysis.SuppressMessage("Minor Code Smell", "S3963:\"static\" fields should be initialized inline", Justification = "Multiple arrays in one go")]
     public Cuckoo(IZobrist zobrist)
     {
+        _cuckooKeys = new HashKey[8192];
+        _cuckooMoves = new Move[8192];
         var count = 0;
         
         foreach (var pc in Piece.All.AsSpan())
@@ -60,12 +61,12 @@ public Cuckoo(IZobrist zobrist)
                         continue;
 
                     var move = Move.Create(sq1, sq2);
-                    var key = zobrist.GetZobristPst(sq1, pc) ^ zobrist.GetZobristPst(sq2, pc) ^ zobrist.GetZobristSide();
+                    var key = zobrist.Psq(sq1, pc) ^ zobrist.Psq(sq2, pc) ^ zobrist.Side();
                     var j = CuckooHashOne(in key);
                     do
                     {
-                        (CuckooKeys[j], key) = (key, CuckooKeys[j]);
-                        (CuckooMoves[j], move) = (move, CuckooMoves[j]);
+                        (_cuckooKeys[j], key) = (key, _cuckooKeys[j]);
+                        (_cuckooMoves[j], move) = (move, _cuckooMoves[j]);
 
                         // check for empty slot
                         if (move.IsNullMove())
@@ -100,18 +101,18 @@ public bool HashCuckooCycle(in IPosition pos, int end, int ply)
             var moveKey = originalKey ^ statePrevious.PositionKey;
 
             var j = CuckooHashOne(in moveKey);
-            var found = CuckooKeys[j] == moveKey;
+            var found = _cuckooKeys[j] == moveKey;
 
             if (!found)
             {
                 j = CuckooHashTwo(in moveKey);
-                found = CuckooKeys[j] == moveKey;
+                found = _cuckooKeys[j] == moveKey;
             }
 
             if (!found)
                 continue;
 
-            var (s1, s2) = CuckooMoves[j];
+            var (s1, s2) = _cuckooMoves[j];
 
             if ((s1.BitboardBetween(s2) & pos.Board.Pieces()).IsEmpty)
                 continue;
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 6cd11843..fbcc06ce 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -88,6 +88,7 @@ public static IServiceCollection AddChessLib(
                 return uci;
             })
             .AddSingleton<ICuckoo, Cuckoo>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddTransient<IKillerMovesFactory, KillerMovesFactory>()
             .AddSingleton<ISearchParameters, SearchParameters>()
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index ed529402..7f06381f 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -36,7 +36,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Protocol.UCI;
-using Rudzoft.ChessLib.Tables.Perft;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib;
@@ -45,7 +44,6 @@ public sealed class Game : IGame
 {
     private readonly ObjectPool<IMoveList> _moveListPool;
     private readonly IPosition _pos;
-    private readonly PerftTable _perftTable;
 
     public Game(
         ITranspositionTable transpositionTable,
@@ -57,7 +55,6 @@ public Game(
     {
         _moveListPool = moveListPool;
         _pos = pos;
-        _perftTable = new PerftTable();
 
         Table = transpositionTable;
         SearchParameters = searchParameters;
@@ -134,22 +131,11 @@ public override string ToString()
 
     public ulong Perft(int depth, bool root = true)
     {
-        // ref var entry = ref _perftTable.TryGet(in _pos, depth, out var tableValue);
-        //
-        // if (tableValue)
-        //     return entry.Count;
-        //
-        // entry.Count = ulong.MinValue;
-        // entry.Depth = depth;
-        // entry.Key = _pos.State.PositionKey;
-
         var tot = ulong.MinValue;
 
         var ml = _moveListPool.Get();
         ml.Generate(in _pos);
 
-        var state = new State();
-
         var moves = ml.Get();
         ref var movesSpace = ref MemoryMarshal.GetReference(moves);
         for (var i = 0; i < moves.Length; ++i)
@@ -162,6 +148,7 @@ public ulong Perft(int depth, bool root = true)
 
             var valMove = Unsafe.Add(ref movesSpace, i);
             var m = valMove.Move;
+            var state = new State();
 
             _pos.MakeMove(m, in state);
 
@@ -181,9 +168,6 @@ public ulong Perft(int depth, bool root = true)
             _pos.TakeMove(m);
         }
 
-        // if (!root)
-        //     _perftTable.Store(_pos.State.PositionKey, depth, tot);
-
         _moveListPool.Return(ml);
 
         return tot;
diff --git a/src/Rudzoft.ChessLib/Hash/IZobrist.cs b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
index c6da9cfd..e54c8c32 100644
--- a/src/Rudzoft.ChessLib/Hash/IZobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
@@ -12,10 +12,10 @@ public interface IZobrist
     HashKey ComputeMaterialKey(IPosition pos);
     HashKey ComputePawnKey(IPosition pos);
     HashKey ComputePositionKey(IPosition pos);
-    ref HashKey GetZobristPst(Square square, Piece piece);
-    ref HashKey GetZobristCastleling(CastleRights index);
-    ref HashKey GetZobristCastleling(CastleRight index);
-    HashKey GetZobristSide();
-    ref HashKey GetZobristEnPassant(File file);
-    HashKey GetZobristEnPassant(Square sq);
+    ref HashKey Psq(Square square, Piece piece);
+    ref HashKey Castleling(CastleRights index);
+    ref HashKey Castleling(CastleRight index);
+    HashKey Side();
+    ref HashKey EnPassant(File file);
+    HashKey EnPassant(Square sq);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/RKiss.cs b/src/Rudzoft.ChessLib/Hash/RKiss.cs
index 81830993..2c63de60 100644
--- a/src/Rudzoft.ChessLib/Hash/RKiss.cs
+++ b/src/Rudzoft.ChessLib/Hash/RKiss.cs
@@ -29,17 +29,22 @@ For further analysis see
 
 namespace Rudzoft.ChessLib.Hash;
 
+/// <summary>
+/// ------------ |----------:|----------:|----------:|
+///       Method |      Mean |     Error |    StdDev |
+/// ------------ |----------:|----------:|----------:|
+///     ULongRnd | 77.335 ns | 1.0416 ns | 0.9743 ns |
+///  RKissRandom |  4.369 ns | 0.0665 ns | 0.0622 ns |
+/// ------------ |----------:|----------:|----------:|
+/// </summary>
 public sealed class RKiss : IRKiss
 {
-    private ulong _s;
+    /// <summary>
+    /// The default value for random seed for improved consistency
+    /// </summary>
+    private const ulong DefaultRandomSeed = 1070372;
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private RKiss(in ulong s)
-    {
-        _s = s;
-    }
-
-    public static IRKiss Create(in ulong seed) => new RKiss(seed);
+    private ulong _s = DefaultRandomSeed;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public IEnumerable<ulong> Get(int count)
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
index e1b86f23..72249562 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 
 [Flags]
-public enum Bound : sbyte
+public enum Bound : byte
 {
     Void = 0,
     Alpha = 1,
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Cluster.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Cluster.cs
deleted file mode 100644
index 7ca85a54..00000000
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Cluster.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2020 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
-
-/// <summary>
-/// Stores an array of <see cref="TranspositionTableEntry"/>. In essence it acts like a entry
-/// bucket of 4 elements for each position stored in the <see cref="TranspositionTable"/>
-/// </summary>
-public readonly struct Cluster
-{
-    public static readonly TranspositionTableEntry DefaultEntry = new(
-        0,
-        Move.EmptyMove,
-        0,
-        1,
-        int.MaxValue,
-        int.MaxValue,
-        Bound.Void);
-
-    private readonly TranspositionTableEntry[] _cluster = new TranspositionTableEntry[4];
-    
-    public Cluster()
-    {
-        Reset();
-    }
-
-    public ref TranspositionTableEntry this[int key] => ref _cluster[key];
-
-    public void Add(int key, in TranspositionTableEntry entry)
-    {
-        _cluster[key] = entry;
-    }
-
-    public void Reset()
-    {
-        _cluster[0] = DefaultEntry;
-        _cluster[1] = DefaultEntry;
-        _cluster[2] = DefaultEntry;
-        _cluster[3] = DefaultEntry;
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
index 1be774b6..f020c4a0 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
@@ -1,99 +1,37 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 
 public interface ITranspositionTable
 {
-    /// <summary>
-    /// Number of table hits
-    /// </summary>
     ulong Hits { get; }
+    
+    /// TranspositionTable::set_size() sets the size of the transposition table,
+    /// measured in megabytes.
+    void SetSize(uint mbSize);
+
+    /// TranspositionTable::clear() overwrites the entire transposition table
+    /// with zeroes. It is called whenever the table is resized, or when the
+    /// user asks the program to clear the table (from the UCI interface).
+    void Clear();
 
-    int Size { get; }
-
-    /// <summary>
-    /// Increases the generation of the table by one
-    /// </summary>
+    /// TranspositionTable::store() writes a new entry containing position key and
+    /// valuable information of current position. The lowest order bits of position
+    /// key are used to decide on which cluster the position will be placed.
+    /// When a new entry is written and there are no empty entries available in cluster,
+    /// it replaces the least valuable of entries. A TTEntry t1 is considered to be
+    /// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
+    /// a previous search, or if the depth of t1 is bigger than the depth of t2.
+    void Store(in HashKey posKey, Value v, Bound t, Depth d, Move m, Value statV, Value kingD);
+
+    /// TranspositionTable::probe() looks up the current position in the
+    /// transposition table. Returns a pointer to the TTEntry or NULL if
+    /// position is not found.
+    bool Probe(in HashKey posKey, ref uint ttePos, out TTEntry entry);
+
+    /// TranspositionTable::new_search() is called at the beginning of every new
+    /// search. It increments the "generation" variable, which is used to
+    /// distinguish transposition table entries from previous searches from
+    /// entries from the current search.
     void NewSearch();
-
-    /// <summary>
-    /// Sets the size of the table in Mb
-    /// </summary>
-    /// <param name="mbSize">The size to set it to</param>
-    /// <returns>The number of clusters in the table</returns>
-    ulong SetSize(int mbSize);
-
-    /// <summary>
-    /// Finds a cluster in the table based on a position key
-    /// </summary>
-    /// <param name="key">The position key</param>
-    /// <returns>The cluster of the keys position in the table</returns>
-    ref Cluster FindCluster(in HashKey key);
-
-    void Refresh(ref TranspositionTableEntry tte);
-
-    /// <summary>
-    /// Probes the transposition table for a entry that matches the position key.
-    /// </summary>
-    /// <param name="key">The position key</param>
-    /// <param name="e">The entry to apply, will be set to default if not found in table</param>
-    /// <returns>(true, entry) if one was found, (false, empty) if not found</returns>
-    bool Probe(in HashKey key, ref TranspositionTableEntry e);
-
-    /// <summary>
-    /// Probes the table for the first cluster index which matches the position key
-    /// </summary>
-    /// <param name="key">The position key</param>
-    /// <returns>The cluster entry</returns>
-    ref TranspositionTableEntry ProbeFirst(in HashKey key);
-
-    /// <summary>
-    /// Stores a move in the transposition table. It will automatically detect the best cluster
-    /// location to store it in. If a similar move already is present, a simple check if done to
-    /// make sure it actually is an improvement of the previous move.
-    /// </summary>
-    /// <param name="key">The position key</param>
-    /// <param name="value">The value of the move</param>
-    /// <param name="type">The bound type, e.i. did it exceed alpha or beta</param>
-    /// <param name="depth">The depth of the move</param>
-    /// <param name="move">The move it self</param>
-    /// <param name="statValue">The static value of the move</param>
-    void Store(in HashKey key, int value, Bound type, sbyte depth, Move move, int statValue);
-
-    /// <summary>
-    /// Get the approximation full % of the table // todo : fix
-    /// </summary>
-    /// <returns>The % as integer value</returns>
-    int Fullness();
-
-    /// <summary>
-    /// Clears the current table
-    /// </summary>
-    void Clear();
-}
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index 6ea3b5d2..08fb2ff1 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -25,254 +25,413 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 using Microsoft.Extensions.Options;
-using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 
 public sealed class TranspositionTable : ITranspositionTable
 {
-    private static readonly int ClusterSize = Unsafe.SizeOf<TranspositionTableEntry>() * 4;
+    //     private static readonly int ClusterSize = Unsafe.SizeOf<TTEntry>() * 4;
+    private const uint ClusterSize = 4;
 
-    private Cluster[] _table;
-    private ulong _elements;
-    private int _fullnessElements;
-    private sbyte _generation;
+    private static readonly TTEntry StaticEntry = new();
+
+    private TTEntry[] _entries;
+    private uint _size;
+    private uint _sizeMask;
+    private byte _generation; // Size must be not bigger then TTEntry::generation8
 
     public TranspositionTable(IOptions<TranspositionTableConfiguration> options)
     {
-        _table = Array.Empty<Cluster>();
-        Size = options.Value.DefaultSize;
-        SetSize(Size);
+        var config = options.Value;
+        SetSize((uint)config.DefaultSize);
     }
 
-    /// <summary>
-    /// Number of table hits
-    /// </summary>
     public ulong Hits { get; private set; }
 
-    public int Size { get; private set; }
-
-    /// <summary>
-    /// Increases the generation of the table by one
-    /// </summary>
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void NewSearch() => ++_generation;
-
     /// <summary>
-    /// Sets the size of the table in Mb
+    /// Sets the size of the transposition table,
+    /// measured in megabytes.
     /// </summary>
-    /// <param name="mbSize">The size to set it to</param>
-    /// <returns>The number of clusters in the table</returns>
-    public ulong SetSize(int mbSize)
+    /// <param name="mbSize"></param>
+    public void SetSize(uint mbSize)
     {
-        switch (mbSize)
-        {
-            case < 0:
-                throw new TranspositionTableFailure($"Unable to create table with negative size: {mbSize}");
-            case 0:
-                return 0;
-        }
+        var newSize = 1024u;
 
-        Size = mbSize;
-        var size = (int)(((ulong)mbSize << 20) / (ulong)ClusterSize);
-        var resize = false;
-        var currentSize = _table.Length;
-        if (_table.Length == 0)
-        {
-            _table = new Cluster[size];
-            PopulateTable(0, size);
-        }
-        else if (currentSize != size)
-            resize = true;
+        // Transposition table consists of clusters and each cluster consists
+        // of ClusterSize number of TTEntries. Each non-empty entry contains
+        // information of exactly one position and newSize is the number of
+        // clusters we are going to allocate.
+        while (2UL * newSize * 64 <= mbSize << 20)
+            newSize *= 2;
 
-        if (resize)
-        {
-            Array.Resize(ref _table, size);
-            if (currentSize < size)
-                PopulateTable(currentSize, size);
-        }
+        if (newSize == _size)
+            return;
 
-        _elements = (ulong)size;
-        _generation = 1;
-        _fullnessElements = Math.Min(size, 250);
+        _size = newSize;
+        _sizeMask = _size - 1;
 
-        return (ulong)(size * ClusterSize);
+        _entries = new TTEntry[_size * 4];
     }
 
     /// <summary>
-    /// Finds a cluster in the table based on a position key
+    /// Overwrites the entire transposition table
+    /// with zeroes. It is called whenever the table is resized, or when the
+    /// user asks the program to clear the table (from the UCI interface).
     /// </summary>
-    /// <param name="key">The position key</param>
-    /// <returns>The cluster of the keys position in the table</returns>
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref Cluster FindCluster(in HashKey key)
+    public void Clear()
     {
-        var idx = (int)(key.LowerKey & (_elements - 1));
-        return ref _table[idx];
+        if (_entries != null)
+            Array.Clear(_entries, 0, _entries.Length);
+        Hits = ulong.MinValue;
     }
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void Refresh(ref TranspositionTableEntry tte) => tte.Generation = _generation;
-
     /// <summary>
-    /// Probes the transposition table for a entry that matches the position key.
+    /// writes a new entry containing position key and
+    /// valuable information of current position. The lowest order bits of position
+    /// key are used to decide on which cluster the position will be placed.
+    /// When a new entry is written and there are no empty entries available in cluster,
+    /// it replaces the least valuable of entries. A TTEntry t1 is considered to be
+    /// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
+    /// a previous search, or if the depth of t1 is bigger than the depth of t2.
     /// </summary>
-    /// <param name="key">The position key</param>
-    /// <param name="e">Target entry</param>
-    /// <returns>(true, entry) if one was found, (false, empty) if not found</returns>
-    public bool Probe(in HashKey key, ref TranspositionTableEntry e)
+    /// <param name="posKey"></param>
+    /// <param name="v"></param>
+    /// <param name="t"></param>
+    /// <param name="d"></param>
+    /// <param name="m"></param>
+    /// <param name="statV"></param>
+    /// <param name="kingD"></param>
+    public void Store(in HashKey posKey, Value v, Bound t, Depth d, Move m, Value statV, Value kingD)
     {
-        ref var ttc = ref FindCluster(in key);
-        var g = _generation;
-
-        // Probing the Table will automatically update the generation of the entry in case the
-        // probing retrieves an element.
-
-        var set = false;
-
-        for (var i = 0; i < ClusterSize; ++i)
-        {
-            ref var entry = ref ttc[i];
-            if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
-                continue;
-
-            entry.Generation = g;
-            e = entry;
-            set = true;
-            Hits++;
-            break;
-        }
-        
-        if (!set)
-            e = default;
-
-        return set;
-    }
+        // Use the high 32 bits as key inside the cluster
+        var posKey32 = posKey.UpperKey;
 
-    public void Save(in HashKey key, in TranspositionTableEntry e)
-    {
-        ref var ttc = ref FindCluster(in key);
-        var entryIndex = 0;
+        var replacePos = (posKey.LowerKey & _sizeMask) << 2;
+        var ttePos = replacePos;
 
-        for (var i = 0; i < ClusterSize; ++i)
+        for (uint i = 0; i < ClusterSize; i++)
         {
-            ref var entry = ref ttc[i];
-            if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
-                continue;
+            ref var tte = ref _entries[ttePos];
 
-            entryIndex = i;
-            break;
-        }
-        
-        ttc[entryIndex] = e;
-    }
-
-    /// <summary>
-    /// Probes the table for the first cluster index which matches the position key
-    /// </summary>
-    /// <param name="key">The position key</param>
-    /// <returns>The cluster entry</returns>
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref TranspositionTableEntry ProbeFirst(in HashKey key) => ref FindCluster(key)[0];
-
-    /// <summary>
-    /// Stores a move in the transposition table. It will automatically detect the best cluster
-    /// location to store it in. If a similar move already is present, a simple check if done to
-    /// make sure it actually is an improvement of the previous move.
-    /// </summary>
-    /// <param name="key">The position key</param>
-    /// <param name="value">The value of the move</param>
-    /// <param name="type">The bound type, e.i. did it exceed alpha or beta</param>
-    /// <param name="depth">The depth of the move</param>
-    /// <param name="move">The move it self</param>
-    /// <param name="statValue">The static value of the move</param>
-    public void Store(in HashKey key, int value, Bound type, sbyte depth, Move move, int statValue)
-    {
-        ref var ttc = ref FindCluster(in key);
-
-        var clusterIndex = 0;
-        var found = false;
-
-        for (var i = 0; i < ClusterSize; ++i)
-        {
-            ref var entry = ref ttc[i];
-            if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
-                continue;
+            if (tte.key == 0 || tte.key == posKey32) // Empty or overwrite old
+            {
+                // Preserve any existing ttMove
+                if (m == Move.EmptyMove)
+                    m = tte.move16;
 
-            clusterIndex = i;
-            found = true;
-            break;
-        }
+                _entries[ttePos].save(posKey32, v, t, d, m, _generation, statV, kingD);
+                return;
+            }
 
-        if (!found)
-        {
-            var index = 0;
-            ref var candidate = ref ttc[index];
-            var g = _generation;
+            // Implement replace strategy
+            //if ((entries[replacePos].generation8 == generation ? 2 : 0) + (tte.generation8 == generation || tte.bound == 3/*Bound.BOUND_EXACT*/ ? -2 : 0) + (tte.depth16 < entries[replacePos].depth16 ? 1 : 0) > 0)
+            //{
+            //    replacePos = ttePos;
+            //}
 
-            for (var i = 0; i < ClusterSize; ++i)
+            if (_entries[replacePos].generation8 == _generation)
             {
-                ref var entry = ref ttc[i];
-                var (cc1, cc2, cc3, cc4) = (candidate.Generation == g, entry.Generation == g, entry.Type == Bound.Exact, entry.Depth <= candidate.Depth);
-                if ((cc1 && cc4) || (!(cc2 || cc3) && (cc4 || cc1)))
+                if (tte.generation8 == _generation || tte.bound == Bound.Exact)
                 {
-                    index = i;
-                    candidate = entry;
+                    // +2 
+                    if (tte.depth16 < _entries[replacePos].depth16) // +1
+                        replacePos = ttePos;
                 }
+                else // +2
+                    replacePos = ttePos;
             }
+            else // 0
+            if (!(tte.generation8 == _generation || tte.bound == Bound.Exact) &&
+                tte.depth16 < _entries[replacePos].depth16) // +1
+                replacePos = ttePos;
 
-            clusterIndex = index;
+            ttePos++;
         }
 
-        var e = new TranspositionTableEntry(key.UpperKey, move, depth, _generation, value, statValue, type);
-
-        ref var target = ref ttc[clusterIndex];
-        target.Save(in e);
+        _entries[replacePos].save(posKey32, v, t, d, m, _generation, statV, kingD);
     }
 
     /// <summary>
-    /// Get the approximation full % of the table // todo : fix
+    /// looks up the current position in the
+    /// transposition table. Returns a pointer to the TTEntry or NULL if
+    /// position is not found.
     /// </summary>
-    /// <returns>The % as integer value</returns>
-    public int Fullness()
+    /// <param name="posKey"></param>
+    /// <param name="ttePos"></param>
+    /// <param name="entry"></param>
+    /// <returns>true if found, otherwise false</returns>
+    public bool Probe(in HashKey posKey, ref uint ttePos, out TTEntry entry)
     {
-        if (_generation == 1)
-            return 0;
-        var gen = _generation;
-        var sum = 0;
+        var posKey32 = posKey.UpperKey;
+        var offset = (posKey.LowerKey & _sizeMask) << 2;
 
-        for (var i = 0; i < _fullnessElements; ++i)
+        for (var i = offset; i < ClusterSize + offset; i++)
         {
-            for (var j = 0; j < ClusterSize; ++j)
+            if (_entries[i].key == posKey32)
             {
-                ref var entry = ref _table[i][j];
-                if (entry.Generation == gen)
-                    sum++;
+                ttePos = i;
+                entry = _entries[i];
+                Hits++;
+                return true;
             }
         }
 
-        return sum * 250 / _fullnessElements;
+        entry = StaticEntry;
+        return false;
     }
 
     /// <summary>
-    /// Clears the current table
+    /// Is called at the beginning of every new
+    /// search. It increments the "generation" variable, which is used to
+    /// distinguish transposition table entries from previous searches from
+    /// entries from the current search.
     /// </summary>
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void Clear()
-    {
-        ref var tableSpace = ref MemoryMarshal.GetArrayDataReference(_table);
-        for (var i = 0; i < _table.Length; ++i)
-            Unsafe.Add(ref tableSpace, i).Reset();
-    }
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private void PopulateTable(int from, int to)
-    {
-        for (var i = from; i < to; ++i)
-            _table[i] = new Cluster();
-    }
+    public void NewSearch() => _generation++;
 }
+
+//
+// public sealed class TranspositionTable : ITranspositionTable
+// {
+//     private static readonly int ClusterSize = Unsafe.SizeOf<TranspositionTableEntry>() * 4;
+//
+//     private Cluster[] _table;
+//     private ulong _elements;
+//     private int _fullnessElements;
+//     private sbyte _generation;
+//
+//     public TranspositionTable(IOptions<TranspositionTableConfiguration> options)
+//     {
+//         _table = Array.Empty<Cluster>();
+//         Size = options.Value.DefaultSize;
+//         SetSize(Size);
+//     }
+//
+//     /// <summary>
+//     /// Number of table hits
+//     /// </summary>
+//     public ulong Hits { get; private set; }
+//
+//     public int Size { get; private set; }
+//
+//     /// <summary>
+//     /// Increases the generation of the table by one
+//     /// </summary>
+//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
+//     public void NewSearch() => ++_generation;
+//
+//     /// <summary>
+//     /// Sets the size of the table in Mb
+//     /// </summary>
+//     /// <param name="mbSize">The size to set it to</param>
+//     /// <returns>The number of clusters in the table</returns>
+//     public ulong SetSize(int mbSize)
+//     {
+//         switch (mbSize)
+//         {
+//             case < 0:
+//                 throw new TranspositionTableFailure($"Unable to create table with negative size: {mbSize}");
+//             case 0:
+//                 return 0;
+//         }
+//
+//         Size = mbSize;
+//         var size = (int)(((ulong)mbSize << 20) / (ulong)ClusterSize);
+//         var resize = false;
+//         var currentSize = _table.Length;
+//         if (_table.Length == 0)
+//         {
+//             _table = new Cluster[size];
+//             PopulateTable(0, size);
+//         }
+//         else if (currentSize != size)
+//             resize = true;
+//
+//         if (resize)
+//         {
+//             Array.Resize(ref _table, size);
+//             if (currentSize < size)
+//                 PopulateTable(currentSize, size);
+//         }
+//
+//         _elements = (ulong)size;
+//         _generation = 1;
+//         _fullnessElements = Math.Min(size, 250);
+//
+//         return (ulong)(size * ClusterSize);
+//     }
+//
+//     /// <summary>
+//     /// Finds a cluster in the table based on a position key
+//     /// </summary>
+//     /// <param name="key">The position key</param>
+//     /// <returns>The cluster of the keys position in the table</returns>
+//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
+//     public ref Cluster FindCluster(in HashKey key)
+//     {
+//         var idx = (int)(key.LowerKey & (_elements - 1));
+//         return ref _table[idx];
+//     }
+//
+//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
+//     public void Refresh(ref TranspositionTableEntry tte) => tte.Generation = _generation;
+//
+//     /// <summary>
+//     /// Probes the transposition table for a entry that matches the position key.
+//     /// </summary>
+//     /// <param name="key">The position key</param>
+//     /// <param name="e">Target entry</param>
+//     /// <returns>(true, entry) if one was found, (false, empty) if not found</returns>
+//     public bool Probe(in HashKey key, ref TranspositionTableEntry e)
+//     {
+//         ref var ttc = ref FindCluster(in key);
+//         var g = _generation;
+//
+//         // Probing the Table will automatically update the generation of the entry in case the
+//         // probing retrieves an element.
+//
+//         var set = false;
+//
+//         for (var i = 0; i < ClusterSize; ++i)
+//         {
+//             ref var entry = ref ttc[i];
+//             if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
+//                 continue;
+//
+//             entry.Generation = g;
+//             e = entry;
+//             set = true;
+//             Hits++;
+//             break;
+//         }
+//         
+//         if (!set)
+//             e = default;
+//
+//         return set;
+//     }
+//
+//     public void Save(in HashKey key, in TranspositionTableEntry e)
+//     {
+//         ref var ttc = ref FindCluster(in key);
+//         var entryIndex = 0;
+//
+//         for (var i = 0; i < ClusterSize; ++i)
+//         {
+//             ref var entry = ref ttc[i];
+//             if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
+//                 continue;
+//
+//             entryIndex = i;
+//             break;
+//         }
+//         
+//         ttc[entryIndex] = e;
+//     }
+//
+//     /// <summary>
+//     /// Probes the table for the first cluster index which matches the position key
+//     /// </summary>
+//     /// <param name="key">The position key</param>
+//     /// <returns>The cluster entry</returns>
+//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
+//     public ref TranspositionTableEntry ProbeFirst(in HashKey key) => ref FindCluster(key)[0];
+//
+//     /// <summary>
+//     /// Stores a move in the transposition table. It will automatically detect the best cluster
+//     /// location to store it in. If a similar move already is present, a simple check if done to
+//     /// make sure it actually is an improvement of the previous move.
+//     /// </summary>
+//     /// <param name="key">The position key</param>
+//     /// <param name="value">The value of the move</param>
+//     /// <param name="type">The bound type, e.i. did it exceed alpha or beta</param>
+//     /// <param name="depth">The depth of the move</param>
+//     /// <param name="move">The move it self</param>
+//     /// <param name="statValue">The static value of the move</param>
+//     public void Store(in HashKey key, int value, Bound type, sbyte depth, Move move, int statValue)
+//     {
+//         ref var ttc = ref FindCluster(in key);
+//
+//         var clusterIndex = 0;
+//         var found = false;
+//
+//         for (var i = 0; i < ClusterSize; ++i)
+//         {
+//             ref var entry = ref ttc[i];
+//             if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
+//                 continue;
+//
+//             clusterIndex = i;
+//             found = true;
+//             break;
+//         }
+//
+//         if (!found)
+//         {
+//             var index = 0;
+//             ref var candidate = ref ttc[index];
+//             var g = _generation;
+//
+//             for (var i = 0; i < ClusterSize; ++i)
+//             {
+//                 ref var entry = ref ttc[i];
+//                 var (cc1, cc2, cc3, cc4) = (candidate.Generation == g, entry.Generation == g, entry.Type == Bound.Exact, entry.Depth <= candidate.Depth);
+//                 if ((cc1 && cc4) || (!(cc2 || cc3) && (cc4 || cc1)))
+//                 {
+//                     index = i;
+//                     candidate = entry;
+//                 }
+//             }
+//
+//             clusterIndex = index;
+//         }
+//
+//         var e = new TranspositionTableEntry(key.UpperKey, move, depth, _generation, value, statValue, type);
+//
+//         ref var target = ref ttc[clusterIndex];
+//         target.Save(in e);
+//     }
+//
+//     /// <summary>
+//     /// Get the approximation full % of the table // todo : fix
+//     /// </summary>
+//     /// <returns>The % as integer value</returns>
+//     public int Fullness()
+//     {
+//         if (_generation == 1)
+//             return 0;
+//         var gen = _generation;
+//         var sum = 0;
+//
+//         for (var i = 0; i < _fullnessElements; ++i)
+//         {
+//             for (var j = 0; j < ClusterSize; ++j)
+//             {
+//                 ref var entry = ref _table[i][j];
+//                 if (entry.Generation == gen)
+//                     sum++;
+//             }
+//         }
+//
+//         return sum * 250 / _fullnessElements;
+//     }
+//
+//     /// <summary>
+//     /// Clears the current table
+//     /// </summary>
+//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
+//     public void Clear()
+//     {
+//         ref var tableSpace = ref MemoryMarshal.GetArrayDataReference(_table);
+//         for (var i = 0; i < _table.Length; ++i)
+//             Unsafe.Add(ref tableSpace, i).Reset();
+//     }
+//
+//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
+//     private void PopulateTable(int from, int to)
+//     {
+//         for (var i = from; i < to; ++i)
+//             _table[i] = new Cluster();
+//     }
+// }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
index 35c3cdb1..9ebfb833 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
@@ -24,63 +24,60 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 
+/// The TTEntry is the class of transposition table entries
+///
+/// A TTEntry needs 128 bits to be stored
+///
+/// bit  0-31: key
+/// bit 32-63: data
+/// bit 64-79: value
+/// bit 80-95: depth
+/// bit 96-111: static value
+/// bit 112-127: margin of static value
+///
+/// the 32 bits of the data field are so defined
+///
+/// bit  0-15: move
+/// bit 16-20: not used
+/// bit 21-22: value type
+/// bit 23-31: generation
 [StructLayout(LayoutKind.Sequential, Pack = 2)]
-public struct TranspositionTableEntry
+public struct TTEntry
 {
-    public uint Key32;
-    public Move Move;
-    public int Value;
-    public int StaticValue;
-    public Bound Type;
-    public sbyte Depth;
-    public sbyte Generation;
-
-    public TranspositionTableEntry(uint k, Move m, sbyte d, sbyte g, int v, int sv, Bound b)
-    {
-        Key32 = k;
-        Move = m;
-        Depth = d;
-        Generation = g;
-        Value = v;
-        StaticValue = sv;
-        Type = b;
-    }
-
-    public TranspositionTableEntry(in TranspositionTableEntry tte)
+    internal uint key;
+    internal Move move16;
+    internal Bound bound;
+    internal byte generation8;
+    internal short value16;
+    internal short depth16;
+    internal short staticValue;
+    internal short staticMargin;
+
+    internal void save(uint k, Value v, Bound t, Depth d, Move m, int g, Value statV, Value statM)
     {
-        this = tte;
+        key = k;
+        move16 = m;
+        bound = t;
+        generation8 = (byte)g;
+        value16 = (short)v.Raw;
+        depth16 = (short)d.Value;
+        staticValue = (short)statV.Raw;
+        staticMargin = (short)statM.Raw;
     }
 
-    public static bool operator ==(TranspositionTableEntry left, TranspositionTableEntry right)
-        => left.Equals(right);
-
-    public static bool operator !=(TranspositionTableEntry left, TranspositionTableEntry right)
-        => !(left == right);
-
-    public void Save(in TranspositionTableEntry tte)
-    {
-        Key32 = tte.Key32;
-        if (!tte.Move.IsNullMove())
-            Move = tte.Move;
-        Depth = tte.Depth;
-        Generation = tte.Generation;
-        Value = tte.Value;
-        StaticValue = tte.StaticValue;
-        Type = tte.Type;
-    }
-
-    private readonly bool Equals(TranspositionTableEntry other)
-        => Key32 == other.Key32 && Generation == other.Generation;
-
-    public readonly override bool Equals(object obj)
-        => obj is TranspositionTableEntry other && Equals(other);
-
-    public readonly override int GetHashCode()
-        => HashCode.Combine(Key32, Move, Depth, Generation, Value, StaticValue, Type);
-}
+    internal void set_generation(int g) { generation8 = (byte)g; }
+
+    //internal UInt32 key() { return key32; }
+    internal Depth depth() { return depth16; }
+    internal Move move() { return move16; }
+    internal Value value() { return value16; }
+    internal Bound type() { return bound; }
+    internal int generation() { return generation8; }
+    internal Value static_value() { return staticValue; }
+    internal Value static_value_margin() { return staticMargin; }
+};
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index d2b9de3e..d6f66516 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -35,13 +35,6 @@ namespace Rudzoft.ChessLib.Hash;
 /// The zobrist key for which this class helps generate, is a "unique" representation of
 /// a complete chess board, including its status.
 ///
-/// ------------ |----------:|----------:|----------:|
-///       Method |      Mean |     Error |    StdDev |
-/// ------------ |----------:|----------:|----------:|
-///     ULongRnd | 77.335 ns | 1.0416 ns | 0.9743 ns |
-///  RKissRandom |  4.369 ns | 0.0665 ns | 0.0622 ns |
-/// ------------ |----------:|----------:|----------:|
-///
 /// Collisions:
 /// Key collisions or type-1 errors are inherent in using Zobrist keys with far less bits than required to encode all reachable chess positions.
 /// This representation is using a 64 bit key, thus it could be expected to get a collision after about 2 ^ 32 or 4 billion positions.
@@ -51,61 +44,63 @@ namespace Rudzoft.ChessLib.Hash;
 /// </summary>
 public sealed class Zobrist : IZobrist
 {
-    /// <summary>
-    /// The default value for random seed for improved consistency
-    /// </summary>
-    private const ulong DefaultRandomSeed = 1070372;
-
     /// <summary>
     /// Represents the piece index (as in EPieces), with each a square of the board value to match.
     /// </summary>
-    private readonly HashKey[][] ZobristPst = new HashKey[Square.Count][];
+    private readonly HashKey[][] _zobristPst;
 
     /// <summary>
     /// Represents the castleling rights.
     /// </summary>
-    private readonly HashKey[] ZobristCastling = new HashKey[CastleRight.Count];
+    private readonly HashKey[] _zobristCastling;
 
     /// <summary>
     /// En-Passant is only required to have 8 entries, one for each possible file where the En-Passant square can occur.
     /// </summary>
-    private readonly HashKey[] ZobristEpFile = new HashKey[File.Count];
+    private readonly HashKey[] _zobristEpFile;
 
     /// <summary>
     /// This is used if the side to move is black, if the side is white, no hashing will occur.
     /// </summary>
-    private readonly HashKey ZobristSide;
+    private readonly HashKey _zobristSide;
 
     /// <summary>
     /// To use as base for pawn hash table
     /// </summary>
     public HashKey ZobristNoPawn { get; }
 
-    public Zobrist()
-    {
-        var rnd = RKiss.Create(DefaultRandomSeed);
-        InitializePst(in rnd);
-        InitializeRandomArray(ZobristEpFile, rnd);
-        InitializeRandomArray(ZobristCastling, rnd);
-        ZobristSide = rnd.Rand();
-        ZobristNoPawn = rnd.Rand();
-    }
-
-    private void InitializePst(in IRKiss rnd)
+    public Zobrist(IRKiss rKiss)
     {
+        _zobristPst = new HashKey[Square.Count][];
+        
         for (var sq = Squares.a1; sq <= Squares.h8; sq++)
         {
             var s = sq.AsInt();
-            ZobristPst[s] = new HashKey[Piece.Count];
+            _zobristPst[s] = new HashKey[Piece.Count];
             foreach (var pc in Piece.All.AsSpan())
-                ZobristPst[s][pc.AsInt()] = rnd.Rand();
+                _zobristPst[s][pc.AsInt()] = rKiss.Rand();
         }
-    }
 
-    private static void InitializeRandomArray(Span<HashKey> array, IRKiss rnd)
-    {
-        for (var i = 0; i < array.Length; i++)
-            array[i] = rnd.Rand();
+        _zobristCastling = new HashKey[CastleRight.Count];
+        
+        for (var cr = CastleRights.None; cr <= CastleRights.Any; cr++)
+        {
+            var v = cr.AsInt();
+            _zobristCastling[v] = HashKey.Empty;
+            var bb = BitBoard.Create((uint)v);
+            while (bb)
+            {
+                var key = _zobristCastling[1UL << BitBoards.PopLsb(ref bb).AsInt()];
+                _zobristCastling[v] ^= !key.IsEmpty? key : rKiss.Rand();
+            }
+        }
+
+        _zobristEpFile = new HashKey[File.Count];
+        for (var i = 0; i < _zobristEpFile.Length; i++)
+            _zobristEpFile[i] = rKiss.Rand();
+        
+        _zobristSide = rKiss.Rand();
+        ZobristNoPawn = rKiss.Rand();
     }
 
     public HashKey ComputeMaterialKey(IPosition pos)
@@ -113,8 +108,8 @@ public HashKey ComputeMaterialKey(IPosition pos)
         var key = HashKey.Empty;
         foreach (var pc in Piece.All.AsSpan())
             for (var count = 0; count < pos.Board.PieceCount(pc); count++)
-                key ^= GetZobristPst(count, pc);
-        
+                key ^= Psq(count, pc);
+
         return key;
     }
 
@@ -125,14 +120,14 @@ public HashKey ComputePawnKey(IPosition pos)
         var pawns = pos.Pieces(Piece.WhitePawn);
 
         while (pawns)
-            key ^= GetZobristPst(BitBoards.PopLsb(ref pawns), Piece.WhitePawn);
+            key ^= Psq(BitBoards.PopLsb(ref pawns), Piece.WhitePawn);
 
         pawns = pos.Pieces(Piece.BlackPawn);
-        
+
         while (pawns)
-            key ^= GetZobristPst(BitBoards.PopLsb(ref pawns), Piece.BlackPawn);
-        
-        return key; 
+            key ^= Psq(BitBoards.PopLsb(ref pawns), Piece.BlackPawn);
+
+        return key;
     }
 
     public HashKey ComputePositionKey(IPosition pos)
@@ -143,36 +138,36 @@ public HashKey ComputePositionKey(IPosition pos)
         {
             var bb = pos.Pieces(pc);
             while (bb)
-                key ^= GetZobristPst(BitBoards.PopLsb(ref bb), pc);
+                key ^= Psq(BitBoards.PopLsb(ref bb), pc);
         }
 
         if (pos.SideToMove.IsWhite)
-            key ^= ZobristSide;
+            key ^= _zobristSide;
+
+        key ^= Castleling(pos.State.CastlelingRights.Rights);
+        key ^= EnPassant(pos.EnPassantSquare.File);
 
-        key ^= GetZobristCastleling(pos.State.CastlelingRights.Rights);
-        key ^= GetZobristEnPassant(pos.EnPassantSquare.File);
-        
         return key;
     }
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey GetZobristPst(Square square, Piece piece) => ref ZobristPst[square.AsInt()][piece.AsInt()];
+    public ref HashKey Psq(Square square, Piece piece) => ref _zobristPst[square.AsInt()][piece.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey GetZobristCastleling(CastleRights index) => ref ZobristCastling[index.AsInt()];
+    public ref HashKey Castleling(CastleRights index) => ref _zobristCastling[index.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey GetZobristCastleling(CastleRight index) => ref GetZobristCastleling(index.Rights);
+    public ref HashKey Castleling(CastleRight index) => ref Castleling(index.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public HashKey GetZobristSide() => ZobristSide;
+    public HashKey Side() => _zobristSide;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey GetZobristEnPassant(File file) => ref ZobristEpFile[file.AsInt()];
+    public ref HashKey EnPassant(File file) => ref _zobristEpFile[file.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public HashKey GetZobristEnPassant(Square sq)
+    public HashKey EnPassant(Square sq)
     {
-        return sq == Square.None ? HashKey.Empty : GetZobristEnPassant(sq.File);
+        return sq == Square.None ? HashKey.Empty : EnPassant(sq.File);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index acf698cb..f6295460 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -38,9 +38,6 @@ namespace Rudzoft.ChessLib.Polyglot;
 
 public sealed class PolyglotBook : IPolyglotBook
 {
-    // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
-    private static readonly int[] PieceMapping = { -1, 1, 3, 5, 7, 9, 11, -1, -1, 0, 2, 4, 6, 8, 10 };
-
     private static readonly CastleRight[] CastleRights =
     {
         CastleRight.WhiteKing,
@@ -215,8 +212,7 @@ public HashKey ComputePolyglotKey(in IPosition pos)
         {
             var s = BitBoards.PopLsb(ref b);
             var pc = pos.GetPiece(s);
-            var p = PieceMapping[pc.AsInt()];
-            k ^= PolyglotBookZobrist.Psq(p, s);
+            k ^= PolyglotBookZobrist.Psq(pc, s);
         }
 
         var crSpan = CastleRights.AsSpan();
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 913d755f..639861b3 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -344,6 +344,14 @@ internal static class PolyglotBookZobrist
 
     private const ulong TurnKey = 0xF8D626AAAF278509UL;
 
+    // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
+    private static readonly int[] PieceMapping = { -1, 1, 3, 5, 7, 9, 11, -1, -1, 0, 2, 4, 6, 8, 10 };
+
+    internal static ulong Psq(Piece pc, Square sq)
+    {
+        return Psq(PieceMapping[pc.AsInt()], sq);
+    }
+    
     internal static ulong Psq(int piece, Square sq)
         => PsqKeys[piece, sq.AsInt()];
 
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 3d582275..72819848 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -400,7 +400,7 @@ public HashKey GetPawnKey()
         {
             var sq = BitBoards.PopLsb(ref pieces);
             var pc = GetPiece(sq);
-            result ^= _zobrist.GetZobristPst(sq, pc);
+            result ^= _zobrist.Psq(sq, pc);
         }
 
         return result;
@@ -418,7 +418,7 @@ public HashKey GetPiecesKey()
         {
             var sq = BitBoards.PopLsb(ref pieces);
             var pc = Board.PieceAt(sq);
-            result ^= _zobrist.GetZobristPst(sq, pc);
+            result ^= _zobrist.Psq(sq, pc);
         }
 
         return result;
@@ -685,11 +685,12 @@ public bool IsPseudoLegal(Move m)
     public void MakeMove(Move m, in State newState, bool givesCheck)
     {
         State.LastMove = m;
+
+        var k = State.PositionKey ^ _zobrist.Side();
+
         State = State.CopyTo(newState);
         var state = State;
 
-        var k = state.PositionKey ^ _zobrist.GetZobristSide();
-
         Ply++;
         state.Rule50++;
         state.PliesFromNull++;
@@ -699,7 +700,6 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         var (from, to, type) = m;
         var pc = GetPiece(from);
         var pt = pc.Type();
-        var isPawn = pt == PieceTypes.Pawn;
         var capturedPiece = m.IsEnPassantMove()
             ? PieceTypes.Pawn.MakePiece(them)
             : GetPiece(to);
@@ -716,7 +716,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
 
             var (rookFrom, rookTo) = DoCastle(us, from, ref to, CastlePerform.Do);
 
-            k ^= _zobrist.GetZobristPst(rookFrom, capturedPiece) ^ _zobrist.GetZobristPst(rookTo, capturedPiece);
+            k ^= _zobrist.Psq(rookFrom, capturedPiece) ^ _zobrist.Psq(rookTo, capturedPiece);
 
             // reset captured piece type as castleling is "king-captures-rook"
             capturedPiece = Piece.EmptyPiece;
@@ -739,12 +739,11 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                     Debug.Assert(GetPiece(captureSquare) == pt.MakePiece(them));
                 }
 
-                state.PawnKey ^= _zobrist.GetZobristPst(captureSquare, capturedPiece);
+                state.PawnKey ^= _zobrist.Psq(captureSquare, capturedPiece);
             }
             else
             {
                 _nonPawnMaterial[them.Side] -= Values.GetPieceValue(capturedPiece, Phases.Mg);
-                // TODO : Update material here
             }
 
             // Update board and piece lists
@@ -752,7 +751,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             if (type == MoveTypes.Enpassant)
                 Board.ClearPiece(captureSquare);
 
-            k ^= _zobrist.GetZobristPst(captureSquare, capturedPiece);
+            k ^= _zobrist.Psq(captureSquare, capturedPiece);
 
             // TODO : Update other depending keys and psq values here
 
@@ -761,21 +760,22 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         }
 
         // update key with moved piece
-        k ^= _zobrist.GetZobristPst(from, pc) ^ _zobrist.GetZobristPst(to, pc);
+        k ^= _zobrist.Psq(from, pc) ^ _zobrist.Psq(to, pc);
 
-        k ^= _zobrist.GetZobristEnPassant(state.EnPassantSquare);
-        
         // reset en-passant square if it is set
         if (state.EnPassantSquare != Square.None)
+        {
+            k ^= _zobrist.EnPassant(state.EnPassantSquare);
             state.EnPassantSquare = Square.None;
+        }
 
         // Update castling rights if needed
         if (state.CastlelingRights != CastleRight.None &&
             (_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]) != CastleRight.None)
         {
-            k ^= _zobrist.GetZobristCastleling(state.CastlelingRights);
+            k ^= _zobrist.Castleling(state.CastlelingRights);
             state.CastlelingRights &= ~(_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]);
-            k ^= _zobrist.GetZobristCastleling(state.CastlelingRights);
+            k ^= _zobrist.Castleling(state.CastlelingRights);
         }
 
         // Move the piece. The tricky Chess960 castle is handled earlier
@@ -783,14 +783,14 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             MovePiece(from, to);
 
         // If the moving piece is a pawn do some special extra work
-        if (isPawn)
+        if (pt == PieceTypes.Pawn)
         {
             // Set en-passant square, only if moved pawn can be captured
             if ((to.Value.AsInt() ^ from.Value.AsInt()) == 16
-                && !((to - us.PawnPushDistance()).PawnAttack(us) & Pieces(PieceTypes.Pawn, them)).IsEmpty)
+                && ((to - us.PawnPushDistance()).PawnAttack(us) & Pieces(PieceTypes.Pawn, them)).IsNotEmpty)
             {
                 state.EnPassantSquare = to - us.PawnPushDistance();
-                k ^= _zobrist.GetZobristEnPassant(state.EnPassantSquare.File);
+                k ^= _zobrist.EnPassant(state.EnPassantSquare.File);
             }
             else if (type == MoveTypes.Promotion)
             {
@@ -803,16 +803,14 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 AddPiece(promotionPiece, to);
 
                 // Update hash keys
-                ref var toKey = ref _zobrist.GetZobristPst(to, pc);
-                
-                k ^= toKey ^ _zobrist.GetZobristPst(to, promotionPiece);
-                state.PawnKey ^= toKey;
+                k ^= _zobrist.Psq(to, pc) ^ _zobrist.Psq(to, promotionPiece);
+                state.PawnKey ^= _zobrist.Psq(to, pc);
 
                 _nonPawnMaterial[us.Side] += Values.GetPieceValue(promotionPiece, Phases.Mg);
             }
 
             // Update pawn hash key
-            state.PawnKey ^= _zobrist.GetZobristPst(from, pc) ^ _zobrist.GetZobristPst(to, pc);
+            state.PawnKey ^= _zobrist.Psq(from, pc) ^ _zobrist.Psq(to, pc);
 
             // Reset rule 50 draw counter
             state.Rule50 = 0;
@@ -843,12 +841,13 @@ public void MakeNullMove(in State newState)
 
         CopyState(in newState);
 
-        State.PositionKey ^= _zobrist.GetZobristEnPassant(State.EnPassantSquare);
-        
         if (State.EnPassantSquare != Square.None)
+        {
+            State.PositionKey ^= _zobrist.EnPassant(State.EnPassantSquare);
             State.EnPassantSquare = Square.None;
+        }
 
-        State.PositionKey ^= _zobrist.GetZobristSide();
+        State.PositionKey ^= _zobrist.Side();
 
         ++State.Rule50;
         State.PliesFromNull = 0;
@@ -1209,7 +1208,7 @@ public IPosition Set(in FenData fenData, ChessMode chessMode, in State state, bo
         SetupEnPassant(fenData.Chunk());
         SetupMoveNumber(fenData);
 
-        SetState();
+        SetState(State);
 
         return this;
     }
@@ -1305,7 +1304,7 @@ public void TakeMove(Move m)
 
 #if DEBUG
         var validatorResult = _positionValidator.Validate(this);
-        Debug.Assert(validatorResult.Ok);
+        Debug.Assert(validatorResult.Ok, validatorResult.Errors);
 #endif
     }
 
@@ -1367,14 +1366,14 @@ public HashKey MovePositionKey(Move m)
         Debug.Assert(m.IsValidMove());
 
         var movePositionKey = State.PositionKey
-                              ^ _zobrist.GetZobristSide()
-                              ^ _zobrist.GetZobristPst(m.FromSquare(), Board.MovedPiece(m))
-                              ^ _zobrist.GetZobristPst(m.ToSquare(), Board.MovedPiece(m));
+                              ^ _zobrist.Side()
+                              ^ _zobrist.Psq(m.FromSquare(), Board.MovedPiece(m))
+                              ^ _zobrist.Psq(m.ToSquare(), Board.MovedPiece(m));
 
         if (!Board.IsEmpty(m.ToSquare()))
-            movePositionKey ^= _zobrist.GetZobristPst(m.ToSquare(), Board.PieceAt(m.ToSquare()));
+            movePositionKey ^= _zobrist.Psq(m.ToSquare(), Board.PieceAt(m.ToSquare()));
 
-        movePositionKey ^= _zobrist.GetZobristEnPassant(State.EnPassantSquare);
+        movePositionKey ^= _zobrist.EnPassant(State.EnPassantSquare);
 
         return movePositionKey;
     }
@@ -1474,43 +1473,71 @@ private void CopyState(in State newState)
         State = State.CopyTo(newState);
     }
     
-    private void SetState()
+    private void SetState(State state)
     {
-        State.Checkers = AttacksTo(GetKingSquare(_sideToMove)) & Board.Pieces(~_sideToMove);
+        var k = HashKey.Empty;
+        state.MaterialKey = HashKey.Empty;
+        state.PawnKey = _zobrist.ZobristNoPawn;
+        
         _nonPawnMaterial[Player.White.Side] = _nonPawnMaterial[Player.Black.Side] = Value.ValueZero;
+        State.Checkers = AttacksTo(GetKingSquare(_sideToMove)) & Board.Pieces(~_sideToMove);
+        
         SetCheckInfo(State);
+        
+        for (var b = Pieces(); b.IsNotEmpty;)
+        {
+            var sq = BitBoards.PopLsb(ref b);
+            var pc = Board.PieceAt(sq);
+            k ^= _zobrist.Psq(sq, pc);
+        }
+
+        if (state.EnPassantSquare != Square.None)
+            k ^= _zobrist.EnPassant(state.EnPassantSquare);
+
+        if (SideToMove.IsBlack)
+            k ^= _zobrist.Side();
+
+        state.PositionKey = k ^ _zobrist.Castleling(state.CastlelingRights);
 
-        // compute hash keys
-        for (var b = Board.Pieces(); b.IsNotEmpty;)
+        for (var b = Pieces(PieceTypes.Pawn); b.IsNotEmpty;)
         {
             var sq = BitBoards.PopLsb(ref b);
-            var pc = GetPiece(sq);
-            var pt = pc.Type();
+            var pc = Board.PieceAt(sq);
+            state.MaterialKey ^= _zobrist.Psq(sq, pc);
+        }
 
-            if (pt != PieceTypes.King)
-                _nonPawnMaterial[pc.ColorOf().Side] += Values.GetPieceValue(pc, Phases.Mg);
+        foreach (var pc in Piece.All.AsSpan())
+        {
+            if (pc.Type() != PieceTypes.Pawn && pc.Type() != PieceTypes.King)
+            {
+                var val = Values.GetPieceValue(pc.Type(), Phases.Mg).AsInt() * Board.PieceCount(pc);
+                _nonPawnMaterial[pc.ColorOf().Side] += val;
+            }
+            
+            for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
+                state.MaterialKey ^= _zobrist.Psq(cnt, pc);
         }
 
-        State.MaterialKey = _zobrist.ComputeMaterialKey(this);
-        State.PawnKey = GetPawnKey();
-        State.PositionKey = GetPiecesKey();
+        // // compute hash keys
+        // for (var b = Board.Pieces(); b.IsNotEmpty;)
+        // {
+        //     var sq = BitBoards.PopLsb(ref b);
+        //     var pc = GetPiece(sq);
+        //     var pt = pc.Type();
+        //
+        //     if (pt != PieceTypes.King)
+        //         _nonPawnMaterial[pc.ColorOf().Side] += Values.GetPieceValue(pc, Phases.Mg);
+        // }
+        //
+        // State.MaterialKey = _zobrist.ComputeMaterialKey(this);
+        // State.PawnKey = GetPawnKey();
+        // State.PositionKey = GetPiecesKey();
     }
 
     private void SetupCastle(ReadOnlySpan<char> castle)
     {
-        Square RookSquare(Square startSq, Piece rook)
+        foreach (var ca in castle)
         {
-            var targetSq = startSq;
-            while (Board.PieceAt(targetSq) != rook)
-                --targetSq;
-            return targetSq;
-        }
-
-        ref var castleSpace = ref MemoryMarshal.GetReference(castle);
-
-        for (var i = 0; i < castle.Length; ++i)
-        {
-            var ca = Unsafe.Add(ref castleSpace, i);
             Player c = char.IsLower(ca);
             var rook = PieceTypes.Rook.MakePiece(c);
             var token = char.ToUpper(ca);
@@ -1529,6 +1556,14 @@ Square RookSquare(Square startSq, Piece rook)
         }
     }
 
+    private Square RookSquare(Square startSq, Piece rook)
+    {
+        var targetSq = startSq;
+        while (Board.PieceAt(targetSq) != rook)
+            --targetSq;
+        return targetSq;
+    }
+
     private (BitBoard, BitBoard) SliderBlockers(in BitBoard sliders, Square s)
     {
         var result = (blockers: BitBoard.Empty, pinners: BitBoard.Empty);
diff --git a/src/Rudzoft.ChessLib/Tables/HashTable.cs b/src/Rudzoft.ChessLib/Tables/HashTable.cs
index 3337c810..0ca4cd93 100644
--- a/src/Rudzoft.ChessLib/Tables/HashTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/HashTable.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Tables;
@@ -35,7 +36,7 @@ public abstract class HashTable<T> : IHashTable<T> where T : ITableEntry
 
     public int Count => _table.Length;
 
-    public ref T this[HashKey key] => ref _table[key.Key & (ulong)(_table.Length - 1)];
+    public ref T this[HashKey key] => ref _table[key.Key & (ulong)_table.Length - 1];
 
     /// <summary>
     /// Initialized the table array. In case the table array is initialized with a different
@@ -50,7 +51,7 @@ public abstract class HashTable<T> : IHashTable<T> where T : ITableEntry
     /// <param name="elementSize">The size of <see cref="T"/></param>
     /// <param name="tableSizeMb">The number of elements to store in the array</param>
     /// <param name="initializer">Initializer function to initialize a single entry object</param>
-    public void Initialize(int elementSize, int tableSizeMb, Func<T> initializer)
+    public void Initialize(int elementSize, int tableSizeMb, Func<HashKey, T> initializer)
     {
         var arraySize = tableSizeMb * 1024 * 1024 / elementSize;
         if (_table.Length != 0)
@@ -66,13 +67,13 @@ public void Initialize(int elementSize, int tableSizeMb, Func<T> initializer)
                 return;
 
             for (var i = currentLength; i < arraySize; ++i)
-                _table[i] = initializer();
+                _table[i] = initializer(ulong.MinValue);
         }
         else
         {
             _table = new T[arraySize];
             for (var i = 0; i < _table.Length; ++i)
-                _table[i] = initializer();
+                _table[i] = initializer(ulong.MinValue);
         }
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Tables/IHashTable.cs b/src/Rudzoft.ChessLib/Tables/IHashTable.cs
index acdd805a..6a5ef323 100644
--- a/src/Rudzoft.ChessLib/Tables/IHashTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/IHashTable.cs
@@ -35,5 +35,5 @@ public interface IHashTable<T> where T : ITableEntry
 
     ref T this[HashKey key] { get; }
 
-    void Initialize(int elementSize, int tableSizeMb, Func<T> initializer);
+    void Initialize(int elementSize, int tableSizeMb, Func<HashKey, T> initializer);
 }
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
index b06e94f7..1519e731 100644
--- a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
@@ -53,7 +53,7 @@ public sealed class PerftTable : HashTable<IPerftTableEntry>
 
     public PerftTable()
     {
-        Initialize(ElementSize, HashMemory, static () => new PerftTableEntry { Key = 0, Count = ulong.MinValue, Depth = -1 });
+        Initialize(ElementSize, HashMemory, static key => new PerftTableEntry { Key = key, Count = ulong.MinValue, Depth = -1 });
         _mask = Count - 1;
     }
     
@@ -63,12 +63,12 @@ public ref IPerftTableEntry TryGet(in IPosition pos, int depth, out bool found)
         var entryKey = posKey & _mask ^ depth;
         ref var entry = ref this[entryKey];
         found = entry.Key == entryKey && entry.Depth == depth;
-        if (!found)
-            entry.Key = entryKey;
+        // if (!found)
+        //     entry.Key = entryKey;
         return ref entry;
     }
 
-    public void Store(in HashKey key, int depth, ulong count)
+    public void Store(in HashKey key, int depth, in ulong count)
     {
         var entryKey = key & _mask ^ depth;
         ref var entry = ref this[entryKey];
diff --git a/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs b/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs
new file mode 100644
index 00000000..0463d08f
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs
@@ -0,0 +1,322 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics.X86;
+
+namespace Rudzoft.ChessLib.Types.Attacks;
+
+public static class Mbb
+{
+    private static readonly ulong[] RookMagics =
+    {
+        0xa8002c000108020UL, 0x6c00049b0002001UL, 0x100200010090040UL, 0x2480041000800801UL,
+        0x280028004000800UL, 0x900410008040022UL, 0x280020001001080UL, 0x2880002041000080UL,
+        0xa000800080400034UL, 0x4808020004000UL, 0x2290802004801000UL, 0x411000d00100020UL,
+        0x402800800040080UL, 0xb000401004208UL, 0x2409000100040200UL, 0x1002100004082UL,
+        0x22878001e24000UL, 0x1090810021004010UL, 0x801030040200012UL, 0x500808008001000UL,
+        0xa08018014000880UL, 0x8000808004000200UL, 0x201008080010200UL, 0x801020000441090UL,
+        0x800080204005UL, 0x1040200040100048UL, 0x120200402082UL, 0xd14880480100080UL,
+        0x12040280080080UL, 0x100040080020080UL, 0x9020010080800200UL, 0x813241200148449UL,
+        0x491604000800080UL, 0x100401000402001UL, 0x4820010021001040UL, 0x400402202000812UL,
+        0x209009005000802UL, 0x810800601800400UL, 0x4301083214000150UL, 0x204026458e00140UL,
+        0x40204000808000UL, 0x8001008040010020UL, 0x8410820820420010UL, 0x1003001000090020UL,
+        0x804040008008080UL, 0x12000810020004UL, 0x1000100200040208UL, 0x430000a044020001UL,
+        0x2800080008000100UL, 0xe0100040002240UL, 0x200100401700UL, 0x2244100408008080UL,
+        0x8000400801980UL, 0x200081004020100UL, 0x8010100228810400UL, 0x2000009044210200UL,
+        0x4080008040102101UL, 0x1a2208080204d101UL, 0x4100010002080088UL, 0x8100042000102001UL
+    };
+
+    private static readonly ulong[] BishopMagics =
+    {
+        0x89a1121896040240UL, 0x2004844802002010UL, 0x2068080051921000UL, 0x62880a0220200808UL,
+        0x404008080020000UL, 0x10082100100a4218UL, 0x8040002811040900UL, 0x8010100a02020058UL,
+        0x20800090488c0080UL, 0x1210815040200050UL, 0x8010008820400082UL, 0x4100210040110042UL,
+        0x880800040081080UL, 0x102008010402040UL, 0x211021800500412UL, 0x1000402000400180UL,
+        0x1040082084200040UL, 0x1208000080008410UL, 0x840081084110800UL, 0x100020004010004UL,
+        0x10004000800400UL, 0x40c0422080a000UL, 0x420804400400UL, 0x110402040081040UL,
+        0x20088080080220UL, 0x810001008080UL, 0x80800080101002UL, 0x82000020100050UL,
+        0x4100001020020UL, 0x4040400808010UL, 0x2010840008001UL, 0x104100002080UL,
+        0x200c000200402000UL, 0x10102000088010UL, 0x80800080004000UL, 0x100008020040UL,
+        0x8004808020100200UL, 0x408000404040UL, 0x208004008008UL, 0x82000082008000UL,
+        0x84008202004000UL, 0x8200200400840080UL, 0x40101000400080UL, 0x401020401100UL,
+        0x100810001002UL, 0x8000400080804UL, 0x800200a0010100UL, 0x10404000808200UL
+    };
+
+    private static readonly int[] RookShifts =
+    {
+        52, 53, 53, 53, 53, 53, 53, 52,
+        53, 54, 54, 54, 54, 54, 54, 53,
+        53, 54, 54, 54, 54, 54, 54, 53,
+        53, 54, 54, 54, 54, 54, 54, 53,
+        53, 54, 54, 54, 54, 54, 54, 53,
+        53, 54, 54, 54, 54, 54, 54, 53,
+        53, 54, 54, 54, 54, 54, 54, 53,
+        52, 53, 53, 53, 53, 53, 53, 52
+    };
+
+    private static readonly int[] BishopShifts =
+    {
+        58, 59, 59, 59, 59, 59, 59, 58,
+        59, 59, 59, 59, 59, 59, 59, 59,
+        59, 59, 57, 57, 57, 57, 59, 59,
+        59, 59, 57, 55, 55, 57, 59, 59,
+        59, 59, 57, 55, 55, 57, 59, 59,
+        59, 59, 57, 57, 57, 57, 59, 59,
+        59, 59, 59, 59, 59, 59, 59, 59,
+        58, 59, 59, 59, 59, 59, 59, 58
+    };
+
+    private static readonly ulong[][] RookAttacks = new ulong[64][];
+    private static readonly ulong[][] BishopAttacks = new ulong[64][];
+
+    static Mbb()
+    {
+        for (int square = 0; square < 64; square++)
+        {
+            RookAttacks[square] = GenerateRookAttacks(square, RookMagics[square], RookShifts[square]);
+            BishopAttacks[square] = GenerateBishopAttacks(square, BishopMagics[square], BishopShifts[square]);
+        }
+    }
+
+    public static BitBoard GetRookAttacks(Square square, in BitBoard occupancy)
+    {
+        return GetAttackSet(square.AsInt(), in occupancy, RookMagics[square.AsInt()], RookShifts[square.AsInt()], RookAttacks);
+    }
+
+    public static BitBoard GetBishopAttacks(Square square, in BitBoard occupancy)
+    {
+        return GetAttackSet(square.AsInt(), in occupancy, BishopMagics[square.AsInt()], BishopShifts[square.AsInt()], BishopAttacks);
+    }
+
+    public static BitBoard GetQueenAttacks(Square square, in BitBoard occupancy)
+    {
+        return GetRookAttacks(square, in occupancy) | GetBishopAttacks(square, in occupancy);
+    }
+
+    private static ulong[] GenerateRookAttacks(int square, ulong magic, int shift)
+    {
+        int indexBits = 1 << shift;
+        ulong[] attacks = new ulong[indexBits];
+        ulong[] occupancy = new ulong[indexBits];
+
+        int rank = square / 8;
+        int file = square % 8;
+
+        for (int i = 0; i < indexBits; i++)
+        {
+            occupancy[i] = SetRookOccupancy(i, rank, file);
+            ulong index = PEXT(occupancy[i], magic) >> (64 - shift);
+            attacks[index] = CalculateRookAttacks(square, occupancy[i]);
+        }
+
+        return attacks;
+    }
+
+    private static ulong[] GenerateBishopAttacks(int square, ulong magic, int shift)
+    {
+        int indexBits = 1 << shift;
+        ulong[] attacks = new ulong[indexBits];
+        ulong[] occupancy = new ulong[indexBits];
+
+        int rank = square / 8;
+        int file = square % 8;
+
+        for (int i = 0; i < indexBits; i++)
+        {
+            occupancy[i] = SetBishopOccupancy(i, rank, file);
+            ulong index = PEXT(occupancy[i], magic) >> (64 - shift);
+            attacks[index] = CalculateBishopAttacks(square, occupancy[i]);
+        }
+
+        return attacks;
+    }
+
+    private static ulong GetAttackSet(int square, in BitBoard occupancy, ulong magic, int shift, ulong[][] attacks)
+    {
+        ulong index = PEXT(occupancy, magic) >> (64 - shift);
+        int maskedIndex = (int)(index & (ulong)(attacks[square].Length - 1));
+        return attacks[square][maskedIndex];
+    }
+
+    private static ulong SetRookOccupancy(int index, int rank, int file)
+    {
+        ulong occupancy = 0UL;
+
+        for (int i = 0; i < 6; i++)
+        {
+            int bit = (index >> i) & 1;
+            int targetRank = rank;
+            int targetFile = file + i + 1;
+            occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
+        }
+
+        for (int i = 0; i < 6; i++)
+        {
+            int bit = (index >> (i + 6)) & 1;
+            int targetRank = rank;
+            int targetFile = file - i - 1;
+            occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
+        }
+
+        for (int i = 0; i < 5; i++)
+        {
+            int bit = (index >> (i + 12)) & 1;
+            int targetRank = rank + i + 1;
+            int targetFile = file;
+            occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
+        }
+
+        for (int i = 0; i < 5; i++)
+        {
+            int bit = (index >> (i + 17)) & 1;
+            int targetRank = rank - i - 1;
+            int targetFile = file;
+            occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
+        }
+
+        return occupancy;
+    }
+
+    private static ulong SetBishopOccupancy(int index, int rank, int file)
+    {
+        ulong occupancy = 0UL;
+
+        for (int i = 0; i < 4; i++)
+        {
+            int bit = (index >> i) & 1;
+            int targetRank = rank + i + 1;
+            int targetFile = file + i + 1;
+            if (targetRank < 8 && targetFile < 8)
+                occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
+        }
+
+        for (int i = 0; i < 4; i++)
+        {
+            int bit = (index >> (i + 4)) & 1;
+            int targetRank = rank - i - 1;
+            int targetFile = file + i + 1;
+            if (targetRank >= 0 && targetFile < 8)
+                occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
+        }
+
+        for (int i = 0; i < 4; i++)
+        {
+            int bit = (index >> (i + 8)) & 1;
+            int targetRank = rank + i + 1;
+            int targetFile = file - i - 1;
+            if (targetRank < 8 && targetFile >= 0)
+                occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
+        }
+
+        for (int i = 0; i < 4; i++)
+        {
+            int bit = (index >> (i + 12)) & 1;
+            int targetRank = rank - i - 1;
+            int targetFile = file - i - 1;
+            if (targetRank >= 0 && targetFile >= 0)
+                occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
+        }
+
+        return occupancy;
+    }
+
+    private static ulong CalculateRookAttacks(int square, ulong occupancy)
+    {
+        int rank = square / 8;
+        int file = square % 8;
+        ulong attacks = 0UL;
+
+        for (int targetRank = rank + 1; targetRank < 8; targetRank++)
+        {
+            attacks |= 1UL << (targetRank * 8 + file);
+            if ((occupancy & (1UL << (targetRank * 8 + file))) != 0) break;
+        }
+
+        for (int targetRank = rank - 1; targetRank >= 0; targetRank--)
+        {
+            attacks |= 1UL << (targetRank * 8 + file);
+            if ((occupancy & (1UL << (targetRank * 8 + file))) != 0) break;
+        }
+
+        for (int targetFile = file + 1; targetFile < 8; targetFile++)
+        {
+            attacks |= 1UL << (rank * 8 + targetFile);
+            if ((occupancy & (1UL << (rank * 8 + targetFile))) != 0) break;
+        }
+
+        for (int targetFile = file - 1; targetFile >= 0; targetFile--)
+        {
+            attacks |= 1UL << (rank * 8 + targetFile);
+            if ((occupancy & (1UL << (rank * 8 + targetFile))) != 0) break;
+        }
+
+        return attacks;
+    }
+
+    private static ulong CalculateBishopAttacks(int square, ulong occupancy)
+    {
+        int rank = square / 8;
+        int file = square % 8;
+        ulong attacks = 0UL;
+
+        for (int r = rank + 1, f = file + 1; r < 8 && f < 8; r++, f++)
+        {
+            attacks |= 1UL << (r * 8 + f);
+            if ((occupancy & (1UL << (r * 8 + f))) != 0) break;
+        }
+
+        for (int r = rank - 1, f = file + 1; r >= 0 && f < 8; r--, f++)
+        {
+            attacks |= 1UL << (r * 8 + f);
+            if ((occupancy & (1UL << (r * 8 + f))) != 0) break;
+        }
+
+        for (int r = rank + 1, f = file - 1; r < 8 && f >= 0; r++, f--)
+        {
+            attacks |= 1UL << (r * 8 + f);
+            if ((occupancy & (1UL << (r * 8 + f))) != 0) break;
+        }
+
+        for (int r = rank - 1, f = file - 1; r >= 0 && f >= 0; r--, f--)
+        {
+            attacks |= 1UL << (r * 8 + f);
+            if ((occupancy & (1UL << (r * 8 + f))) != 0) break;
+        }
+
+        return attacks;
+    }
+    
+    private static ulong PEXT(in BitBoard source, in ulong mask)
+    {
+        if (Bmi2.X64.IsSupported)
+        {
+            return Bmi2.X64.ParallelBitExtract(source.Value, mask);
+        }
+
+        // Fallback implementation for when the PEXT instruction is not available
+        ulong result = 0;
+        ulong one = 1;
+        for (ulong bb = mask; bb != 0; bb &= (bb - 1))
+        {
+            int index = BitScanForward(bb);
+            if ((source & (one << index)) != 0)
+            {
+                result |= (one << BitScanForward(bb & ~(bb - 1)));
+            }
+        }
+        return result;
+    }
+    
+    private static int BitScanForward(ulong bb)
+    {
+        if (bb == 0) return -1;
+
+        int index = 0;
+        while ((bb & 1) == 0)
+        {
+            bb >>= 1;
+            index++;
+        }
+
+        return index;
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index db77fb6e..51ed2926 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -42,7 +42,7 @@ namespace Rudzoft.ChessLib.Types;
 public static class BitBoards
 {
     internal const ulong One = 0x1ul;
-
+    
     public static readonly BitBoard WhiteArea = new(0x00000000FFFFFFFFUL);
 
     public static readonly BitBoard BlackArea = ~WhiteArea;
@@ -231,8 +231,6 @@ static BitBoards()
             ForwardRanksBB[0][rank] = ~(ForwardRanksBB[1][rank + 1] = ForwardRanksBB[1][rank] | r.BitBoardRank());
         }
 
-        BitBoard bb;
-
         foreach (var player in Player.AllPlayers.AsSpan())
         {
             foreach (var square in Square.All.AsSpan())
@@ -251,7 +249,7 @@ static BitBoards()
 
         Span<PieceTypes> validMagicPieces = stackalloc PieceTypes[] { PieceTypes.Bishop, PieceTypes.Rook };
 
-        bb = AllSquares;
+        var bb = AllSquares;
         // Pseudo attacks for all pieces
         while (bb)
         {
@@ -300,7 +298,7 @@ static BitBoards()
         {
             FileEBB | FileFBB | FileGBB | FileHBB, // King
             FileABB | FileBBB | FileCBB | FileDBB, // Queen
-            FileCBB | FileDBB | FileEBB | FileFBB // Center
+            FileCBB | FileDBB | FileEBB | FileFBB  // Center
         };
     }
 
@@ -513,9 +511,8 @@ public static string Stringify(in BitBoard bb, string title = "")
         const string line   = "+---+---+---+---+---+---+---+---+---+";
         const string bottom = "|   | A | B | C | D | E | F | G | H |";
         Span<char> span = stackalloc char[768];
-        var idx = 0;
-        foreach (var c in line)
-            span[idx++] = c;
+        line.CopyTo(span);
+        var idx = line.Length;
         span[idx++] = '\n';
         foreach (var c in title.Length > 64 ? title.AsSpan()[..64] : title.AsSpan())
             span[idx++] = c;
diff --git a/src/Rudzoft.ChessLib/Types/HashKey.cs b/src/Rudzoft.ChessLib/Types/HashKey.cs
index fe85ae7e..e6ccc47d 100644
--- a/src/Rudzoft.ChessLib/Types/HashKey.cs
+++ b/src/Rudzoft.ChessLib/Types/HashKey.cs
@@ -51,6 +51,8 @@ private HashKey(uint key32)
 
     [field: FieldOffset(4)] public uint UpperKey { get; }
 
+    public bool IsEmpty => Key == 0;
+    
     public static HashKey Empty => new();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.Perft/Options/TTOptions.cs b/src/Rudzoft.Perft/Options/TTOptions.cs
index 243a752d..33483a07 100644
--- a/src/Rudzoft.Perft/Options/TTOptions.cs
+++ b/src/Rudzoft.Perft/Options/TTOptions.cs
@@ -35,5 +35,5 @@ public class TTOptions : IPerftOptions
     public bool Use { get; set; }
 
     [Option('s', "size", Required = false, Default = 32, HelpText = "Set the size of the transposition table in mb")]
-    public int Size { get; set; }
+    public uint Size { get; set; }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Perft/PerftRunner.cs b/src/Rudzoft.Perft/Perft/PerftRunner.cs
index 87353c6c..45ddfbd2 100644
--- a/src/Rudzoft.Perft/Perft/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Perft/PerftRunner.cs
@@ -89,7 +89,7 @@ public PerftRunner(
         _log = log;
         _buildTimeStamp = buildTimeStamp;
         _perft = perft;
-        _perft.BoardPrintCallback ??= s => _log.Information("Board:\n{0}", s);
+        _perft.BoardPrintCallback ??= s => _log.Information("Board:\n{Board}", s);
         _transpositionTable = transpositionTable;
         _resultPool = resultPool;
         _uci = uci;
@@ -134,7 +134,7 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
                 var result = await ComputePerft(cancellationToken).ConfigureAwait(false);
                 Interlocked.Add(ref errors, result.Errors);
                 if (errors != 0)
-                    _log.Error("Parsing failed for Id={0}", position.Id);
+                    _log.Error("Parsing failed for Id={Id}", position.Id);
                 _resultPool.Return(result);
             }
             catch (AggregateException e)
@@ -171,9 +171,10 @@ private async IAsyncEnumerable<PerftPosition> ParseEpd(EpdOptions options)
 
         sw.Stop();
         var elapsedMs = sw.ElapsedMilliseconds;
-        _log.Information("Parsed {0} epd entries in {1} ms", parsedCount, elapsedMs);
+        _log.Information("Parsed {Parsed} epd entries in {Elapsed} ms", parsedCount, elapsedMs);
 
-        var perftPositions = _epdParser.Sets.Select(static set => PerftPositionFactory.Create(set.Id, set.Epd, set.Perft));
+        var perftPositions =
+            _epdParser.Sets.Select(static set => PerftPositionFactory.Create(set.Id, set.Epd, set.Perft));
 
         foreach (var perftPosition in perftPositions)
             yield return perftPosition;
@@ -205,7 +206,7 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
         var result = _resultPool.Get();
         result.Clear();
 
-        var pp = _perft.Positions.Last();
+        var pp = _perft.Positions[^1];
         var baseFileName = SaveResults
             ? Path.Combine(CurrentDirectory.Value, $"{FixFileName(pp.Fen)}[")
             : string.Empty;
@@ -215,24 +216,21 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
         result.Fen = pp.Fen;
         _perft.SetGamePosition(pp);
         _perft.BoardPrintCallback(_perft.GetBoard());
-        _log.Information("Fen         : {0}", pp.Fen);
+        _log.Information("Fen         : {Fen}", pp.Fen);
         _log.Information(Line);
 
-        var sw = new Stopwatch();
-
         foreach (var (depth, expected) in pp.Value)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
-            _log.Information("Depth       : {0}", depth);
-            sw.Restart();
+            _log.Information("Depth       : {Depth}", depth);
 
-            var perftResult = await _perft.DoPerftAsync(depth).ConfigureAwait(false);
-            sw.Stop();
+            var start = Stopwatch.GetTimestamp();
 
-            var elapsedMs = sw.ElapsedMilliseconds;
+            var perftResult = await _perft.DoPerftAsync(depth).ConfigureAwait(false);
+            var elapsedMs = Stopwatch.GetElapsedTime(start);
 
-            ComputeResults(perftResult, depth, expected, elapsedMs, result);
+            ComputeResults(in perftResult, depth, in expected, in elapsedMs, result);
 
             errors += LogResults(result);
 
@@ -242,14 +240,17 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
             await WriteOutput(result, baseFileName, cancellationToken);
         }
 
-        _log.Information("{0} parsing complete. Encountered {1} errors.", _usingEpd ? "EPD" : "FEN", errors);
+        _log.Information("{Info} parsing complete. Encountered {Errors} errors", _usingEpd ? "EPD" : "FEN", errors);
 
         result.Errors = errors;
 
         return result;
     }
 
-    private static async ValueTask WriteOutput(IPerftResult result, string baseFileName, CancellationToken cancellationToken)
+    private static async ValueTask WriteOutput(
+        IPerftResult result,
+        string baseFileName,
+        CancellationToken cancellationToken)
     {
         // ReSharper disable once MethodHasAsyncOverload
         var contents = JsonSerializer.Serialize(result);
@@ -261,13 +262,13 @@ await File.WriteAllTextAsync(
             .ConfigureAwait(false);
     }
 
-    private void ComputeResults(ulong result, int depth, ulong expected, long elapsedMs, IPerftResult results)
+    private void ComputeResults(in ulong result, int depth, in ulong expected, in TimeSpan elapsedMs, IPerftResult results)
     {
         // compute results
         results.Result = result;
         results.Depth = depth;
         // add 1 to avoid potential dbz
-        results.Elapsed = TimeSpan.FromMilliseconds(elapsedMs + 1);
+        results.Elapsed = elapsedMs.Add(TimeSpan.FromMicroseconds(1));
         results.Nps = _uci.Nps(in result, results.Elapsed);
         results.CorrectResult = expected;
         results.Passed = expected == result;
@@ -276,28 +277,28 @@ private void ComputeResults(ulong result, int depth, ulong expected, long elapse
 
     private void LogInfoHeader()
     {
-        _log.Information("ChessLib Perft test program {0} ({1})", Version, _buildTimeStamp.TimeStamp);
-        _log.Information("High timer resolution : {0}", Stopwatch.IsHighResolution);
+        _log.Information("ChessLib Perft test program {Version} ({Time})", Version, _buildTimeStamp.TimeStamp);
+        _log.Information("High timer resolution : {HighRes}", Stopwatch.IsHighResolution);
         _log.Information("Initializing..");
     }
 
     private int LogResults(IPerftResult result)
     {
-        _log.Information("Time passed : {0}", result.Elapsed);
-        _log.Information("Nps         : {0}", result.Nps);
+        _log.Information("Time passed : {Elapsed}", result.Elapsed);
+        _log.Information("Nps         : {Nps}", result.Nps);
         if (_usingEpd)
         {
-            _log.Information("Result      : {0} - should be {1}", result.Result, result.CorrectResult);
+            _log.Information("Result      : {Result} - should be {Expected}", result.Result, result.CorrectResult);
             if (result.Result != result.CorrectResult)
             {
                 var difference = (long)(result.CorrectResult - result.Result);
-                _log.Information("Difference  : {0}", Math.Abs(difference));
+                _log.Information("Difference  : {Diff}", Math.Abs(difference));
             }
         }
         else
-            _log.Information("Result      : {0}", result.Result);
+            _log.Information("Result      : {Result}", result.Result);
 
-        _log.Information("TT hits     : {0}", _transpositionTable.Hits);
+        _log.Information("TT hits     : {Hits}", _transpositionTable.Hits);
 
         var error = 0;
 
@@ -308,7 +309,7 @@ private int LogResults(IPerftResult result)
             _log.Information("Move count matches!");
         else
         {
-            _log.Error("Failed for position: {0}", _perft.Game.Pos.GenerateFen());
+            _log.Error("Failed for position: {Fen}", _perft.Game.Pos.GenerateFen());
             error = 1;
         }
 

From 20ef9faa6c2c8b05fb817476e5824b42e9382a0c Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Tue, 15 Aug 2023 23:07:04 +0200
Subject: [PATCH 047/119] Cleanup

---
 .../Transposition/TranspositionTable.cs       | 247 +-----------------
 1 file changed, 1 insertion(+), 246 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index 08fb2ff1..b93a32eb 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -189,249 +189,4 @@ public bool Probe(in HashKey posKey, ref uint ttePos, out TTEntry entry)
     /// entries from the current search.
     /// </summary>
     public void NewSearch() => _generation++;
-}
-
-//
-// public sealed class TranspositionTable : ITranspositionTable
-// {
-//     private static readonly int ClusterSize = Unsafe.SizeOf<TranspositionTableEntry>() * 4;
-//
-//     private Cluster[] _table;
-//     private ulong _elements;
-//     private int _fullnessElements;
-//     private sbyte _generation;
-//
-//     public TranspositionTable(IOptions<TranspositionTableConfiguration> options)
-//     {
-//         _table = Array.Empty<Cluster>();
-//         Size = options.Value.DefaultSize;
-//         SetSize(Size);
-//     }
-//
-//     /// <summary>
-//     /// Number of table hits
-//     /// </summary>
-//     public ulong Hits { get; private set; }
-//
-//     public int Size { get; private set; }
-//
-//     /// <summary>
-//     /// Increases the generation of the table by one
-//     /// </summary>
-//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-//     public void NewSearch() => ++_generation;
-//
-//     /// <summary>
-//     /// Sets the size of the table in Mb
-//     /// </summary>
-//     /// <param name="mbSize">The size to set it to</param>
-//     /// <returns>The number of clusters in the table</returns>
-//     public ulong SetSize(int mbSize)
-//     {
-//         switch (mbSize)
-//         {
-//             case < 0:
-//                 throw new TranspositionTableFailure($"Unable to create table with negative size: {mbSize}");
-//             case 0:
-//                 return 0;
-//         }
-//
-//         Size = mbSize;
-//         var size = (int)(((ulong)mbSize << 20) / (ulong)ClusterSize);
-//         var resize = false;
-//         var currentSize = _table.Length;
-//         if (_table.Length == 0)
-//         {
-//             _table = new Cluster[size];
-//             PopulateTable(0, size);
-//         }
-//         else if (currentSize != size)
-//             resize = true;
-//
-//         if (resize)
-//         {
-//             Array.Resize(ref _table, size);
-//             if (currentSize < size)
-//                 PopulateTable(currentSize, size);
-//         }
-//
-//         _elements = (ulong)size;
-//         _generation = 1;
-//         _fullnessElements = Math.Min(size, 250);
-//
-//         return (ulong)(size * ClusterSize);
-//     }
-//
-//     /// <summary>
-//     /// Finds a cluster in the table based on a position key
-//     /// </summary>
-//     /// <param name="key">The position key</param>
-//     /// <returns>The cluster of the keys position in the table</returns>
-//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-//     public ref Cluster FindCluster(in HashKey key)
-//     {
-//         var idx = (int)(key.LowerKey & (_elements - 1));
-//         return ref _table[idx];
-//     }
-//
-//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-//     public void Refresh(ref TranspositionTableEntry tte) => tte.Generation = _generation;
-//
-//     /// <summary>
-//     /// Probes the transposition table for a entry that matches the position key.
-//     /// </summary>
-//     /// <param name="key">The position key</param>
-//     /// <param name="e">Target entry</param>
-//     /// <returns>(true, entry) if one was found, (false, empty) if not found</returns>
-//     public bool Probe(in HashKey key, ref TranspositionTableEntry e)
-//     {
-//         ref var ttc = ref FindCluster(in key);
-//         var g = _generation;
-//
-//         // Probing the Table will automatically update the generation of the entry in case the
-//         // probing retrieves an element.
-//
-//         var set = false;
-//
-//         for (var i = 0; i < ClusterSize; ++i)
-//         {
-//             ref var entry = ref ttc[i];
-//             if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
-//                 continue;
-//
-//             entry.Generation = g;
-//             e = entry;
-//             set = true;
-//             Hits++;
-//             break;
-//         }
-//         
-//         if (!set)
-//             e = default;
-//
-//         return set;
-//     }
-//
-//     public void Save(in HashKey key, in TranspositionTableEntry e)
-//     {
-//         ref var ttc = ref FindCluster(in key);
-//         var entryIndex = 0;
-//
-//         for (var i = 0; i < ClusterSize; ++i)
-//         {
-//             ref var entry = ref ttc[i];
-//             if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
-//                 continue;
-//
-//             entryIndex = i;
-//             break;
-//         }
-//         
-//         ttc[entryIndex] = e;
-//     }
-//
-//     /// <summary>
-//     /// Probes the table for the first cluster index which matches the position key
-//     /// </summary>
-//     /// <param name="key">The position key</param>
-//     /// <returns>The cluster entry</returns>
-//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-//     public ref TranspositionTableEntry ProbeFirst(in HashKey key) => ref FindCluster(key)[0];
-//
-//     /// <summary>
-//     /// Stores a move in the transposition table. It will automatically detect the best cluster
-//     /// location to store it in. If a similar move already is present, a simple check if done to
-//     /// make sure it actually is an improvement of the previous move.
-//     /// </summary>
-//     /// <param name="key">The position key</param>
-//     /// <param name="value">The value of the move</param>
-//     /// <param name="type">The bound type, e.i. did it exceed alpha or beta</param>
-//     /// <param name="depth">The depth of the move</param>
-//     /// <param name="move">The move it self</param>
-//     /// <param name="statValue">The static value of the move</param>
-//     public void Store(in HashKey key, int value, Bound type, sbyte depth, Move move, int statValue)
-//     {
-//         ref var ttc = ref FindCluster(in key);
-//
-//         var clusterIndex = 0;
-//         var found = false;
-//
-//         for (var i = 0; i < ClusterSize; ++i)
-//         {
-//             ref var entry = ref ttc[i];
-//             if (entry.Key32 != uint.MinValue && entry.Key32 != key.UpperKey)
-//                 continue;
-//
-//             clusterIndex = i;
-//             found = true;
-//             break;
-//         }
-//
-//         if (!found)
-//         {
-//             var index = 0;
-//             ref var candidate = ref ttc[index];
-//             var g = _generation;
-//
-//             for (var i = 0; i < ClusterSize; ++i)
-//             {
-//                 ref var entry = ref ttc[i];
-//                 var (cc1, cc2, cc3, cc4) = (candidate.Generation == g, entry.Generation == g, entry.Type == Bound.Exact, entry.Depth <= candidate.Depth);
-//                 if ((cc1 && cc4) || (!(cc2 || cc3) && (cc4 || cc1)))
-//                 {
-//                     index = i;
-//                     candidate = entry;
-//                 }
-//             }
-//
-//             clusterIndex = index;
-//         }
-//
-//         var e = new TranspositionTableEntry(key.UpperKey, move, depth, _generation, value, statValue, type);
-//
-//         ref var target = ref ttc[clusterIndex];
-//         target.Save(in e);
-//     }
-//
-//     /// <summary>
-//     /// Get the approximation full % of the table // todo : fix
-//     /// </summary>
-//     /// <returns>The % as integer value</returns>
-//     public int Fullness()
-//     {
-//         if (_generation == 1)
-//             return 0;
-//         var gen = _generation;
-//         var sum = 0;
-//
-//         for (var i = 0; i < _fullnessElements; ++i)
-//         {
-//             for (var j = 0; j < ClusterSize; ++j)
-//             {
-//                 ref var entry = ref _table[i][j];
-//                 if (entry.Generation == gen)
-//                     sum++;
-//             }
-//         }
-//
-//         return sum * 250 / _fullnessElements;
-//     }
-//
-//     /// <summary>
-//     /// Clears the current table
-//     /// </summary>
-//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-//     public void Clear()
-//     {
-//         ref var tableSpace = ref MemoryMarshal.GetArrayDataReference(_table);
-//         for (var i = 0; i < _table.Length; ++i)
-//             Unsafe.Add(ref tableSpace, i).Reset();
-//     }
-//
-//     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-//     private void PopulateTable(int from, int to)
-//     {
-//         for (var i = from; i < to; ++i)
-//             _table[i] = new Cluster();
-//     }
-// }
\ No newline at end of file
+}
\ No newline at end of file

From 7044184cc17a4110ee0438c96a5a6c384e75b15d Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Tue, 15 Aug 2023 23:31:50 +0200
Subject: [PATCH 048/119] Simplified

---
 .editorconfig                                 | 226 ++++++++++++++++++
 Directory.Build.props                         |  10 +
 Rudzoft.ChessLib.sln                          |   3 +
 src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs  |   3 +-
 .../CharConvertBenchmark.cs                   |   4 +-
 .../IsNumericBenchmark.cs                     |   2 -
 .../IterateBenchmark.cs                       |   4 +-
 .../KeyBenchmarks.cs                          |   3 +-
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |   3 -
 .../Rudzoft.ChessLib.Benchmark.csproj         |  38 ++-
 .../ShiftFuncBench.cs                         |   2 -
 .../SquareIterateBenchmark.cs                 |   3 +-
 .../StringBenchmarks.cs                       |   4 +-
 .../Rudzoft.ChessLib.PGN.Test.csproj          |  17 +-
 .../Rudzoft.ChessLib.PGN.csproj               |   5 +-
 .../IPerft.cs                                 |   4 -
 .../PerftPosition.cs                          |   2 -
 .../Rudzoft.ChessLib.Perft.Interfaces.csproj  |  12 +-
 src/Rudzoft.ChessLib.Perft/Perft.cs           |   4 -
 .../PerftPositionFactory.cs                   |   1 -
 src/Rudzoft.ChessLib.Perft/PerftTable.cs      |   1 -
 .../Rudzoft.ChessLib.Perft.csproj             |  17 +-
 .../BitboardTests/BitboardTests.cs            |   2 +-
 .../BoardTests/BoardTests.cs                  |   1 -
 .../BookTests/BookFixture.cs                  |   2 -
 .../BookTests/PolyglotTests.cs                |   3 -
 .../CastleTests/BasicCastleTests.cs           |   1 -
 .../DataTests/DataTests.cs                    |   1 -
 .../DataTests/RecordHashTests.cs              |   4 +-
 .../DataTests/StringExtensionsTests.cs        |   2 -
 .../EvaluationTests/KpkBitBaseTests.cs        |   1 -
 .../FENTests/FenTests.cs                      |   1 -
 .../FenceTests/FenceTests.cs                  |   1 -
 .../FileTests/FileEdgeDistanceTests.cs        |   1 +
 .../FileTests/FileTests.cs                    |   1 +
 .../GameplayTests/FoolsCheckMateTests.cs      |   2 -
 .../MoveTests/MoveGen_49.cs                   |   1 -
 .../MoveTests/MoveGeneratorTests.cs           |   1 -
 .../MoveTests/MoveTests.cs                    |   2 -
 .../NotationTests/FanTests.cs                 |   1 -
 .../NotationTests/IccfTests.cs                |   1 -
 .../NotationTests/RanTests.cs                 |   1 -
 .../NotationTests/SanTests.cs                 |   2 -
 .../PerftTests/PerftVerify.cs                 |   3 +-
 .../PiecesTests/PawnDoubleAttackTests.cs      |   1 -
 .../PiecesTests/PieceAttacksKingTests.cs      |   1 -
 .../PiecesTests/PieceAttacksRookTests.cs      |   1 -
 .../PiecesTests/RegularMobilityFixture.cs     |   1 -
 .../PiecesTests/SliderMobilityTests.cs        |   1 -
 .../PositionTests/EnPassantFenTests.cs        |   1 -
 .../PositionTests/PositionTests.cs            |   1 -
 .../PositionTests/ValidationTests.cs          |   1 -
 .../ProtocolTests/UciTests.cs                 |   1 -
 .../Rudzoft.ChessLib.Test.csproj              |  76 +++---
 .../ScoreTests/ScoreTests.cs                  |   1 -
 .../SizesTests/BaseTypesSizeTests.cs          |   1 +
 .../SquareTests/SquareRangeTests.cs           |   3 +-
 .../SquareTests/SquareTests.cs                |   1 +
 .../ZobristTests/ZobristHashTests.cs          |   3 +-
 .../Rudzoft.ChessLib.WebApi.csproj            |  26 +-
 .../Services/MoveGeneratorService.cs          |   3 +-
 src/Rudzoft.ChessLib/Blockage.cs              |   2 +-
 src/Rudzoft.ChessLib/Board.cs                 |   3 -
 src/Rudzoft.ChessLib/Cuckoo.cs                |   3 -
 src/Rudzoft.ChessLib/Enums/GameEndTypes.cs    |   2 -
 src/Rudzoft.ChessLib/Enums/GameResults.cs     |   2 -
 src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs |   1 -
 .../Enums/MoveGenerationTypes.cs              |   1 -
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |   3 +-
 .../Exceptions/InvalidFenException.cs         |   1 -
 .../Exceptions/InvalidMove.cs                 |   1 -
 .../Exceptions/InvalidSquare.cs               |   1 -
 .../Exceptions/TranspositionTableFailure.cs   |   1 -
 .../Extensions/ArrayExtensions.cs             |   1 -
 .../ChessLibServiceCollectionExtensions.cs    |   2 -
 src/Rudzoft.ChessLib/Extensions/Maths.cs      |   1 -
 .../Extensions/SpanExtensions.cs              |   1 -
 .../Extensions/StringExtensions.cs            |   3 -
 .../Factories/ServiceFactory.cs               |   2 -
 src/Rudzoft.ChessLib/Fen/Fen.cs               |   1 -
 src/Rudzoft.ChessLib/Fen/FenData.cs           |   2 -
 src/Rudzoft.ChessLib/Fen/IFenData.cs          |   2 -
 src/Rudzoft.ChessLib/Game.cs                  |   2 -
 src/Rudzoft.ChessLib/Hash/IRKiss.cs           |   2 -
 src/Rudzoft.ChessLib/Hash/IZobrist.cs         |   1 +
 src/Rudzoft.ChessLib/Hash/RKiss.cs            |   2 -
 .../Hash/Tables/Transposition/Bound.cs        |   2 -
 .../Transposition/TranspositionTable.cs       |   1 -
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          |   2 +-
 src/Rudzoft.ChessLib/IBoard.cs                |   2 -
 src/Rudzoft.ChessLib/IGame.cs                 |   2 -
 src/Rudzoft.ChessLib/IPosition.cs             |   2 -
 .../MoveGeneration/IMoveList.cs               |   2 -
 .../MoveGeneration/MoveGenerator.cs           |   1 -
 .../MoveGeneration/MoveList.cs                |   3 -
 .../Notation/INotationToMove.cs               |   2 -
 .../Notation/NotationToMove.cs                |   3 -
 .../Notation/Notations/CoordinateNotation.cs  |   1 -
 .../Notation/Notations/FanNotation.cs         |   1 -
 .../Notation/Notations/IccfNotation.cs        |   1 -
 .../Notation/Notations/LanNotation.cs         |   1 -
 .../Notation/Notations/Notation.cs            |   1 -
 .../Notation/Notations/RanNotation.cs         |   1 -
 .../Notation/Notations/SanNotation.cs         |   2 -
 .../Notation/Notations/SmithNotation.cs       |   1 -
 .../Polyglot/IPolyglotBook.cs                 |   1 -
 .../LittleEndianBinaryStreamReader.cs         |   3 -
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |   2 -
 .../Polyglot/PolyglotBookZobrist.cs           |   1 +
 src/Rudzoft.ChessLib/Position.cs              |   3 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs      |   1 -
 .../Protocol/UCI/HiResTimer.cs                |   3 -
 .../Protocol/UCI/IInputOutput.cs              |   3 -
 src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs  |   2 -
 .../Protocol/UCI/ISearchParameters.cs         |   1 -
 src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs     |   2 -
 .../Protocol/UCI/InputOutput.cs               |   5 -
 .../Protocol/UCI/MovesToGoModel.cs            |   1 -
 src/Rudzoft.ChessLib/Protocol/UCI/Option.cs   |   1 -
 .../Protocol/UCI/OptionComparer.cs            |   2 -
 .../Protocol/UCI/SearchParameters.cs          |   2 -
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |   5 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  | 146 ++++++-----
 src/Rudzoft.ChessLib/State.cs                 |   2 -
 src/Rudzoft.ChessLib/Tables/HashTable.cs      |   2 -
 src/Rudzoft.ChessLib/Tables/IHashTable.cs     |   1 -
 .../Tables/KillerMoves/IKillerMoves.cs        |   1 -
 .../Tables/Perft/PerftTable.cs                |   1 -
 src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs     |   4 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |   4 -
 src/Rudzoft.ChessLib/Types/BitBoards.cs       |   4 -
 src/Rudzoft.ChessLib/Types/CastleRight.cs     |   2 -
 src/Rudzoft.ChessLib/Types/Depth.cs           |   1 -
 src/Rudzoft.ChessLib/Types/File.cs            |   1 -
 src/Rudzoft.ChessLib/Types/HashKey.cs         |   1 -
 src/Rudzoft.ChessLib/Types/IPieceSquare.cs    |   2 -
 src/Rudzoft.ChessLib/Types/MagicBB.cs         |  61 +++--
 src/Rudzoft.ChessLib/Types/Move.cs            |   1 -
 src/Rudzoft.ChessLib/Types/Piece.cs           |   1 -
 .../Types/PieceSquareEventArgs.cs             |   1 -
 src/Rudzoft.ChessLib/Types/Player.cs          |   1 -
 src/Rudzoft.ChessLib/Types/Rank.cs            |   1 -
 src/Rudzoft.ChessLib/Types/RootMove.cs        |   4 +-
 src/Rudzoft.ChessLib/Types/Score.cs           |   1 -
 src/Rudzoft.ChessLib/Types/Square.cs          |   2 -
 src/Rudzoft.ChessLib/Types/StateStack.cs      |   3 -
 src/Rudzoft.ChessLib/Types/ValMove.cs         |   1 -
 src/Rudzoft.ChessLib/Types/Value.cs           |   2 -
 src/Rudzoft.ChessLib/Types/Values.cs          |   1 -
 .../Validation/PositionValidator.cs           |   3 -
 .../Environment/FrameworkEnvironment.cs       |   1 -
 .../Extensions/ObjectExtensions.cs            |   4 -
 .../Rudzoft.Perft.Framework.csproj            |   3 -
 src/Rudzoft.Perft/Host/PerftHost.cs           |   2 -
 src/Rudzoft.Perft/Options/EpdOptions.cs       |   1 -
 src/Rudzoft.Perft/Options/Fens.cs             |   1 -
 src/Rudzoft.Perft/Options/IOptionsFactory.cs  |   2 -
 src/Rudzoft.Perft/Options/OptionsFactory.cs   |   1 -
 src/Rudzoft.Perft/Parsers/EpdParser.cs        |   4 -
 src/Rudzoft.Perft/Parsers/EpdSet.cs           |   1 -
 src/Rudzoft.Perft/Parsers/IEpdParser.cs       |   3 -
 src/Rudzoft.Perft/Parsers/IEpdSet.cs          |   1 -
 src/Rudzoft.Perft/Perft/IPerftRunner.cs       |   2 -
 src/Rudzoft.Perft/Perft/PerftRunner.cs        |   5 -
 src/Rudzoft.Perft/Program.cs                  |   1 -
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        | 101 ++++----
 src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs |   1 -
 167 files changed, 497 insertions(+), 530 deletions(-)
 create mode 100644 .editorconfig
 create mode 100644 Directory.Build.props

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..8fa36ec8
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,226 @@
+[*]
+end_of_line = crlf
+indent_size = 4
+indent_style = space
+insert_final_newline = false
+max_line_length = 120
+tab_width = 4
+trim_trailing_whitespace = false
+ij_continuation_indent_size = 8
+ij_formatter_off_tag = @formatter:off
+ij_formatter_on_tag = @formatter:on
+ij_formatter_tags_enabled = true
+ij_smart_tabs = false
+ij_visual_guides = 
+ij_wrap_on_typing = false
+charset = utf-8-bom
+
+# Microsoft .NET properties
+csharp_new_line_before_members_in_object_initializers = false
+csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
+csharp_style_prefer_utf8_string_literals = true:suggestion
+csharp_style_var_elsewhere = true:warning
+csharp_style_var_for_built_in_types = true:warning
+csharp_style_var_when_type_is_apparent = true:warning
+dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True
+dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field
+dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef
+dotnet_naming_rule.unity_serialized_field_rule.severity = warning
+dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style
+dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols
+dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
+dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = *
+dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds = 
+dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field
+dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance
+dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none
+dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
+
+# ReSharper properties
+resharper_accessor_owner_body = accessors_with_expression_body
+resharper_autodetect_indent_settings = true
+resharper_braces_for_dowhile = required_for_multiline
+resharper_braces_for_fixed = required_for_multiline
+resharper_braces_for_for = required_for_multiline
+resharper_braces_for_foreach = required_for_multiline
+resharper_braces_for_ifelse = required_for_multiline
+resharper_braces_for_lock = required_for_multiline
+resharper_braces_for_using = required_for_multiline
+resharper_braces_for_while = required_for_multiline
+resharper_csharp_empty_block_style = together_same_line
+resharper_csharp_wrap_after_declaration_lpar = true
+resharper_csharp_wrap_multiple_declaration_style = chop_always
+resharper_enforce_line_ending_style = true
+resharper_force_attribute_style = join
+resharper_formatter_off_tag = @formatter:off
+resharper_formatter_on_tag = @formatter:on
+resharper_formatter_tags_enabled = true
+resharper_keep_existing_attribute_arrangement = true
+resharper_keep_existing_enum_arrangement = true
+resharper_local_function_body = expression_body
+resharper_max_enum_members_on_line = 8
+resharper_method_or_operator_body = expression_body
+resharper_object_creation_when_type_not_evident = target_typed
+resharper_place_accessorholder_attribute_on_same_line = false
+resharper_place_accessor_attribute_on_same_line = false
+resharper_place_field_attribute_on_same_line = false
+resharper_place_record_field_attribute_on_same_line = true
+resharper_prefer_explicit_discard_declaration = true
+resharper_show_autodetect_configure_formatting_tip = false
+resharper_use_indent_from_vs = false
+resharper_wrap_before_eq = true
+resharper_wrap_before_extends_colon = true
+resharper_wrap_enum_declaration = wrap_if_long
+resharper_wrap_lines = true
+resharper_xmldoc_space_after_last_attribute = true
+
+# ReSharper inspection severities
+resharper_arrange_redundant_parentheses_highlighting = hint
+resharper_arrange_this_qualifier_highlighting = hint
+resharper_arrange_type_member_modifiers_highlighting = hint
+resharper_arrange_type_modifiers_highlighting = hint
+resharper_built_in_type_reference_style_for_member_access_highlighting = hint
+resharper_built_in_type_reference_style_highlighting = hint
+resharper_redundant_base_qualifier_highlighting = warning
+resharper_remove_redundant_braces_highlighting = hint
+resharper_web_config_module_not_resolved_highlighting = warning
+resharper_web_config_type_not_resolved_highlighting = warning
+resharper_web_config_wrong_module_highlighting = warning
+
+[*.properties]
+ij_properties_align_group_field_declarations = false
+ij_properties_keep_blank_lines = false
+ij_properties_key_value_delimiter = equals
+ij_properties_spaces_around_key_value_delimiter = false
+
+[.editorconfig]
+ij_editorconfig_align_group_field_declarations = false
+ij_editorconfig_space_after_colon = false
+ij_editorconfig_space_after_comma = true
+ij_editorconfig_space_before_colon = false
+ij_editorconfig_space_before_comma = false
+ij_editorconfig_spaces_around_assignment_operators = true
+
+[{*.ant,*.appxmanifest,*.axml,*.cscfg,*.csdef,*.disco,*.dotsettings,*.filelayout,*.fxml,*.jhm,*.jnlp,*.jrxml,*.manifest,*.myapp,*.nuspec,*.rng,*.stylecop,*.svcmap,*.tld,*.wadcfgx,*.webref,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,StyleCop.Cache}]
+ij_xml_align_attributes = true
+ij_xml_align_text = false
+ij_xml_attribute_wrap = normal
+ij_xml_block_comment_add_space = false
+ij_xml_block_comment_at_first_column = true
+ij_xml_keep_blank_lines = 2
+ij_xml_keep_indents_on_empty_lines = false
+ij_xml_keep_line_breaks = true
+ij_xml_keep_line_breaks_in_text = true
+ij_xml_keep_whitespaces = false
+ij_xml_keep_whitespaces_around_cdata = preserve
+ij_xml_keep_whitespaces_inside_cdata = false
+ij_xml_line_comment_at_first_column = true
+ij_xml_space_after_tag_name = false
+ij_xml_space_around_equals_in_attribute = false
+ij_xml_space_inside_empty_tag = false
+ij_xml_text_wrap = normal
+ij_xml_use_custom_settings = false
+
+[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}]
+indent_size = 2
+ij_json_array_wrapping = split_into_lines
+ij_json_keep_blank_lines_in_code = 0
+ij_json_keep_indents_on_empty_lines = false
+ij_json_keep_line_breaks = true
+ij_json_keep_trailing_comma = false
+ij_json_object_wrapping = split_into_lines
+ij_json_property_alignment = do_not_align
+ij_json_space_after_colon = true
+ij_json_space_after_comma = true
+ij_json_space_before_colon = false
+ij_json_space_before_comma = false
+ij_json_spaces_within_braces = false
+ij_json_spaces_within_brackets = false
+ij_json_wrap_long_lines = false
+indent_style = space
+
+[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]
+ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
+ij_html_align_attributes = true
+ij_html_align_text = false
+ij_html_attribute_wrap = normal
+ij_html_block_comment_add_space = false
+ij_html_block_comment_at_first_column = true
+ij_html_do_not_align_children_of_min_lines = 0
+ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
+ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
+ij_html_enforce_quotes = false
+ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
+ij_html_keep_blank_lines = 2
+ij_html_keep_indents_on_empty_lines = false
+ij_html_keep_line_breaks = true
+ij_html_keep_line_breaks_in_text = true
+ij_html_keep_whitespaces = false
+ij_html_keep_whitespaces_inside = span,pre,textarea
+ij_html_line_comment_at_first_column = true
+ij_html_new_line_after_last_attribute = never
+ij_html_new_line_before_first_attribute = never
+ij_html_quote_style = double
+ij_html_remove_new_line_before_tags = br
+ij_html_space_after_tag_name = false
+ij_html_space_around_equality_in_attribute = false
+ij_html_space_inside_empty_tag = false
+ij_html_text_wrap = normal
+
+[{*.markdown,*.md}]
+ij_markdown_force_one_space_after_blockquote_symbol = true
+ij_markdown_force_one_space_after_header_symbol = true
+ij_markdown_force_one_space_after_list_bullet = true
+ij_markdown_force_one_space_between_words = true
+ij_markdown_format_tables = true
+ij_markdown_insert_quote_arrows_on_wrap = true
+ij_markdown_keep_indents_on_empty_lines = false
+ij_markdown_keep_line_breaks_inside_text_blocks = true
+ij_markdown_max_lines_around_block_elements = 1
+ij_markdown_max_lines_around_header = 1
+ij_markdown_max_lines_between_paragraphs = 1
+ij_markdown_min_lines_around_block_elements = 1
+ij_markdown_min_lines_around_header = 1
+ij_markdown_min_lines_between_paragraphs = 1
+ij_markdown_wrap_text_if_long = true
+ij_markdown_wrap_text_inside_blockquotes = true
+
+[{*.pb,*.textproto}]
+indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_prototext_keep_blank_lines_in_code = 2
+ij_prototext_keep_indents_on_empty_lines = false
+ij_prototext_keep_line_breaks = true
+ij_prototext_space_after_colon = true
+ij_prototext_space_after_comma = true
+ij_prototext_space_before_colon = false
+ij_prototext_space_before_comma = false
+ij_prototext_spaces_within_braces = true
+ij_prototext_spaces_within_brackets = false
+
+[{*.yaml,*.yml}]
+indent_size = 2
+ij_yaml_align_values_properties = do_not_align
+ij_yaml_autoinsert_sequence_marker = true
+ij_yaml_block_mapping_on_new_line = false
+ij_yaml_indent_sequence_value = true
+ij_yaml_keep_indents_on_empty_lines = false
+ij_yaml_keep_line_breaks = true
+ij_yaml_sequence_on_new_line = false
+ij_yaml_space_before_colon = false
+ij_yaml_spaces_within_braces = true
+ij_yaml_spaces_within_brackets = true
+
+[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cppm,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}]
+indent_style = space
+indent_size = 4
+tab_width = 4
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..f9a7a7a6
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,10 @@
+<Project>
+    <PropertyGroup>
+        <TargetFramework>net7.0</TargetFramework>
+        <LangVersion>latest</LangVersion>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>warnings</Nullable>
+        <IsPackable>false</IsPackable>
+        <TieredPGO>true</TieredPGO>
+    </PropertyGroup>
+</Project>
diff --git a/Rudzoft.ChessLib.sln b/Rudzoft.ChessLib.sln
index 403cdb89..bb310e51 100644
--- a/Rudzoft.ChessLib.sln
+++ b/Rudzoft.ChessLib.sln
@@ -32,6 +32,9 @@ EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solutionitems", "solutionitems", "{5B4D0B75-CE2C-45E2-92D3-987704C20001}"
 	ProjectSection(SolutionItems) = preProject
 		README.md = README.md
+		Directory.Build.props = Directory.Build.props
+		global.json = global.json
+		.editorconfig = .editorconfig
 	EndProjectSection
 EndProject
 Global
diff --git a/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs b/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
index fd5d1041..bb30d8e5 100644
--- a/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using System.Numerics;
+using System.Numerics;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
diff --git a/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
index efae835a..d4166363 100644
--- a/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
@@ -1,6 +1,4 @@
-using System.Collections.Generic;
-using System.Linq;
-using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
index d5a60918..34fc8fd4 100644
--- a/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
@@ -1,6 +1,4 @@
 using Rudzoft.ChessLib.Extensions;
-using System;
-using System.Linq;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
index 3e1475d6..84cb795f 100644
--- a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Runtime.CompilerServices;
-using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs b/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
index b4386df8..67405012 100644
--- a/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Intrinsics;
 using System.Runtime.Intrinsics.X86;
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index b03b1b87..8dc641bc 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -24,9 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
 using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Hash;
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 70dc2bf3..dbc3a9c6 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -1,27 +1,25 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <TargetFramework>net7.0</TargetFramework>
-    <TieredPGO>true</TieredPGO>
-    <Platforms>AnyCPU</Platforms>
-    <LangVersion>default</LangVersion>
-    <Configurations>Debug;Release</Configurations>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-  </PropertyGroup>
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <Platforms>AnyCPU</Platforms>
+        <LangVersion>default</LangVersion>
+        <Configurations>Debug;Release</Configurations>
+        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    </PropertyGroup>
 
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <PlatformTarget>x64</PlatformTarget>
-  </PropertyGroup>
+    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+        <PlatformTarget>x64</PlatformTarget>
+    </PropertyGroup>
 
-  <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
-    <PackageReference Include="System.Runtime" Version="4.3.1" />
-  </ItemGroup>
+    <ItemGroup>
+        <PackageReference Include="BenchmarkDotNet" Version="0.13.5"/>
+        <PackageReference Include="System.Runtime" Version="4.3.1"/>
+    </ItemGroup>
 
-  <ItemGroup>
-    <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
-    <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
-  </ItemGroup>
+    <ItemGroup>
+        <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj"/>
+        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
+    </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
index 4140c5e3..f0681987 100644
--- a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
@@ -1,5 +1,3 @@
-using System;
-using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
index cf2d2f15..f5bd38a3 100644
--- a/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
index 0c713aa2..b211c7e3 100644
--- a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
-using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
 
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index 03fccd68..a95a6f34 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -1,19 +1,14 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
     <PropertyGroup>
-        <TargetFramework>net7.0</TargetFramework>
-        <TieredPGO>true</TieredPGO>
-        <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
-
-        <IsPackable>false</IsPackable>
         <IsTestProject>true</IsTestProject>
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
-        <PackageReference Include="xunit" Version="2.4.2" />
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"/>
+        <PackageReference Include="xunit" Version="2.4.2"/>
         <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
@@ -25,12 +20,12 @@
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\Rudzoft.ChessLib.PGN\Rudzoft.ChessLib.PGN.csproj" />
-      <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
+        <ProjectReference Include="..\Rudzoft.ChessLib.PGN\Rudzoft.ChessLib.PGN.csproj"/>
+        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
     </ItemGroup>
 
     <ItemGroup>
-      <Content Include="samples\sample.pgn" CopyToOutputDirectory="PreserveNewest" />
+        <Content Include="samples\sample.pgn" CopyToOutputDirectory="PreserveNewest"/>
     </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
index 5a91149e..8c40511a 100644
--- a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
+++ b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
@@ -1,14 +1,11 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
     <PropertyGroup>
-        <TargetFramework>net7.0</TargetFramework>
-        <TieredPGO>true</TieredPGO>
-        <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
     </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
index d8d9e006..d4629eb6 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
@@ -24,10 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
 namespace Rudzoft.ChessLib.Perft.Interfaces;
 
 public interface IPerft
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs b/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
index 32a32c98..cf58154d 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
-
 namespace Rudzoft.ChessLib.Perft.Interfaces;
 
 public record struct PerftPositionValue(int Depth, ulong MoveCount);
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
index 9ed3c4f8..38eefd3f 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
@@ -1,13 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
-  <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
-    <TieredPGO>true</TieredPGO>
-    <LangVersion>default</LangVersion>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
-  </ItemGroup>
+    <ItemGroup>
+        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
+    </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/Rudzoft.ChessLib.Perft/Perft.cs
index aaddb48d..2e1a8e5b 100644
--- a/src/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/Rudzoft.ChessLib.Perft/Perft.cs
@@ -24,11 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Perft.Interfaces;
diff --git a/src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs b/src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs
index 4d2faaa2..734460e2 100644
--- a/src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs
+++ b/src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Perft.Interfaces;
 
diff --git a/src/Rudzoft.ChessLib.Perft/PerftTable.cs b/src/Rudzoft.ChessLib.Perft/PerftTable.cs
index 529628c8..6ed3c625 100644
--- a/src/Rudzoft.ChessLib.Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib.Perft/PerftTable.cs
@@ -25,7 +25,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Rudzoft.ChessLib.Types;
-using System;
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Perft;
diff --git a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
index fe983558..f049d234 100644
--- a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
+++ b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
@@ -1,15 +1,12 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
-  <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
-    <TieredPGO>true</TieredPGO>
-    <LangVersion>default</LangVersion>
-    <Nullable>enable</Nullable>
-  </PropertyGroup>
+    <PropertyGroup>
+        <Nullable>enable</Nullable>
+    </PropertyGroup>
 
-  <ItemGroup>
-    <ProjectReference Include="..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
-    <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
-  </ItemGroup>
+    <ItemGroup>
+        <ProjectReference Include="..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj"/>
+        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
+    </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
index 2271528e..b15cc0e2 100644
--- a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
@@ -24,9 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Test.BitboardTests;
 
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 4d569cab..13ee6324 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs b/src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs
index b1a2d4f0..7a110ab8 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.IO;
-
 namespace Rudzoft.ChessLib.Test.BookTests;
 
 // ReSharper disable once ClassNeverInstantiated.Global
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index 9fbde89a..4d73e1b3 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -24,9 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.IO;
-using System.Linq;
 using System.Reflection;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
index 619f1c51..2df8b211 100644
--- a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
+++ b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs b/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
index 1146a632..84480f3c 100644
--- a/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
+++ b/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs b/src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs
index ee9f5ce3..5a5f9780 100644
--- a/src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs
+++ b/src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs
@@ -1,6 +1,4 @@
-using System;
-
-namespace Rudzoft.ChessLib.Test.DataTests;
+namespace Rudzoft.ChessLib.Test.DataTests;
 
 public sealed class RecordHashTests
 {
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs b/src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs
index f7646b44..61938644 100644
--- a/src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs
+++ b/src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
-using System.Linq;
 using Rudzoft.ChessLib.Extensions;
 
 namespace Rudzoft.ChessLib.Test.DataTests;
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
index d806c9d9..852a3aae 100644
--- a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
+++ b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index fc22700b..159ab212 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
index dfddd260..15443f96 100644
--- a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs b/src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs
index cb2f302c..3eb48df6 100644
--- a/src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Test.FileTests;
 
diff --git a/src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs b/src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs
index d3b23ce5..ce354dad 100644
--- a/src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Test.FileTests;
 
diff --git a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
index 824707bc..183d32df 100644
--- a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
+++ b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
@@ -24,13 +24,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
index b6abbd1f..07480079 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
index 9230221f..10a2cadf 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index 10ec5cd6..77fa31c9 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using System.Text;
 using Microsoft.Extensions.DependencyInjection;
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index 1d99938c..23e0a89b 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index 085e55c4..5fec6213 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index ebce82a7..1d449514 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index 4158e5c1..f921f1dd 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Linq;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
index 71f7ca5e..0eaf8ea9 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
@@ -1,5 +1,4 @@
-using System;
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Hash;
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
index 5133f5c4..20a6343f 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
index 42c2429d..5185111d 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Linq;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
index 3ba1db11..305a1fbc 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Hash;
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
index a932b569..5e9012d4 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
index e3b174ca..0eaa1561 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Linq;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
index f59c6bb1..2875aa48 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 152fcde8..07841172 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index 603223cf..2aba2acf 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
index acd73b8f..74ae6fde 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Microsoft.Extensions.Options;
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 2d1ec991..caa662fa 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -1,44 +1,40 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
-  <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
-    <TieredPGO>true</TieredPGO>
-    <IsPackable>false</IsPackable>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <LangVersion>default</LangVersion>
-  </PropertyGroup>
-
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <PlatformTarget>x64</PlatformTarget>
-  </PropertyGroup>
-
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
-    <PlatformTarget>x64</PlatformTarget>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <None Remove="BookTests\gm2600.bin" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Content Include="BookTests\gm2600.bin">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
-    <PackageReference Include="xunit" Version="2.4.2" />
-    <PackageReference Include="xunit.analyzers" Version="1.1.0" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
-      <PrivateAssets>all</PrivateAssets>
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-    </PackageReference>
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
-  </ItemGroup>
+    <PropertyGroup>
+        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    </PropertyGroup>
+
+    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+        <PlatformTarget>x64</PlatformTarget>
+    </PropertyGroup>
+
+    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+        <PlatformTarget>x64</PlatformTarget>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <None Remove="BookTests\gm2600.bin"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <Content Include="BookTests\gm2600.bin">
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </Content>
+    </ItemGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
+        <PackageReference Include="xunit" Version="2.4.2"/>
+        <PackageReference Include="xunit.analyzers" Version="1.1.0"/>
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+            <PrivateAssets>all</PrivateAssets>
+            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+        </PackageReference>
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
+    </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs b/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
index 01aa360a..6962a4cf 100644
--- a/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Linq;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.ScoreTests;
diff --git a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
index 5a693985..21276ee6 100644
--- a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
@@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Test.SizesTests;
 
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs b/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
index 8e9d131a..b3a5e9b8 100644
--- a/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
@@ -1,5 +1,4 @@
-using System;
-using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.SquareTests;
 
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs b/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
index 3f535f8c..118ed358 100644
--- a/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Test.SquareTests;
 
diff --git a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
index 3f6c01aa..6d3b0613 100644
--- a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
@@ -33,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Test.ZobristTests;
 
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index b6944f51..8fc28364 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -1,31 +1,27 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
     <PropertyGroup>
-        <TargetFramework>net7.0</TargetFramework>
-        <TieredPGO>true</TieredPGO>
         <Nullable>enable</Nullable>
-        <ImplicitUsings>enable</ImplicitUsings>
         <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
-        <LangVersion>default</LangVersion>
     </PropertyGroup>
 
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="7.0.4">
-          <PrivateAssets>all</PrivateAssets>
-          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+            <PrivateAssets>all</PrivateAssets>
+            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
-        <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.4" />
-        <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
-        <PackageReference Include="Microsoft.OpenApi" Version="1.6.3" />
-        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
-        <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
-        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
-        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
+        <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.4"/>
+        <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1"/>
+        <PackageReference Include="Microsoft.OpenApi" Version="1.6.3"/>
+        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
+        <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0"/>
+        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0"/>
+        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0"/>
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
+        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
     </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
index 1abeb48f..dd211f70 100644
--- a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
@@ -1,5 +1,4 @@
-using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.WebApi.Queries;
 
 namespace Rudzoft.ChessLib.WebApi.Services;
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 50d73d0b..f646234b 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -24,10 +24,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 [assembly: InternalsVisibleTo("Chess.Test")]
 
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index b49e29b3..a3527eae 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -24,11 +24,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections;
-using System.Collections.Generic;
 using System.Diagnostics;
-using System.Linq;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 297eb3e5..0a8ec8a9 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -24,10 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Enums/GameEndTypes.cs b/src/Rudzoft.ChessLib/Enums/GameEndTypes.cs
index a6ffd4fe..15bbb2d8 100644
--- a/src/Rudzoft.ChessLib/Enums/GameEndTypes.cs
+++ b/src/Rudzoft.ChessLib/Enums/GameEndTypes.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
 namespace Rudzoft.ChessLib.Enums;
 
 [Flags]
diff --git a/src/Rudzoft.ChessLib/Enums/GameResults.cs b/src/Rudzoft.ChessLib/Enums/GameResults.cs
index cdec8cc4..1b6fc71e 100644
--- a/src/Rudzoft.ChessLib/Enums/GameResults.cs
+++ b/src/Rudzoft.ChessLib/Enums/GameResults.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
 namespace Rudzoft.ChessLib.Enums;
 
 /// <summary>
diff --git a/src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs b/src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs
index 8742bef9..05cf6f3c 100644
--- a/src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs
+++ b/src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs b/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
index 02d8602f..88625698 100644
--- a/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
+++ b/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index f46d9842..e716e032 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -24,14 +24,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections;
 using System.Diagnostics;
-using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Evaluation;
 
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs b/src/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
index 675cbbbb..b387a7fb 100644
--- a/src/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
+++ b/src/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.Serialization;
 
 namespace Rudzoft.ChessLib.Exceptions;
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs b/src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
index d3e912c4..a49fc88b 100644
--- a/src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
+++ b/src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.Serialization;
 
 namespace Rudzoft.ChessLib.Exceptions;
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs b/src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
index 24df47c1..62fbd79d 100644
--- a/src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
+++ b/src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.Serialization;
 
 namespace Rudzoft.ChessLib.Exceptions;
diff --git a/src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs b/src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
index f8e09cee..3da66205 100644
--- a/src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
+++ b/src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.Serialization;
 
 namespace Rudzoft.ChessLib.Exceptions;
diff --git a/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
index 0ccce26b..a756f695 100644
--- a/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index fbcc06ce..3013f4da 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.IO;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
diff --git a/src/Rudzoft.ChessLib/Extensions/Maths.cs b/src/Rudzoft.ChessLib/Extensions/Maths.cs
index e57afad3..90ea180c 100644
--- a/src/Rudzoft.ChessLib/Extensions/Maths.cs
+++ b/src/Rudzoft.ChessLib/Extensions/Maths.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Extensions;
diff --git a/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs b/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
index 9a7c7a16..8a52339b 100644
--- a/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Extensions;
diff --git a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
index e5d02726..2859980b 100644
--- a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
@@ -24,9 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
-using System.IO;
 using System.Runtime.CompilerServices;
 using System.Text;
 
diff --git a/src/Rudzoft.ChessLib/Factories/ServiceFactory.cs b/src/Rudzoft.ChessLib/Factories/ServiceFactory.cs
index d0be1672..1163d3a7 100644
--- a/src/Rudzoft.ChessLib/Factories/ServiceFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/ServiceFactory.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
 namespace Rudzoft.ChessLib.Factories;
 
 public sealed class ServiceFactory<T> : IServiceFactory<T>
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index edbecbaf..bfcd38c1 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text.RegularExpressions;
diff --git a/src/Rudzoft.ChessLib/Fen/FenData.cs b/src/Rudzoft.ChessLib/Fen/FenData.cs
index c2960675..575b1ed2 100644
--- a/src/Rudzoft.ChessLib/Fen/FenData.cs
+++ b/src/Rudzoft.ChessLib/Fen/FenData.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
diff --git a/src/Rudzoft.ChessLib/Fen/IFenData.cs b/src/Rudzoft.ChessLib/Fen/IFenData.cs
index 566dc652..d4fd34a1 100644
--- a/src/Rudzoft.ChessLib/Fen/IFenData.cs
+++ b/src/Rudzoft.ChessLib/Fen/IFenData.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
 namespace Rudzoft.ChessLib.Fen;
 
 public interface IFenData
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 7f06381f..e99a4628 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -24,9 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections;
-using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Microsoft.Extensions.ObjectPool;
diff --git a/src/Rudzoft.ChessLib/Hash/IRKiss.cs b/src/Rudzoft.ChessLib/Hash/IRKiss.cs
index 70837d1f..d15e9421 100644
--- a/src/Rudzoft.ChessLib/Hash/IRKiss.cs
+++ b/src/Rudzoft.ChessLib/Hash/IRKiss.cs
@@ -27,8 +27,6 @@
 
  */
 
-using System.Collections.Generic;
-
 namespace Rudzoft.ChessLib.Hash;
 
 public interface IRKiss
diff --git a/src/Rudzoft.ChessLib/Hash/IZobrist.cs b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
index e54c8c32..2aaefe3a 100644
--- a/src/Rudzoft.ChessLib/Hash/IZobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
@@ -1,4 +1,5 @@
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Hash;
 
diff --git a/src/Rudzoft.ChessLib/Hash/RKiss.cs b/src/Rudzoft.ChessLib/Hash/RKiss.cs
index 2c63de60..ba0f3be2 100644
--- a/src/Rudzoft.ChessLib/Hash/RKiss.cs
+++ b/src/Rudzoft.ChessLib/Hash/RKiss.cs
@@ -23,8 +23,6 @@ For further analysis see
 
  */
 
-using System.Collections.Generic;
-using System.Linq;
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Hash;
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
index 72249562..9090816d 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
 namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
 
 [Flags]
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index b93a32eb..87ed6e02 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index d6f66516..c02a2514 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -24,9 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Hash;
 
diff --git a/src/Rudzoft.ChessLib/IBoard.cs b/src/Rudzoft.ChessLib/IBoard.cs
index 8532068f..90956d70 100644
--- a/src/Rudzoft.ChessLib/IBoard.cs
+++ b/src/Rudzoft.ChessLib/IBoard.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib;
diff --git a/src/Rudzoft.ChessLib/IGame.cs b/src/Rudzoft.ChessLib/IGame.cs
index 0dd156c8..df9579f6 100644
--- a/src/Rudzoft.ChessLib/IGame.cs
+++ b/src/Rudzoft.ChessLib/IGame.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Protocol.UCI;
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index 2235a5d3..e4c0f243 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using System.Text;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
index c5ad0c3d..738f1379 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 57ef0319..0c15df95 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index a74b356d..56664df6 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -24,10 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib/Notation/INotationToMove.cs b/src/Rudzoft.ChessLib/Notation/INotationToMove.cs
index 705b2642..db9acf8f 100644
--- a/src/Rudzoft.ChessLib/Notation/INotationToMove.cs
+++ b/src/Rudzoft.ChessLib/Notation/INotationToMove.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Notation/NotationToMove.cs b/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
index ed80abf9..99640fe8 100644
--- a/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
+++ b/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
@@ -24,9 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Notation.Notations;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
index 5fad669b..57eda35f 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
index 5ca7930c..1fc07cba 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index 6fcd8921..1306abe1 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
index 21857aea..c3b5cf42 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
index 7e4440c0..10327cfe 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.MoveGeneration;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
index 80b786e8..488e5d50 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
index ce66f02b..058f1320 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Linq;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.MoveGeneration;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
index 43058d6e..2821570d 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
index 590fb952..b1199610 100644
--- a/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Polyglot;
diff --git a/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs b/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs
index dc4f7d62..0602a87b 100644
--- a/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs
@@ -24,9 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.IO;
-
 namespace Rudzoft.ChessLib.Polyglot;
 
 internal sealed class LittleEndianBinaryStreamReader : BinaryReader
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index f6295460..260b94f9 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -24,9 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
-using System.IO;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Microsoft.Extensions.ObjectPool;
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 639861b3..8b97b019 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Rudzoft.ChessLib.Types;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Polyglot;
 
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 72819848..bbbb3a3a 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -24,9 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -40,6 +38,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib;
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
index d0a9e618..beae5c5b 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
index 4e061ec6..34e78282 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
@@ -24,10 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
-using System.Threading;
-using System.Threading.Tasks;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs
index 511e6cd7..a7be15aa 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs
@@ -24,9 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.IO;
-using System.Threading.Tasks;
-
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
 public interface IInputOutput
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs
index 0671f819..fa3a338e 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
 public interface IOption
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
index ff3c254f..77f4a1c6 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
index a92fa317..6d6eeabd 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs b/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
index 7294162d..d789ad46 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
@@ -24,11 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
 public sealed class InputOutput : IInputOutput
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
index 9e40a464..49e81173 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
index 2b62d251..12539778 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
 using Rudzoft.ChessLib.Extensions;
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs
index ae7f28b6..7c3dad22 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
-
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
 public sealed class OptionComparer : IComparer<IOption>
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index 4f778233..63c4fdce 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index f8280369..e3975803 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -24,10 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
 using System.Runtime.InteropServices;
 using System.Text;
 using Microsoft.Extensions.ObjectPool;
@@ -36,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using System.Runtime.CompilerServices;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib.Protocol.UCI;
 
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 1bc88c4c..d4a55eb5 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -1,79 +1,77 @@
 <Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <LangVersion>default</LangVersion>
-    <Platforms>AnyCPU</Platforms>
-    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
-    <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
-    <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
-    <TieredPGO>true</TieredPGO>
-    <PackageVersion>0.0.4</PackageVersion>
-    <Authors>Rudy Alex Kohn</Authors>
-    <Company>None</Company>
-    <Version>0.0.4</Version>
-    <Description>Chess library with data structures and move generation.</Description>
-    <Copyright>(C) 2017-2023 Rudy Alex Kohn</Copyright>
-    <AssemblyVersion>0.0.4</AssemblyVersion>
-    <FileVersion>0.0.4</FileVersion>
-    <PackageProjectUrl>https://github.com/rudzen/ChessLib</PackageProjectUrl>
-    <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
-    <PackageLicenseFile>LICENSE</PackageLicenseFile>
-    <Nullable>warnings</Nullable>
-    <EnforceCodeStyleInBuild>False</EnforceCodeStyleInBuild>
-    <Title>Rudzoft.ChessLib</Title>
-    <PackageReadmeFile>README.md</PackageReadmeFile>
-    <PackageTags>chess bitboard datastructure movegeneration magicbb</PackageTags>
-    <PackageIcon>ChessLib.png</PackageIcon>
-    <PackageId>Rudzoft.ChessLib</PackageId>
-    <RepositoryUrl>https://github.com/rudzen/ChessLib</RepositoryUrl>
-    <RepositoryType>git</RepositoryType>
-    <Product>Rudzoft.ChessLib</Product>
-    <Configurations>Debug;Release</Configurations>
-    <TargetFramework>net7.0</TargetFramework>
-  </PropertyGroup>
+    <PropertyGroup>
+        <LangVersion>default</LangVersion>
+        <Platforms>AnyCPU</Platforms>
+        <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+        <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
+        <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
+        <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
+        <PackageVersion>0.0.4</PackageVersion>
+        <Authors>Rudy Alex Kohn</Authors>
+        <Company>None</Company>
+        <Version>0.0.4</Version>
+        <Description>Chess library with data structures and move generation.</Description>
+        <Copyright>(C) 2017-2023 Rudy Alex Kohn</Copyright>
+        <AssemblyVersion>0.0.4</AssemblyVersion>
+        <FileVersion>0.0.4</FileVersion>
+        <PackageProjectUrl>https://github.com/rudzen/ChessLib</PackageProjectUrl>
+        <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
+        <PackageLicenseFile>LICENSE</PackageLicenseFile>
+        <EnforceCodeStyleInBuild>False</EnforceCodeStyleInBuild>
+        <Title>Rudzoft.ChessLib</Title>
+        <PackageReadmeFile>README.md</PackageReadmeFile>
+        <PackageTags>chess bitboard datastructure movegeneration magicbb</PackageTags>
+        <PackageIcon>ChessLib.png</PackageIcon>
+        <PackageId>Rudzoft.ChessLib</PackageId>
+        <RepositoryUrl>https://github.com/rudzen/ChessLib</RepositoryUrl>
+        <RepositoryType>git</RepositoryType>
+        <Product>Rudzoft.ChessLib</Product>
+        <Configurations>Debug;Release</Configurations>
+        <IsPackable>true</IsPackable>
+    </PropertyGroup>
 
-  <PropertyGroup Condition="'$(IsWindows)'=='true'">
-    <DefineConstants>Windows</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(IsOSX)'=='true'">
-    <DefineConstants>OSX</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(IsLinux)'=='true'">
-    <DefineConstants>Linux</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <PlatformTarget>x64</PlatformTarget>
-  </PropertyGroup>
+    <PropertyGroup Condition="'$(IsWindows)'=='true'">
+        <DefineConstants>Windows</DefineConstants>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(IsOSX)'=='true'">
+        <DefineConstants>OSX</DefineConstants>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(IsLinux)'=='true'">
+        <DefineConstants>Linux</DefineConstants>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+        <PlatformTarget>x64</PlatformTarget>
+    </PropertyGroup>
 
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-  </PropertyGroup>
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
-    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
-    <PackageReference Include="ZString" Version="2.5.0" />
-  </ItemGroup>
-  <ItemGroup>
-    <Folder Include="Protocol\" />
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="..\..\README.md">
-      <Pack>True</Pack>
-      <PackagePath>\</PackagePath>
-    </None>
-    <None Include="..\..\LICENSE">
-      <Pack>True</Pack>
-      <PackagePath></PackagePath>
-    </None>
-    <None Include="Icon\ChessLib.png" Pack="true" PackagePath="" />
-  </ItemGroup>
+    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+        <PlatformTarget>AnyCPU</PlatformTarget>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+        <PlatformTarget>AnyCPU</PlatformTarget>
+    </PropertyGroup>
+    <ItemGroup>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5"/>
+        <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0"/>
+        <PackageReference Include="ZString" Version="2.5.0"/>
+    </ItemGroup>
+    <ItemGroup>
+        <Folder Include="Protocol\"/>
+    </ItemGroup>
+    <ItemGroup>
+        <None Include="..\..\README.md">
+            <Pack>True</Pack>
+            <PackagePath>\</PackagePath>
+        </None>
+        <None Include="..\..\LICENSE">
+            <Pack>True</Pack>
+            <PackagePath></PackagePath>
+        </None>
+        <None Include="Icon\ChessLib.png" Pack="true" PackagePath=""/>
+    </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index 96558970..2389508d 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Linq;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib/Tables/HashTable.cs b/src/Rudzoft.ChessLib/Tables/HashTable.cs
index 0ca4cd93..a8b18b8a 100644
--- a/src/Rudzoft.ChessLib/Tables/HashTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/HashTable.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Tables;
diff --git a/src/Rudzoft.ChessLib/Tables/IHashTable.cs b/src/Rudzoft.ChessLib/Tables/IHashTable.cs
index 6a5ef323..a961ca1c 100644
--- a/src/Rudzoft.ChessLib/Tables/IHashTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/IHashTable.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Tables;
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs
index b47eb2a7..0343f190 100644
--- a/src/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Tables.KillerMoves;
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
index 1519e731..ed420542 100644
--- a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs b/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs
index 0463d08f..69a98bd6 100644
--- a/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs
+++ b/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.Intrinsics.X86;
+using System.Runtime.Intrinsics.X86;
 
 namespace Rudzoft.ChessLib.Types.Attacks;
 
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index ac38e124..7c2039df 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -24,12 +24,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Collections;
-using System.Collections.Generic;
 using System.Diagnostics;
-using System.IO;
-using System.Linq;
 using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 51ed2926..2c504182 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -28,14 +28,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // ReSharper disable MemberCanBePrivate.Global
 // ReSharper disable MemberCanBeInternal
 
-using System;
-using System.Collections.Generic;
 using System.Diagnostics;
-using System.Linq;
 using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
-using Rudzoft.ChessLib.Extensions;
 
 namespace Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index 8d831966..94d4b361 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -24,9 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
-using Rudzoft.ChessLib.Hash;
 
 namespace Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Types/Depth.cs b/src/Rudzoft.ChessLib/Types/Depth.cs
index af27e288..fd3a8af6 100644
--- a/src/Rudzoft.ChessLib/Types/Depth.cs
+++ b/src/Rudzoft.ChessLib/Types/Depth.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index 7eeba7cc..18ebbbf4 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -25,7 +25,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Rudzoft.ChessLib.Extensions;
-using System;
 using System.Numerics;
 using System.Runtime.CompilerServices;
 
diff --git a/src/Rudzoft.ChessLib/Types/HashKey.cs b/src/Rudzoft.ChessLib/Types/HashKey.cs
index e6ccc47d..a6c28673 100644
--- a/src/Rudzoft.ChessLib/Types/HashKey.cs
+++ b/src/Rudzoft.ChessLib/Types/HashKey.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
diff --git a/src/Rudzoft.ChessLib/Types/IPieceSquare.cs b/src/Rudzoft.ChessLib/Types/IPieceSquare.cs
index 6165d533..07f0ca30 100644
--- a/src/Rudzoft.ChessLib/Types/IPieceSquare.cs
+++ b/src/Rudzoft.ChessLib/Types/IPieceSquare.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
 namespace Rudzoft.ChessLib.Types;
 
 public interface IPieceSquare : IEquatable<IPieceSquare>
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index 28e4a79b..80a523d6 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -1,35 +1,34 @@
 /*
-*Copyright (C) 2007 Pradyumna Kannan.
-*
-*This code is provided 'as-is', without any expressed or implied warranty.
-*In no event will the authors be held liable for any damages arising from
-*the use of this code. Permission is granted to anyone to use this
-*code for any purpose, including commercial applications, and to alter
-*it and redistribute it freely, subject to the following restrictions:
-*
-*1. The origin of this code must not be misrepresented; you must not
-*claim that you wrote the original code. If you use this code in a
-*product, an acknowledgment in the product documentation would be
-*appreciated but is not required.
-*
-*2. Altered source versions must be plainly marked as such, and must not be
-*misrepresented as being the original code.
-*
-*3. This notice may not be removed or altered from any source distribution.
-*
-* -------------------------------------------------------------------------
-* Converted from C to C# code by Rudy Alex Kohn, 2017 - for use in MUCI.
-* The same conditions as described above is *STILL* in effect.
-*
-* Original files: magicmoves.c and magicmoves.h
-* Conditional compile paths were removed.
-* The original algorithm and data were not changed in any way.
-*
-* WHATEVER YOU DO, DO NOT ALTER ANYTHING IN HERE!
-*
-*/
-
-using System;
+ *Copyright (C) 2007 Pradyumna Kannan.
+ *
+ *This code is provided 'as-is', without any expressed or implied warranty.
+ *In no event will the authors be held liable for any damages arising from
+ *the use of this code. Permission is granted to anyone to use this
+ *code for any purpose, including commercial applications, and to alter
+ *it and redistribute it freely, subject to the following restrictions:
+ *
+ *1. The origin of this code must not be misrepresented; you must not
+ *claim that you wrote the original code. If you use this code in a
+ *product, an acknowledgment in the product documentation would be
+ *appreciated but is not required.
+ *
+ *2. Altered source versions must be plainly marked as such, and must not be
+ *misrepresented as being the original code.
+ *
+ *3. This notice may not be removed or altered from any source distribution.
+ *
+ * -------------------------------------------------------------------------
+ * Converted from C to C# code by Rudy Alex Kohn, 2017 - for use in MUCI.
+ * The same conditions as described above is *STILL* in effect.
+ *
+ * Original files: magicmoves.c and magicmoves.h
+ * Conditional compile paths were removed.
+ * The original algorithm and data were not changed in any way.
+ *
+ * WHATEVER YOU DO, DO NOT ALTER ANYTHING IN HERE!
+ *
+ */
+
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index 9ccabaaf..e5543599 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 8b8576f2..6fe82efe 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 
diff --git a/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs b/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
index c02c7b65..71064a5b 100644
--- a/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
+++ b/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 
diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Player.cs
index c3847130..d6dc4b6c 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Player.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Numerics;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index 99d9b8fa..47e703c9 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 // ReSharper disable UnusedMember.Global
diff --git a/src/Rudzoft.ChessLib/Types/RootMove.cs b/src/Rudzoft.ChessLib/Types/RootMove.cs
index f02bdf88..a216fbb0 100644
--- a/src/Rudzoft.ChessLib/Types/RootMove.cs
+++ b/src/Rudzoft.ChessLib/Types/RootMove.cs
@@ -1,6 +1,4 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
+using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index dbd18c4c..89b1c2db 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Numerics;
 using System.Runtime.CompilerServices;
 
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index bb3cf35a..5b33fbd8 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Linq;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 
diff --git a/src/Rudzoft.ChessLib/Types/StateStack.cs b/src/Rudzoft.ChessLib/Types/StateStack.cs
index 49c337a4..4190141e 100644
--- a/src/Rudzoft.ChessLib/Types/StateStack.cs
+++ b/src/Rudzoft.ChessLib/Types/StateStack.cs
@@ -25,10 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Cysharp.Text;
-using System;
 using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
 
 namespace Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.ChessLib/Types/ValMove.cs b/src/Rudzoft.ChessLib/Types/ValMove.cs
index 47ef4078..cb4d5bdb 100644
--- a/src/Rudzoft.ChessLib/Types/ValMove.cs
+++ b/src/Rudzoft.ChessLib/Types/ValMove.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 
 namespace Rudzoft.ChessLib.Types;
diff --git a/src/Rudzoft.ChessLib/Types/Value.cs b/src/Rudzoft.ChessLib/Types/Value.cs
index 5a6500de..2a7929e8 100644
--- a/src/Rudzoft.ChessLib/Types/Value.cs
+++ b/src/Rudzoft.ChessLib/Types/Value.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-
 namespace Rudzoft.ChessLib.Types;
 
 /// <summary>
diff --git a/src/Rudzoft.ChessLib/Types/Values.cs b/src/Rudzoft.ChessLib/Types/Values.cs
index 3375a4af..56043871 100644
--- a/src/Rudzoft.ChessLib/Types/Values.cs
+++ b/src/Rudzoft.ChessLib/Types/Values.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Runtime.CompilerServices;
 using System.Text.Json.Serialization;
 using Rudzoft.ChessLib.Enums;
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index e0bd6faa..3c84a894 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -24,9 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Types;
 
diff --git a/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs b/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
index 35de0b6c..ca1880e8 100644
--- a/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
+++ b/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
 using System.Diagnostics;
 using System.Reflection;
 using System.Runtime.InteropServices;
diff --git a/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs b/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
index 06dc0a18..80a22f2a 100644
--- a/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
+++ b/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
@@ -24,10 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
 namespace Perft.Extensions;
 
 public static class ObjectExtensions
diff --git a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
index 24047c6a..83fc19e2 100644
--- a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
+++ b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
@@ -1,10 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
-    <TieredPGO>true</TieredPGO>
     <RootNamespace>Perft</RootNamespace>
-    <LangVersion>default</LangVersion>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/src/Rudzoft.Perft/Host/PerftHost.cs b/src/Rudzoft.Perft/Host/PerftHost.cs
index 09be270e..0fc3d6f4 100644
--- a/src/Rudzoft.Perft/Host/PerftHost.cs
+++ b/src/Rudzoft.Perft/Host/PerftHost.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Threading;
-using System.Threading.Tasks;
 using Microsoft.Extensions.Hosting;
 using Perft.Environment;
 using Rudzoft.Perft.Options;
diff --git a/src/Rudzoft.Perft/Options/EpdOptions.cs b/src/Rudzoft.Perft/Options/EpdOptions.cs
index f67d8080..2d1ec939 100644
--- a/src/Rudzoft.Perft/Options/EpdOptions.cs
+++ b/src/Rudzoft.Perft/Options/EpdOptions.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
 using CommandLine;
 
 namespace Rudzoft.Perft.Options;
diff --git a/src/Rudzoft.Perft/Options/Fens.cs b/src/Rudzoft.Perft/Options/Fens.cs
index a9851567..a041c75d 100644
--- a/src/Rudzoft.Perft/Options/Fens.cs
+++ b/src/Rudzoft.Perft/Options/Fens.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
 using CommandLine;
 
 namespace Rudzoft.Perft.Options;
diff --git a/src/Rudzoft.Perft/Options/IOptionsFactory.cs b/src/Rudzoft.Perft/Options/IOptionsFactory.cs
index b1afead6..a72a43d5 100644
--- a/src/Rudzoft.Perft/Options/IOptionsFactory.cs
+++ b/src/Rudzoft.Perft/Options/IOptionsFactory.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
-
 namespace Rudzoft.Perft.Options;
 
 public interface IOptionsFactory
diff --git a/src/Rudzoft.Perft/Options/OptionsFactory.cs b/src/Rudzoft.Perft/Options/OptionsFactory.cs
index 0b5c296a..ad2f1e60 100644
--- a/src/Rudzoft.Perft/Options/OptionsFactory.cs
+++ b/src/Rudzoft.Perft/Options/OptionsFactory.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
 using CommandLine;
 using Rudzoft.Perft.Host;
 using Serilog;
diff --git a/src/Rudzoft.Perft/Parsers/EpdParser.cs b/src/Rudzoft.Perft/Parsers/EpdParser.cs
index 5521753f..552f68e6 100644
--- a/src/Rudzoft.Perft/Parsers/EpdParser.cs
+++ b/src/Rudzoft.Perft/Parsers/EpdParser.cs
@@ -24,10 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Perft.Interfaces;
 
diff --git a/src/Rudzoft.Perft/Parsers/EpdSet.cs b/src/Rudzoft.Perft/Parsers/EpdSet.cs
index b014a436..782ac2b2 100644
--- a/src/Rudzoft.Perft/Parsers/EpdSet.cs
+++ b/src/Rudzoft.Perft/Parsers/EpdSet.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
 using Rudzoft.ChessLib.Perft.Interfaces;
 
 namespace Rudzoft.Perft.Parsers;
diff --git a/src/Rudzoft.Perft/Parsers/IEpdParser.cs b/src/Rudzoft.Perft/Parsers/IEpdParser.cs
index 889827f2..fbb2bb71 100644
--- a/src/Rudzoft.Perft/Parsers/IEpdParser.cs
+++ b/src/Rudzoft.Perft/Parsers/IEpdParser.cs
@@ -24,9 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
 namespace Rudzoft.Perft.Parsers;
 
 public interface IEpdParser
diff --git a/src/Rudzoft.Perft/Parsers/IEpdSet.cs b/src/Rudzoft.Perft/Parsers/IEpdSet.cs
index c3ca7995..96bc7667 100644
--- a/src/Rudzoft.Perft/Parsers/IEpdSet.cs
+++ b/src/Rudzoft.Perft/Parsers/IEpdSet.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
 using Rudzoft.ChessLib.Perft.Interfaces;
 
 namespace Rudzoft.Perft.Parsers;
diff --git a/src/Rudzoft.Perft/Perft/IPerftRunner.cs b/src/Rudzoft.Perft/Perft/IPerftRunner.cs
index f3c34cc1..011d3ad2 100644
--- a/src/Rudzoft.Perft/Perft/IPerftRunner.cs
+++ b/src/Rudzoft.Perft/Perft/IPerftRunner.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Threading;
-using System.Threading.Tasks;
 using Rudzoft.Perft.Options;
 
 namespace Rudzoft.Perft.Perft;
diff --git a/src/Rudzoft.Perft/Perft/PerftRunner.cs b/src/Rudzoft.Perft/Perft/PerftRunner.cs
index 45ddfbd2..c5e9bbff 100644
--- a/src/Rudzoft.Perft/Perft/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Perft/PerftRunner.cs
@@ -24,15 +24,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections.Generic;
 using System.Diagnostics;
-using System.IO;
-using System.Linq;
 using System.Runtime;
 using System.Runtime.CompilerServices;
 using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Extensions;
diff --git a/src/Rudzoft.Perft/Program.cs b/src/Rudzoft.Perft/Program.cs
index e301718a..952be1e7 100644
--- a/src/Rudzoft.Perft/Program.cs
+++ b/src/Rudzoft.Perft/Program.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.IO;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index eaa5dc7c..c945d98a 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -1,60 +1,57 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <TargetFramework>net7.0</TargetFramework>
-    <TieredPGO>true</TieredPGO>
-    <Platforms>AnyCPU</Platforms>
-    <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
-    <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
-    <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
-    <LangVersion>default</LangVersion>
-  </PropertyGroup>
-  
-  <PropertyGroup Condition="'$(IsWindows)'=='true'">
-    <DefineConstants>Windows</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(IsOSX)'=='true'">
-    <DefineConstants>OSX</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(IsLinux)'=='true'">
-    <DefineConstants>Linux</DefineConstants>
-  </PropertyGroup>  
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <PlatformTarget>x64</PlatformTarget>
-  </PropertyGroup>
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <Platforms>AnyCPU</Platforms>
+        <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
+        <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
+        <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
+    </PropertyGroup>
 
-  <ItemGroup>
-    <None Remove="appsettings.json" />
-  </ItemGroup>
+    <PropertyGroup Condition="'$(IsWindows)'=='true'">
+        <DefineConstants>Windows</DefineConstants>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(IsOSX)'=='true'">
+        <DefineConstants>OSX</DefineConstants>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(IsLinux)'=='true'">
+        <DefineConstants>Linux</DefineConstants>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+        <PlatformTarget>x64</PlatformTarget>
+    </PropertyGroup>
 
-  <ItemGroup>
-    <Content Include="appsettings.json">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-  </ItemGroup>
+    <ItemGroup>
+        <None Remove="appsettings.json"/>
+    </ItemGroup>
 
-  <ItemGroup>
-    <PackageReference Include="CommandLineParser" Version="2.9.1" />
-    <PackageReference Include="ConsoleGUI" Version="1.4.1" />
-    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
-    <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
-    <PackageReference Include="Serilog" Version="2.12.0" />
-    <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
-    <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />
-    <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
-    <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
-    <PackageReference Include="System.Reactive" Version="5.0.0" />
-    <PackageReference Include="System.Threading.Tasks.Dataflow" Version="7.0.0" />
-    <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageReference Include="Timestamp" Version="1.0.2" />
-  </ItemGroup>
+    <ItemGroup>
+        <Content Include="appsettings.json">
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </Content>
+    </ItemGroup>
 
-  <ItemGroup>
-    <ProjectReference Include="..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
-    <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
-    <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
-    <ProjectReference Include="..\Rudzoft.Perft.Framework\Rudzoft.Perft.Framework.csproj" />
-  </ItemGroup>
+    <ItemGroup>
+        <PackageReference Include="CommandLineParser" Version="2.9.1"/>
+        <PackageReference Include="ConsoleGUI" Version="1.4.1"/>
+        <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1"/>
+        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5"/>
+        <PackageReference Include="Serilog" Version="2.12.0"/>
+        <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/>
+        <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0"/>
+        <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0"/>
+        <PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/>
+        <PackageReference Include="System.Reactive" Version="5.0.0"/>
+        <PackageReference Include="System.Threading.Tasks.Dataflow" Version="7.0.0"/>
+        <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4"/>
+        <PackageReference Include="Timestamp" Version="1.0.2"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj"/>
+        <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj"/>
+        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
+        <ProjectReference Include="..\Rudzoft.Perft.Framework\Rudzoft.Perft.Framework.csproj"/>
+    </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs b/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
index b6bd5059..4a015007 100644
--- a/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
+++ b/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Linq;
 using System.Reflection;
 
 namespace Rudzoft.Perft.TimeStamp;

From 48d86709caddf380837bed8671d2cade0f94eb55 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Wed, 16 Aug 2023 21:32:17 +0200
Subject: [PATCH 049/119] Cleanup and fixed pgn tests

---
 .../PgnMoveNotationTests/SanToMoveTests.cs    |  1 +
 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs | 37 ++++++++++---------
 src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs    |  4 +-
 .../PiecesTests/SliderMobilityFixture.cs      |  2 +-
 src/Rudzoft.ChessLib/Fen/FenData.cs           |  2 +-
 .../MoveGeneration/IMoveList.cs               |  2 +-
 .../MoveGeneration/MoveGenerator.cs           |  4 +-
 .../MoveGeneration/MoveList.cs                | 14 ++++---
 .../Notation/Notations/IccfNotation.cs        |  4 +-
 .../Notation/Notations/Notation.cs            |  4 +-
 src/Rudzoft.ChessLib/Position.cs              | 24 ++++++------
 src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs      |  2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Option.cs   |  4 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       |  4 +-
 src/Rudzoft.ChessLib/Types/Move.cs            |  4 +-
 src/Rudzoft.ChessLib/Types/Piece.cs           | 24 ++++--------
 16 files changed, 66 insertions(+), 70 deletions(-)

diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
index 62cda8df..5bc193cc 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
@@ -51,6 +51,7 @@ public SanToMoveTests()
             .AddSingleton<IValues, Values>()
             .AddSingleton<IZobrist, Zobrist>()
             .AddSingleton<ICuckoo, Cuckoo>()
+            .AddSingleton<IRKiss, RKiss>()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IPosition, Position>()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
index 1b62726a..e4b91d20 100644
--- a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
@@ -61,7 +61,7 @@ public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationTok
                     else if (char.IsLetter(word[0]))
                     {
                         if (currentGameMoves.Count == 0 || !string.IsNullOrEmpty(currentGameMoves[^1].BlackMove))
-                            currentGameMoves.Add(new PgnMove(currentMoveNumber, word, string.Empty));
+                            currentGameMoves.Add(new(currentMoveNumber, word, string.Empty));
                         else
                         {
                             var lastMove = currentGameMoves[^1];
@@ -71,35 +71,36 @@ public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationTok
                     else if (word.Contains("1-0") || word.Contains("0-1") || word.Contains("1/2-1/2") ||
                              word.Contains('*'))
                     {
-                        yield return new PgnGame(currentGameTags, currentGameMoves);
-                        currentGameTags = new Dictionary<string, string>();
-                        currentGameMoves = new List<PgnMove>();
+                        yield return new(currentGameTags, currentGameMoves);
+                        currentGameTags = new();
+                        currentGameMoves = new();
                         inMoveSection = false;
                     }
                 }
             }
             else
             {
-                if (line.StartsWith("[") && line.EndsWith("]") && line.Contains("\""))
-                {
-                    var firstSpaceIndex = line.IndexOf(' ');
-                    var firstQuoteIndex = line.IndexOf('"');
-                    var lastQuoteIndex = line.LastIndexOf('"');
+                if (!line.StartsWith("[") || !line.EndsWith("]") || !line.Contains('"'))
+                    continue;
+                
+                var firstSpaceIndex = line.IndexOf(' ');
+                var firstQuoteIndex = line.IndexOf('"');
+                var lastQuoteIndex = line.LastIndexOf('"');
 
-                    if (firstSpaceIndex <= 0 || firstQuoteIndex <= firstSpaceIndex
-                                             || lastQuoteIndex <= firstQuoteIndex)
-                        continue;
+                if (firstSpaceIndex <= 0 || firstQuoteIndex <= firstSpaceIndex
+                                         || lastQuoteIndex <= firstQuoteIndex)
+                    continue;
                     
-                    var tagName = line.Substring(1, firstSpaceIndex - 1).Trim();
-                    var tagValue = line.Substring(firstQuoteIndex + 1, lastQuoteIndex - firstQuoteIndex - 1)
-                        .Trim();
+                var tagName = line.Substring(1, firstSpaceIndex - 1).Trim();
+                var tagValue = line
+                    .Substring(firstQuoteIndex + 1, lastQuoteIndex - firstQuoteIndex - 1)
+                    .Trim();
 
-                    currentGameTags[tagName] = tagValue;
-                }
+                currentGameTags[tagName] = tagValue;
             }
         }
 
         if (currentGameTags.Count > 0 && currentGameMoves.Count > 0)
-            yield return new PgnGame(currentGameTags, currentGameMoves);
+            yield return new(currentGameTags, currentGameMoves);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
index 344cd558..1270463f 100644
--- a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
@@ -70,8 +70,8 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
                 if (IsPgnGameResult(line))
                 {
                     yield return new(currentGameTags, currentGameMoves);
-                    currentGameTags = new Dictionary<string, string>(DefaultTagCapacity);
-                    currentGameMoves = new List<PgnMove>(DefaultMoveListCapacity);
+                    currentGameTags = new(DefaultTagCapacity);
+                    currentGameMoves = new(DefaultMoveListCapacity);
                     inMoveSection = false;
                 }
             }
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
index 02997d82..d476b2d4 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
@@ -43,7 +43,7 @@ public BitBoard SliderAttacks(PieceTypes pt, Square sq, in BitBoard occ)
         {
             0 => sq.BishopAttacks(in occ),
             1 => sq.RookAttacks(in occ),
-            _ => sq.QueenAttacks(in occ)
+            var _ => sq.QueenAttacks(in occ)
         };
     }
 
diff --git a/src/Rudzoft.ChessLib/Fen/FenData.cs b/src/Rudzoft.ChessLib/Fen/FenData.cs
index 575b1ed2..7d7f6f5f 100644
--- a/src/Rudzoft.ChessLib/Fen/FenData.cs
+++ b/src/Rudzoft.ChessLib/Fen/FenData.cs
@@ -45,7 +45,7 @@ private record struct SplitPoint(int Begin, int End);
 
     private FenData()
     {
-        _splitPoints = new Queue<SplitPoint>(6);
+        _splitPoints = new(6);
     }
 
     public FenData(ReadOnlyMemory<char> fen) : this()
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
index 738f1379..ce3e8c9c 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
@@ -34,7 +34,7 @@ public interface IMoveList : IReadOnlyCollection<ValMove>
     ValMove this[int index] { get; set; }
 
     int Length { get; }
-    Move CurrentMove { get; }
+    ref ValMove CurrentMove { get; }
     void Add(in ValMove item);
     void Add(Move item);
 
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 0c15df95..35c6296c 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -128,7 +128,7 @@ private static int GenerateCapturesQuietsNonEvasions(
             MoveGenerationTypes.Captures => pos.Pieces(~us),
             MoveGenerationTypes.Quiets => ~pos.Pieces(),
             MoveGenerationTypes.NonEvasions => ~pos.Pieces(us),
-            _ => BitBoard.Empty
+            var _ => BitBoard.Empty
         };
 
         return GenerateAll(in pos, moves, index, in target, us, types);
@@ -288,7 +288,7 @@ private static int GeneratePawnMoves(
         {
             MoveGenerationTypes.Evasions => pos.Pieces(them) & target,
             MoveGenerationTypes.Captures => target,
-            _ => pos.Pieces(them)
+            var _ => pos.Pieces(them)
         };
 
         var ksq = pos.GetKingSquare(them);
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index 56664df6..e411400d 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -39,16 +39,20 @@ namespace Rudzoft.ChessLib.MoveGeneration;
  */
 public sealed class MoveList : IMoveList
 {
-    private readonly ValMove[] _moves;
+    private readonly ValMove[] _moves = new ValMove[218];
     private int _cur;
 
-    public MoveList() => _moves = new ValMove[218];
-
-    int IReadOnlyCollection<ValMove>.Count => Length;
+    int IReadOnlyCollection<ValMove>.Count
+    {
+        get => Length;
+    }
 
     public int Length { get; private set; }
 
-    public Move CurrentMove => _moves[_cur].Move;
+    public ref ValMove CurrentMove
+    {
+        get => ref _moves[_cur];
+    }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static MoveList operator ++(MoveList moveList)
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index 1306abe1..4418fd50 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -59,12 +59,12 @@ public override string Convert(Move move)
                 PieceTypes.Rook => 2,
                 PieceTypes.Bishop => 3,
                 PieceTypes.Knight => 4,
-                _ => throw new NotImplementedException()
+                var _ => throw new NotImplementedException()
             };
 
             re[i++] = (char)('0' + c);
         }
 
-        return new string(re[..i]);
+        return new(re[..i]);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
index 10327cfe..3cb47515 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
@@ -44,7 +44,7 @@ protected char GetCheckChar() =>
         Pos.GenerateMoves().Length switch
         {
             0 => '+',
-            _ => '#'
+            var _ => '#'
         };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -123,7 +123,7 @@ private BitBoard GetSimilarAttacks(Move move)
         return pt switch
         {
             PieceTypes.Pawn or PieceTypes.King => BitBoard.Empty,
-            _ => Pos.GetAttacks(move.ToSquare(), pt, Pos.Pieces()) ^ from
+            var _ => Pos.GetAttacks(move.ToSquare(), pt, Pos.Pieces()) ^ from
         };
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index bbbb3a3a..8a4677e1 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -83,7 +83,7 @@ public Position(
         Values = values;
         _zobrist = zobrist;
         _cuckoo = cuckoo;
-        State = new State();
+        State = new();
         Clear();
     }
 
@@ -181,7 +181,7 @@ public bool IsCaptureOrPromotion(Move m)
         return mt switch
         {
             MoveTypes.Normal => Board.Pieces(~SideToMove).Contains(m.ToSquare()),
-            _ => mt != MoveTypes.Castling
+            var _ => mt != MoveTypes.Castling
         };
     }
 
@@ -294,14 +294,14 @@ public FenData GenerateFen()
             for (var file = Files.FileA; file <= Files.FileH; file++)
             {
                 var empty = 0;
-                for (; file <= Files.FileH && Board.IsEmpty(new Square(rank, file)); ++file)
+                for (; file <= Files.FileH && Board.IsEmpty(new(rank, file)); ++file)
                     ++empty;
 
                 if (empty != 0)
                     fen[length++] = (char)(zero + empty);
 
                 if (file <= Files.FileH)
-                    fen[length++] = Board.PieceAt(new Square(rank, file)).GetPieceChar();
+                    fen[length++] = Board.PieceAt(new(rank, file)).GetPieceChar();
             }
 
             if (rank > Ranks.Rank1)
@@ -362,7 +362,7 @@ public FenData GenerateFen()
         fen[length++] = space;
         length = fen.Append(1 + (Ply - _sideToMove.IsBlack.AsByte() / 2), length);
 
-        return new FenData(new string(fen[..length]));
+        return new(new string(fen[..length]));
     }
 
     public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
@@ -376,7 +376,7 @@ public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
             PieceTypes.Bishop => sq.BishopAttacks(in occ),
             PieceTypes.Rook => sq.RookAttacks(in occ),
             PieceTypes.Queen => sq.QueenAttacks(in occ),
-            _ => BitBoard.Empty
+            var _ => BitBoard.Empty
         };
     }
 
@@ -519,7 +519,7 @@ public bool IsDraw(int ply)
         => State.Rule50 switch
         {
             > 99 when State.Checkers.IsEmpty || HasMoves() => true,
-            _ => State.Repetition > 0 && State.Repetition < ply
+            var _ => State.Repetition > 0 && State.Repetition < ply
         };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -884,7 +884,7 @@ public void MoveToString(Move m, in StringBuilder output)
         if (type == MoveTypes.Castling && ChessMode == ChessMode.Normal)
         {
             var file = to > from ? Files.FileG : Files.FileC;
-            to = new Square(from.Rank, file);
+            to = new(from.Rank, file);
         }
 
         Span<char> s = stackalloc char[5];
@@ -1140,7 +1140,7 @@ private void SetupEnPassant(ReadOnlySpan<char> fenChunk)
 
         if (enPassant)
         {
-            State.EnPassantSquare = new Square(fenChunk[1] - '1', fenChunk[0] - 'a');
+            State.EnPassantSquare = new(fenChunk[1] - '1', fenChunk[0] - 'a');
 
             var otherSide = ~_sideToMove;
 
@@ -1340,7 +1340,7 @@ public override string ToString()
 
             for (var file = Files.FileA; file <= Files.FileH; file++)
             {
-                var piece = GetPiece(new Square(rank, file));
+                var piece = GetPiece(new(rank, file));
                 row[rowIndex++] = splitter;
                 row[rowIndex++] = space;
                 row[rowIndex++] = piece.GetPieceChar();
@@ -1545,8 +1545,8 @@ private void SetupCastle(ReadOnlySpan<char> castle)
             {
                 'K' => RookSquare(Square.H1.Relative(c), rook),
                 'Q' => RookSquare(Square.A1.Relative(c), rook),
-                _ => char.IsBetween(token, 'A', 'H')
-                    ? new Square(Rank.Rank1.Relative(c), new File(token - 'A'))
+                var _ => char.IsBetween(token, 'A', 'H')
+                    ? new(Rank.Rank1.Relative(c), new(token - 'A'))
                     : Square.None
             };
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
index beae5c5b..6c31813c 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
@@ -80,7 +80,7 @@ private double Usage()
         return percentage switch
         {
             <= 0 => 0,
-            _ => Math.Round(percentage * 1000, MidpointRounding.ToEven)
+            var _ => Math.Round(percentage * 1000, MidpointRounding.ToEven)
         };
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
index 12539778..46e674a4 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
@@ -106,7 +106,7 @@ public int GetInt()
         return Type switch
         {
             UciOptionType.Spin => Convert.ToInt32(_currentValue),
-            _ => bool.Parse(_currentValue).AsByte()
+            var _ => bool.Parse(_currentValue).AsByte()
         };
     }
 
@@ -134,7 +134,7 @@ public IOption SetCurrentValue(string v)
     {
         var isButton = Type == UciOptionType.Button;
         if (((!isButton && v.IsNullOrEmpty())
-             || (Type == UciOptionType.Check && !bool.TryParse(v, out _))
+             || (Type == UciOptionType.Check && !bool.TryParse(v, out var _))
              || Type == UciOptionType.Spin) && Maths.ToIntegral(v, out int val) && val < Min && val > Max)
             return this;
 
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 2c504182..e21a2ea5 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -544,7 +544,7 @@ public static string Stringify(in BitBoard bb, string title = "")
 
         span[idx] = '\n';
 
-        return new string(span[..idx]);
+        return new(span[..idx]);
     }
 
     /// <summary>
@@ -762,7 +762,7 @@ private static BitBoard GetAttacks(this in Square sq, PieceTypes pt, in BitBoard
             PieceTypes.Bishop => sq.BishopAttacks(in occ),
             PieceTypes.Rook => sq.RookAttacks(in occ),
             PieceTypes.Queen => sq.QueenAttacks(in occ),
-            _ => EmptyBitBoard
+            var _ => EmptyBitBoard
         };
     }
 
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index e5543599..f2686b68 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -79,7 +79,7 @@ public void Deconstruct(out Square from, out Square to, out MoveTypes type)
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Move(string value)
-        => new(new Square(value[1] - '1', value[0] - 'a'), new Square(value[3] - '1', value[2] - 'a'));
+        => new(new(value[1] - '1', value[0] - 'a'), new(value[3] - '1', value[2] - 'a'));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Move(ValMove valMove) => valMove.Move;
@@ -155,7 +155,7 @@ public override string ToString()
 
         Span<char> s = stackalloc char[MaxMoveStringSize];
         return TryFormat(s, out var size)
-            ? new string(s[..size])
+            ? new(s[..size])
             : "(error)";
     }
 
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 6fe82efe..feb38a0d 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -197,24 +197,14 @@ private Piece(Piece pc) : this(pc.Value) { }
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string ToString() => this.GetPieceString();
 
-    public static Piece GetPiece(char character)
+    public static Piece GetPiece(char c)
     {
-        return character switch
-        {
-            'P' => WhitePawn,
-            'p' => BlackPawn,
-            'N' => WhiteKnight,
-            'B' => WhiteBishop,
-            'R' => WhiteRook,
-            'Q' => WhiteQueen,
-            'K' => WhiteKing,
-            'n' => BlackKnight,
-            'b' => BlackBishop,
-            'r' => BlackRook,
-            'q' => BlackQueen,
-            'k' => BlackKing,
-            _ => EmptyPiece
-        };
+        var pcIndex = PieceExtensions.PieceChars.IndexOf(c);
+        if (pcIndex == -1)
+            return EmptyPiece;
+
+        Player p = new(char.IsLower(PieceExtensions.PieceChars[pcIndex]));
+        return ((PieceTypes)pcIndex).MakePiece(p);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]

From 3f0fde1f515aba20d09760a2787e063746abc5b7 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Tue, 29 Aug 2023 22:41:18 +0200
Subject: [PATCH 050/119] Update dependencies + update perft applicaton to use
 actors (not completed yet)

---
 Rudzoft.ChessLib.sln                          |  21 ++--
 .../Rudzoft.ChessLib.Benchmark.csproj         |   2 +-
 .../PgnTests/PgnTests.cs                      |  12 +-
 .../Rudzoft.ChessLib.PGN.Test.csproj          |   8 +-
 .../Rudzoft.ChessLib.Test.csproj              |   8 +-
 .../ZobristTests/ZobristHashTests.cs          |   7 +-
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |  20 ++--
 .../Transposition/ITranspositionTable.cs      |   2 +-
 .../Transposition/TranspositionTable.cs       |   6 +-
 src/Rudzoft.ChessLib/IPosition.cs             |   3 +
 src/Rudzoft.ChessLib/Position.cs              |  63 +++++------
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |   2 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |   2 +-
 .../Environment/FrameworkEnvironment.cs       |  79 -------------
 .../Environment/IFrameworkEnvironment.cs      |  42 -------
 .../Extensions/ArrayExtensions.cs             |  43 --------
 .../Extensions/ObjectExtensions.cs            |  59 ----------
 .../Factories/ConfigurationFactory.cs         |  37 -------
 .../Rudzoft.Perft.Framework.csproj            |  32 ------
 src/Rudzoft.Perft/Actors/EpdParserActor.cs    |  20 ++++
 src/Rudzoft.Perft/Actors/PerftActor.cs        |  35 ++++++
 src/Rudzoft.Perft/Actors/PerftComputeActor.cs |  70 ++++++++++++
 src/Rudzoft.Perft/Actors/PerftRunner.cs       |  21 ++++
 src/Rudzoft.Perft/Actors/ProgressActor.cs     |  42 +++++++
 src/Rudzoft.Perft/Host/CommandLineArgs.cs     |  29 -----
 src/Rudzoft.Perft/Host/PerftHost.cs           |  71 ------------
 src/Rudzoft.Perft/Models/CommandLineArgs.cs   |   3 +
 .../{Perft => Models}/IPerftResult.cs         |   2 +-
 .../{Perft => Models}/PerftResult.cs          |   2 +-
 src/Rudzoft.Perft/Options/OptionsFactory.cs   |  15 +--
 src/Rudzoft.Perft/Options/TTOptions.cs        |   2 +-
 src/Rudzoft.Perft/Parsers/EpdParser.cs        |  29 ++---
 src/Rudzoft.Perft/Parsers/IEpdParser.cs       |   5 +-
 src/Rudzoft.Perft/Program.cs                  | 104 +++++++-----------
 .../Properties/launchSettings.json            |   8 --
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |  57 +++-------
 .../{TimeStamp => Services}/BuildTimeStamp.cs |   8 +-
 .../IBuildTimeStamp.cs                        |   2 +-
 .../{Perft => Services}/IPerftRunner.cs       |   2 +-
 .../{Perft => Services}/PerftRunner.cs        |  33 +++---
 src/Rudzoft.Perft/Services/PerftService.cs    |  50 +++++++++
 41 files changed, 419 insertions(+), 639 deletions(-)
 delete mode 100644 src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
 delete mode 100644 src/Rudzoft.Perft.Framework/Environment/IFrameworkEnvironment.cs
 delete mode 100644 src/Rudzoft.Perft.Framework/Extensions/ArrayExtensions.cs
 delete mode 100644 src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
 delete mode 100644 src/Rudzoft.Perft.Framework/Factories/ConfigurationFactory.cs
 delete mode 100644 src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
 create mode 100644 src/Rudzoft.Perft/Actors/EpdParserActor.cs
 create mode 100644 src/Rudzoft.Perft/Actors/PerftActor.cs
 create mode 100644 src/Rudzoft.Perft/Actors/PerftComputeActor.cs
 create mode 100644 src/Rudzoft.Perft/Actors/PerftRunner.cs
 create mode 100644 src/Rudzoft.Perft/Actors/ProgressActor.cs
 delete mode 100644 src/Rudzoft.Perft/Host/CommandLineArgs.cs
 delete mode 100644 src/Rudzoft.Perft/Host/PerftHost.cs
 create mode 100644 src/Rudzoft.Perft/Models/CommandLineArgs.cs
 rename src/Rudzoft.Perft/{Perft => Models}/IPerftResult.cs (97%)
 rename src/Rudzoft.Perft/{Perft => Models}/PerftResult.cs (98%)
 delete mode 100644 src/Rudzoft.Perft/Properties/launchSettings.json
 rename src/Rudzoft.Perft/{TimeStamp => Services}/BuildTimeStamp.cs (88%)
 rename src/Rudzoft.Perft/{TimeStamp => Services}/IBuildTimeStamp.cs (97%)
 rename src/Rudzoft.Perft/{Perft => Services}/IPerftRunner.cs (97%)
 rename src/Rudzoft.Perft/{Perft => Services}/PerftRunner.cs (93%)
 create mode 100644 src/Rudzoft.Perft/Services/PerftService.cs

diff --git a/Rudzoft.ChessLib.sln b/Rudzoft.ChessLib.sln
index bb310e51..efa0c88b 100644
--- a/Rudzoft.ChessLib.sln
+++ b/Rudzoft.ChessLib.sln
@@ -13,10 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.ChessLib.Perft", "s
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.ChessLib.Perft.Interfaces", "src\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj", "{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.Perft", "src\Rudzoft.Perft\Rudzoft.Perft.csproj", "{8BE6ADD1-7347-412E-B974-02C5866E59A4}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.Perft.Framework", "src\Rudzoft.Perft.Framework\Rudzoft.Perft.Framework.csproj", "{55E449CF-B5AB-4405-8B64-2F1AB1346D90}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.ChessLib.WebApi", "src\Rudzoft.ChessLib.WebApi\Rudzoft.ChessLib.WebApi.csproj", "{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chess-lib", "chess-lib", "{9F0623CB-4463-40FE-874F-4A18D1871A91}"
@@ -37,6 +33,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solutionitems", "solutionit
 		.editorconfig = .editorconfig
 	EndProjectSection
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.Perft", "src\Rudzoft.Perft\Rudzoft.Perft.csproj", "{5E02C821-DB31-4D63-9E0D-681B08BD22E1}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -63,14 +61,6 @@ Global
 		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Release|Any CPU.Build.0 = Release|Any CPU
-		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{8BE6ADD1-7347-412E-B974-02C5866E59A4}.Release|Any CPU.Build.0 = Release|Any CPU
-		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{55E449CF-B5AB-4405-8B64-2F1AB1346D90}.Release|Any CPU.Build.0 = Release|Any CPU
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -83,6 +73,10 @@ Global
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5E02C821-DB31-4D63-9E0D-681B08BD22E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5E02C821-DB31-4D63-9E0D-681B08BD22E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5E02C821-DB31-4D63-9E0D-681B08BD22E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5E02C821-DB31-4D63-9E0D-681B08BD22E1}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -93,11 +87,10 @@ Global
 		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
 		{F253BA49-993B-4119-8226-A6C6DF0B556D} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
 		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
-		{8BE6ADD1-7347-412E-B974-02C5866E59A4} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
-		{55E449CF-B5AB-4405-8B64-2F1AB1346D90} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8} = {F82A6A7E-4551-4667-9E0A-57036E1B5117}
 		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
+		{5E02C821-DB31-4D63-9E0D-681B08BD22E1} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {0B507B2E-EC45-4DDA-94DD-B7CD5A2B8CF3}
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index dbc3a9c6..29211017 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -13,7 +13,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="BenchmarkDotNet" Version="0.13.5"/>
+        <PackageReference Include="BenchmarkDotNet" Version="0.13.7" />
         <PackageReference Include="System.Runtime" Version="4.3.1"/>
     </ItemGroup>
 
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
index 4a98c69d..3ad6a3a2 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
@@ -24,13 +24,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Text.Json;
 using Microsoft.Extensions.DependencyInjection;
 
 namespace Rudzoft.ChessLib.PGN.Test.PgnTests;
 
 public sealed class PgnTests
 {
-    private const string SampleFilePath = @"samples/sample.pgn";
+    private const string SampleFilePath = "samples/sample.pgn";
     private const int ExpectedGameCount = 2;
 
     private readonly IServiceProvider _serviceProvider;
@@ -51,16 +52,19 @@ public async Task ParseFile_WithTestContent_ReturnsCorrectGamesAndMoves()
 
         await foreach (var game in parser.ParseFile(SampleFilePath))
             games.Add(game);
-        
-        Assert.Equal(ExpectedGameCount, games.Count);
 
         var game1 = games[0];
+        var game2 = games[1];
+
+        var g = JsonSerializer.Serialize(game1);
+
+        Assert.Equal(ExpectedGameCount, games.Count);
+        
         Assert.Equal("Test event", game1.Tags["Event"]);
         Assert.Equal(3, game1.Moves.Count);
         Assert.Equal("e4", game1.Moves[0].WhiteMove);
         Assert.Equal("e5", game1.Moves[0].BlackMove);
 
-        var game2 = games[1];
         Assert.Equal("Test event 2", game2.Tags["Event"]);
         Assert.Equal(3, game2.Moves.Count);
         Assert.Equal("d4", game2.Moves[0].WhiteMove);
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index a95a6f34..423aedd6 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -7,13 +7,13 @@
 
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"/>
-        <PackageReference Include="xunit" Version="2.4.2"/>
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
+        <PackageReference Include="xunit" Version="2.5.0" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
-        <PackageReference Include="coverlet.collector" Version="3.2.0">
+        <PackageReference Include="coverlet.collector" Version="6.0.0">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index caa662fa..850c6491 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -24,10 +24,10 @@
 
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
-        <PackageReference Include="xunit" Version="2.4.2"/>
-        <PackageReference Include="xunit.analyzers" Version="1.1.0"/>
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
+        <PackageReference Include="xunit" Version="2.5.0" />
+        <PackageReference Include="xunit.analyzers" Version="1.2.0" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
             <PrivateAssets>all</PrivateAssets>
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
diff --git a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
index 6d3b0613..47092046 100644
--- a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
@@ -77,7 +77,7 @@ public void BackAndForthBasicMoves(string fen, Squares from, Squares to)
         var move = new Move(from, to);
 
         stateIndex++;
-        states.Add(new State());
+        states.Add(new());
 
         pos.MakeMove(move, states[stateIndex]);
 
@@ -111,7 +111,7 @@ public void AdvancedMoves(string fen, Squares from, Squares to, MoveTypes moveTy
         var move = new Move(from, to, moveType);
 
         stateIndex++;
-        states.Add(new State());
+        states.Add(new());
 
         pos.MakeMove(move, states[stateIndex]);
 
@@ -119,7 +119,8 @@ public void AdvancedMoves(string fen, Squares from, Squares to, MoveTypes moveTy
 
         pos.TakeMove(move);
 
-        var finalKey = pos.State.PositionKey;
+        var finalState = pos.State;
+        var finalKey = finalState.PositionKey;
 
         Assert.NotEqual(startKey, moveKey);
         Assert.Equal(startKey, states[0].PositionKey);
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index e716e032..5f92c908 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -122,7 +122,7 @@ public static KpkPosition Create(int idx)
             Results results;
             var stm = new Player((idx >> 12) & 0x01);
             var ksq = new Square[] { (idx >> 0) & 0x3F, (idx >> 6) & 0x3F };
-            var psq = Square.Create(new Rank(Ranks.Rank7.AsInt() - ((idx >> 15) & 0x7)), new File((idx >> 13) & 0x3));
+            var psq = Square.Create(new(Ranks.Rank7.AsInt() - ((idx >> 15) & 0x7)), new((idx >> 13) & 0x3));
 
             // Invalid if two pieces are on the same square or if a king can be captured
             if (ksq[Player.White.Side].Distance(ksq[Player.Black.Side]) <= 1
@@ -152,11 +152,11 @@ public static KpkPosition Create(int idx)
             else
                 results = Results.Unknown;
 
-            return new KpkPosition(
-                results,
-                stm,
-                ksq,
-                psq);
+            return new(
+                Result: results,
+                Stm: stm,
+                KingSquares: ksq,
+                PawnSquare: psq);
         }
 
         /// <summary>
@@ -253,9 +253,9 @@ public bool Probe(bool stngActive, Square skSq, Square wkSq, Square spSq)
     public bool IsDraw(IPosition pos)
     {
         return !Probe(
-            pos.GetPieceSquare(PieceTypes.King, Player.White),
-            pos.Pieces(PieceTypes.Pawn).Lsb(),
-            pos.GetPieceSquare(PieceTypes.King, Player.Black),
-            pos.SideToMove);
+            strongKsq: pos.GetPieceSquare(PieceTypes.King, Player.White),
+            strongPawnSq: pos.Pieces(PieceTypes.Pawn).Lsb(),
+            weakKsq: pos.GetPieceSquare(PieceTypes.King, Player.Black),
+            stm: pos.SideToMove);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
index f020c4a0..f2c41421 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
@@ -8,7 +8,7 @@ public interface ITranspositionTable
     
     /// TranspositionTable::set_size() sets the size of the transposition table,
     /// measured in megabytes.
-    void SetSize(uint mbSize);
+    void SetSize(int mbSize);
 
     /// TranspositionTable::clear() overwrites the entire transposition table
     /// with zeroes. It is called whenever the table is resized, or when the
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index 87ed6e02..01707380 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -44,7 +44,7 @@ public sealed class TranspositionTable : ITranspositionTable
     public TranspositionTable(IOptions<TranspositionTableConfiguration> options)
     {
         var config = options.Value;
-        SetSize((uint)config.DefaultSize);
+        SetSize(config.DefaultSize);
     }
 
     public ulong Hits { get; private set; }
@@ -54,7 +54,7 @@ public TranspositionTable(IOptions<TranspositionTableConfiguration> options)
     /// measured in megabytes.
     /// </summary>
     /// <param name="mbSize"></param>
-    public void SetSize(uint mbSize)
+    public void SetSize(int mbSize)
     {
         var newSize = 1024u;
 
@@ -62,7 +62,7 @@ public void SetSize(uint mbSize)
         // of ClusterSize number of TTEntries. Each non-empty entry contains
         // information of exactly one position and newSize is the number of
         // clusters we are going to allocate.
-        while (2UL * newSize * 64 <= mbSize << 20)
+        while (2UL * newSize * 64 <= (ulong)(mbSize << 20))
             newSize *= 2;
 
         if (newSize == _size)
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index e4c0f243..1cdb3ff0 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Text;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -48,6 +49,8 @@ public interface IPosition : IEnumerable<Piece>
 
     IBoard Board { get; }
 
+    IZobrist Zobrist { get; }
+    
     IValues Values { get; }
 
     BitBoard Checkers { get; }
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 8a4677e1..fecf6918 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -54,7 +54,6 @@ public sealed class Position : IPosition
     private readonly CastleRight[] _castlingRightsMask;
     private readonly Square[] _castlingRookSquare;
     private readonly Value[] _nonPawnMaterial;
-    private readonly IZobrist _zobrist;
     private readonly ICuckoo _cuckoo;
     private readonly ObjectPool<StringBuilder> _outputObjectPool;
     private readonly ObjectPool<IMoveList> _moveListPool;
@@ -81,7 +80,7 @@ public Position(
         Board = board;
         IsProbing = true;
         Values = values;
-        _zobrist = zobrist;
+        Zobrist = zobrist;
         _cuckoo = cuckoo;
         State = new();
         Clear();
@@ -89,6 +88,8 @@ public Position(
 
     public IBoard Board { get; }
 
+    public IZobrist Zobrist { get; }
+
     public BitBoard Checkers => State.Checkers;
 
     public ChessMode ChessMode { get; set; }
@@ -393,13 +394,13 @@ public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public HashKey GetPawnKey()
     {
-        var result = _zobrist.ZobristNoPawn;
+        var result = Zobrist.ZobristNoPawn;
         var pieces = Board.Pieces(PieceTypes.Pawn);
         while (pieces)
         {
             var sq = BitBoards.PopLsb(ref pieces);
             var pc = GetPiece(sq);
-            result ^= _zobrist.Psq(sq, pc);
+            result ^= Zobrist.Psq(sq, pc);
         }
 
         return result;
@@ -417,7 +418,7 @@ public HashKey GetPiecesKey()
         {
             var sq = BitBoards.PopLsb(ref pieces);
             var pc = Board.PieceAt(sq);
-            result ^= _zobrist.Psq(sq, pc);
+            result ^= Zobrist.Psq(sq, pc);
         }
 
         return result;
@@ -685,7 +686,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
     {
         State.LastMove = m;
 
-        var k = State.PositionKey ^ _zobrist.Side();
+        var k = State.PositionKey ^ Zobrist.Side();
 
         State = State.CopyTo(newState);
         var state = State;
@@ -715,7 +716,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
 
             var (rookFrom, rookTo) = DoCastle(us, from, ref to, CastlePerform.Do);
 
-            k ^= _zobrist.Psq(rookFrom, capturedPiece) ^ _zobrist.Psq(rookTo, capturedPiece);
+            k ^= Zobrist.Psq(rookFrom, capturedPiece) ^ Zobrist.Psq(rookTo, capturedPiece);
 
             // reset captured piece type as castleling is "king-captures-rook"
             capturedPiece = Piece.EmptyPiece;
@@ -738,7 +739,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                     Debug.Assert(GetPiece(captureSquare) == pt.MakePiece(them));
                 }
 
-                state.PawnKey ^= _zobrist.Psq(captureSquare, capturedPiece);
+                state.PawnKey ^= Zobrist.Psq(captureSquare, capturedPiece);
             }
             else
             {
@@ -750,7 +751,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             if (type == MoveTypes.Enpassant)
                 Board.ClearPiece(captureSquare);
 
-            k ^= _zobrist.Psq(captureSquare, capturedPiece);
+            k ^= Zobrist.Psq(captureSquare, capturedPiece);
 
             // TODO : Update other depending keys and psq values here
 
@@ -759,12 +760,12 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         }
 
         // update key with moved piece
-        k ^= _zobrist.Psq(from, pc) ^ _zobrist.Psq(to, pc);
+        k ^= Zobrist.Psq(from, pc) ^ Zobrist.Psq(to, pc);
 
         // reset en-passant square if it is set
         if (state.EnPassantSquare != Square.None)
         {
-            k ^= _zobrist.EnPassant(state.EnPassantSquare);
+            k ^= Zobrist.EnPassant(state.EnPassantSquare);
             state.EnPassantSquare = Square.None;
         }
 
@@ -772,9 +773,9 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         if (state.CastlelingRights != CastleRight.None &&
             (_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]) != CastleRight.None)
         {
-            k ^= _zobrist.Castleling(state.CastlelingRights);
+            k ^= Zobrist.Castleling(state.CastlelingRights);
             state.CastlelingRights &= ~(_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]);
-            k ^= _zobrist.Castleling(state.CastlelingRights);
+            k ^= Zobrist.Castleling(state.CastlelingRights);
         }
 
         // Move the piece. The tricky Chess960 castle is handled earlier
@@ -789,7 +790,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 && ((to - us.PawnPushDistance()).PawnAttack(us) & Pieces(PieceTypes.Pawn, them)).IsNotEmpty)
             {
                 state.EnPassantSquare = to - us.PawnPushDistance();
-                k ^= _zobrist.EnPassant(state.EnPassantSquare.File);
+                k ^= Zobrist.EnPassant(state.EnPassantSquare.File);
             }
             else if (type == MoveTypes.Promotion)
             {
@@ -802,14 +803,14 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 AddPiece(promotionPiece, to);
 
                 // Update hash keys
-                k ^= _zobrist.Psq(to, pc) ^ _zobrist.Psq(to, promotionPiece);
-                state.PawnKey ^= _zobrist.Psq(to, pc);
+                k ^= Zobrist.Psq(to, pc) ^ Zobrist.Psq(to, promotionPiece);
+                state.PawnKey ^= Zobrist.Psq(to, pc);
 
                 _nonPawnMaterial[us.Side] += Values.GetPieceValue(promotionPiece, Phases.Mg);
             }
 
             // Update pawn hash key
-            state.PawnKey ^= _zobrist.Psq(from, pc) ^ _zobrist.Psq(to, pc);
+            state.PawnKey ^= Zobrist.Psq(from, pc) ^ Zobrist.Psq(to, pc);
 
             // Reset rule 50 draw counter
             state.Rule50 = 0;
@@ -842,11 +843,11 @@ public void MakeNullMove(in State newState)
 
         if (State.EnPassantSquare != Square.None)
         {
-            State.PositionKey ^= _zobrist.EnPassant(State.EnPassantSquare);
+            State.PositionKey ^= Zobrist.EnPassant(State.EnPassantSquare);
             State.EnPassantSquare = Square.None;
         }
 
-        State.PositionKey ^= _zobrist.Side();
+        State.PositionKey ^= Zobrist.Side();
 
         ++State.Rule50;
         State.PliesFromNull = 0;
@@ -1365,14 +1366,14 @@ public HashKey MovePositionKey(Move m)
         Debug.Assert(m.IsValidMove());
 
         var movePositionKey = State.PositionKey
-                              ^ _zobrist.Side()
-                              ^ _zobrist.Psq(m.FromSquare(), Board.MovedPiece(m))
-                              ^ _zobrist.Psq(m.ToSquare(), Board.MovedPiece(m));
+                              ^ Zobrist.Side()
+                              ^ Zobrist.Psq(m.FromSquare(), Board.MovedPiece(m))
+                              ^ Zobrist.Psq(m.ToSquare(), Board.MovedPiece(m));
 
         if (!Board.IsEmpty(m.ToSquare()))
-            movePositionKey ^= _zobrist.Psq(m.ToSquare(), Board.PieceAt(m.ToSquare()));
+            movePositionKey ^= Zobrist.Psq(m.ToSquare(), Board.PieceAt(m.ToSquare()));
 
-        movePositionKey ^= _zobrist.EnPassant(State.EnPassantSquare);
+        movePositionKey ^= Zobrist.EnPassant(State.EnPassantSquare);
 
         return movePositionKey;
     }
@@ -1476,7 +1477,7 @@ private void SetState(State state)
     {
         var k = HashKey.Empty;
         state.MaterialKey = HashKey.Empty;
-        state.PawnKey = _zobrist.ZobristNoPawn;
+        state.PawnKey = Zobrist.ZobristNoPawn;
         
         _nonPawnMaterial[Player.White.Side] = _nonPawnMaterial[Player.Black.Side] = Value.ValueZero;
         State.Checkers = AttacksTo(GetKingSquare(_sideToMove)) & Board.Pieces(~_sideToMove);
@@ -1487,22 +1488,22 @@ private void SetState(State state)
         {
             var sq = BitBoards.PopLsb(ref b);
             var pc = Board.PieceAt(sq);
-            k ^= _zobrist.Psq(sq, pc);
+            k ^= Zobrist.Psq(sq, pc);
         }
 
         if (state.EnPassantSquare != Square.None)
-            k ^= _zobrist.EnPassant(state.EnPassantSquare);
+            k ^= Zobrist.EnPassant(state.EnPassantSquare);
 
         if (SideToMove.IsBlack)
-            k ^= _zobrist.Side();
+            k ^= Zobrist.Side();
 
-        state.PositionKey = k ^ _zobrist.Castleling(state.CastlelingRights);
+        state.PositionKey = k ^ Zobrist.Castleling(state.CastlelingRights);
 
         for (var b = Pieces(PieceTypes.Pawn); b.IsNotEmpty;)
         {
             var sq = BitBoards.PopLsb(ref b);
             var pc = Board.PieceAt(sq);
-            state.MaterialKey ^= _zobrist.Psq(sq, pc);
+            state.MaterialKey ^= Zobrist.Psq(sq, pc);
         }
 
         foreach (var pc in Piece.All.AsSpan())
@@ -1514,7 +1515,7 @@ private void SetState(State state)
             }
             
             for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
-                state.MaterialKey ^= _zobrist.Psq(cnt, pc);
+                state.MaterialKey ^= Zobrist.Psq(cnt, pc);
         }
 
         // // compute hash keys
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index e3975803..a0dc3856 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -49,7 +49,7 @@ public Uci()
     {
         var policy = new StringBuilderPooledObjectPolicy();
         _pvPool = new DefaultObjectPool<StringBuilder>(policy, 128);
-        _options = new Dictionary<string, IOption>();
+        _options = new();
     }
 
     public int MaxThreads { get; set; }
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index d4a55eb5..c497fa5c 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -55,7 +55,7 @@
         <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0"/>
         <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0"/>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5"/>
+        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.10" />
         <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0"/>
         <PackageReference Include="ZString" Version="2.5.0"/>
     </ItemGroup>
diff --git a/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs b/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
deleted file mode 100644
index ca1880e8..00000000
--- a/src/Rudzoft.Perft.Framework/Environment/FrameworkEnvironment.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-Perft, a chess perft test library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using System.Diagnostics;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Runtime.Versioning;
-using System.Security.Principal;
-
-namespace Perft.Environment;
-
-public sealed class FrameworkEnvironment : IFrameworkEnvironment
-{
-    [DllImport("libc")]
-    // ReSharper disable once IdentifierTypo
-    public static extern uint getuid();
-
-    private static readonly string FrameWork = Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
-
-    public bool IsDevelopment { get; set; }
-
-    public string Configuration => IsDevelopment ? "Development" : "Production";
-
-    public string DotNetFrameWork => FrameWork;
-
-    public string Os => RuntimeInformation.OSDescription;
-
-    public bool IsAdmin => CheckIsAdmin();
-
-    public bool HighresTimer => Stopwatch.IsHighResolution;
-
-    public FrameworkEnvironment()
-    {
-#if RELEASE
-        IsDevelopment = false;
-#endif
-    }
-
-    private static bool CheckIsAdmin()
-    {
-        try
-        {
-            // Perform OS check
-            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-                return getuid() == 0;
-
-            using var identity = WindowsIdentity.GetCurrent();
-            var principal = new WindowsPrincipal(identity);
-            return principal.IsInRole(WindowsBuiltInRole.Administrator);
-        }
-        catch (Exception ex)
-        {
-            throw new ApplicationException("Unable to determine administrator or root status", ex);
-        }
-    }
-}
diff --git a/src/Rudzoft.Perft.Framework/Environment/IFrameworkEnvironment.cs b/src/Rudzoft.Perft.Framework/Environment/IFrameworkEnvironment.cs
deleted file mode 100644
index ecbe5c03..00000000
--- a/src/Rudzoft.Perft.Framework/Environment/IFrameworkEnvironment.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-Perft, a chess perft test library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-namespace Perft.Environment;
-
-public interface IFrameworkEnvironment
-{
-    bool IsDevelopment { get; set; }
-
-    string Configuration { get; }
-
-    string DotNetFrameWork { get; }
-
-    string Os { get; }
-
-    bool IsAdmin { get; }
-
-    bool HighresTimer { get; }
-}
diff --git a/src/Rudzoft.Perft.Framework/Extensions/ArrayExtensions.cs b/src/Rudzoft.Perft.Framework/Extensions/ArrayExtensions.cs
deleted file mode 100644
index a940aa03..00000000
--- a/src/Rudzoft.Perft.Framework/Extensions/ArrayExtensions.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-Perft, a chess perft test library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-namespace Perft.Extensions;
-
-using System.Collections.Generic;
-using System.Linq;
-
-public static class ArrayExtensions
-{
-    public static bool IsEmpty<T>(T[] arr)
-    {
-        return arr == null || arr.Length == 0;
-    }
-
-    public static IEnumerable<T> Append<T>(this IEnumerable<T> @this, T toAppend)
-    {
-        return @this.Concat(new[] { toAppend });
-    }
-}
diff --git a/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs b/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
deleted file mode 100644
index 80a22f2a..00000000
--- a/src/Rudzoft.Perft.Framework/Extensions/ObjectExtensions.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-Perft, a chess perft test library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-namespace Perft.Extensions;
-
-public static class ObjectExtensions
-{
-    /// <summary>
-    /// Concats parameters onto an object.
-    /// The resulting array is a copy of the objects, which makes this fast for small array sizes.
-    /// </summary>
-    /// <param name="this"></param>
-    /// <param name="args"></param>
-    /// <returns></returns>
-    public static object[] ConcatParametersCopy(this object @this, params object[] args)
-    {
-        var result = new object[args.Length + 1];
-        result[0] = @this;
-        // forced to use Array copy as there is no way to get a byte size of an managed object in c#
-        // as per : http://blogs.msdn.com/cbrumme/archive/2003/04/15/51326.aspx
-        Array.Copy(args, 0, result, 1, args.Length);
-        return result;
-    }
-
-    /// <summary>
-    /// Concats parameters onto an object.
-    /// The resulting array is deferred object which means this will potentially be faster for larger object arrays
-    /// </summary>
-    /// <param name="this"></param>
-    /// <param name="args"></param>
-    /// <returns></returns>
-    public static IEnumerable<object> ConcatParameters(this object @this, params object[] args)
-    {
-        return new[] { @this }.Concat(args);
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft.Framework/Factories/ConfigurationFactory.cs b/src/Rudzoft.Perft.Framework/Factories/ConfigurationFactory.cs
deleted file mode 100644
index ad350414..00000000
--- a/src/Rudzoft.Perft.Framework/Factories/ConfigurationFactory.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-Perft, a chess perft test library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Microsoft.Extensions.Configuration;
-
-namespace Perft.Factories;
-
-public static class ConfigurationFactory
-{
-    public static IConfiguration CreateConfiguration(IConfigurationBuilder configurationBuilder)
-    {
-        return configurationBuilder.Build();
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj b/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
deleted file mode 100644
index 83fc19e2..00000000
--- a/src/Rudzoft.Perft.Framework/Rudzoft.Perft.Framework.csproj
+++ /dev/null
@@ -1,32 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <RootNamespace>Perft</RootNamespace>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <Compile Remove="Enviroment\**" />
-    <EmbeddedResource Remove="Enviroment\**" />
-    <None Remove="Enviroment\**" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="6.1.1" />
-    <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
-    <PackageReference Include="Serilog" Version="2.12.0" />
-    <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
-    <PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
-    <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Folder Include="Factories\" />
-  </ItemGroup>
-
-</Project>
diff --git a/src/Rudzoft.Perft/Actors/EpdParserActor.cs b/src/Rudzoft.Perft/Actors/EpdParserActor.cs
new file mode 100644
index 00000000..1d7e4a28
--- /dev/null
+++ b/src/Rudzoft.Perft/Actors/EpdParserActor.cs
@@ -0,0 +1,20 @@
+using Akka.Actor;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.Perft.Parsers;
+
+namespace Rudzoft.Perft.Actors;
+
+public sealed class EpdParserActor : ReceiveActor
+{
+    public EpdParserActor(IServiceProvider sp)
+    {
+        var epdParser = sp.CreateScope().ServiceProvider.GetRequiredService<IEpdParser>();
+        
+        ReceiveAsync<string>(async filename =>
+        {
+            await foreach(var parsed in epdParser.Parse(filename).ConfigureAwait(false))
+                Sender.Tell(parsed);
+        });
+    }
+
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/PerftActor.cs b/src/Rudzoft.Perft/Actors/PerftActor.cs
new file mode 100644
index 00000000..7eed29e2
--- /dev/null
+++ b/src/Rudzoft.Perft/Actors/PerftActor.cs
@@ -0,0 +1,35 @@
+using Akka.Actor;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.Perft.Options;
+using Rudzoft.Perft.Services;
+
+namespace Rudzoft.Perft.Actors;
+
+public sealed class PerftActor : ReceiveActor
+{
+    public sealed record StartPerft;
+    
+    private readonly IServiceProvider _sp;
+    private readonly IPerftRunner _perftRunner;
+    
+    public PerftActor(IServiceProvider sp)
+    {
+        _sp = sp;
+        _perftRunner = _sp.GetRequiredService<IPerftRunner>();
+        var optionsFactory = _sp.GetRequiredService<IOptionsFactory>();
+        foreach (var option in optionsFactory.Parse())
+        {
+            if (option.Type == OptionType.TTOptions)
+                _perftRunner.TranspositionTableOptions = option.PerftOptions;
+            else
+                _perftRunner.Options = option.PerftOptions;
+        }
+        
+        ReceiveAsync<StartPerft>(_ => _perftRunner.Run());
+    }
+
+    public static Props Prop(IServiceProvider sp)
+    {
+        return Props.Create<PerftActor>(sp);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/PerftComputeActor.cs b/src/Rudzoft.Perft/Actors/PerftComputeActor.cs
new file mode 100644
index 00000000..96c63eab
--- /dev/null
+++ b/src/Rudzoft.Perft/Actors/PerftComputeActor.cs
@@ -0,0 +1,70 @@
+// using System.Diagnostics;
+// using Akka.Actor;
+// using Microsoft.Extensions.DependencyInjection;
+// using Rudzoft.ChessLib.Perft.Interfaces;
+// using Rudzoft.Perft2.Models;
+//
+// namespace Rudzoft.Perft2.Actors;
+//
+// public sealed class PerftComputeActor : ReceiveActor
+// {
+//     public sealed record PerftPositions(List<PerftPosition> Positions);
+//     
+//     private readonly IPerft _perft;
+//
+//     public PerftComputeActor(IServiceProvider sp)
+//     {
+//         _perft = sp.GetRequiredService<IPerft>();
+//         ReceiveAsync<PerftPositions>(async pp =>
+//         {
+//             var result = await ComputePerft(pp.Positions).ConfigureAwait(false);
+//             Sender.Tell(result);
+//         });
+//     }
+//     
+//     private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken)
+//     {
+//         var result = _resultPool.Get();
+//         result.Clear();
+//
+//         var pp = _perft.Positions[^1];
+//         var baseFileName = SaveResults
+//             ? Path.Combine(CurrentDirectory.Value, $"{FixFileName(pp.Fen)}[")
+//             : string.Empty;
+//
+//         var errors = 0;
+//
+//         result.Fen = pp.Fen;
+//         _perft.SetGamePosition(pp);
+//         _perft.BoardPrintCallback(_perft.GetBoard());
+//         _log.Information("Fen         : {Fen}", pp.Fen);
+//         _log.Information(Line);
+//
+//         foreach (var (depth, expected) in pp.Value)
+//         {
+//             cancellationToken.ThrowIfCancellationRequested();
+//
+//             _log.Information("Depth       : {Depth}", depth);
+//
+//             var start = Stopwatch.GetTimestamp();
+//
+//             var perftResult = await _perft.DoPerftAsync(depth).ConfigureAwait(false);
+//             var elapsedMs = Stopwatch.GetElapsedTime(start);
+//
+//             ComputeResults(in perftResult, depth, in expected, in elapsedMs, result);
+//
+//             errors += LogResults(result);
+//
+//             if (baseFileName.IsNullOrEmpty())
+//                 continue;
+//
+//             await WriteOutput(result, baseFileName, cancellationToken);
+//         }
+//
+//         _log.Information("{Info} parsing complete. Encountered {Errors} errors", _usingEpd ? "EPD" : "FEN", errors);
+//
+//         result.Errors = errors;
+//
+//         return result;
+//     }
+// }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/PerftRunner.cs b/src/Rudzoft.Perft/Actors/PerftRunner.cs
new file mode 100644
index 00000000..1b34f186
--- /dev/null
+++ b/src/Rudzoft.Perft/Actors/PerftRunner.cs
@@ -0,0 +1,21 @@
+using Akka.Actor;
+using Rudzoft.Perft.Options;
+
+namespace Rudzoft.Perft.Actors;
+
+public sealed class PerftRunner : ReceiveActor
+{
+    public PerftRunner()
+    {
+        Receive<EpdOptions>(epd =>
+        {
+            
+        });
+        
+        Receive<FenOptions>(epd =>
+        {
+            
+        });
+    }
+    
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/ProgressActor.cs b/src/Rudzoft.Perft/Actors/ProgressActor.cs
new file mode 100644
index 00000000..e02aa2e9
--- /dev/null
+++ b/src/Rudzoft.Perft/Actors/ProgressActor.cs
@@ -0,0 +1,42 @@
+using System.Runtime.InteropServices;
+using Akka.Actor;
+
+namespace Rudzoft.Perft.Actors;
+
+public sealed class ProgressActor : ReceiveActor, IWithTimers, IWithStash
+{
+    private sealed class ProgressInternal
+    {
+        public string Fen { get; init; }
+        public int CurrentDepth { get; set; }
+        public int MaxDepth { get; }
+        public TimeSpan StartTime { get; }
+    }
+
+    public sealed record PerftProgress(string Fen, int Depth);
+
+    private readonly Dictionary<string, ProgressInternal> _progress = new();
+
+    public ITimerScheduler Timers { get; set; }
+    public IStash Stash { get; set; }
+
+    public ProgressActor()
+    {
+        Receive<PerftProgress>(progress =>
+        {
+            ref var existing = ref CollectionsMarshal.GetValueRefOrAddDefault(_progress, progress.Fen, out var exists);
+
+            if (!exists)
+            {
+                existing = new();
+                existing.CurrentDepth = progress.Depth;
+                
+            }
+            else
+            {
+                existing = new ProgressInternal();
+            }
+            
+        });
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Host/CommandLineArgs.cs b/src/Rudzoft.Perft/Host/CommandLineArgs.cs
deleted file mode 100644
index fb50f255..00000000
--- a/src/Rudzoft.Perft/Host/CommandLineArgs.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-Perft, a chess perft testing application
-
-MIT License
-
-Copyright (c) 2019-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-namespace Rudzoft.Perft.Host;
-
-public record CommandLineArgs(string[] Args);
diff --git a/src/Rudzoft.Perft/Host/PerftHost.cs b/src/Rudzoft.Perft/Host/PerftHost.cs
deleted file mode 100644
index 0fc3d6f4..00000000
--- a/src/Rudzoft.Perft/Host/PerftHost.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-Perft, a chess perft testing application
-
-MIT License
-
-Copyright (c) 2019-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Microsoft.Extensions.Hosting;
-using Perft.Environment;
-using Rudzoft.Perft.Options;
-using Rudzoft.Perft.Perft;
-using Serilog;
-
-namespace Rudzoft.Perft.Host;
-
-public sealed class PerftHost : IHostedService
-{
-    private readonly IPerftRunner _perftRunner;
-    private readonly ILogger _logger;
-
-    public PerftHost(
-        IOptionsFactory optionsFactory,
-        IPerftRunner perftRunner,
-        IFrameworkEnvironment environment,
-        ILogger logger)
-    {
-        _perftRunner = perftRunner;
-        _logger = logger;
-        foreach (var option in optionsFactory.Parse())
-        {
-            if (option.Type == OptionType.TTOptions)
-                _perftRunner.TranspositionTableOptions = option.PerftOptions;
-            else
-                _perftRunner.Options = option.PerftOptions;
-        }
-        logger.Information("Perft Framework started in {0}...", environment.Configuration);
-
-    }
-    
-    public async Task StartAsync(CancellationToken cancellationToken)
-    {
-        var errors = await _perftRunner.Run(cancellationToken);
-        if (errors != 0)
-            _logger.Error("Total errors: {0}", errors);
-    }
-
-    public Task StopAsync(CancellationToken cancellationToken)
-    {
-        _logger.Information("Stopped");
-        return Task.CompletedTask;
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Models/CommandLineArgs.cs b/src/Rudzoft.Perft/Models/CommandLineArgs.cs
new file mode 100644
index 00000000..54413cf5
--- /dev/null
+++ b/src/Rudzoft.Perft/Models/CommandLineArgs.cs
@@ -0,0 +1,3 @@
+namespace Rudzoft.Perft.Models;
+
+public sealed record CommandLineArgs(string[] Args);
diff --git a/src/Rudzoft.Perft/Perft/IPerftResult.cs b/src/Rudzoft.Perft/Models/IPerftResult.cs
similarity index 97%
rename from src/Rudzoft.Perft/Perft/IPerftResult.cs
rename to src/Rudzoft.Perft/Models/IPerftResult.cs
index cd052f18..a7df8533 100644
--- a/src/Rudzoft.Perft/Perft/IPerftResult.cs
+++ b/src/Rudzoft.Perft/Models/IPerftResult.cs
@@ -24,7 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-namespace Rudzoft.Perft.Perft;
+namespace Rudzoft.Perft.Models;
 
 public interface IPerftResult
 {
diff --git a/src/Rudzoft.Perft/Perft/PerftResult.cs b/src/Rudzoft.Perft/Models/PerftResult.cs
similarity index 98%
rename from src/Rudzoft.Perft/Perft/PerftResult.cs
rename to src/Rudzoft.Perft/Models/PerftResult.cs
index a2527a6d..54a14a58 100644
--- a/src/Rudzoft.Perft/Perft/PerftResult.cs
+++ b/src/Rudzoft.Perft/Models/PerftResult.cs
@@ -24,7 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-namespace Rudzoft.Perft.Perft;
+namespace Rudzoft.Perft.Models;
 
 public sealed class PerftResult : IPerftResult
 {
diff --git a/src/Rudzoft.Perft/Options/OptionsFactory.cs b/src/Rudzoft.Perft/Options/OptionsFactory.cs
index ad2f1e60..3eb45f3f 100644
--- a/src/Rudzoft.Perft/Options/OptionsFactory.cs
+++ b/src/Rudzoft.Perft/Options/OptionsFactory.cs
@@ -25,20 +25,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using CommandLine;
-using Rudzoft.Perft.Host;
-using Serilog;
+using Rudzoft.Perft.Models;
 
 namespace Rudzoft.Perft.Options;
 
 public sealed class OptionsFactory : IOptionsFactory
 {
     private readonly string[] _args;
-    private readonly ILogger _logger;
 
-    public OptionsFactory(CommandLineArgs args, ILogger logger)
+    public OptionsFactory(CommandLineArgs args)
     {
         _args = args.Args;
-        _logger = logger;
     }
     
     public IEnumerable<PerftOption> Parse()
@@ -82,13 +79,11 @@ public IEnumerable<PerftOption> Parse()
             yield break;
 
         if (optionsUsed.HasFlagFast(OptionType.EdpOptions))
-            yield return new PerftOption(OptionType.EdpOptions, options);
+            yield return new(OptionType.EdpOptions, options!);
         else
-            yield return new PerftOption(OptionType.FenOptions, options);
+            yield return new(OptionType.FenOptions, options!);
 
         if (optionsUsed.HasFlagFast(OptionType.TTOptions))
-            yield return new PerftOption(OptionType.TTOptions, ttOptions);
-
-
+            yield return new(OptionType.TTOptions, ttOptions!);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Options/TTOptions.cs b/src/Rudzoft.Perft/Options/TTOptions.cs
index 33483a07..243a752d 100644
--- a/src/Rudzoft.Perft/Options/TTOptions.cs
+++ b/src/Rudzoft.Perft/Options/TTOptions.cs
@@ -35,5 +35,5 @@ public class TTOptions : IPerftOptions
     public bool Use { get; set; }
 
     [Option('s', "size", Required = false, Default = 32, HelpText = "Set the size of the transposition table in mb")]
-    public uint Size { get; set; }
+    public int Size { get; set; }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Parsers/EpdParser.cs b/src/Rudzoft.Perft/Parsers/EpdParser.cs
index 552f68e6..1bb3ec8a 100644
--- a/src/Rudzoft.Perft/Parsers/EpdParser.cs
+++ b/src/Rudzoft.Perft/Parsers/EpdParser.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Perft.Interfaces;
 
@@ -32,22 +33,12 @@ namespace Rudzoft.Perft.Parsers;
 /// <summary>
 /// Fast epd file parser
 /// </summary>
-public class EpdParser : IEpdParser
+public sealed class EpdParser : IEpdParser
 {
-    public EpdParser(IEpdParserSettings settings)
-    {
-        Settings = settings;
-    }
-
-    public List<IEpdSet> Sets { get; set; }
-
-    public IEpdParserSettings Settings { get; set; }
-
-    public async Task<ulong> ParseAsync()
+    public async IAsyncEnumerable<IEpdSet> Parse(string epdFile)
     {
         const char space = ' ';
-        Sets = new List<IEpdSet>();
-        await using var fs = File.Open(Settings.Filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+        await using var fs = File.Open(epdFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
         await using var bs = new BufferedStream(fs);
         using var sr = new StreamReader(bs);
         var id = string.Empty;
@@ -62,7 +53,7 @@ public async Task<ulong> ParseAsync()
             {
                 if (idSet && epdSet)
                 {
-                    Sets.Add(new EpdSet { Epd = epd, Id = id, Perft = perftData.Select(ParsePerftLines).ToList() });
+                    yield return new EpdSet { Epd = epd, Id = id, Perft = perftData.Select(ParsePerftLines).ToList() };
                     id = epd = string.Empty;
                     idSet = epdSet = false;
                     perftData.Clear();
@@ -92,21 +83,15 @@ public async Task<ulong> ParseAsync()
                 perftData.Add(s[firstSpace..]);
             }
         }
-
-        return (ulong)Sets.Count;
-    }
-
-    public ulong Parse()
-    {
-        throw new NotImplementedException();
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static PerftPositionValue ParsePerftLines(string perftData)
     {
         var s = perftData.Split(' ', StringSplitOptions.RemoveEmptyEntries);
         var result = (depth: 0, count: ulong.MinValue);
         Maths.ToIntegral(s[0], out result.depth);
         Maths.ToIntegral(s[1], out result.count);
-        return new PerftPositionValue(result.depth, result.count);
+        return new(result.depth, result.count);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Parsers/IEpdParser.cs b/src/Rudzoft.Perft/Parsers/IEpdParser.cs
index fbb2bb71..9c23f225 100644
--- a/src/Rudzoft.Perft/Parsers/IEpdParser.cs
+++ b/src/Rudzoft.Perft/Parsers/IEpdParser.cs
@@ -28,8 +28,5 @@ namespace Rudzoft.Perft.Parsers;
 
 public interface IEpdParser
 {
-    List<IEpdSet> Sets { get; set; }
-    IEpdParserSettings Settings { get; set; }
-    Task<ulong> ParseAsync();
-    ulong Parse();
+    IAsyncEnumerable<IEpdSet> Parse(string epdFile);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Program.cs b/src/Rudzoft.Perft/Program.cs
index 952be1e7..69800aa4 100644
--- a/src/Rudzoft.Perft/Program.cs
+++ b/src/Rudzoft.Perft/Program.cs
@@ -1,75 +1,17 @@
-/*
-Perft, a chess perft testing application
-
-MIT License
-
-Copyright (c) 2019-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.Hosting;
 using Microsoft.Extensions.ObjectPool;
-using Perft.Environment;
 using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.Perft;
 using Rudzoft.ChessLib.Perft.Interfaces;
-using Rudzoft.Perft.Host;
+using Rudzoft.Perft.Models;
 using Rudzoft.Perft.Options;
 using Rudzoft.Perft.Parsers;
-using Rudzoft.Perft.Perft;
-using Rudzoft.Perft.TimeStamp;
+using Rudzoft.Perft.Services;
 using Serilog;
 
-
-static IConfigurationBuilder ConfigurationBuilder()
-{
-#if RELEASE
-        const string envName = "Production";
-#else
-    const string envName = "Development";
-#endif
-    // Create our configuration sources
-    return new ConfigurationBuilder()
-        // Add environment variables
-        .AddEnvironmentVariables()
-        // Set base path for Json files as the startup location of the application
-        .SetBasePath(Directory.GetCurrentDirectory())
-        // Add application settings json files
-        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
-        .AddJsonFile($"appsettings.{envName}.json", optional: true, reloadOnChange: true);
-}
-
-static ILogger ConfigureLogger(IConfiguration configuration)
-{
-    // Apply the config to the logger
-    Log.Logger = new LoggerConfiguration()
-        .ReadFrom.Configuration(configuration)
-        .Enrich.WithThreadId()
-        .Enrich.FromLogContext()
-        .CreateLogger();
-    AppDomain.CurrentDomain.ProcessExit += static (_, _) => Log.CloseAndFlush();
-    return Log.Logger;
-}
-
 var host = new HostBuilder()
     .ConfigureServices((_, services) =>
     {
@@ -82,8 +24,7 @@ static ILogger ConfigureLogger(IConfiguration configuration)
         services.AddSingleton(ConfigureLogger(configuration));
 
         services.AddSingleton<IBuildTimeStamp, BuildTimeStamp>();
-        services.AddSingleton<IFrameworkEnvironment, FrameworkEnvironment>();
-        services.AddTransient<IPerft, Rudzoft.ChessLib.Perft.Perft>();
+        services.AddTransient<IPerft, Perft>();
         services.AddTransient<IPerftRunner, PerftRunner>();
         services.AddSingleton<IOptionsFactory, OptionsFactory>();
 
@@ -99,8 +40,39 @@ static ILogger ConfigureLogger(IConfiguration configuration)
             return provider.Create(policy);
         });
 
-        services.AddHostedService<PerftHost>();
+        services.AddHostedService<PerftService>();
     })
     .Build();
 
-host.Run();
\ No newline at end of file
+host.Run();
+return;
+
+static ILogger ConfigureLogger(IConfiguration configuration)
+{
+    // Apply the config to the logger
+    Log.Logger = new LoggerConfiguration()
+        .ReadFrom.Configuration(configuration)
+        .Enrich.WithThreadId()
+        .Enrich.FromLogContext()
+        .CreateLogger();
+    AppDomain.CurrentDomain.ProcessExit += static (_, _) => Log.CloseAndFlush();
+    return Log.Logger;
+}
+
+static IConfigurationBuilder ConfigurationBuilder()
+{
+#if RELEASE
+    const string envName = "Production";
+#else
+    const string envName = "Development";
+#endif
+    // Create our configuration sources
+    return new ConfigurationBuilder()
+        // Add environment variables
+        .AddEnvironmentVariables()
+        // Set base path for Json files as the startup location of the application
+        .SetBasePath(Directory.GetCurrentDirectory())
+        // Add application settings json files
+        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
+        .AddJsonFile($"appsettings.{envName}.json", optional: true, reloadOnChange: false);
+}
diff --git a/src/Rudzoft.Perft/Properties/launchSettings.json b/src/Rudzoft.Perft/Properties/launchSettings.json
deleted file mode 100644
index 4ad40311..00000000
--- a/src/Rudzoft.Perft/Properties/launchSettings.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "profiles": {
-    "Perft": {
-      "commandName": "Project",
-      "commandLineArgs": "epd -f D:\\perft-random.epd"
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index c945d98a..3439814f 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -1,57 +1,36 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
     <PropertyGroup>
         <OutputType>Exe</OutputType>
-        <Platforms>AnyCPU</Platforms>
-        <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
-        <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
-        <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
-    </PropertyGroup>
-
-    <PropertyGroup Condition="'$(IsWindows)'=='true'">
-        <DefineConstants>Windows</DefineConstants>
-    </PropertyGroup>
-    <PropertyGroup Condition="'$(IsOSX)'=='true'">
-        <DefineConstants>OSX</DefineConstants>
-    </PropertyGroup>
-    <PropertyGroup Condition="'$(IsLinux)'=='true'">
-        <DefineConstants>Linux</DefineConstants>
-    </PropertyGroup>
-    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-        <PlatformTarget>x64</PlatformTarget>
     </PropertyGroup>
 
     <ItemGroup>
-        <None Remove="appsettings.json"/>
+      <PackageReference Include="Akka" Version="1.5.13-beta1" />
+      <PackageReference Include="Akka.DependencyInjection" Version="1.5.13-beta1" />
+      <PackageReference Include="CommandLineParser" Version="2.9.1" />
+      <PackageReference Include="Serilog" Version="3.0.1" />
+      <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0"/>
+      <PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/>
+      <PackageReference Include="Serilog.Settings.Configuration" Version="7.0.1" />
+      <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/>
+      <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
+      <PackageReference Include="Timestamp" Version="1.0.2"/>
     </ItemGroup>
 
     <ItemGroup>
-        <Content Include="appsettings.json">
-            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-        </Content>
+      <ProjectReference Include="..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
+      <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
     </ItemGroup>
 
     <ItemGroup>
-        <PackageReference Include="CommandLineParser" Version="2.9.1"/>
-        <PackageReference Include="ConsoleGUI" Version="1.4.1"/>
-        <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1"/>
-        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5"/>
-        <PackageReference Include="Serilog" Version="2.12.0"/>
-        <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/>
-        <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0"/>
-        <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0"/>
-        <PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/>
-        <PackageReference Include="System.Reactive" Version="5.0.0"/>
-        <PackageReference Include="System.Threading.Tasks.Dataflow" Version="7.0.0"/>
-        <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4"/>
-        <PackageReference Include="Timestamp" Version="1.0.2"/>
+        <None Remove="appsettings.json"/>
     </ItemGroup>
 
     <ItemGroup>
-        <ProjectReference Include="..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj"/>
-        <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj"/>
-        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
-        <ProjectReference Include="..\Rudzoft.Perft.Framework\Rudzoft.Perft.Framework.csproj"/>
+        <Content Include="appsettings.json">
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </Content>
     </ItemGroup>
 
+
 </Project>
diff --git a/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs b/src/Rudzoft.Perft/Services/BuildTimeStamp.cs
similarity index 88%
rename from src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
rename to src/Rudzoft.Perft/Services/BuildTimeStamp.cs
index 4a015007..c6e8f0bb 100644
--- a/src/Rudzoft.Perft/TimeStamp/BuildTimeStamp.cs
+++ b/src/Rudzoft.Perft/Services/BuildTimeStamp.cs
@@ -26,7 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System.Reflection;
 
-namespace Rudzoft.Perft.TimeStamp;
+namespace Rudzoft.Perft.Services;
 
 internal sealed class BuildTimeStamp : IBuildTimeStamp
 {
@@ -42,6 +42,10 @@ private static string GetTimestamp()
             .GetCustomAttributesData()
             .First(static x => x.AttributeType.Name == AttributeName);
 
-        return (string)attribute.ConstructorArguments.First().Value;
+        var first = attribute.ConstructorArguments.FirstOrDefault();
+
+        var value = first.Value as string;
+
+        return string.IsNullOrWhiteSpace(value) ? string.Empty : value;
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/TimeStamp/IBuildTimeStamp.cs b/src/Rudzoft.Perft/Services/IBuildTimeStamp.cs
similarity index 97%
rename from src/Rudzoft.Perft/TimeStamp/IBuildTimeStamp.cs
rename to src/Rudzoft.Perft/Services/IBuildTimeStamp.cs
index e2f10b56..c7a5609e 100644
--- a/src/Rudzoft.Perft/TimeStamp/IBuildTimeStamp.cs
+++ b/src/Rudzoft.Perft/Services/IBuildTimeStamp.cs
@@ -24,7 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-namespace Rudzoft.Perft.TimeStamp;
+namespace Rudzoft.Perft.Services;
 
 public interface IBuildTimeStamp
 {
diff --git a/src/Rudzoft.Perft/Perft/IPerftRunner.cs b/src/Rudzoft.Perft/Services/IPerftRunner.cs
similarity index 97%
rename from src/Rudzoft.Perft/Perft/IPerftRunner.cs
rename to src/Rudzoft.Perft/Services/IPerftRunner.cs
index 011d3ad2..ca7540e3 100644
--- a/src/Rudzoft.Perft/Perft/IPerftRunner.cs
+++ b/src/Rudzoft.Perft/Services/IPerftRunner.cs
@@ -26,7 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using Rudzoft.Perft.Options;
 
-namespace Rudzoft.Perft.Perft;
+namespace Rudzoft.Perft.Services;
 
 public interface IPerftRunner
 {
diff --git a/src/Rudzoft.Perft/Perft/PerftRunner.cs b/src/Rudzoft.Perft/Services/PerftRunner.cs
similarity index 93%
rename from src/Rudzoft.Perft/Perft/PerftRunner.cs
rename to src/Rudzoft.Perft/Services/PerftRunner.cs
index c5e9bbff..0b915e18 100644
--- a/src/Rudzoft.Perft/Perft/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Services/PerftRunner.cs
@@ -35,12 +35,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Perft;
 using Rudzoft.ChessLib.Perft.Interfaces;
 using Rudzoft.ChessLib.Protocol.UCI;
+using Rudzoft.Perft.Models;
 using Rudzoft.Perft.Options;
 using Rudzoft.Perft.Parsers;
-using Rudzoft.Perft.TimeStamp;
 using Serilog;
 
-namespace Rudzoft.Perft.Perft;
+namespace Rudzoft.Perft.Services;
 
 public sealed class PerftRunner : IPerftRunner
 {
@@ -92,7 +92,7 @@ public PerftRunner(
 
         configuration.Bind("TranspositionTable", TranspositionTableOptions);
 
-        _cpu = new Cpu();
+        _cpu = new();
     }
 
     public bool SaveResults { get; set; }
@@ -110,7 +110,7 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
         InternalRunArgumentCheck(Options);
 
         if (TranspositionTableOptions is TTOptions { Use: true } ttOptions)
-            _transpositionTable.SetSize(ttOptions.Size);
+            _transpositionTable.SetSize((int)ttOptions.Size);
 
         var errors = 0;
         var runnerIndex = (Options is FenOptions).AsByte();
@@ -159,20 +159,25 @@ private IAsyncEnumerable<PerftPosition> ParseEpd(CancellationToken cancellationT
 
     private async IAsyncEnumerable<PerftPosition> ParseEpd(EpdOptions options)
     {
-        _epdParser.Settings.Filename = options.Epds.First();
-        var sw = Stopwatch.StartNew();
+        foreach (var epd in options.Epds)
+        {
+            var sw = Stopwatch.StartNew();
 
-        var parsedCount = await _epdParser.ParseAsync().ConfigureAwait(false);
+            var parsedCount = 0L;
+            
+            await foreach(var epdPos in _epdParser.Parse(epd))
+            {
+                parsedCount++;
 
-        sw.Stop();
-        var elapsedMs = sw.ElapsedMilliseconds;
-        _log.Information("Parsed {Parsed} epd entries in {Elapsed} ms", parsedCount, elapsedMs);
+                var perftPosition = PerftPositionFactory.Create(epdPos.Id, epdPos.Epd, epdPos.Perft);
 
-        var perftPositions =
-            _epdParser.Sets.Select(static set => PerftPositionFactory.Create(set.Id, set.Epd, set.Perft));
+                yield return perftPosition;
+            }
 
-        foreach (var perftPosition in perftPositions)
-            yield return perftPosition;
+            sw.Stop();
+            var elapsedMs = sw.ElapsedMilliseconds;
+            _log.Information("Processed {Parsed} epd entries in {Elapsed} ms", parsedCount, elapsedMs);
+        }
     }
 
     private IAsyncEnumerable<PerftPosition> ParseFen(CancellationToken cancellationToken)
diff --git a/src/Rudzoft.Perft/Services/PerftService.cs b/src/Rudzoft.Perft/Services/PerftService.cs
new file mode 100644
index 00000000..4db7d1e8
--- /dev/null
+++ b/src/Rudzoft.Perft/Services/PerftService.cs
@@ -0,0 +1,50 @@
+using Akka.Actor;
+using Akka.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Rudzoft.Perft.Actors;
+
+namespace Rudzoft.Perft.Services;
+
+public sealed class PerftService : IHostedService
+{
+    private readonly IServiceProvider _serviceProvider;
+    private readonly IHostApplicationLifetime _applicationLifetime;
+    
+    private ActorSystem _actorSystem;
+    private IActorRef _perftActor;
+
+    public PerftService(IServiceProvider serviceProvider)
+    {
+        _serviceProvider = serviceProvider;
+    }
+
+    public Task StartAsync(CancellationToken cancellationToken)
+    {
+        var bootstrap = BootstrapSetup.Create();
+
+        // enable DI support inside this ActorSystem, if needed
+        var diSetup = DependencyResolverSetup.Create(_serviceProvider);
+
+        // merge this setup (and any others) together into ActorSystemSetup
+        var actorSystemSetup = bootstrap.And(diSetup);
+
+        // start ActorSystem
+        _actorSystem = ActorSystem.Create("perft", actorSystemSetup);
+        _perftActor = _actorSystem.ActorOf(PerftActor.Prop(_serviceProvider));
+        
+        _perftActor.Tell(new PerftActor.StartPerft());
+        
+        // add a continuation task that will guarantee shutdown of application if ActorSystem terminates
+        _actorSystem.WhenTerminated.ContinueWith(_ => {
+            _applicationLifetime.StopApplication();
+        }, cancellationToken);
+        return Task.CompletedTask;
+    }
+
+    public async Task StopAsync(CancellationToken cancellationToken)
+    {
+        // strictly speaking this may not be necessary - terminating the ActorSystem would also work
+        // but this call guarantees that the shutdown of the cluster is graceful regardless
+        await CoordinatedShutdown.Get(_actorSystem).Run(CoordinatedShutdown.ClrExitReason.Instance);
+    }
+}
\ No newline at end of file

From 67548b5fdc3c174d6d42877037bb1780d66fd769 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Tue, 29 Aug 2023 22:42:52 +0200
Subject: [PATCH 051/119] Microsoft.NET.Test.Sdk 17.7.1 -> 17.7.2

---
 src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj | 2 +-
 src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index 423aedd6..212cd893 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -7,7 +7,7 @@
 
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
         <PackageReference Include="xunit" Version="2.5.0" />
         <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 850c6491..314d577b 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -24,7 +24,7 @@
 
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
         <PackageReference Include="xunit" Version="2.5.0" />
         <PackageReference Include="xunit.analyzers" Version="1.2.0" />
         <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">

From f25a25b9e50cbfdba999fd47c01afe30fb8fe004 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Wed, 30 Aug 2023 22:52:46 +0200
Subject: [PATCH 052/119] minor update to perft + uci now uses move list pool
 to convert move

---
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |   8 +-
 .../BookTests/PolyglotTests.cs                |   6 +-
 .../ProtocolTests/OptionsTests.cs             |   8 +-
 .../ProtocolTests/UciTests.cs                 |  14 +-
 .../ChessLibServiceCollectionExtensions.cs    |   6 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |  21 +-
 src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs     | 320 ------------------
 src/Rudzoft.Perft/Actors/EpdParserActor.cs    |  20 --
 src/Rudzoft.Perft/Actors/FenTargetActor.cs    |   6 +
 src/Rudzoft.Perft/Actors/PerftActor.cs        |  22 +-
 src/Rudzoft.Perft/Actors/PerftComputeActor.cs |  70 ----
 src/Rudzoft.Perft/Actors/PerftRunner.cs       |  21 --
 src/Rudzoft.Perft/Actors/PerftRunnerActor.cs  |  50 +++
 src/Rudzoft.Perft/Options/IOptions.cs         |   2 +-
 src/Rudzoft.Perft/Services/PerftRunner.cs     |  55 ++-
 src/Rudzoft.Perft/Services/PerftService.cs    |  20 +-
 16 files changed, 147 insertions(+), 502 deletions(-)
 delete mode 100644 src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs
 delete mode 100644 src/Rudzoft.Perft/Actors/EpdParserActor.cs
 create mode 100644 src/Rudzoft.Perft/Actors/FenTargetActor.cs
 delete mode 100644 src/Rudzoft.Perft/Actors/PerftComputeActor.cs
 delete mode 100644 src/Rudzoft.Perft/Actors/PerftRunner.cs
 create mode 100644 src/Rudzoft.Perft/Actors/PerftRunnerActor.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index 8dc641bc..5e686ac5 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -53,7 +53,7 @@ public void Setup()
         var pp = PerftPositionFactory.Create(
             Guid.NewGuid().ToString(),
             Fen.Fen.StartPositionFen,
-            new List<PerftPositionValue>(6)
+            new(6)
             {
                 new(1, 20),
                 new(2, 400),
@@ -67,12 +67,12 @@ public void Setup()
         var options = Options.Create(ttConfig);
         var tt = new TranspositionTable(options);
 
-        var uci = new Uci();
+        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        
+        var uci = new Uci(moveListObjectPool);
         uci.Initialize();
         var cpu = new Cpu();
 
-        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
-
         var sp = new SearchParameters();
 
         var board = new Board();
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index 4d73e1b3..5bfbb437 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -32,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Factories;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
@@ -68,9 +69,10 @@ public PolyglotTests(BookFixture fixture)
                 var policy = new MoveListPolicy();
                 return provider.Create(policy);
             })
-            .AddSingleton(static _ =>
+            .AddSingleton(static sp =>
             {
-                IUci uci = new Uci();
+                var mlPool = sp.GetRequiredService<ObjectPool<IMoveList>>();
+                IUci uci = new Uci(mlPool);
                 uci.Initialize();
                 return uci;
             })
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
index 78b7de68..5b8185a6 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
@@ -24,6 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 
 namespace Rudzoft.ChessLib.Test.ProtocolTests;
@@ -35,7 +38,10 @@ public sealed class OptionsTests
     [InlineData("Boolean Test", false, false, "option name Boolean Test type Check default false")]
     public void Boolean(string name, bool value, bool expected, string uciString)
     {
-        IUci uci = new Uci();
+        var policy = new MoveListPolicy();
+        var provider = new DefaultObjectPool<IMoveList>(policy);
+
+        IUci uci = new Uci(provider);
         
         uci.Initialize();
         
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
index 74ae6fde..fd529ca7 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
@@ -31,6 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
@@ -56,12 +57,6 @@ public UciTests()
             .AddSingleton<IPositionValidator, PositionValidator>()
             .AddTransient<IBoard, Board>()
             .AddTransient<IPosition, Position>()
-            .AddSingleton(static _ =>
-            {
-                IUci uci = new Uci();
-                uci.Initialize();
-                return uci;
-            })
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
             {
@@ -69,6 +64,13 @@ public UciTests()
                 var policy = new MoveListPolicy();
                 return provider.Create(policy);
             })
+            .AddSingleton(static sp =>
+            {
+                var pool = sp.GetRequiredService<ObjectPool<IMoveList>>();
+                IUci uci = new Uci(pool);
+                uci.Initialize();
+                return uci;
+            })
             .BuildServiceProvider();
     }
 
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 3013f4da..176aa4a3 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -32,6 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Factories;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
@@ -79,9 +80,10 @@ public static IServiceCollection AddChessLib(
             return provider.Create(policy);
         });
 
-        return serviceCollection.AddSingleton(static _ =>
+        return serviceCollection.AddSingleton(static sp =>
             {
-                IUci uci = new Uci();
+                var pool = sp.GetRequiredService<ObjectPool<IMoveList>>();
+                IUci uci = new Uci(pool);
                 uci.Initialize();
                 return uci;
             })
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index a0dc3856..c0c77e31 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -43,13 +43,15 @@ public class Uci : IUci
     private static readonly string[] OptionTypeStrings = Enum.GetNames(typeof(UciOptionType));
 
     private readonly ObjectPool<StringBuilder> _pvPool;
+    private readonly ObjectPool<IMoveList> _moveListPool;
     private readonly Dictionary<string, IOption> _options;
 
-    public Uci()
+    public Uci(ObjectPool<IMoveList> moveListPool)
     {
         var policy = new StringBuilderPooledObjectPolicy();
         _pvPool = new DefaultObjectPool<StringBuilder>(policy, 128);
         _options = new();
+        _moveListPool = moveListPool;
     }
 
     public int MaxThreads { get; set; }
@@ -98,17 +100,26 @@ public ulong Nps(in ulong nodes, in TimeSpan time)
 
     public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
     {
-        var moveList = pos.GenerateMoves();
+        var ml = _moveListPool.Get();
+        
+        ml.Generate(in pos);
 
-        var moves = moveList.Get();
+        var moves = ml.Get();
 
+        var m = Move.EmptyMove;
+        
         foreach (var move in moves)
         {
             if (uciMove.Equals(move.Move.ToString(), StringComparison.InvariantCultureIgnoreCase))
-                return move.Move;
+            {
+                m = move.Move;
+                break;
+            }
         }
         
-        return Move.EmptyMove;
+        _moveListPool.Return(ml);
+
+        return m;
     }
 
     public IEnumerable<Move> MovesFromUci(IPosition pos, Stack<State> states, IEnumerable<string> moves)
diff --git a/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs b/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs
deleted file mode 100644
index 69a98bd6..00000000
--- a/src/Rudzoft.ChessLib/Types/Attacks/Mbb.cs
+++ /dev/null
@@ -1,320 +0,0 @@
-using System.Runtime.Intrinsics.X86;
-
-namespace Rudzoft.ChessLib.Types.Attacks;
-
-public static class Mbb
-{
-    private static readonly ulong[] RookMagics =
-    {
-        0xa8002c000108020UL, 0x6c00049b0002001UL, 0x100200010090040UL, 0x2480041000800801UL,
-        0x280028004000800UL, 0x900410008040022UL, 0x280020001001080UL, 0x2880002041000080UL,
-        0xa000800080400034UL, 0x4808020004000UL, 0x2290802004801000UL, 0x411000d00100020UL,
-        0x402800800040080UL, 0xb000401004208UL, 0x2409000100040200UL, 0x1002100004082UL,
-        0x22878001e24000UL, 0x1090810021004010UL, 0x801030040200012UL, 0x500808008001000UL,
-        0xa08018014000880UL, 0x8000808004000200UL, 0x201008080010200UL, 0x801020000441090UL,
-        0x800080204005UL, 0x1040200040100048UL, 0x120200402082UL, 0xd14880480100080UL,
-        0x12040280080080UL, 0x100040080020080UL, 0x9020010080800200UL, 0x813241200148449UL,
-        0x491604000800080UL, 0x100401000402001UL, 0x4820010021001040UL, 0x400402202000812UL,
-        0x209009005000802UL, 0x810800601800400UL, 0x4301083214000150UL, 0x204026458e00140UL,
-        0x40204000808000UL, 0x8001008040010020UL, 0x8410820820420010UL, 0x1003001000090020UL,
-        0x804040008008080UL, 0x12000810020004UL, 0x1000100200040208UL, 0x430000a044020001UL,
-        0x2800080008000100UL, 0xe0100040002240UL, 0x200100401700UL, 0x2244100408008080UL,
-        0x8000400801980UL, 0x200081004020100UL, 0x8010100228810400UL, 0x2000009044210200UL,
-        0x4080008040102101UL, 0x1a2208080204d101UL, 0x4100010002080088UL, 0x8100042000102001UL
-    };
-
-    private static readonly ulong[] BishopMagics =
-    {
-        0x89a1121896040240UL, 0x2004844802002010UL, 0x2068080051921000UL, 0x62880a0220200808UL,
-        0x404008080020000UL, 0x10082100100a4218UL, 0x8040002811040900UL, 0x8010100a02020058UL,
-        0x20800090488c0080UL, 0x1210815040200050UL, 0x8010008820400082UL, 0x4100210040110042UL,
-        0x880800040081080UL, 0x102008010402040UL, 0x211021800500412UL, 0x1000402000400180UL,
-        0x1040082084200040UL, 0x1208000080008410UL, 0x840081084110800UL, 0x100020004010004UL,
-        0x10004000800400UL, 0x40c0422080a000UL, 0x420804400400UL, 0x110402040081040UL,
-        0x20088080080220UL, 0x810001008080UL, 0x80800080101002UL, 0x82000020100050UL,
-        0x4100001020020UL, 0x4040400808010UL, 0x2010840008001UL, 0x104100002080UL,
-        0x200c000200402000UL, 0x10102000088010UL, 0x80800080004000UL, 0x100008020040UL,
-        0x8004808020100200UL, 0x408000404040UL, 0x208004008008UL, 0x82000082008000UL,
-        0x84008202004000UL, 0x8200200400840080UL, 0x40101000400080UL, 0x401020401100UL,
-        0x100810001002UL, 0x8000400080804UL, 0x800200a0010100UL, 0x10404000808200UL
-    };
-
-    private static readonly int[] RookShifts =
-    {
-        52, 53, 53, 53, 53, 53, 53, 52,
-        53, 54, 54, 54, 54, 54, 54, 53,
-        53, 54, 54, 54, 54, 54, 54, 53,
-        53, 54, 54, 54, 54, 54, 54, 53,
-        53, 54, 54, 54, 54, 54, 54, 53,
-        53, 54, 54, 54, 54, 54, 54, 53,
-        53, 54, 54, 54, 54, 54, 54, 53,
-        52, 53, 53, 53, 53, 53, 53, 52
-    };
-
-    private static readonly int[] BishopShifts =
-    {
-        58, 59, 59, 59, 59, 59, 59, 58,
-        59, 59, 59, 59, 59, 59, 59, 59,
-        59, 59, 57, 57, 57, 57, 59, 59,
-        59, 59, 57, 55, 55, 57, 59, 59,
-        59, 59, 57, 55, 55, 57, 59, 59,
-        59, 59, 57, 57, 57, 57, 59, 59,
-        59, 59, 59, 59, 59, 59, 59, 59,
-        58, 59, 59, 59, 59, 59, 59, 58
-    };
-
-    private static readonly ulong[][] RookAttacks = new ulong[64][];
-    private static readonly ulong[][] BishopAttacks = new ulong[64][];
-
-    static Mbb()
-    {
-        for (int square = 0; square < 64; square++)
-        {
-            RookAttacks[square] = GenerateRookAttacks(square, RookMagics[square], RookShifts[square]);
-            BishopAttacks[square] = GenerateBishopAttacks(square, BishopMagics[square], BishopShifts[square]);
-        }
-    }
-
-    public static BitBoard GetRookAttacks(Square square, in BitBoard occupancy)
-    {
-        return GetAttackSet(square.AsInt(), in occupancy, RookMagics[square.AsInt()], RookShifts[square.AsInt()], RookAttacks);
-    }
-
-    public static BitBoard GetBishopAttacks(Square square, in BitBoard occupancy)
-    {
-        return GetAttackSet(square.AsInt(), in occupancy, BishopMagics[square.AsInt()], BishopShifts[square.AsInt()], BishopAttacks);
-    }
-
-    public static BitBoard GetQueenAttacks(Square square, in BitBoard occupancy)
-    {
-        return GetRookAttacks(square, in occupancy) | GetBishopAttacks(square, in occupancy);
-    }
-
-    private static ulong[] GenerateRookAttacks(int square, ulong magic, int shift)
-    {
-        int indexBits = 1 << shift;
-        ulong[] attacks = new ulong[indexBits];
-        ulong[] occupancy = new ulong[indexBits];
-
-        int rank = square / 8;
-        int file = square % 8;
-
-        for (int i = 0; i < indexBits; i++)
-        {
-            occupancy[i] = SetRookOccupancy(i, rank, file);
-            ulong index = PEXT(occupancy[i], magic) >> (64 - shift);
-            attacks[index] = CalculateRookAttacks(square, occupancy[i]);
-        }
-
-        return attacks;
-    }
-
-    private static ulong[] GenerateBishopAttacks(int square, ulong magic, int shift)
-    {
-        int indexBits = 1 << shift;
-        ulong[] attacks = new ulong[indexBits];
-        ulong[] occupancy = new ulong[indexBits];
-
-        int rank = square / 8;
-        int file = square % 8;
-
-        for (int i = 0; i < indexBits; i++)
-        {
-            occupancy[i] = SetBishopOccupancy(i, rank, file);
-            ulong index = PEXT(occupancy[i], magic) >> (64 - shift);
-            attacks[index] = CalculateBishopAttacks(square, occupancy[i]);
-        }
-
-        return attacks;
-    }
-
-    private static ulong GetAttackSet(int square, in BitBoard occupancy, ulong magic, int shift, ulong[][] attacks)
-    {
-        ulong index = PEXT(occupancy, magic) >> (64 - shift);
-        int maskedIndex = (int)(index & (ulong)(attacks[square].Length - 1));
-        return attacks[square][maskedIndex];
-    }
-
-    private static ulong SetRookOccupancy(int index, int rank, int file)
-    {
-        ulong occupancy = 0UL;
-
-        for (int i = 0; i < 6; i++)
-        {
-            int bit = (index >> i) & 1;
-            int targetRank = rank;
-            int targetFile = file + i + 1;
-            occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
-        }
-
-        for (int i = 0; i < 6; i++)
-        {
-            int bit = (index >> (i + 6)) & 1;
-            int targetRank = rank;
-            int targetFile = file - i - 1;
-            occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
-        }
-
-        for (int i = 0; i < 5; i++)
-        {
-            int bit = (index >> (i + 12)) & 1;
-            int targetRank = rank + i + 1;
-            int targetFile = file;
-            occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
-        }
-
-        for (int i = 0; i < 5; i++)
-        {
-            int bit = (index >> (i + 17)) & 1;
-            int targetRank = rank - i - 1;
-            int targetFile = file;
-            occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
-        }
-
-        return occupancy;
-    }
-
-    private static ulong SetBishopOccupancy(int index, int rank, int file)
-    {
-        ulong occupancy = 0UL;
-
-        for (int i = 0; i < 4; i++)
-        {
-            int bit = (index >> i) & 1;
-            int targetRank = rank + i + 1;
-            int targetFile = file + i + 1;
-            if (targetRank < 8 && targetFile < 8)
-                occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
-        }
-
-        for (int i = 0; i < 4; i++)
-        {
-            int bit = (index >> (i + 4)) & 1;
-            int targetRank = rank - i - 1;
-            int targetFile = file + i + 1;
-            if (targetRank >= 0 && targetFile < 8)
-                occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
-        }
-
-        for (int i = 0; i < 4; i++)
-        {
-            int bit = (index >> (i + 8)) & 1;
-            int targetRank = rank + i + 1;
-            int targetFile = file - i - 1;
-            if (targetRank < 8 && targetFile >= 0)
-                occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
-        }
-
-        for (int i = 0; i < 4; i++)
-        {
-            int bit = (index >> (i + 12)) & 1;
-            int targetRank = rank - i - 1;
-            int targetFile = file - i - 1;
-            if (targetRank >= 0 && targetFile >= 0)
-                occupancy |= (ulong)bit << (targetRank * 8 + targetFile);
-        }
-
-        return occupancy;
-    }
-
-    private static ulong CalculateRookAttacks(int square, ulong occupancy)
-    {
-        int rank = square / 8;
-        int file = square % 8;
-        ulong attacks = 0UL;
-
-        for (int targetRank = rank + 1; targetRank < 8; targetRank++)
-        {
-            attacks |= 1UL << (targetRank * 8 + file);
-            if ((occupancy & (1UL << (targetRank * 8 + file))) != 0) break;
-        }
-
-        for (int targetRank = rank - 1; targetRank >= 0; targetRank--)
-        {
-            attacks |= 1UL << (targetRank * 8 + file);
-            if ((occupancy & (1UL << (targetRank * 8 + file))) != 0) break;
-        }
-
-        for (int targetFile = file + 1; targetFile < 8; targetFile++)
-        {
-            attacks |= 1UL << (rank * 8 + targetFile);
-            if ((occupancy & (1UL << (rank * 8 + targetFile))) != 0) break;
-        }
-
-        for (int targetFile = file - 1; targetFile >= 0; targetFile--)
-        {
-            attacks |= 1UL << (rank * 8 + targetFile);
-            if ((occupancy & (1UL << (rank * 8 + targetFile))) != 0) break;
-        }
-
-        return attacks;
-    }
-
-    private static ulong CalculateBishopAttacks(int square, ulong occupancy)
-    {
-        int rank = square / 8;
-        int file = square % 8;
-        ulong attacks = 0UL;
-
-        for (int r = rank + 1, f = file + 1; r < 8 && f < 8; r++, f++)
-        {
-            attacks |= 1UL << (r * 8 + f);
-            if ((occupancy & (1UL << (r * 8 + f))) != 0) break;
-        }
-
-        for (int r = rank - 1, f = file + 1; r >= 0 && f < 8; r--, f++)
-        {
-            attacks |= 1UL << (r * 8 + f);
-            if ((occupancy & (1UL << (r * 8 + f))) != 0) break;
-        }
-
-        for (int r = rank + 1, f = file - 1; r < 8 && f >= 0; r++, f--)
-        {
-            attacks |= 1UL << (r * 8 + f);
-            if ((occupancy & (1UL << (r * 8 + f))) != 0) break;
-        }
-
-        for (int r = rank - 1, f = file - 1; r >= 0 && f >= 0; r--, f--)
-        {
-            attacks |= 1UL << (r * 8 + f);
-            if ((occupancy & (1UL << (r * 8 + f))) != 0) break;
-        }
-
-        return attacks;
-    }
-    
-    private static ulong PEXT(in BitBoard source, in ulong mask)
-    {
-        if (Bmi2.X64.IsSupported)
-        {
-            return Bmi2.X64.ParallelBitExtract(source.Value, mask);
-        }
-
-        // Fallback implementation for when the PEXT instruction is not available
-        ulong result = 0;
-        ulong one = 1;
-        for (ulong bb = mask; bb != 0; bb &= (bb - 1))
-        {
-            int index = BitScanForward(bb);
-            if ((source & (one << index)) != 0)
-            {
-                result |= (one << BitScanForward(bb & ~(bb - 1)));
-            }
-        }
-        return result;
-    }
-    
-    private static int BitScanForward(ulong bb)
-    {
-        if (bb == 0) return -1;
-
-        int index = 0;
-        while ((bb & 1) == 0)
-        {
-            bb >>= 1;
-            index++;
-        }
-
-        return index;
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/EpdParserActor.cs b/src/Rudzoft.Perft/Actors/EpdParserActor.cs
deleted file mode 100644
index 1d7e4a28..00000000
--- a/src/Rudzoft.Perft/Actors/EpdParserActor.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Akka.Actor;
-using Microsoft.Extensions.DependencyInjection;
-using Rudzoft.Perft.Parsers;
-
-namespace Rudzoft.Perft.Actors;
-
-public sealed class EpdParserActor : ReceiveActor
-{
-    public EpdParserActor(IServiceProvider sp)
-    {
-        var epdParser = sp.CreateScope().ServiceProvider.GetRequiredService<IEpdParser>();
-        
-        ReceiveAsync<string>(async filename =>
-        {
-            await foreach(var parsed in epdParser.Parse(filename).ConfigureAwait(false))
-                Sender.Tell(parsed);
-        });
-    }
-
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/FenTargetActor.cs b/src/Rudzoft.Perft/Actors/FenTargetActor.cs
new file mode 100644
index 00000000..db3914da
--- /dev/null
+++ b/src/Rudzoft.Perft/Actors/FenTargetActor.cs
@@ -0,0 +1,6 @@
+namespace Rudzoft.Perft.Actors;
+
+public class FenTargetActor
+{
+    
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/PerftActor.cs b/src/Rudzoft.Perft/Actors/PerftActor.cs
index 7eed29e2..719967dc 100644
--- a/src/Rudzoft.Perft/Actors/PerftActor.cs
+++ b/src/Rudzoft.Perft/Actors/PerftActor.cs
@@ -1,31 +1,29 @@
-using Akka.Actor;
+using System.Collections.Immutable;
+using Akka.Actor;
 using Microsoft.Extensions.DependencyInjection;
 using Rudzoft.Perft.Options;
-using Rudzoft.Perft.Services;
 
 namespace Rudzoft.Perft.Actors;
 
+// ReSharper disable once ClassNeverInstantiated.Global
 public sealed class PerftActor : ReceiveActor
 {
     public sealed record StartPerft;
     
     private readonly IServiceProvider _sp;
-    private readonly IPerftRunner _perftRunner;
     
     public PerftActor(IServiceProvider sp)
     {
         _sp = sp;
-        _perftRunner = _sp.GetRequiredService<IPerftRunner>();
         var optionsFactory = _sp.GetRequiredService<IOptionsFactory>();
-        foreach (var option in optionsFactory.Parse())
-        {
-            if (option.Type == OptionType.TTOptions)
-                _perftRunner.TranspositionTableOptions = option.PerftOptions;
-            else
-                _perftRunner.Options = option.PerftOptions;
-        }
+        var options = optionsFactory.Parse().ToImmutableArray();
         
-        ReceiveAsync<StartPerft>(_ => _perftRunner.Run());
+        Receive<StartPerft>(_ =>
+        {
+            var runner = Context.ActorOf(PerftRunnerActor.Prop(_sp), "runner-actor");
+            runner.Tell(new PerftRunnerActor.RunOptions(options));
+            runner.Tell(new PerftRunnerActor.Run());
+        });
     }
 
     public static Props Prop(IServiceProvider sp)
diff --git a/src/Rudzoft.Perft/Actors/PerftComputeActor.cs b/src/Rudzoft.Perft/Actors/PerftComputeActor.cs
deleted file mode 100644
index 96c63eab..00000000
--- a/src/Rudzoft.Perft/Actors/PerftComputeActor.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-// using System.Diagnostics;
-// using Akka.Actor;
-// using Microsoft.Extensions.DependencyInjection;
-// using Rudzoft.ChessLib.Perft.Interfaces;
-// using Rudzoft.Perft2.Models;
-//
-// namespace Rudzoft.Perft2.Actors;
-//
-// public sealed class PerftComputeActor : ReceiveActor
-// {
-//     public sealed record PerftPositions(List<PerftPosition> Positions);
-//     
-//     private readonly IPerft _perft;
-//
-//     public PerftComputeActor(IServiceProvider sp)
-//     {
-//         _perft = sp.GetRequiredService<IPerft>();
-//         ReceiveAsync<PerftPositions>(async pp =>
-//         {
-//             var result = await ComputePerft(pp.Positions).ConfigureAwait(false);
-//             Sender.Tell(result);
-//         });
-//     }
-//     
-//     private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken)
-//     {
-//         var result = _resultPool.Get();
-//         result.Clear();
-//
-//         var pp = _perft.Positions[^1];
-//         var baseFileName = SaveResults
-//             ? Path.Combine(CurrentDirectory.Value, $"{FixFileName(pp.Fen)}[")
-//             : string.Empty;
-//
-//         var errors = 0;
-//
-//         result.Fen = pp.Fen;
-//         _perft.SetGamePosition(pp);
-//         _perft.BoardPrintCallback(_perft.GetBoard());
-//         _log.Information("Fen         : {Fen}", pp.Fen);
-//         _log.Information(Line);
-//
-//         foreach (var (depth, expected) in pp.Value)
-//         {
-//             cancellationToken.ThrowIfCancellationRequested();
-//
-//             _log.Information("Depth       : {Depth}", depth);
-//
-//             var start = Stopwatch.GetTimestamp();
-//
-//             var perftResult = await _perft.DoPerftAsync(depth).ConfigureAwait(false);
-//             var elapsedMs = Stopwatch.GetElapsedTime(start);
-//
-//             ComputeResults(in perftResult, depth, in expected, in elapsedMs, result);
-//
-//             errors += LogResults(result);
-//
-//             if (baseFileName.IsNullOrEmpty())
-//                 continue;
-//
-//             await WriteOutput(result, baseFileName, cancellationToken);
-//         }
-//
-//         _log.Information("{Info} parsing complete. Encountered {Errors} errors", _usingEpd ? "EPD" : "FEN", errors);
-//
-//         result.Errors = errors;
-//
-//         return result;
-//     }
-// }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/PerftRunner.cs b/src/Rudzoft.Perft/Actors/PerftRunner.cs
deleted file mode 100644
index 1b34f186..00000000
--- a/src/Rudzoft.Perft/Actors/PerftRunner.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Akka.Actor;
-using Rudzoft.Perft.Options;
-
-namespace Rudzoft.Perft.Actors;
-
-public sealed class PerftRunner : ReceiveActor
-{
-    public PerftRunner()
-    {
-        Receive<EpdOptions>(epd =>
-        {
-            
-        });
-        
-        Receive<FenOptions>(epd =>
-        {
-            
-        });
-    }
-    
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs b/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
new file mode 100644
index 00000000..582888cf
--- /dev/null
+++ b/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
@@ -0,0 +1,50 @@
+using System.Collections.Immutable;
+using Akka.Actor;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.Perft.Options;
+using Rudzoft.Perft.Services;
+
+namespace Rudzoft.Perft.Actors;
+
+// ReSharper disable once ClassNeverInstantiated.Global
+public sealed class PerftRunnerActor : ReceiveActor
+{
+    public sealed record RunOptions(ImmutableArray<PerftOption> Options);
+
+    public sealed record Run;
+
+    private readonly IPerftRunner _perftRunner;
+
+    public PerftRunnerActor(IServiceProvider sp)
+    {
+        var scope = sp.CreateScope();
+        _perftRunner = scope.ServiceProvider.GetRequiredService<IPerftRunner>();
+        Become(OptionsBehaviour);
+    }
+
+    public static Props Prop(IServiceProvider sp)
+    {
+        return Props.Create<PerftRunnerActor>(sp);
+    }
+
+    private void RunBehaviour()
+    {
+        ReceiveAsync<Run>(_ => _perftRunner.Run());
+    }
+
+    private void OptionsBehaviour()
+    {
+        Receive<RunOptions>(options =>
+        {
+            foreach (var option in options.Options)
+            {
+                if (option.Type == OptionType.TTOptions)
+                    _perftRunner.TranspositionTableOptions = option.PerftOptions;
+                else
+                    _perftRunner.Options = option.PerftOptions;
+            }
+
+            Become(RunBehaviour);
+        });
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Options/IOptions.cs b/src/Rudzoft.Perft/Options/IOptions.cs
index b21ed276..cbd19a5e 100644
--- a/src/Rudzoft.Perft/Options/IOptions.cs
+++ b/src/Rudzoft.Perft/Options/IOptions.cs
@@ -26,7 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.Perft.Options;
 
-public record PerftOption(OptionType Type, IPerftOptions PerftOptions);
+public sealed record PerftOption(OptionType Type, IPerftOptions PerftOptions);
 
 public interface IPerftOptions
 {
diff --git a/src/Rudzoft.Perft/Services/PerftRunner.cs b/src/Rudzoft.Perft/Services/PerftRunner.cs
index 0b915e18..fe25faae 100644
--- a/src/Rudzoft.Perft/Services/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Services/PerftRunner.cs
@@ -44,7 +44,7 @@ namespace Rudzoft.Perft.Services;
 
 public sealed class PerftRunner : IPerftRunner
 {
-    private const string Version = "v0.2.0";
+    private static readonly ILogger Log = Serilog.Log.ForContext<PerftRunner>();
 
     private static readonly string Line = new('-', 65);
 
@@ -54,10 +54,6 @@ public sealed class PerftRunner : IPerftRunner
 
     private readonly IEpdParser _epdParser;
 
-    private readonly ILogger _log;
-
-    private readonly IBuildTimeStamp _buildTimeStamp;
-
     private readonly IPerft _perft;
 
     private readonly ITranspositionTable _transpositionTable;
@@ -72,8 +68,6 @@ public sealed class PerftRunner : IPerftRunner
 
     public PerftRunner(
         IEpdParser parser,
-        ILogger log,
-        IBuildTimeStamp buildTimeStamp,
         IPerft perft,
         IConfiguration configuration,
         ITranspositionTable transpositionTable,
@@ -81,10 +75,8 @@ public PerftRunner(
         IUci uci)
     {
         _epdParser = parser;
-        _log = log;
-        _buildTimeStamp = buildTimeStamp;
         _perft = perft;
-        _perft.BoardPrintCallback ??= s => _log.Information("Board:\n{Board}", s);
+        _perft.BoardPrintCallback ??= s => Log.Information("Board:\n{Board}", s);
         _transpositionTable = transpositionTable;
         _resultPool = resultPool;
         _uci = uci;
@@ -105,19 +97,17 @@ public PerftRunner(
 
     private async Task<int> InternalRun(CancellationToken cancellationToken = default)
     {
-        LogInfoHeader();
-
         InternalRunArgumentCheck(Options);
 
         if (TranspositionTableOptions is TTOptions { Use: true } ttOptions)
-            _transpositionTable.SetSize((int)ttOptions.Size);
+            _transpositionTable.SetSize(ttOptions.Size);
 
         var errors = 0;
         var runnerIndex = (Options is FenOptions).AsByte();
         _usingEpd = runnerIndex == 0;
         var positions = _runners[runnerIndex].Invoke(cancellationToken);
 
-        _perft.Positions = new List<PerftPosition>();
+        _perft.Positions = new();
 
         GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;
 
@@ -129,12 +119,12 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
                 var result = await ComputePerft(cancellationToken).ConfigureAwait(false);
                 Interlocked.Add(ref errors, result.Errors);
                 if (errors != 0)
-                    _log.Error("Parsing failed for Id={Id}", position.Id);
+                    Log.Error("Parsing failed for Id={Id}", position.Id);
                 _resultPool.Return(result);
             }
             catch (AggregateException e)
             {
-                _log.Error(e.GetBaseException(), "Cancel requested.");
+                Log.Error(e.GetBaseException(), "Cancel requested.");
                 Interlocked.Increment(ref errors);
                 break;
             }
@@ -176,7 +166,7 @@ private async IAsyncEnumerable<PerftPosition> ParseEpd(EpdOptions options)
 
             sw.Stop();
             var elapsedMs = sw.ElapsedMilliseconds;
-            _log.Information("Processed {Parsed} epd entries in {Elapsed} ms", parsedCount, elapsedMs);
+            Log.Information("Processed {Parsed} epd entries in {Elapsed} ms", parsedCount, elapsedMs);
         }
     }
 
@@ -216,14 +206,14 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
         result.Fen = pp.Fen;
         _perft.SetGamePosition(pp);
         _perft.BoardPrintCallback(_perft.GetBoard());
-        _log.Information("Fen         : {Fen}", pp.Fen);
-        _log.Information(Line);
+        Log.Information("Fen         : {Fen}", pp.Fen);
+        Log.Information(Line);
 
         foreach (var (depth, expected) in pp.Value)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
-            _log.Information("Depth       : {Depth}", depth);
+            Log.Information("Depth       : {Depth}", depth);
 
             var start = Stopwatch.GetTimestamp();
 
@@ -240,7 +230,7 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
             await WriteOutput(result, baseFileName, cancellationToken);
         }
 
-        _log.Information("{Info} parsing complete. Encountered {Errors} errors", _usingEpd ? "EPD" : "FEN", errors);
+        Log.Information("{Info} parsing complete. Encountered {Errors} errors", _usingEpd ? "EPD" : "FEN", errors);
 
         result.Errors = errors;
 
@@ -275,30 +265,23 @@ private void ComputeResults(in ulong result, int depth, in ulong expected, in Ti
         results.TableHits = _transpositionTable.Hits;
     }
 
-    private void LogInfoHeader()
-    {
-        _log.Information("ChessLib Perft test program {Version} ({Time})", Version, _buildTimeStamp.TimeStamp);
-        _log.Information("High timer resolution : {HighRes}", Stopwatch.IsHighResolution);
-        _log.Information("Initializing..");
-    }
-
     private int LogResults(IPerftResult result)
     {
-        _log.Information("Time passed : {Elapsed}", result.Elapsed);
-        _log.Information("Nps         : {Nps}", result.Nps);
+        Log.Information("Time passed : {Elapsed}", result.Elapsed);
+        Log.Information("Nps         : {Nps}", result.Nps);
         if (_usingEpd)
         {
-            _log.Information("Result      : {Result} - should be {Expected}", result.Result, result.CorrectResult);
+            Log.Information("Result      : {Result} - should be {Expected}", result.Result, result.CorrectResult);
             if (result.Result != result.CorrectResult)
             {
                 var difference = (long)(result.CorrectResult - result.Result);
-                _log.Information("Difference  : {Diff}", Math.Abs(difference));
+                Log.Information("Difference  : {Diff}", Math.Abs(difference));
             }
         }
         else
-            _log.Information("Result      : {Result}", result.Result);
+            Log.Information("Result      : {Result}", result.Result);
 
-        _log.Information("TT hits     : {Hits}", _transpositionTable.Hits);
+        Log.Information("TT hits     : {Hits}", _transpositionTable.Hits);
 
         var error = 0;
 
@@ -306,10 +289,10 @@ private int LogResults(IPerftResult result)
             return error;
 
         if (result.CorrectResult == result.Result)
-            _log.Information("Move count matches!");
+            Log.Information("Move count matches!");
         else
         {
-            _log.Error("Failed for position: {Fen}", _perft.Game.Pos.GenerateFen());
+            Log.Error("Failed for position: {Fen}", _perft.Game.Pos.GenerateFen());
             error = 1;
         }
 
diff --git a/src/Rudzoft.Perft/Services/PerftService.cs b/src/Rudzoft.Perft/Services/PerftService.cs
index 4db7d1e8..0ff20049 100644
--- a/src/Rudzoft.Perft/Services/PerftService.cs
+++ b/src/Rudzoft.Perft/Services/PerftService.cs
@@ -1,25 +1,41 @@
-using Akka.Actor;
+using System.Diagnostics;
+using Akka.Actor;
 using Akka.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 using Rudzoft.Perft.Actors;
+using Serilog;
 
 namespace Rudzoft.Perft.Services;
 
 public sealed class PerftService : IHostedService
 {
+    private const string Version = "v0.2.0";
+
+    private static readonly ILogger Log = Serilog.Log.ForContext<PerftService>();
+
     private readonly IServiceProvider _serviceProvider;
     private readonly IHostApplicationLifetime _applicationLifetime;
+    private readonly IBuildTimeStamp _buildTimeStamp;
     
     private ActorSystem _actorSystem;
     private IActorRef _perftActor;
 
-    public PerftService(IServiceProvider serviceProvider)
+    public PerftService(
+        IServiceProvider serviceProvider,
+        IHostApplicationLifetime applicationLifetime,
+        IBuildTimeStamp buildTimeStamp)
     {
         _serviceProvider = serviceProvider;
+        _applicationLifetime = applicationLifetime;
+        _buildTimeStamp = buildTimeStamp;
     }
 
     public Task StartAsync(CancellationToken cancellationToken)
     {
+        Log.Information("ChessLib Perft test program {Version} ({Time})", Version, _buildTimeStamp.TimeStamp);
+        Log.Information("High timer resolution : {HighRes}", Stopwatch.IsHighResolution);
+        Log.Information("Initializing..");
+
         var bootstrap = BootstrapSetup.Create();
 
         // enable DI support inside this ActorSystem, if needed

From e45ca03ae9d440db025122f9e1df26dae7e6eb58 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 12 Oct 2023 21:25:47 +0200
Subject: [PATCH 053/119] minor code update + updated some dependencies

---
 .../Rudzoft.ChessLib.Benchmark.csproj         |  2 +-
 .../Rudzoft.ChessLib.PGN.Test.csproj          |  4 +-
 .../Rudzoft.ChessLib.Test.csproj              |  6 +--
 src/Rudzoft.ChessLib/Board.cs                 | 15 ++++---
 .../Transposition/TranspositionTable.cs       | 43 ++++++++++++-------
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      | 33 ++++++--------
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  2 +-
 src/Rudzoft.Perft/Actors/FenTargetActor.cs    | 30 ++++++++++++-
 src/Rudzoft.Perft/Actors/PerftRunnerActor.cs  |  1 +
 src/Rudzoft.Perft/Actors/ProgressActor.cs     |  1 -
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |  4 +-
 src/Rudzoft.Perft/Services/PerftRunner.cs     |  7 ++-
 12 files changed, 93 insertions(+), 55 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 29211017..d778b29e 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -13,7 +13,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="BenchmarkDotNet" Version="0.13.7" />
+        <PackageReference Include="BenchmarkDotNet" Version="0.13.9" />
         <PackageReference Include="System.Runtime" Version="4.3.1"/>
     </ItemGroup>
 
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index 212cd893..fbb2acd9 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -8,8 +8,8 @@
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
-        <PackageReference Include="xunit" Version="2.5.0" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
+        <PackageReference Include="xunit" Version="2.5.1" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.1">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 314d577b..c2a24f65 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -25,9 +25,9 @@
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
-        <PackageReference Include="xunit" Version="2.5.0" />
-        <PackageReference Include="xunit.analyzers" Version="1.2.0" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
+        <PackageReference Include="xunit" Version="2.5.1" />
+        <PackageReference Include="xunit.analyzers" Version="1.3.0" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.1">
             <PrivateAssets>all</PrivateAssets>
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index a3527eae..baabbb0a 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -90,6 +90,8 @@ public void AddPiece(Piece pc, Square sq)
 
     public void RemovePiece(Square sq)
     {
+        Debug.Assert(sq.IsOk);
+        
         // WARNING: This is not a reversible operation. If we remove a piece in MakeMove() and
         // then replace it in TakeMove() we will put it at the end of the list and not in its
         // original place, it means index[] and pieceList[] are not invariant to a MakeMove() +
@@ -113,15 +115,18 @@ public void MovePiece(Square from, Square to)
     {
         // _index[from] is not updated and becomes stale. This works as long as _index[] is
         // accessed just by known occupied squares.
-        var pc = _pieces[from.AsInt()];
+        var f = from.AsInt();
+        var t = to.AsInt();
+        
+        var pc = _pieces[f];
         var fromTo = from | to;
         _byType[PieceTypes.AllPieces.AsInt()] ^= fromTo;
         _byType[pc.Type().AsInt()] ^= fromTo;
         _bySide[pc.ColorOf().Side] ^= fromTo;
-        _pieces[from.AsInt()] = Piece.EmptyPiece;
-        _pieces[to.AsInt()] = pc;
-        _index[to.AsInt()] = _index[from.AsInt()];
-        _pieceList[pc.AsInt()][_index[to.AsInt()]] = to;
+        _pieces[f] = Piece.EmptyPiece;
+        _pieces[t] = pc;
+        _index[t] = _index[f];
+        _pieceList[pc.AsInt()][_index[t]] = to;
     }
 
     public Piece MovedPiece(Move m) => PieceAt(m.FromSquare());
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index 01707380..81cd22d4 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -24,6 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Buffers;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Types;
 
@@ -110,9 +113,12 @@ public void Store(in HashKey posKey, Value v, Bound t, Depth d, Move m, Value st
         var replacePos = (posKey.LowerKey & _sizeMask) << 2;
         var ttePos = replacePos;
 
+        var entriesSpan = _entries.AsSpan();
+        ref var entries = ref MemoryMarshal.GetReference(entriesSpan);
+
         for (uint i = 0; i < ClusterSize; i++)
         {
-            ref var tte = ref _entries[ttePos];
+            ref var tte = ref Unsafe.Add(ref entries, ttePos);
 
             if (tte.key == 0 || tte.key == posKey32) // Empty or overwrite old
             {
@@ -120,36 +126,38 @@ public void Store(in HashKey posKey, Value v, Bound t, Depth d, Move m, Value st
                 if (m == Move.EmptyMove)
                     m = tte.move16;
 
-                _entries[ttePos].save(posKey32, v, t, d, m, _generation, statV, kingD);
+                tte.save(posKey32, v, t, d, m, _generation, statV, kingD);
                 return;
             }
 
+            ref var replaceTte = ref Unsafe.Add(ref entries, replacePos);
+
             // Implement replace strategy
             //if ((entries[replacePos].generation8 == generation ? 2 : 0) + (tte.generation8 == generation || tte.bound == 3/*Bound.BOUND_EXACT*/ ? -2 : 0) + (tte.depth16 < entries[replacePos].depth16 ? 1 : 0) > 0)
             //{
             //    replacePos = ttePos;
             //}
 
-            if (_entries[replacePos].generation8 == _generation)
+            if (replaceTte.generation8 == _generation)
             {
                 if (tte.generation8 == _generation || tte.bound == Bound.Exact)
                 {
                     // +2 
-                    if (tte.depth16 < _entries[replacePos].depth16) // +1
+                    if (tte.depth16 < replaceTte.depth16) // +1
                         replacePos = ttePos;
                 }
                 else // +2
                     replacePos = ttePos;
             }
-            else // 0
-            if (!(tte.generation8 == _generation || tte.bound == Bound.Exact) &&
-                tte.depth16 < _entries[replacePos].depth16) // +1
+            // 0
+            else if (!(tte.generation8 == _generation || tte.bound == Bound.Exact) &&
+                     tte.depth16 < replaceTte.depth16) // +1
                 replacePos = ttePos;
 
             ttePos++;
         }
 
-        _entries[replacePos].save(posKey32, v, t, d, m, _generation, statV, kingD);
+        Unsafe.Add(ref entries, replacePos).save(posKey32, v, t, d, m, _generation, statV, kingD);
     }
 
     /// <summary>
@@ -166,15 +174,20 @@ public bool Probe(in HashKey posKey, ref uint ttePos, out TTEntry entry)
         var posKey32 = posKey.UpperKey;
         var offset = (posKey.LowerKey & _sizeMask) << 2;
 
+        var entries = _entries.AsSpan();
+        ref var entriesRef = ref MemoryMarshal.GetReference(entries);
+
         for (var i = offset; i < ClusterSize + offset; i++)
         {
-            if (_entries[i].key == posKey32)
-            {
-                ttePos = i;
-                entry = _entries[i];
-                Hits++;
-                return true;
-            }
+            ref var tte = ref Unsafe.Add(ref entriesRef, i);
+
+            if (tte.key != posKey32)
+                continue;
+
+            ttePos = i;
+            entry = tte;
+            Hits++;
+            return true;
         }
 
         entry = StaticEntry;
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index c0c77e31..7f978808 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -42,8 +42,9 @@ public class Uci : IUci
 
     private static readonly string[] OptionTypeStrings = Enum.GetNames(typeof(UciOptionType));
 
+    protected readonly ObjectPool<IMoveList> MoveListPool;
+    
     private readonly ObjectPool<StringBuilder> _pvPool;
-    private readonly ObjectPool<IMoveList> _moveListPool;
     private readonly Dictionary<string, IOption> _options;
 
     public Uci(ObjectPool<IMoveList> moveListPool)
@@ -51,7 +52,7 @@ public Uci(ObjectPool<IMoveList> moveListPool)
         var policy = new StringBuilderPooledObjectPolicy();
         _pvPool = new DefaultObjectPool<StringBuilder>(policy, 128);
         _options = new();
-        _moveListPool = moveListPool;
+        MoveListPool = moveListPool;
     }
 
     public int MaxThreads { get; set; }
@@ -84,15 +85,9 @@ public void Initialize(int maxThreads = 128)
     public bool TryGetOption(string name, out IOption option)
     {
         ref var opt = ref CollectionsMarshal.GetValueRefOrNullRef(_options, name);
-        
-        if (Unsafe.IsNullRef(ref opt))
-        {
-            option = default;
-            return false;
-        }
-        
-        option = opt;
-        return true;
+        var result = !Unsafe.IsNullRef(ref opt);
+        option = result ? opt : default;
+        return result;
     }
 
     public ulong Nps(in ulong nodes, in TimeSpan time)
@@ -100,7 +95,7 @@ public ulong Nps(in ulong nodes, in TimeSpan time)
 
     public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
     {
-        var ml = _moveListPool.Get();
+        var ml = MoveListPool.Get();
         
         ml.Generate(in pos);
 
@@ -110,14 +105,14 @@ public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
         
         foreach (var move in moves)
         {
-            if (uciMove.Equals(move.Move.ToString(), StringComparison.InvariantCultureIgnoreCase))
-            {
-                m = move.Move;
-                break;
-            }
+            if (!uciMove.Equals(move.Move.ToString(), StringComparison.InvariantCultureIgnoreCase))
+                continue;
+            
+            m = move.Move;
+            break;
         }
         
-        _moveListPool.Return(ml);
+        MoveListPool.Return(ml);
 
         return m;
     }
@@ -224,7 +219,7 @@ public string MoveToString(Move m, ChessMode chessMode = ChessMode.Normal)
         if (type == MoveTypes.Promotion)
             s[index++] = m.PromotedPieceType().GetPieceChar();
 
-        return new string(s[..index]);
+        return new(s[..index]);
     }
 
     /// <summary>
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index c497fa5c..8b208f3f 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -57,7 +57,7 @@
         <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
         <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.10" />
         <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0"/>
-        <PackageReference Include="ZString" Version="2.5.0"/>
+        <PackageReference Include="ZString" Version="2.5.1" />
     </ItemGroup>
     <ItemGroup>
         <Folder Include="Protocol\"/>
diff --git a/src/Rudzoft.Perft/Actors/FenTargetActor.cs b/src/Rudzoft.Perft/Actors/FenTargetActor.cs
index db3914da..6a76945e 100644
--- a/src/Rudzoft.Perft/Actors/FenTargetActor.cs
+++ b/src/Rudzoft.Perft/Actors/FenTargetActor.cs
@@ -1,6 +1,32 @@
-namespace Rudzoft.Perft.Actors;
+using System.Collections.Immutable;
+using Akka.Actor;
+using Rudzoft.ChessLib.Types;
+using Serilog;
 
-public class FenTargetActor
+namespace Rudzoft.Perft.Actors;
+
+public sealed class FenTargetActor : ReceiveActor, IWithTimers
 {
+    public sealed record Inititalize(string Fen, ImmutableArray<Move> Moves, int DepthTarget);
+
+    public sealed record Update();
+
+    private static readonly ILogger Logger = Log.ForContext<FenTargetActor>();
     
+    private string _fen;
+    private ImmutableArray<Move> _moves;
+    private int _depthTarget;
+
+    public FenTargetActor()
+    {
+        Receive<Inititalize>(initialize =>
+        {
+            _fen = initialize.Fen;
+            _moves = initialize.Moves;
+            _depthTarget = initialize.DepthTarget;
+        });
+    }
+    
+    public ITimerScheduler Timers { get; set; }
+
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs b/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
index 582888cf..6429a069 100644
--- a/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
+++ b/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
@@ -1,6 +1,7 @@
 using System.Collections.Immutable;
 using Akka.Actor;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
 using Rudzoft.Perft.Options;
 using Rudzoft.Perft.Services;
 
diff --git a/src/Rudzoft.Perft/Actors/ProgressActor.cs b/src/Rudzoft.Perft/Actors/ProgressActor.cs
index e02aa2e9..373eadfd 100644
--- a/src/Rudzoft.Perft/Actors/ProgressActor.cs
+++ b/src/Rudzoft.Perft/Actors/ProgressActor.cs
@@ -30,7 +30,6 @@ public ProgressActor()
             {
                 existing = new();
                 existing.CurrentDepth = progress.Depth;
-                
             }
             else
             {
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index 3439814f..ab1c38a3 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -5,8 +5,8 @@
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Akka" Version="1.5.13-beta1" />
-      <PackageReference Include="Akka.DependencyInjection" Version="1.5.13-beta1" />
+      <PackageReference Include="Akka" Version="1.5.13" />
+      <PackageReference Include="Akka.DependencyInjection" Version="1.5.13" />
       <PackageReference Include="CommandLineParser" Version="2.9.1" />
       <PackageReference Include="Serilog" Version="3.0.1" />
       <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0"/>
diff --git a/src/Rudzoft.Perft/Services/PerftRunner.cs b/src/Rudzoft.Perft/Services/PerftRunner.cs
index fe25faae..61ca5c4a 100644
--- a/src/Rudzoft.Perft/Services/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Services/PerftRunner.cs
@@ -151,7 +151,7 @@ private async IAsyncEnumerable<PerftPosition> ParseEpd(EpdOptions options)
     {
         foreach (var epd in options.Epds)
         {
-            var sw = Stopwatch.StartNew();
+            var start = Stopwatch.GetTimestamp();
 
             var parsedCount = 0L;
             
@@ -164,9 +164,8 @@ private async IAsyncEnumerable<PerftPosition> ParseEpd(EpdOptions options)
                 yield return perftPosition;
             }
 
-            sw.Stop();
-            var elapsedMs = sw.ElapsedMilliseconds;
-            Log.Information("Processed {Parsed} epd entries in {Elapsed} ms", parsedCount, elapsedMs);
+            var elapsed = Stopwatch.GetElapsedTime(start);
+            Log.Information("EPD processing completed. parsed={Parsed},time={Elapsed}", parsedCount, elapsed);
         }
     }
 

From 3430bfb220c81cb31006af12ef41874a3b7f4d13 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 20:51:16 +0100
Subject: [PATCH 054/119] updated kpk bit base code to use refs

---
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index 5f92c908..7e18abcd 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -66,7 +66,7 @@ public KpkBitBase()
             repeat = 1;
             for (var i = 0; i < db.Length; ++i)
             {
-                var kpkPosition = Unsafe.Add(ref dbSpace, i);
+                ref var kpkPosition = ref Unsafe.Add(ref dbSpace, i);
                 repeat = (kpkPosition.Result == Results.Unknown
                           && kpkPosition.Classify(db) != Results.Unknown).AsByte();
             }
@@ -75,7 +75,7 @@ public KpkBitBase()
         // Fill the bitbase with the decisive results
         for (var i = 0; i < db.Length; ++i)
         {
-            var kpkPosition = Unsafe.Add(ref dbSpace, i);
+            ref var kpkPosition = ref Unsafe.Add(ref dbSpace, i);
             if (kpkPosition.Result == Results.Win)
                 _kpKbb.Set(i, true);
         }

From dfcc35432aa2f48837dd17a062ef8d4c2d2d4c66 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 20:51:31 +0100
Subject: [PATCH 055/119] update zobrist with material key method

---
 src/Rudzoft.ChessLib/Hash/IZobrist.cs | 1 +
 src/Rudzoft.ChessLib/Hash/Zobrist.cs  | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/src/Rudzoft.ChessLib/Hash/IZobrist.cs b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
index 2aaefe3a..f20dbdeb 100644
--- a/src/Rudzoft.ChessLib/Hash/IZobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
@@ -14,6 +14,7 @@ public interface IZobrist
     HashKey ComputePawnKey(IPosition pos);
     HashKey ComputePositionKey(IPosition pos);
     ref HashKey Psq(Square square, Piece piece);
+    ref HashKey Psq(int pieceCount, Piece piece);
     ref HashKey Castleling(CastleRights index);
     ref HashKey Castleling(CastleRight index);
     HashKey Side();
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index c02a2514..9fe47c34 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -153,6 +153,9 @@ public HashKey ComputePositionKey(IPosition pos)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public ref HashKey Psq(Square square, Piece piece) => ref _zobristPst[square.AsInt()][piece.AsInt()];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public ref HashKey Psq(int pieceCount, Piece piece) => ref _zobristPst[pieceCount][piece.AsInt()];
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public ref HashKey Castleling(CastleRights index) => ref _zobristCastling[index.AsInt()];
 

From 8a67e1cfdad10cafe18fcc56def977d5472d9429 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 20:51:49 +0100
Subject: [PATCH 056/119] update state stack to use regular string builder for
 now

---
 src/Rudzoft.ChessLib/Types/StateStack.cs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Types/StateStack.cs b/src/Rudzoft.ChessLib/Types/StateStack.cs
index 4190141e..9621daa7 100644
--- a/src/Rudzoft.ChessLib/Types/StateStack.cs
+++ b/src/Rudzoft.ChessLib/Types/StateStack.cs
@@ -24,8 +24,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using Cysharp.Text;
 using System.Collections;
+using System.Text;
 
 namespace Rudzoft.ChessLib.Types;
 
@@ -143,13 +143,13 @@ IEnumerator<State> IEnumerable<State>.GetEnumerator()
 
     public override string ToString()
     {
-        using var sb = ZString.CreateStringBuilder();
+        var       sb = new StringBuilder();
         for (var i = Size; i > 0; i--)
         {
             var o = _stack[i];
             sb.Append(' ');
             if (o != null)
-                sb.Append(o.ToString()!);
+                sb.Append(o);
             else
                 sb.Append(' ');
             sb.Append(' ');

From 4d7367a9896c08c6ed61628029c6bfbeb40ff91b Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 20:52:58 +0100
Subject: [PATCH 057/119] simplify perft runner a bit

---
 src/Rudzoft.Perft/Services/PerftRunner.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Rudzoft.Perft/Services/PerftRunner.cs b/src/Rudzoft.Perft/Services/PerftRunner.cs
index 61ca5c4a..ae6dd517 100644
--- a/src/Rudzoft.Perft/Services/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Services/PerftRunner.cs
@@ -48,7 +48,7 @@ public sealed class PerftRunner : IPerftRunner
 
     private static readonly string Line = new('-', 65);
 
-    private static readonly Lazy<string> CurrentDirectory = new(static () => Environment.CurrentDirectory);
+    private static string CurrentDirectory => Environment.CurrentDirectory;
 
     private readonly Func<CancellationToken, IAsyncEnumerable<PerftPosition>>[] _runners;
 
@@ -197,7 +197,7 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
 
         var pp = _perft.Positions[^1];
         var baseFileName = SaveResults
-            ? Path.Combine(CurrentDirectory.Value, $"{FixFileName(pp.Fen)}[")
+            ? Path.Combine(CurrentDirectory, $"{FixFileName(pp.Fen)}[")
             : string.Empty;
 
         var errors = 0;

From b8fe3bdad30ed0591d83dc3ff15b46832902af9c Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 20:53:15 +0100
Subject: [PATCH 058/119] removed not needed dependency

---
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 8b208f3f..ae749303 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -57,7 +57,6 @@
         <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
         <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.10" />
         <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0"/>
-        <PackageReference Include="ZString" Version="2.5.1" />
     </ItemGroup>
     <ItemGroup>
         <Folder Include="Protocol\"/>

From ac6f462bc97a9fa92ad702d553a104e0205868a6 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 20:53:28 +0100
Subject: [PATCH 059/119] update san to move tests

---
 .../PgnMoveNotationTests/SanToMoveTests.cs                      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
index 5bc193cc..f827a445 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
@@ -121,7 +121,7 @@ public async Task AllAtOnceConvert()
 
         var sanMoves = new List<string>();
 
-        foreach (var pgnMove in games.First().Moves)
+        foreach (var pgnMove in games[0].Moves)
         {
             sanMoves.Add(pgnMove.WhiteMove);
             if (!string.IsNullOrWhiteSpace(pgnMove.BlackMove))

From 3c62e9b62c418b0b8f898b00c9d3a004e178e8d5 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 20:53:50 +0100
Subject: [PATCH 060/119] update regex pgn parser

---
 src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
index 1270463f..81b06237 100644
--- a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
@@ -34,7 +34,7 @@ public sealed partial class RegexPgnParser : IPgnParser
     private const int DefaultTagCapacity = 7;
     private const int DefaultMoveListCapacity = 24;
 
-    [GeneratedRegex(@"\[(?<tagName>\w+)\s+""(?<tagValue>[^""]+)""\]",  RegexOptions.NonBacktracking)]
+    [GeneratedRegex("""\[(?<tagName>\w+)\s+"(?<tagValue>[^"]+)"\]""",  RegexOptions.NonBacktracking)]
     private static partial Regex TagPairRegex();
 
     [GeneratedRegex(@"(?<moveNumber>\d+)\.\s*(?<whiteMove>\S+)(\s+(?<blackMove>\S+))?",  RegexOptions.NonBacktracking)]

From 00ced65da5e1210f2a06a117a13a9ab887353f14 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 20:54:13 +0100
Subject: [PATCH 061/119] update perft slightly

---
 src/Rudzoft.ChessLib.Perft/Perft.cs |  2 +-
 src/Rudzoft.ChessLib/Game.cs        | 11 +++++++----
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/Rudzoft.ChessLib.Perft/Perft.cs
index 2e1a8e5b..d055fabd 100644
--- a/src/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/Rudzoft.ChessLib.Perft/Perft.cs
@@ -119,7 +119,7 @@ public bool HasPositionCount(int index, int depth)
         if (pos.Value.Count == 0)
             return false;
 
-        var depthValue = pos.Value.FirstOrDefault(v => v.Depth == depth && v.MoveCount > 0);
+        var depthValue = pos.Value.Find(v => v.Depth == depth && v.MoveCount > 0);
 
         return !depthValue.Equals(default);
     }
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index e99a4628..6a3c5de4 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -34,6 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Protocol.UCI;
+using Rudzoft.ChessLib.Tables.Perft;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib;
@@ -41,7 +42,8 @@ namespace Rudzoft.ChessLib;
 public sealed class Game : IGame
 {
     private readonly ObjectPool<IMoveList> _moveListPool;
-    private readonly IPosition _pos;
+    private readonly IPosition             _pos;
+    private readonly PerftTable            _perftTable;
 
     public Game(
         ITranspositionTable transpositionTable,
@@ -54,10 +56,11 @@ public Game(
         _moveListPool = moveListPool;
         _pos = pos;
 
-        Table = transpositionTable;
+        Table            = transpositionTable;
         SearchParameters = searchParameters;
-        Uci = uci;
-        Cpu = cpu;
+        Uci              = uci;
+        Cpu              = cpu;
+        _perftTable      = new PerftTable();
     }
 
     public Action<IPieceSquare> PieceUpdated => _pos.PieceUpdated;

From c3a834ec4cef1a1a3de02705f72e221a719911b1 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 20:54:45 +0100
Subject: [PATCH 062/119] update position with an improved CanEnPassant()
 functionality

---
 src/Rudzoft.ChessLib/IPosition.cs             |   2 +-
 src/Rudzoft.ChessLib/Position.cs              | 161 ++++++++++--------
 src/Rudzoft.ChessLib/State.cs                 |  30 ++--
 .../Validation/PositionValidator.cs           |   4 +-
 4 files changed, 112 insertions(+), 85 deletions(-)

diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index 1cdb3ff0..c5e68f6c 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -197,7 +197,7 @@ public interface IPosition : IEnumerable<Piece>
 
     IPosition Set(ReadOnlySpan<char> code, Player p, in State state);
     
-    HashKey GetPiecesKey();
+    HashKey GetKey();
 
     HashKey GetPawnKey();
 
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index fecf6918..66dbe306 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -115,7 +115,7 @@ public Position(
 
     public int Ply { get; private set; }
 
-    public int Rule50 => State.Rule50;
+    public int Rule50 => State.ClockPly;
 
     public Player SideToMove => _sideToMove;
 
@@ -359,7 +359,7 @@ public FenData GenerateFen()
         }
 
         fen[length++] = space;
-        length = fen.Append(State.Rule50, length);
+        length = fen.Append(State.ClockPly, length);
         fen[length++] = space;
         length = fen.Append(1 + (Ply - _sideToMove.IsBlack.AsByte() / 2), length);
 
@@ -410,7 +410,7 @@ public HashKey GetPawnKey()
     public Piece GetPiece(Square sq) => Board.PieceAt(sq);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public HashKey GetPiecesKey()
+    public HashKey GetKey()
     {
         var result = HashKey.Empty;
         var pieces = Board.Pieces();
@@ -421,6 +421,12 @@ public HashKey GetPiecesKey()
             result ^= Zobrist.Psq(sq, pc);
         }
 
+        if (State.EnPassantSquare != Square.None)
+            result ^= Zobrist.EnPassant(State.EnPassantSquare);
+        
+        if (_sideToMove == Player.Black)
+            result ^= Zobrist.Side();
+        
         return result;
     }
 
@@ -517,7 +523,7 @@ public bool HasRepetition()
     }
 
     public bool IsDraw(int ply)
-        => State.Rule50 switch
+        => State.ClockPly switch
         {
             > 99 when State.Checkers.IsEmpty || HasMoves() => true,
             var _ => State.Repetition > 0 && State.Repetition < ply
@@ -686,20 +692,19 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
     {
         State.LastMove = m;
 
-        var k = State.PositionKey ^ Zobrist.Side();
+        var posKey = State.PositionKey ^ Zobrist.Side();
 
         State = State.CopyTo(newState);
         var state = State;
 
         Ply++;
-        state.Rule50++;
-        state.PliesFromNull++;
+        state.ClockPly++;
+        state.NullPly++;
 
         var us = _sideToMove;
         var them = ~us;
         var (from, to, type) = m;
         var pc = GetPiece(from);
-        var pt = pc.Type();
         var capturedPiece = m.IsEnPassantMove()
             ? PieceTypes.Pawn.MakePiece(them)
             : GetPiece(to);
@@ -716,7 +721,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
 
             var (rookFrom, rookTo) = DoCastle(us, from, ref to, CastlePerform.Do);
 
-            k ^= Zobrist.Psq(rookFrom, capturedPiece) ^ Zobrist.Psq(rookTo, capturedPiece);
+            posKey ^= Zobrist.Psq(rookFrom, capturedPiece) ^ Zobrist.Psq(rookTo, capturedPiece);
 
             // reset captured piece type as castleling is "king-captures-rook"
             capturedPiece = Piece.EmptyPiece;
@@ -732,40 +737,39 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 {
                     captureSquare -= us.PawnPushDistance();
 
-                    Debug.Assert(pt == PieceTypes.Pawn);
+                    Debug.Assert(pc.Type() == PieceTypes.Pawn);
                     Debug.Assert(to == State.EnPassantSquare);
                     Debug.Assert(to.RelativeRank(us) == Ranks.Rank6);
                     Debug.Assert(!IsOccupied(to));
-                    Debug.Assert(GetPiece(captureSquare) == pt.MakePiece(them));
+                    Debug.Assert(GetPiece(captureSquare) == pc.Type().MakePiece(them));
                 }
 
                 state.PawnKey ^= Zobrist.Psq(captureSquare, capturedPiece);
             }
             else
-            {
                 _nonPawnMaterial[them.Side] -= Values.GetPieceValue(capturedPiece, Phases.Mg);
-            }
 
             // Update board and piece lists
             RemovePiece(captureSquare);
             if (type == MoveTypes.Enpassant)
                 Board.ClearPiece(captureSquare);
 
-            k ^= Zobrist.Psq(captureSquare, capturedPiece);
+            posKey ^= Zobrist.Psq(captureSquare, capturedPiece);
+            State.MaterialKey ^= Zobrist.Psq(Board.PieceCount(capturedPiece), capturedPiece);
 
             // TODO : Update other depending keys and psq values here
 
             // Reset rule 50 counter
-            state.Rule50 = 0;
+            state.ClockPly = 0;
         }
 
         // update key with moved piece
-        k ^= Zobrist.Psq(from, pc) ^ Zobrist.Psq(to, pc);
+        posKey ^= Zobrist.Psq(from, pc) ^ Zobrist.Psq(to, pc);
 
         // reset en-passant square if it is set
         if (state.EnPassantSquare != Square.None)
         {
-            k ^= Zobrist.EnPassant(state.EnPassantSquare);
+            posKey ^= Zobrist.EnPassant(state.EnPassantSquare);
             state.EnPassantSquare = Square.None;
         }
 
@@ -773,9 +777,9 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         if (state.CastlelingRights != CastleRight.None &&
             (_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]) != CastleRight.None)
         {
-            k ^= Zobrist.Castleling(state.CastlelingRights);
+            posKey ^= Zobrist.Castleling(state.CastlelingRights);
             state.CastlelingRights &= ~(_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]);
-            k ^= Zobrist.Castleling(state.CastlelingRights);
+            posKey ^= Zobrist.Castleling(state.CastlelingRights);
         }
 
         // Move the piece. The tricky Chess960 castle is handled earlier
@@ -783,14 +787,14 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             MovePiece(from, to);
 
         // If the moving piece is a pawn do some special extra work
-        if (pt == PieceTypes.Pawn)
+        if (pc.Type() == PieceTypes.Pawn)
         {
             // Set en-passant square, only if moved pawn can be captured
             if ((to.Value.AsInt() ^ from.Value.AsInt()) == 16
-                && ((to - us.PawnPushDistance()).PawnAttack(us) & Pieces(PieceTypes.Pawn, them)).IsNotEmpty)
+                && CanEnPassant(them, from + us.PawnPushDistance()))
             {
-                state.EnPassantSquare = to - us.PawnPushDistance();
-                k ^= Zobrist.EnPassant(state.EnPassantSquare.File);
+                state.EnPassantSquare = from + us.PawnPushDistance();
+                posKey ^= Zobrist.EnPassant(state.EnPassantSquare.File);
             }
             else if (type == MoveTypes.Promotion)
             {
@@ -803,7 +807,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 AddPiece(promotionPiece, to);
 
                 // Update hash keys
-                k ^= Zobrist.Psq(to, pc) ^ Zobrist.Psq(to, promotionPiece);
+                posKey ^= Zobrist.Psq(to, pc) ^ Zobrist.Psq(to, promotionPiece);
                 state.PawnKey ^= Zobrist.Psq(to, pc);
 
                 _nonPawnMaterial[us.Side] += Values.GetPieceValue(promotionPiece, Phases.Mg);
@@ -813,7 +817,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             state.PawnKey ^= Zobrist.Psq(from, pc) ^ Zobrist.Psq(to, pc);
 
             // Reset rule 50 draw counter
-            state.Rule50 = 0;
+            state.ClockPly = 0;
         }
 
         // TODO : Update piece values here
@@ -822,7 +826,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         Debug.Assert(GetKingSquare(them).IsOk);
 
         // Update state properties
-        state.PositionKey = k;
+        state.PositionKey = posKey;
         state.CapturedPiece = capturedPiece.Type();
 
         state.Checkers = givesCheck ? AttacksTo(GetKingSquare(them)) & Board.Pieces(us) : BitBoard.Empty;
@@ -849,8 +853,8 @@ public void MakeNullMove(in State newState)
 
         State.PositionKey ^= Zobrist.Side();
 
-        ++State.Rule50;
-        State.PliesFromNull = 0;
+        ++State.ClockPly;
+        State.NullPly = 0;
 
         _sideToMove = ~_sideToMove;
 
@@ -1177,7 +1181,7 @@ private void SetupMoveNumber(IFenData fenData)
                 moveNum--;
         }
 
-        State.Rule50 = halfMoveNum;
+        State.ClockPly = halfMoveNum;
         Ply = moveNum;
     }
 
@@ -1244,19 +1248,18 @@ public void TakeMove(Move m)
 
         var us = _sideToMove;
         var (from, to, type) = m;
-        var pc = GetPiece(to);
 
         Debug.Assert(!IsOccupied(from) || m.IsCastleMove());
         Debug.Assert(State.CapturedPiece != PieceTypes.King);
 
         if (type == MoveTypes.Promotion)
         {
-            Debug.Assert(pc.Type() == m.PromotedPieceType());
+            Debug.Assert(GetPiece(to).Type() == m.PromotedPieceType());
             Debug.Assert(to.RelativeRank(us) == Rank.Rank8);
             Debug.Assert(m.PromotedPieceType() >= PieceTypes.Knight && m.PromotedPieceType() <= PieceTypes.Queen);
 
             RemovePiece(to);
-            pc = PieceTypes.Pawn.MakePiece(us);
+            var pc = PieceTypes.Pawn.MakePiece(us);
             AddPiece(pc, to);
             _nonPawnMaterial[_sideToMove.Side] -= Values.GetPieceValue(pc, Phases.Mg);
         }
@@ -1279,7 +1282,7 @@ public void TakeMove(Move m)
                 {
                     captureSquare -= us.PawnPushDistance();
 
-                    Debug.Assert(pc.Type() == PieceTypes.Pawn);
+                    Debug.Assert(GetPiece(to).Type() == PieceTypes.Pawn);
                     Debug.Assert(to == State.Previous.EnPassantSquare);
                     Debug.Assert(to.RelativeRank(us) == Ranks.Rank6);
                     Debug.Assert(!IsOccupied(captureSquare));
@@ -1313,7 +1316,7 @@ public void TakeNullMove()
     {
         Debug.Assert(State != null);
         Debug.Assert(State.Previous != null);
-        Debug.Assert(State.PliesFromNull == 0);
+        Debug.Assert(State.NullPly == 0);
         Debug.Assert(State.CapturedPiece == PieceTypes.NoPieceType);
         Debug.Assert(State.Checkers.IsEmpty);
 
@@ -1389,7 +1392,6 @@ private static CastleRights OrCastlingRight(Player c, bool isKingSide)
     private (Square, Square) DoCastle(Player us, Square from, ref Square to, CastlePerform castlePerform)
     {
         var kingSide = to > from;
-        var doCastleling = castlePerform == CastlePerform.Do;
 
         // Castling is encoded as "king captures friendly rook"
         var rookFromTo = (rookFrom: to, rookTo: (kingSide ? Square.F1 : Square.D1).Relative(us));
@@ -1397,7 +1399,7 @@ private static CastleRights OrCastlingRight(Player c, bool isKingSide)
         to = (kingSide ? Square.G1 : Square.C1).Relative(us);
 
         // If we are performing castle move, just swap the squares
-        if (doCastleling)
+        if (castlePerform == CastlePerform.Do)
         {
             (to, from) = (from, to);
             (rookFromTo.rookFrom, rookFromTo.rookTo) = (rookFromTo.rookTo, rookFromTo.rookFrom);
@@ -1475,29 +1477,15 @@ private void CopyState(in State newState)
     
     private void SetState(State state)
     {
-        var k = HashKey.Empty;
         state.MaterialKey = HashKey.Empty;
-        state.PawnKey = Zobrist.ZobristNoPawn;
-        
+
         _nonPawnMaterial[Player.White.Side] = _nonPawnMaterial[Player.Black.Side] = Value.ValueZero;
         State.Checkers = AttacksTo(GetKingSquare(_sideToMove)) & Board.Pieces(~_sideToMove);
-        
-        SetCheckInfo(State);
-        
-        for (var b = Pieces(); b.IsNotEmpty;)
-        {
-            var sq = BitBoards.PopLsb(ref b);
-            var pc = Board.PieceAt(sq);
-            k ^= Zobrist.Psq(sq, pc);
-        }
-
-        if (state.EnPassantSquare != Square.None)
-            k ^= Zobrist.EnPassant(state.EnPassantSquare);
 
-        if (SideToMove.IsBlack)
-            k ^= Zobrist.Side();
+        SetCheckInfo(State);
 
-        state.PositionKey = k ^ Zobrist.Castleling(state.CastlelingRights);
+        state.PositionKey = GetKey();
+        state.PawnKey     = GetPawnKey();
 
         for (var b = Pieces(PieceTypes.Pawn); b.IsNotEmpty;)
         {
@@ -1517,21 +1505,6 @@ private void SetState(State state)
             for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
                 state.MaterialKey ^= Zobrist.Psq(cnt, pc);
         }
-
-        // // compute hash keys
-        // for (var b = Board.Pieces(); b.IsNotEmpty;)
-        // {
-        //     var sq = BitBoards.PopLsb(ref b);
-        //     var pc = GetPiece(sq);
-        //     var pt = pc.Type();
-        //
-        //     if (pt != PieceTypes.King)
-        //         _nonPawnMaterial[pc.ColorOf().Side] += Values.GetPieceValue(pc, Phases.Mg);
-        // }
-        //
-        // State.MaterialKey = _zobrist.ComputeMaterialKey(this);
-        // State.PawnKey = GetPawnKey();
-        // State.PositionKey = GetPiecesKey();
     }
 
     private void SetupCastle(ReadOnlySpan<char> castle)
@@ -1594,6 +1567,7 @@ private Square RookSquare(Square startSq, Piece rook)
         return result;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private bool HasMoves()
     {
         var ml = _moveListPool.Get();
@@ -1604,6 +1578,7 @@ private bool HasMoves()
         return hasMoves;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private bool ContainsMove(Move m)
     {
         var ml = _moveListPool.Get();
@@ -1612,4 +1587,52 @@ private bool ContainsMove(Move m)
         _moveListPool.Return(ml);
         return contains;
     }
+
+    /// <summary>
+    /// Checks if a given position allows for EnPassant by the given color
+    /// </summary>
+    /// <param name="us">The color to check</param>
+    /// <param name="epSquare">The ep square to check</param>
+    /// <param name="moved">flag if piece is moved or not</param>
+    /// <returns>true if allows, otherwise false</returns>
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
+    {
+        Debug.Assert(epSquare.IsOk);
+        Debug.Assert(epSquare.RelativeRank(us) != Rank.Rank3);
+
+        var them       = ~us;
+
+        if (moved && !(Pieces(PieceTypes.Pawn, ~us).Contains(epSquare + them.PawnPushDistance()) && Board.IsEmpty(epSquare) && Board.IsEmpty(epSquare + us.PawnPushDistance())))
+            return false;
+
+        // En-passant attackers
+        var attackers = Pieces(PieceTypes.Pawn, us) & epSquare.PawnAttack(them);
+        
+        Debug.Assert(attackers.Count <= 2);
+        
+        if (attackers.IsEmpty)
+            return false;
+
+        var cap = moved ? epSquare - us.PawnPushDistance() : epSquare + us.PawnPushDistance();
+        Debug.Assert(Board.PieceAt(cap) == PieceTypes.Pawn.MakePiece(them));
+
+        var ksq = Board.Square(PieceTypes.King, us);
+        var bq = Pieces(PieceTypes.Bishop, PieceTypes.Queen, them) & GetAttacks(ksq, PieceTypes.Bishop);
+        var rq = Pieces(PieceTypes.Rook, PieceTypes.Queen, them) & GetAttacks(ksq, PieceTypes.Rook);
+        
+        var mocc = (Pieces() ^ cap) | epSquare;
+        
+        while (attackers) {
+            var sq = BitBoards.PopLsb(ref attackers);
+            var amocc =  mocc ^ sq;
+            // Check en-passant is legal for the position
+            if (
+                (bq.IsEmpty || (bq & GetAttacks(ksq, PieceTypes.Bishop, in amocc)).IsEmpty) &&
+                (rq.IsEmpty || (rq & GetAttacks(ksq, PieceTypes.Rook, in amocc)).IsEmpty))
+                return true;
+        }
+        
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index 2389508d..c2b61090 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.ComponentModel;
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
@@ -33,19 +34,22 @@ namespace Rudzoft.ChessLib;
 
 public sealed class State : IEquatable<State>
 {
+    [Description("Hash key for material")]
     public HashKey MaterialKey { get; set; }
 
+    [Description("Hash key for pawns")]
     public HashKey PawnKey { get; set; }
 
-    public int Rule50 { get; set; }
+    [Description("Number of half moves clock since the last pawn advance or any capture")]
+    public int ClockPly { get; set; }
 
-    public int PliesFromNull { get; set; }
+    public int NullPly { get; set; }
 
     public CastleRight CastlelingRights { get; set; }
 
+    [Description("En-passant -> 'in-passing' square")]
     public Square EnPassantSquare { get; set; }
 
-
     // -----------------------------
     // Properties below this point are not copied from other state
     // since they are always recomputed
@@ -81,8 +85,8 @@ public State(State other)
         PawnKey = other.PawnKey;
         MaterialKey = other.MaterialKey;
         CastlelingRights = other.CastlelingRights;
-        Rule50 = other.Rule50;
-        PliesFromNull = other.PliesFromNull;
+        ClockPly = other.ClockPly;
+        NullPly = other.NullPly;
         EnPassantSquare = other.EnPassantSquare;
         Previous = other;
 
@@ -112,8 +116,8 @@ public State CopyTo(State other)
         // copy over preserved values
         other.MaterialKey = MaterialKey;
         other.PawnKey = PawnKey;
-        other.Rule50 = Rule50;
-        other.PliesFromNull = PliesFromNull;
+        other.ClockPly = ClockPly;
+        other.NullPly = NullPly;
         other.CastlelingRights = CastlelingRights;
         other.EnPassantSquare = EnPassantSquare;
         other.Previous = this;
@@ -128,7 +132,7 @@ public void Clear()
     {
         LastMove = Move.EmptyMove;
         PawnKey = PositionKey = MaterialKey = HashKey.Empty;
-        PliesFromNull = 0;
+        NullPly = 0;
         Repetition = 0;
         CastlelingRights = CastleRight.None;
         EnPassantSquare = Square.None;
@@ -160,7 +164,7 @@ public void UpdateRepetition()
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int End() => Math.Min(Rule50, PliesFromNull);
+    public int End() => Math.Min(ClockPly, NullPly);
 
     public bool Equals(State other)
     {
@@ -170,8 +174,8 @@ public bool Equals(State other)
                && PawnKey.Equals(other.PawnKey)
                && EnPassantSquare.Equals(other.EnPassantSquare)
                && CastlelingRights == other.CastlelingRights
-               && PliesFromNull == other.PliesFromNull
-               && Rule50 == other.Rule50
+               && NullPly == other.NullPly
+               && ClockPly == other.ClockPly
                && Pinners.Equals(other.Pinners)
                && Checkers.Equals(other.Checkers)
                && CapturedPiece == other.CapturedPiece
@@ -186,8 +190,8 @@ public override int GetHashCode()
         hashCode.Add(LastMove);
         hashCode.Add(PawnKey);
         hashCode.Add(MaterialKey);
-        hashCode.Add(PliesFromNull);
-        hashCode.Add(Rule50);
+        hashCode.Add(NullPly);
+        hashCode.Add(ClockPly);
         hashCode.Add(PositionKey);
         hashCode.Add(CastlelingRights.Rights.AsInt());
         hashCode.Add(EnPassantSquare);
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index 3c84a894..e4089e9b 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -198,8 +198,8 @@ private IEnumerable<string> ValidateState(IPosition pos)
         if (state.Repetition < 0)
             yield return $"{nameof(state.Repetition)} is negative";
 
-        if (state.Rule50 < 0)
-            yield return $"{nameof(state.Rule50)} is negative";
+        if (state.ClockPly < 0)
+            yield return $"{nameof(state.ClockPly)} is negative";
 
         if (state.Equals(state.Previous))
             yield return "state has itself as previous state";

From c516c022767a15f6619c27a9d690851c38160acb Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 22:48:48 +0100
Subject: [PATCH 063/119] update position key create

---
 src/Rudzoft.ChessLib/Position.cs | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 66dbe306..ccd56696 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1487,15 +1487,19 @@ private void SetState(State state)
         state.PositionKey = GetKey();
         state.PawnKey     = GetPawnKey();
 
-        for (var b = Pieces(PieceTypes.Pawn); b.IsNotEmpty;)
+        var b = Pieces(PieceTypes.Pawn);
+        while (b.IsNotEmpty)
         {
             var sq = BitBoards.PopLsb(ref b);
             var pc = Board.PieceAt(sq);
             state.MaterialKey ^= Zobrist.Psq(sq, pc);
         }
 
-        foreach (var pc in Piece.All.AsSpan())
+        ref var pieces = ref MemoryMarshal.GetArrayDataReference(Piece.All);
+
+        for (var i = 0; i < Piece.All.Length; i++)
         {
+            ref var pc = ref Unsafe.Add(ref pieces, i);
             if (pc.Type() != PieceTypes.Pawn && pc.Type() != PieceTypes.King)
             {
                 var val = Values.GetPieceValue(pc.Type(), Phases.Mg).AsInt() * Board.PieceCount(pc);

From f18ce7832eb9971a536b41ee6f306b812f9eed68 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 22:48:56 +0100
Subject: [PATCH 064/119] simplify perft tests

---
 .../PerftTests/PerftTest.cs                   | 138 ++++++++----------
 1 file changed, 63 insertions(+), 75 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
index 0d10706d..916ce33f 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
@@ -28,24 +28,19 @@ namespace Rudzoft.ChessLib.Test.PerftTests;
 
 public sealed class PerftOneTest : PerftVerify
 {
-    private static readonly string[] Fens = {
-        Fen.Fen.StartPositionFen,
-        Fen.Fen.StartPositionFen,
-        Fen.Fen.StartPositionFen,
-        Fen.Fen.StartPositionFen,
-        Fen.Fen.StartPositionFen,
-        Fen.Fen.StartPositionFen
-    };
+    private static readonly string[] Fens = Enumerable.Repeat(Fen.Fen.StartPositionFen, 6).ToArray();
 
-    private static readonly int[] Depths = {
+    private static readonly int[] Depths =
+    {
         1, 2, 3, 4, 5, 6
     };
 
-    private static readonly ulong[] Results = {
+    private static readonly ulong[] Results =
+    {
         20UL, 400UL, 8_902UL, 197_281UL, 4_865_609UL, 119_060_324UL
     };
 
-    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
     [Theory]
     [MemberData(nameof(PerftTheoryData))]
@@ -55,23 +50,21 @@ public void Perft(string fen, int depth, ulong expected)
 
 public sealed class PerftTwoTest : PerftVerify
 {
-    private static readonly string[] Fens = {
-        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
-        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
-        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
-        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
-        "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"
-    };
+    private const string Fen = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1";
+
+    private static readonly string[] Fens = Enumerable.Repeat(Fen, 5).ToArray();
 
-    private static readonly int[] Depths = {
+    private static readonly int[] Depths =
+    {
         1, 2, 3, 4, 5
     };
 
-    private static readonly ulong[] Results = {
+    private static readonly ulong[] Results =
+    {
         48UL, 2_039UL, 97_862UL, 4_085_603UL, 193_690_690UL
     };
 
-    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
     [Theory]
     [MemberData(nameof(PerftTheoryData))]
@@ -81,25 +74,21 @@ public void Perft(string fen, int depth, ulong expected)
 
 public sealed class PerftThreeTest : PerftVerify
 {
-    private static readonly string[] Fens = {
-        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
-        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
-        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
-        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
-        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
-        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
-        "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1"
-    };
+    private const string Fen = "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1";
+
+    private static readonly string[] Fens = Enumerable.Repeat(Fen, 7).ToArray();
 
-    private static readonly int[] Depths = {
+    private static readonly int[] Depths =
+    {
         1, 2, 3, 4, 5, 6, 7
     };
 
-    private static readonly ulong[] Results = {
+    private static readonly ulong[] Results =
+    {
         14UL, 191UL, 2_812UL, 43_238UL, 674_624UL, 11_030_083UL, 178_633_661UL
     };
 
-    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
     [Theory]
     [MemberData(nameof(PerftTheoryData))]
@@ -109,23 +98,21 @@ public void Perft(string fen, int depth, ulong expected)
 
 public sealed class PerftFourTest : PerftVerify
 {
-    private static readonly string[] Fens = {
-        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
-        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
-        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
-        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
-        "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
-    };
+    private const string Fen = "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1";
 
-    private static readonly int[] Depths = {
+    private static readonly string[] Fens = Enumerable.Repeat(Fen, 5).ToArray();
+
+    private static readonly int[] Depths =
+    {
         1, 2, 3, 4, 5
     };
 
-    private static readonly ulong[] Results = {
+    private static readonly ulong[] Results =
+    {
         6UL, 264UL, 9_467UL, 422_333UL, 15_833_292UL
     };
 
-    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
     [Theory]
     [MemberData(nameof(PerftTheoryData))]
@@ -135,24 +122,22 @@ public void Perft(string fen, int depth, ulong expected)
 
 public sealed class PerftFiveTest : PerftVerify
 {
-    private static readonly string[] Fens = {
-        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
-        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
-        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
-        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
-        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10"
-    };
+    private const string Fen = "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10";
+
+    private static readonly string[] Fens = Enumerable.Repeat(Fen, 5).ToArray();
 
-    private static readonly int[] Depths = {
+    private static readonly int[] Depths =
+    {
         1, 2, 3, 4, 5
     };
 
-    private static readonly ulong[] Results = {
+    private static readonly ulong[] Results =
+    {
         46UL, 2_079UL, 89_890UL, 3_894_594UL, 164_075_551UL
     };
 
-    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
-    
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
+
     [Theory]
     [MemberData(nameof(PerftTheoryData))]
     public void Perft(string fen, int depth, ulong expected)
@@ -161,36 +146,39 @@ public void Perft(string fen, int depth, ulong expected)
 
 public sealed class TalkChessPerftTests : PerftVerify
 {
-    private static readonly string[] Fens = {
-        "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1", //--Illegal ep move #1
-        "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1", //--Illegal ep move #2
-        "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1", //--EP Capture Checks Opponent
-        "5k2/8/8/8/8/8/8/4K2R w K - 0 1", //--Short Castling Gives Check
-        "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1", //--Long Castling Gives Check
+    private static readonly string[] Fens =
+    {
+        "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1",         //--Illegal ep move #1
+        "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1",        //--Illegal ep move #2
+        "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1",       //--EP Capture Checks Opponent
+        "5k2/8/8/8/8/8/8/4K2R w K - 0 1",            //--Short Castling Gives Check
+        "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1",            //--Long Castling Gives Check
         "r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1", //--Castle Rights
-        "r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1", //--Castling Prevented
-        "2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1", //--Promote out of Check
-        "8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1", //--Discovered Check
-        "4k3/1P6/8/8/8/8/K7/8 w - - 0 1", //--Promote to give check
-        "8/P1k5/K7/8/8/8/8/8 w - - 0 1", //--Under Promote to give check
-        "K1k5/8/P7/8/8/8/8/8 w - - 0 1", //--Self Stalemate
-        "8/k1P5/8/1K6/8/8/8/8 w - - 0 1", //--Stalemate & Checkmate
-        "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1", //--Stalemate & Checkmate
-    };
-
-    private static readonly int[] Depths = {
+        "r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1",  //--Castling Prevented
+        "2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1",         //--Promote out of Check
+        "8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1",       //--Discovered Check
+        "4k3/1P6/8/8/8/8/K7/8 w - - 0 1",            //--Promote to give check
+        "8/P1k5/K7/8/8/8/8/8 w - - 0 1",             //--Under Promote to give check
+        "K1k5/8/P7/8/8/8/8/8 w - - 0 1",             //--Self Stalemate
+        "8/k1P5/8/1K6/8/8/8/8 w - - 0 1",            //--Stalemate & Checkmate
+        "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1",         //--Stalemate & Checkmate
+    };
+
+    private static readonly int[] Depths =
+    {
         6, 6, 6, 6, 6,
         4, 4, 6, 5, 6,
         6, 6, 7, 4
     };
 
-    private static readonly ulong[] Results = {
-        1_134_888UL, 1_015_133UL, 1_440_467UL,   661_072UL, 803_711UL,
+    private static readonly ulong[] Results =
+    {
+        1_134_888UL, 1_015_133UL, 1_440_467UL, 661_072UL, 803_711UL,
         1_274_206UL, 1_720_476UL, 3_821_001UL, 1_004_658UL, 217_342UL,
-           92_683UL,     2_217UL,   567_584UL,    23_527UL
+        92_683UL, 2_217UL, 567_584UL, 23_527UL
     };
 
-    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results); 
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
     [Theory]
     [MemberData(nameof(PerftTheoryData))]

From 6d7a83b1829fcbc76f153b299b11c5d0582b7ad4 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 13 Nov 2023 22:51:50 +0100
Subject: [PATCH 065/119] update name of polyglot stream

---
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs                 | 2 +-
 ...naryStreamReader.cs => ReverseEndianBinaryStreamReader.cs} | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)
 rename src/Rudzoft.ChessLib/Polyglot/{LittleEndianBinaryStreamReader.cs => ReverseEndianBinaryStreamReader.cs} (93%)

diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index 260b94f9..1d56a522 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -85,7 +85,7 @@ public string BookFile
             _bookFilePath = value;
             _fileStream = new FileStream(value, FileMode.Open, FileAccess.Read);
             _binaryReader = BitConverter.IsLittleEndian
-                ? new LittleEndianBinaryStreamReader(_fileStream)
+                ? new ReverseEndianBinaryStreamReader(_fileStream)
                 : new BinaryReader(_fileStream);
         }
     }
diff --git a/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs b/src/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs
similarity index 93%
rename from src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs
rename to src/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs
index 0602a87b..01eeee37 100644
--- a/src/Rudzoft.ChessLib/Polyglot/LittleEndianBinaryStreamReader.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs
@@ -26,9 +26,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Polyglot;
 
-internal sealed class LittleEndianBinaryStreamReader : BinaryReader
+internal sealed class ReverseEndianBinaryStreamReader : BinaryReader
 {
-    public LittleEndianBinaryStreamReader(Stream stream) : base(stream)
+    public ReverseEndianBinaryStreamReader(Stream stream) : base(stream)
     {
     }
 

From d60dda33f344dd2604010fee192d3aa1417809ed Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Tue, 14 Nov 2023 21:56:13 +0100
Subject: [PATCH 066/119] update to .net 8

---
 Directory.Build.props                                |  4 ++--
 global.json                                          |  2 +-
 .../Rudzoft.ChessLib.Benchmark.csproj                |  2 +-
 .../Rudzoft.ChessLib.PGN.Test.csproj                 |  9 +++++----
 src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj |  2 +-
 .../Rudzoft.ChessLib.Test.csproj                     |  8 ++++----
 .../Rudzoft.ChessLib.WebApi.csproj                   | 10 +++++-----
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj         | 12 ++++++------
 src/Rudzoft.ChessLib/Types/PextBitBoards.cs          |  6 ++++++
 src/Rudzoft.Perft/Rudzoft.Perft.csproj               | 10 +++++-----
 10 files changed, 36 insertions(+), 29 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib/Types/PextBitBoards.cs

diff --git a/Directory.Build.props b/Directory.Build.props
index f9a7a7a6..e006c439 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,7 +1,7 @@
 <Project>
     <PropertyGroup>
-        <TargetFramework>net7.0</TargetFramework>
-        <LangVersion>latest</LangVersion>
+        <TargetFramework>net8.0</TargetFramework>
+        <LangVersion>12</LangVersion>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>warnings</Nullable>
         <IsPackable>false</IsPackable>
diff --git a/global.json b/global.json
index aaac9e04..2ddda36c 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
 {
   "sdk": {
-    "version": "7.0.0",
+    "version": "8.0.0",
     "rollForward": "latestMinor",
     "allowPrerelease": false
   }
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index d778b29e..9342770e 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -13,7 +13,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="BenchmarkDotNet" Version="0.13.9" />
+        <PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
         <PackageReference Include="System.Runtime" Version="4.3.1"/>
     </ItemGroup>
 
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index fbb2acd9..cc90642f 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -6,10 +6,11 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
-        <PackageReference Include="xunit" Version="2.5.1" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.1">
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
+        <PackageReference Include="xunit" Version="2.6.1" />
+        <PackageReference Include="xunit.analyzers" Version="1.5.0" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
diff --git a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
index 8c40511a..fcedd72c 100644
--- a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
+++ b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
@@ -5,7 +5,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
     </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index c2a24f65..e3ac4f20 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -23,11 +23,11 @@
     </ItemGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
-        <PackageReference Include="xunit" Version="2.5.1" />
-        <PackageReference Include="xunit.analyzers" Version="1.3.0" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.1">
+        <PackageReference Include="xunit" Version="2.6.1" />
+        <PackageReference Include="xunit.analyzers" Version="1.5.0" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
             <PrivateAssets>all</PrivateAssets>
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index 8fc28364..c91f7731 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -6,14 +6,14 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="7.0.4">
+        <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.0">
             <PrivateAssets>all</PrivateAssets>
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
-        <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.4"/>
-        <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1"/>
-        <PackageReference Include="Microsoft.OpenApi" Version="1.6.3"/>
+        <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Options" Version="8.0.0"/>
+        <PackageReference Include="Microsoft.OpenApi" Version="1.6.10"/>
         <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
         <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0"/>
         <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0"/>
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index ae749303..896a34da 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -51,12 +51,12 @@
         <PlatformTarget>AnyCPU</PlatformTarget>
     </PropertyGroup>
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.10" />
-        <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
+        <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
+        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
+        <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0"/>
     </ItemGroup>
     <ItemGroup>
         <Folder Include="Protocol\"/>
diff --git a/src/Rudzoft.ChessLib/Types/PextBitBoards.cs b/src/Rudzoft.ChessLib/Types/PextBitBoards.cs
new file mode 100644
index 00000000..0e74ed65
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Types/PextBitBoards.cs
@@ -0,0 +1,6 @@
+namespace Rudzoft.ChessLib.Types;
+
+public class PextBitBoards
+{
+    
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index ab1c38a3..9d4784d9 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -5,15 +5,15 @@
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Akka" Version="1.5.13" />
-      <PackageReference Include="Akka.DependencyInjection" Version="1.5.13" />
+      <PackageReference Include="Akka" Version="1.5.14" />
+      <PackageReference Include="Akka.DependencyInjection" Version="1.5.14" />
       <PackageReference Include="CommandLineParser" Version="2.9.1" />
-      <PackageReference Include="Serilog" Version="3.0.1" />
-      <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0"/>
+      <PackageReference Include="Serilog" Version="3.1.1" />
+      <PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
       <PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/>
       <PackageReference Include="Serilog.Settings.Configuration" Version="7.0.1" />
       <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/>
-      <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
+      <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
       <PackageReference Include="Timestamp" Version="1.0.2"/>
     </ItemGroup>
 

From d2b28db9104fe62d7d4523d25f91e112a028046c Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Tue, 14 Nov 2023 22:02:44 +0100
Subject: [PATCH 067/119] minor cleanup

---
 .../StringBenchmarks.cs                       | 205 ++++++------------
 .../PgnMoveNotationTests/SanToMoveTests.cs    |   2 +-
 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs |  12 +-
 src/Rudzoft.ChessLib/Position.cs              |   1 -
 src/Rudzoft.ChessLib/Types/Values.cs          |  10 +-
 5 files changed, 82 insertions(+), 148 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
index b211c7e3..b55b0d05 100644
--- a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
@@ -29,144 +29,73 @@ public static IEnumerable<object> Squares()
 
     private static string GetNameOf(Squares sq)
     {
-        switch (sq)
+        return sq switch
         {
-            case Types.Squares.a1:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.b1:
-                return nameof(Types.Squares.b1);
-            case Types.Squares.c1:
-                return nameof(Types.Squares.c1);
-            case Types.Squares.d1:
-                return nameof(Types.Squares.d1);
-            case Types.Squares.e1:
-                return nameof(Types.Squares.e1);
-            case Types.Squares.f1:
-                return nameof(Types.Squares.f1);
-            case Types.Squares.g1:
-                return nameof(Types.Squares.g1);
-            case Types.Squares.h1:
-                return nameof(Types.Squares.h1);
-            case Types.Squares.a2:
-                return nameof(Types.Squares.a2);
-            case Types.Squares.b2:
-                return nameof(Types.Squares.b2);
-            case Types.Squares.c2:
-                return nameof(Types.Squares.c2);
-            case Types.Squares.d2:
-                return nameof(Types.Squares.d2);
-            case Types.Squares.e2:
-                return nameof(Types.Squares.e2);
-            case Types.Squares.f2:
-                return nameof(Types.Squares.f2);
-            case Types.Squares.g2:
-                return nameof(Types.Squares.g2);
-            case Types.Squares.h2:
-                return nameof(Types.Squares.h2);
-            case Types.Squares.a3:
-                return nameof(Types.Squares.a3);
-            case Types.Squares.b3:
-                return nameof(Types.Squares.b3);
-            case Types.Squares.c3:
-                return nameof(Types.Squares.c3);
-            case Types.Squares.d3:
-                return nameof(Types.Squares.d3);
-            case Types.Squares.e3:
-                return nameof(Types.Squares.e3);
-            case Types.Squares.f3:
-                return nameof(Types.Squares.f3);
-            case Types.Squares.g3:
-                return nameof(Types.Squares.g3);
-            case Types.Squares.h3:
-                return nameof(Types.Squares.h3);
-            case Types.Squares.a4:
-                return nameof(Types.Squares.a4);
-            case Types.Squares.b4:
-                return nameof(Types.Squares.b4);
-            case Types.Squares.c4:
-                return nameof(Types.Squares.c4);
-            case Types.Squares.d4:
-                return nameof(Types.Squares.d4);
-            case Types.Squares.e4:
-                return nameof(Types.Squares.e4);
-            case Types.Squares.f4:
-                return nameof(Types.Squares.f4);
-            case Types.Squares.g4:
-                return nameof(Types.Squares.g4);
-            case Types.Squares.h4:
-                return nameof(Types.Squares.h4);
-            case Types.Squares.a5:
-                return nameof(Types.Squares.a5);
-            case Types.Squares.b5:
-                return nameof(Types.Squares.b5);
-            case Types.Squares.c5:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.d5:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.e5:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.f5:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.g5:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.h5:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.a6:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.b6:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.c6:
-                return nameof(Types.Squares.a1);
-
-            case Types.Squares.e6:
-                return nameof(Types.Squares.a1);
-
-            case Types.Squares.f6:
-                return nameof(Types.Squares.a1);
-
-            case Types.Squares.g6:
-                return nameof(Types.Squares.a1);
-
-            case Types.Squares.h6:
-                return nameof(Types.Squares.a1);
-
-            case Types.Squares.a7:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.b7:
-                return nameof(Types.Squares.a1);
-            case Types.Squares.c7:
-                return nameof(Types.Squares.c7);
-            case Types.Squares.d7:
-                return nameof(Types.Squares.d7);
-            case Types.Squares.e7:
-                return nameof(Types.Squares.e7);
-            case Types.Squares.f7:
-                return nameof(Types.Squares.f7);
-            case Types.Squares.g7:
-                return nameof(Types.Squares.g7);
-            case Types.Squares.h7:
-                return nameof(Types.Squares.h7);
-            case Types.Squares.a8:
-                return nameof(Types.Squares.a8);
-            case Types.Squares.b8:
-                return nameof(Types.Squares.b8);
-            case Types.Squares.c8:
-                return nameof(Types.Squares.c8);
-            case Types.Squares.d8:
-                return nameof(Types.Squares.d8);
-            case Types.Squares.e8:
-                return nameof(Types.Squares.e8);
-            case Types.Squares.f8:
-                return nameof(Types.Squares.f8);
-            case Types.Squares.g8:
-                return nameof(Types.Squares.g8);
-            case Types.Squares.h8:
-                return nameof(Types.Squares.h8);
-            case Types.Squares.none:
-                return nameof(Types.Squares.a1);
-
-            default:
-                throw new ArgumentOutOfRangeException();
-        }
+            Types.Squares.a1   => nameof(Types.Squares.a1),
+            Types.Squares.b1   => nameof(Types.Squares.b1),
+            Types.Squares.c1   => nameof(Types.Squares.c1),
+            Types.Squares.d1   => nameof(Types.Squares.d1),
+            Types.Squares.e1   => nameof(Types.Squares.e1),
+            Types.Squares.f1   => nameof(Types.Squares.f1),
+            Types.Squares.g1   => nameof(Types.Squares.g1),
+            Types.Squares.h1   => nameof(Types.Squares.h1),
+            Types.Squares.a2   => nameof(Types.Squares.a2),
+            Types.Squares.b2   => nameof(Types.Squares.b2),
+            Types.Squares.c2   => nameof(Types.Squares.c2),
+            Types.Squares.d2   => nameof(Types.Squares.d2),
+            Types.Squares.e2   => nameof(Types.Squares.e2),
+            Types.Squares.f2   => nameof(Types.Squares.f2),
+            Types.Squares.g2   => nameof(Types.Squares.g2),
+            Types.Squares.h2   => nameof(Types.Squares.h2),
+            Types.Squares.a3   => nameof(Types.Squares.a3),
+            Types.Squares.b3   => nameof(Types.Squares.b3),
+            Types.Squares.c3   => nameof(Types.Squares.c3),
+            Types.Squares.d3   => nameof(Types.Squares.d3),
+            Types.Squares.e3   => nameof(Types.Squares.e3),
+            Types.Squares.f3   => nameof(Types.Squares.f3),
+            Types.Squares.g3   => nameof(Types.Squares.g3),
+            Types.Squares.h3   => nameof(Types.Squares.h3),
+            Types.Squares.a4   => nameof(Types.Squares.a4),
+            Types.Squares.b4   => nameof(Types.Squares.b4),
+            Types.Squares.c4   => nameof(Types.Squares.c4),
+            Types.Squares.d4   => nameof(Types.Squares.d4),
+            Types.Squares.e4   => nameof(Types.Squares.e4),
+            Types.Squares.f4   => nameof(Types.Squares.f4),
+            Types.Squares.g4   => nameof(Types.Squares.g4),
+            Types.Squares.h4   => nameof(Types.Squares.h4),
+            Types.Squares.a5   => nameof(Types.Squares.a5),
+            Types.Squares.b5   => nameof(Types.Squares.b5),
+            Types.Squares.c5   => nameof(Types.Squares.a1),
+            Types.Squares.d5   => nameof(Types.Squares.a1),
+            Types.Squares.e5   => nameof(Types.Squares.a1),
+            Types.Squares.f5   => nameof(Types.Squares.a1),
+            Types.Squares.g5   => nameof(Types.Squares.a1),
+            Types.Squares.h5   => nameof(Types.Squares.a1),
+            Types.Squares.a6   => nameof(Types.Squares.a1),
+            Types.Squares.b6   => nameof(Types.Squares.a1),
+            Types.Squares.c6   => nameof(Types.Squares.a1),
+            Types.Squares.e6   => nameof(Types.Squares.a1),
+            Types.Squares.f6   => nameof(Types.Squares.a1),
+            Types.Squares.g6   => nameof(Types.Squares.a1),
+            Types.Squares.h6   => nameof(Types.Squares.a1),
+            Types.Squares.a7   => nameof(Types.Squares.a1),
+            Types.Squares.b7   => nameof(Types.Squares.a1),
+            Types.Squares.c7   => nameof(Types.Squares.c7),
+            Types.Squares.d7   => nameof(Types.Squares.d7),
+            Types.Squares.e7   => nameof(Types.Squares.e7),
+            Types.Squares.f7   => nameof(Types.Squares.f7),
+            Types.Squares.g7   => nameof(Types.Squares.g7),
+            Types.Squares.h7   => nameof(Types.Squares.h7),
+            Types.Squares.a8   => nameof(Types.Squares.a8),
+            Types.Squares.b8   => nameof(Types.Squares.b8),
+            Types.Squares.c8   => nameof(Types.Squares.c8),
+            Types.Squares.d8   => nameof(Types.Squares.d8),
+            Types.Squares.e8   => nameof(Types.Squares.e8),
+            Types.Squares.f8   => nameof(Types.Squares.f8),
+            Types.Squares.g8   => nameof(Types.Squares.g8),
+            Types.Squares.h8   => nameof(Types.Squares.h8),
+            Types.Squares.none => nameof(Types.Squares.a1),
+            _                  => throw new ArgumentOutOfRangeException()
+        };
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
index f827a445..f2520974 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
@@ -39,7 +39,7 @@ namespace Rudzoft.ChessLib.PGN.Test.PgnMoveNotationTests;
 
 public sealed class SanToMoveTests
 {
-    private const string SampleFilePath = @"samples/sample.pgn";
+    private const string SampleFilePath = "samples/sample.pgn";
     private const int ExpectedGameCount = 2;
 
     private readonly IServiceProvider _serviceProvider;
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
index e4b91d20..8b0d1f35 100644
--- a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
@@ -24,11 +24,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Runtime.CompilerServices;
+
 namespace Rudzoft.ChessLib.PGN;
 
 public sealed class NonRegexPgnParser : IPgnParser
 {
-    public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationToken cancellationToken = default)
+    private static readonly char[] Separators = { ' ', '\t' };
+
+    public async IAsyncEnumerable<PgnGame> ParseFile(
+        string pgnFile,
+        [EnumeratorCancellation] CancellationToken cancellationToken = default)
     {
         await using var fileStream = new FileStream(pgnFile, FileMode.Open, FileAccess.Read);
         using var streamReader = new StreamReader(fileStream);
@@ -47,7 +53,7 @@ public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationTok
 
             if (inMoveSection)
             {
-                var words = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
+                var words = line.Split(Separators, StringSplitOptions.RemoveEmptyEntries);
 
                 var currentMoveNumber = 0;
 
@@ -80,7 +86,7 @@ public async IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationTok
             }
             else
             {
-                if (!line.StartsWith("[") || !line.EndsWith("]") || !line.Contains('"'))
+                if (!line.StartsWith('[') || !line.EndsWith(']') || !line.Contains('"'))
                     continue;
                 
                 var firstSpaceIndex = line.IndexOf(' ');
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index ccd56696..5112b924 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -38,7 +38,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
-using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib;
 
diff --git a/src/Rudzoft.ChessLib/Types/Values.cs b/src/Rudzoft.ChessLib/Types/Values.cs
index 56043871..73a89203 100644
--- a/src/Rudzoft.ChessLib/Types/Values.cs
+++ b/src/Rudzoft.ChessLib/Types/Values.cs
@@ -40,8 +40,8 @@ public enum DefaultPieceValues
     ValueMinusInfinite = -32001,
     ValueNone = 32002,
 
-    ValueMateInMaxPly = ValueMate - 2 * Values.MAX_PLY,
-    ValueMatedInMaxPly = -ValueMate + 2 * Values.MAX_PLY,
+    ValueMateInMaxPly = ValueMate - 2 * Values.MaxPly,
+    ValueMatedInMaxPly = -ValueMate + 2 * Values.MaxPly,
 
     PawnValueMg = 128,
     PawnValueEg = 213,
@@ -66,7 +66,7 @@ public static class PieceValuesExtensions
 
 public sealed class Values : IValues
 {
-    public const int MAX_PLY = 246;
+    public const int MaxPly = 246;
 
     private readonly DefaultPieceValues[][] _defaults;
 
@@ -127,8 +127,8 @@ public Value ValueMate
                 return;
 
             _valueMate = value;
-            _valueMateInMaxPly = value - 2 * MAX_PLY;
-            _valueMatedInMaxPly = value + 2 * MAX_PLY;
+            _valueMateInMaxPly = value - 2 * MaxPly;
+            _valueMatedInMaxPly = value + 2 * MaxPly;
         }
     }
 

From dfbb979066dd3058b4db51a7be0d1648e857220a Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Tue, 14 Nov 2023 23:25:07 +0100
Subject: [PATCH 068/119] minor simplifications

---
 .../ShiftFuncBench.cs                         | 49 +++++++++----------
 .../PgnMoveNotationTests/SanToMoveTests.cs    |  2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |  2 +-
 src/Rudzoft.Perft/Actors/ProgressActor.cs     | 11 ++---
 4 files changed, 30 insertions(+), 34 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
index f0681987..5dc00972 100644
--- a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
@@ -1,5 +1,4 @@
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
+using System.Collections.Frozen;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Benchmark;
@@ -8,9 +7,10 @@ namespace Rudzoft.ChessLib.Benchmark;
 // ReSharper disable once ClassCanBeSealed.Global
 public class ShiftFuncBench
 {
-    private static readonly Dictionary<Direction, Func<BitBoard, BitBoard>> ShiftFuncs = MakeShiftFuncs();
+    private static readonly FrozenDictionary<Direction, Func<BitBoard, BitBoard>> ShiftFuncs = MakeShiftFuncs();
 
-    private static readonly Direction[] AllDirections = {
+    private static readonly Direction[] AllDirections =
+    {
         Direction.North, Direction.South, Direction.East, Direction.West,
         Direction.NorthEast, Direction.NorthWest, Direction.SouthEast, Direction.SouthWest,
         Direction.NorthDouble, Direction.SouthDouble, Direction.SouthFill, Direction.NorthFill,
@@ -57,47 +57,44 @@ private static BitBoard ShiftF(in BitBoard bb, Direction direction)
 
     private static BitBoard ShiftF2(in BitBoard bb, Direction direction)
     {
-        ref var func = ref CollectionsMarshal.GetValueRefOrNullRef(ShiftFuncs, direction);
+        if (ShiftFuncs.TryGetValue(direction, out var func))
+            return func(bb);
         
-        if (Unsafe.IsNullRef(ref func))
-            throw new ArgumentException("Invalid shift argument.", nameof(direction));
-            
-        return func(bb);
+        throw new ArgumentException("Invalid shift argument.", nameof(direction));
     }
 
     private static BitBoard Shift(in BitBoard bb, Direction direction)
     {
         if (direction == Direction.North)
             return bb.NorthOne();
-        else if (direction == Direction.South)
+        if (direction == Direction.South)
             return bb.SouthOne();
-        else if (direction == Direction.SouthEast)
+        if (direction == Direction.SouthEast)
             return bb.SouthEastOne();
-        else if (direction == Direction.SouthWest)
+        if (direction == Direction.SouthWest)
             return bb.SouthWestOne();
-        else if (direction == Direction.NorthEast)
+        if (direction == Direction.NorthEast)
             return bb.NorthEastOne();
-        else if (direction == Direction.NorthWest)
+        if (direction == Direction.NorthWest)
             return bb.NorthWestOne();
-        else if (direction == Direction.NorthDouble)
+        if (direction == Direction.NorthDouble)
             return bb.NorthOne().NorthOne();
-        else if (direction == Direction.SouthDouble)
+        if (direction == Direction.SouthDouble)
             return bb.SouthOne().SouthOne();
-        else if (direction == Direction.East)
+        if (direction == Direction.East)
             return bb.EastOne();
-        else if (direction == Direction.West)
+        if (direction == Direction.West)
             return bb.WestOne();
-        else if (direction == Direction.NorthFill)
+        if (direction == Direction.NorthFill)
             return bb.NorthFill();
-        else if (direction == Direction.SouthFill)
+        if (direction == Direction.SouthFill)
             return bb.SouthFill();
-        else if (direction == Direction.None)
+        if (direction == Direction.None)
             return bb;
-        else
-            throw new ArgumentException("Invalid shift argument.", nameof(direction));
+        throw new ArgumentException("Invalid shift argument.", nameof(direction));
     }
 
-    private static Dictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
+    private static FrozenDictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
     {
         return new Dictionary<Direction, Func<BitBoard, BitBoard>>(13)
         {
@@ -114,6 +111,6 @@ private static Dictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
             { Direction.SouthFill, static board => board.SouthFill() },
             { Direction.East, static board => board.EastOne() },
             { Direction.West, static board => board.WestOne() }
-        };
+        }.ToFrozenDictionary();
     }
-}
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
index f2520974..70af2328 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
@@ -82,7 +82,7 @@ public async Task BasicSanConvert()
 
         var sanMoves = new List<string>();
 
-        foreach (var pgnMove in games.First().Moves)
+        foreach (var pgnMove in games[0].Moves)
         {
             sanMoves.Add(pgnMove.WhiteMove);
             if (!string.IsNullOrWhiteSpace(pgnMove.BlackMove))
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index 7f978808..3cd47638 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -81,7 +81,7 @@ public void Initialize(int maxThreads = 128)
     }
 
     public void AddOption(string name, IOption option) => _options[name] = option;
-    
+
     public bool TryGetOption(string name, out IOption option)
     {
         ref var opt = ref CollectionsMarshal.GetValueRefOrNullRef(_options, name);
diff --git a/src/Rudzoft.Perft/Actors/ProgressActor.cs b/src/Rudzoft.Perft/Actors/ProgressActor.cs
index 373eadfd..76237a2d 100644
--- a/src/Rudzoft.Perft/Actors/ProgressActor.cs
+++ b/src/Rudzoft.Perft/Actors/ProgressActor.cs
@@ -28,14 +28,13 @@ public ProgressActor()
 
             if (!exists)
             {
-                existing = new();
-                existing.CurrentDepth = progress.Depth;
+                existing = new()
+                {
+                    CurrentDepth = progress.Depth
+                };
             }
             else
-            {
-                existing = new ProgressInternal();
-            }
-            
+                existing = new();
         });
     }
 }
\ No newline at end of file

From aee0b20301778221173f6e24e603f1cf8c60e8fb Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Tue, 14 Nov 2023 23:26:37 +0100
Subject: [PATCH 069/119] update todo file

---
 src/Rudzoft.ChessLib/{ToDo.txt => ToDo.md} | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)
 rename src/Rudzoft.ChessLib/{ToDo.txt => ToDo.md} (95%)

diff --git a/src/Rudzoft.ChessLib/ToDo.txt b/src/Rudzoft.ChessLib/ToDo.md
similarity index 95%
rename from src/Rudzoft.ChessLib/ToDo.txt
rename to src/Rudzoft.ChessLib/ToDo.md
index 6f375047..2f8b2d91 100644
--- a/src/Rudzoft.ChessLib/ToDo.txt
+++ b/src/Rudzoft.ChessLib/ToDo.md
@@ -22,13 +22,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 
-
-Add:
-- CastlelingRights type
-
-Improve:
+# Improve
 - Move notation functionality (add more notaion variation, including humanoid readable)
 
-
-Unit Tests:
+# Unit Tests
 - Cuckoo

From 2910a6529dc2cfc19d81ab3d56c41f0480bc75a8 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Wed, 15 Nov 2023 00:44:13 +0100
Subject: [PATCH 070/119] minor cleanups after .net 8 upgrade

---
 .../Rudzoft.ChessLib.Perft.csproj             |  1 +
 src/Rudzoft.ChessLib/Board.cs                 | 10 ++--
 .../Extensions/MathExtensions.cs              |  5 --
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  1 -
 src/Rudzoft.ChessLib/State.cs                 | 51 ++++---------------
 src/Rudzoft.ChessLib/Types/Piece.cs           |  5 +-
 src/Rudzoft.ChessLib/Types/Player.cs          |  2 +-
 7 files changed, 22 insertions(+), 53 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
index f049d234..b7f3560c 100644
--- a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
+++ b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
@@ -2,6 +2,7 @@
 
     <PropertyGroup>
         <Nullable>enable</Nullable>
+        <PublishAot>true</PublishAot>
     </PropertyGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index baabbb0a..b6a3e46d 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -54,9 +54,13 @@ public Board()
         _pieceList = new Square[Types.Square.Count][];
         for (var i = 0; i < _pieceList.Length; i++)
         {
-            var arr = new Square[Piece.Count];
-            arr.Fill(Types.Square.None);
-            _pieceList[i] = arr;
+            _pieceList[i] = new[]
+            {
+                Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None,
+                Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None,
+                Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None,
+                Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None
+            };
         }
 
         _index = new int[Types.Square.Count];
diff --git a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
index e82da5f9..932c7761 100644
--- a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
@@ -45,11 +45,6 @@ public static class MathExtensions
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool IsBetween(this uint v, int min, int max) => v - (uint)min <= (uint)max - (uint)min;
 
-#if !NET7_0_OR_GREATER
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool IsAsciiDigit(this char c) => IsBetween(c, '0', '9');
-#endif
-
     /// <summary>
     /// Converts a bool to a byte (0 or 1)
     ///
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 896a34da..1f9ed389 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <LangVersion>default</LangVersion>
         <Platforms>AnyCPU</Platforms>
         <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index c2b61090..3de21f7f 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -45,10 +45,10 @@ public sealed class State : IEquatable<State>
 
     public int NullPly { get; set; }
 
-    public CastleRight CastlelingRights { get; set; }
+    public CastleRight CastlelingRights { get; set; } = CastleRight.None;
 
     [Description("En-passant -> 'in-passing' square")]
-    public Square EnPassantSquare { get; set; }
+    public Square EnPassantSquare { get; set; } = Square.None;
 
     // -----------------------------
     // Properties below this point are not copied from other state
@@ -60,58 +60,25 @@ public sealed class State : IEquatable<State>
     /// <summary>
     /// Represents checked squares for side to move
     /// </summary>
-    public BitBoard Checkers { get; set; }
+    public BitBoard Checkers { get; set; } = BitBoard.Empty;
 
-    public BitBoard[] BlockersForKing { get; }
+    public BitBoard[] BlockersForKing { get; } = { BitBoard.Empty, BitBoard.Empty };
 
-    public BitBoard[] Pinners { get; }
+    public BitBoard[] Pinners { get; } = { BitBoard.Empty, BitBoard.Empty };
 
-    public BitBoard[] CheckedSquares { get; private set; }
+    public BitBoard[] CheckedSquares { get; private set; } = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
 
-    public PieceTypes CapturedPiece { get; set; }
+    public PieceTypes CapturedPiece { get; set; } = PieceTypes.NoPieceType;
 
-    public Move LastMove { get; set; }
+    public Move LastMove { get; set; } = Move.EmptyMove;
 
     public int Repetition { get; set; }
 
     public State Previous { get; private set; }
 
-    /// <summary>
-    /// Partial copy from existing state The properties not copied are re-calculated
-    /// </summary>
-    /// <param name="other">The current state</param>
-    public State(State other)
-    {
-        PawnKey = other.PawnKey;
-        MaterialKey = other.MaterialKey;
-        CastlelingRights = other.CastlelingRights;
-        ClockPly = other.ClockPly;
-        NullPly = other.NullPly;
-        EnPassantSquare = other.EnPassantSquare;
-        Previous = other;
-
-        Checkers = BitBoard.Empty;
-        BlockersForKing = new[] { BitBoard.Empty, BitBoard.Empty };
-        Pinners = new[] { BitBoard.Empty, BitBoard.Empty };
-        CheckedSquares = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
-        CapturedPiece = PieceTypes.NoPieceType;
-    }
-
-    public State()
-    {
-        LastMove = Move.EmptyMove;
-        CastlelingRights = CastleRight.None;
-        EnPassantSquare = Square.None;
-        Checkers = BitBoard.Empty;
-        CheckedSquares = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
-        Pinners = new[] { BitBoard.Empty, BitBoard.Empty };
-        BlockersForKing = new[] { BitBoard.Empty, BitBoard.Empty };
-        CapturedPiece = PieceTypes.NoPieceType;
-    }
-
     public State CopyTo(State other)
     {
-        other ??= new State();
+        other ??= new();
 
         // copy over preserved values
         other.MaterialKey = MaterialKey;
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index feb38a0d..9cd67852 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -24,6 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+#if NET7_0_OR_GREATER
+using System.Numerics;
+#endif
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 
@@ -80,7 +83,7 @@ public static bool InBetween(this PieceTypes v, PieceTypes min, PieceTypes max)
 /// Piece. Contains the piece type which indicate what type and color the piece is
 /// </summary>
 public readonly record struct Piece(Pieces Value)
-#if net7_0
+#if NET7_0_OR_GREATER
     : IMinMaxValue<Piece>
 #endif
 {
diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Player.cs
index d6dc4b6c..5eb7b59c 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Player.cs
@@ -44,7 +44,7 @@ public enum PlayerTypes
 }
 
 public readonly record struct Player(byte Side) : ISpanFormattable
-#if NET7_0
+#if NET7_0_OR_GREATER
     , IMinMaxValue<Player>
 #endif
 {

From 2af2513c3169d2950d6200366faee8ad68e2bfc8 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 8 Jan 2024 23:22:04 +0100
Subject: [PATCH 071/119] c# 12 update to collections + minor update to
 bitboard ToString

---
 .editorconfig                                 |   8 +-
 src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs  |  12 +-
 .../CharConvertBenchmark.cs                   |   2 +-
 .../KeyBenchmarks.cs                          |   2 +-
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |   5 +-
 .../ShiftFuncBench.cs                         |  23 ++-
 .../StringBenchmarks.cs                       |  33 ++--
 .../PgnTests/PgnTests.cs                      |   7 +-
 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs |  14 +-
 ...> PgnParserServiceCollectionExtensions.cs} |   2 +-
 .../Rudzoft.ChessLib.Perft.csproj             |   2 +
 .../BitboardTests/BitboardDataTests.cs        |   3 +
 .../BoardTests/BoardTests.cs                  |  26 +--
 .../PerftTests/PerftTest.cs                   |  54 +++----
 .../PiecesTests/PieceAttacks.cs               |   4 +-
 .../PiecesTests/RegularMobilityFixture.cs     |   8 +-
 .../PiecesTests/SliderMobilityFixture.cs      |   4 +-
 .../SizesTests/BaseTypesSizeTests.cs          |   4 +-
 src/Rudzoft.ChessLib.WebApi/Program.cs        |   2 +-
 .../Services/MoveGeneratorService.cs          |   2 +-
 src/Rudzoft.ChessLib/Board.cs                 |   6 +-
 src/Rudzoft.ChessLib/Cuckoo.cs                |  20 +--
 .../ChessLibServiceCollectionExtensions.cs    |   3 +-
 .../Extensions/PieceExtensions.cs             |   9 +-
 .../Extensions/StringExtensions.cs            |   4 +-
 src/Rudzoft.ChessLib/Fen/Fen.cs               |  41 ++---
 src/Rudzoft.ChessLib/Fen/FenData.cs           |  11 +-
 src/Rudzoft.ChessLib/Game.cs                  |   2 +-
 .../Transposition/TranspositionTableEntry.cs  |   2 +-
 .../MoveGeneration/MoveGenerator.cs           |   4 +-
 src/Rudzoft.ChessLib/Notation/MoveNotation.cs |   6 +-
 .../Notation/Notations/CoordinateNotation.cs  |   2 +-
 .../Notation/Notations/FanNotation.cs         |   2 +-
 .../Notation/Notations/LanNotation.cs         |   2 +-
 .../Notation/Notations/RanNotation.cs         |   2 +-
 .../Notation/Notations/SanNotation.cs         |   2 +-
 .../Notation/Notations/SmithNotation.cs       |   2 +-
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |  22 +--
 .../Polyglot/PolyglotBookZobrist.cs           |  10 +-
 src/Rudzoft.ChessLib/Position.cs              |  72 +++++----
 .../Protocol/UCI/HiResTimer.cs                |  16 +-
 .../Protocol/UCI/InputOutput.cs               |   6 +-
 .../Protocol/UCI/MovesToGoModel.cs            |   2 +-
 src/Rudzoft.ChessLib/Protocol/UCI/Option.cs   |   4 +-
 .../Protocol/UCI/SearchParameters.cs          |  22 +--
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |   9 +-
 src/Rudzoft.ChessLib/State.cs                 |   4 +-
 .../Tables/KillerMoves/KillerMoves.cs         |   6 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 148 +++++++++---------
 src/Rudzoft.ChessLib/Types/CastleRight.cs     |  10 +-
 src/Rudzoft.ChessLib/Types/File.cs            |  31 ++--
 src/Rudzoft.ChessLib/Types/Piece.cs           |  15 +-
 src/Rudzoft.ChessLib/Types/Player.cs          |  39 +++--
 src/Rudzoft.ChessLib/Types/Rank.cs            |   6 +-
 src/Rudzoft.ChessLib/Types/Score.cs           |  10 +-
 src/Rudzoft.ChessLib/Types/Square.cs          |   8 +-
 src/Rudzoft.ChessLib/Types/StateStack.cs      |   2 +-
 src/Rudzoft.ChessLib/Types/Value.cs           |   2 +-
 src/Rudzoft.ChessLib/Types/Values.cs          |   6 +-
 .../Validation/PositionValidator.cs           |   2 +-
 src/Rudzoft.Perft/Parsers/EpdParser.cs        |   2 +-
 src/Rudzoft.Perft/Services/PerftRunner.cs     |  75 +++++----
 62 files changed, 447 insertions(+), 419 deletions(-)
 rename src/Rudzoft.ChessLib.PGN/{PgnParserFactory.cs => PgnParserServiceCollectionExtensions.cs} (96%)

diff --git a/.editorconfig b/.editorconfig
index 8fa36ec8..a25ed3a6 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -5,7 +5,7 @@ indent_style = space
 insert_final_newline = false
 max_line_length = 120
 tab_width = 4
-trim_trailing_whitespace = false
+trim_trailing_whitespace = true
 ij_continuation_indent_size = 8
 ij_formatter_off_tag = @formatter:off
 ij_formatter_on_tag = @formatter:on
@@ -13,7 +13,7 @@ ij_formatter_tags_enabled = true
 ij_smart_tabs = false
 ij_visual_guides = 
 ij_wrap_on_typing = false
-charset = utf-8-bom
+charset = utf-8
 
 # Microsoft .NET properties
 csharp_new_line_before_members_in_object_initializers = false
@@ -45,7 +45,7 @@ dotnet_style_qualification_for_property = false:suggestion
 dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
 
 # ReSharper properties
-resharper_accessor_owner_body = accessors_with_expression_body
+resharper_accessor_owner_body = expression_body
 resharper_autodetect_indent_settings = true
 resharper_braces_for_dowhile = required_for_multiline
 resharper_braces_for_fixed = required_for_multiline
@@ -129,7 +129,7 @@ ij_xml_space_inside_empty_tag = false
 ij_xml_text_wrap = normal
 ij_xml_use_custom_settings = false
 
-[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}]
+[*.json]
 indent_size = 2
 ij_json_array_wrapping = split_into_lines
 ij_json_keep_blank_lines_in_code = 0
diff --git a/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs b/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
index bb30d8e5..a69d50de 100644
--- a/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
@@ -7,8 +7,8 @@ namespace Rudzoft.ChessLib.Benchmark;
 public class BitOpBench
 {
     private static readonly int[] Lsb64Table =
-    {
-            63, 30,  3, 32, 59, 14, 11, 33,
+    [
+        63, 30,  3, 32, 59, 14, 11, 33,
             60, 24, 50,  9, 55, 19, 21, 34,
             61, 29,  2, 53, 51, 23, 41, 18,
             56, 28,  1, 43, 46, 27,  0, 35,
@@ -16,11 +16,11 @@ public class BitOpBench
             15, 52, 12, 40,  7, 42, 45, 16,
             25, 57, 48, 13, 10, 39,  8, 44,
             20, 47, 38, 22, 17, 37, 36, 26
-        };
+    ];
 
     private static readonly int[] Msb64Table =
-    {
-            0, 47,  1, 56, 48, 27,  2, 60,
+    [
+        0, 47,  1, 56, 48, 27,  2, 60,
             57, 49, 41, 37, 28, 16,  3, 61,
             54, 58, 35, 52, 50, 42, 21, 44,
             38, 32, 29, 23, 17, 11,  4, 62,
@@ -28,7 +28,7 @@ public class BitOpBench
             34, 51, 20, 43, 31, 22, 10, 45,
             25, 39, 14, 33, 19, 30,  9, 24,
             13, 18,  8, 12,  7,  6,  5, 63
-        };
+    ];
 
     [Benchmark]
     [ArgumentsSource(nameof(BitboardParams))]
diff --git a/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
index d4166363..b25b0b08 100644
--- a/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
@@ -5,7 +5,7 @@ namespace Rudzoft.ChessLib.Benchmark;
 [MemoryDiagnoser]
 public class CharConvertBenchmark
 {
-    private static readonly char[] FileChars = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+    private static readonly char[] FileChars = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
 
     private static readonly string[] FileStrings = FileChars.Select(static x => x.ToString()).ToArray();
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs b/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
index 67405012..f6887c54 100644
--- a/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
@@ -66,7 +66,7 @@ private static Affe2 A(string s1, string s2, in DateOnly dateOnly)
         finalBytes[14] = s2Bytes[6];
         finalBytes[15] = s2Bytes[7];
 
-        return new(new Guid(finalBytes), dateOnly);
+        return new(new(finalBytes), dateOnly);
         
         //var h = MemoryMarshal.TryRead(finalBytes, out ulong v);
     }
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index 5e686ac5..61fd296f 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -53,15 +53,14 @@ public void Setup()
         var pp = PerftPositionFactory.Create(
             Guid.NewGuid().ToString(),
             Fen.Fen.StartPositionFen,
-            new(6)
-            {
+            [
                 new(1, 20),
                 new(2, 400),
                 new(3, 8902),
                 new(4, 197281),
                 new(5, 4865609),
                 new(6, 119060324)
-            });
+            ]);
 
         var ttConfig = new TranspositionTableConfiguration { DefaultSize = 1 };
         var options = Options.Create(ttConfig);
diff --git a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
index 5dc00972..66edc78f 100644
--- a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
@@ -10,12 +10,12 @@ public class ShiftFuncBench
     private static readonly FrozenDictionary<Direction, Func<BitBoard, BitBoard>> ShiftFuncs = MakeShiftFuncs();
 
     private static readonly Direction[] AllDirections =
-    {
+    [
         Direction.North, Direction.South, Direction.East, Direction.West,
         Direction.NorthEast, Direction.NorthWest, Direction.SouthEast, Direction.SouthWest,
         Direction.NorthDouble, Direction.SouthDouble, Direction.SouthFill, Direction.NorthFill,
         Direction.None
-    };
+    ];
 
     [Benchmark]
     public BitBoard ShiftFuncLookup()
@@ -28,7 +28,7 @@ public BitBoard ShiftFuncLookup()
     }
 
     [Benchmark]
-    public BitBoard ShiftFunc2Lookup()
+    public BitBoard ShiftFunc2RefLookup()
     {
         var bb = BitBoards.EmptyBitBoard;
         foreach (var direction in AllDirections)
@@ -47,6 +47,18 @@ public BitBoard BasicLookup()
         return bb;
     }
 
+    [Benchmark]
+    public BitBoard NorthFillRegular()
+    {
+        return BitBoards.PawnSquares.NorthFill();
+    }
+
+    [Benchmark]
+    public BitBoard SouthFillRegular()
+    {
+        return BitBoards.PawnSquares.SouthFill();
+    }
+
     private static BitBoard ShiftF(in BitBoard bb, Direction direction)
     {
         if (ShiftFuncs.TryGetValue(direction, out var func))
@@ -57,10 +69,7 @@ private static BitBoard ShiftF(in BitBoard bb, Direction direction)
 
     private static BitBoard ShiftF2(in BitBoard bb, Direction direction)
     {
-        if (ShiftFuncs.TryGetValue(direction, out var func))
-            return func(bb);
-        
-        throw new ArgumentException("Invalid shift argument.", nameof(direction));
+        return bb.Shift(direction);
     }
 
     private static BitBoard Shift(in BitBoard bb, Direction direction)
diff --git a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
index b55b0d05..f71e432d 100644
--- a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
@@ -65,21 +65,22 @@ private static string GetNameOf(Squares sq)
             Types.Squares.h4   => nameof(Types.Squares.h4),
             Types.Squares.a5   => nameof(Types.Squares.a5),
             Types.Squares.b5   => nameof(Types.Squares.b5),
-            Types.Squares.c5   => nameof(Types.Squares.a1),
-            Types.Squares.d5   => nameof(Types.Squares.a1),
-            Types.Squares.e5   => nameof(Types.Squares.a1),
-            Types.Squares.f5   => nameof(Types.Squares.a1),
-            Types.Squares.g5   => nameof(Types.Squares.a1),
-            Types.Squares.h5   => nameof(Types.Squares.a1),
-            Types.Squares.a6   => nameof(Types.Squares.a1),
-            Types.Squares.b6   => nameof(Types.Squares.a1),
-            Types.Squares.c6   => nameof(Types.Squares.a1),
-            Types.Squares.e6   => nameof(Types.Squares.a1),
-            Types.Squares.f6   => nameof(Types.Squares.a1),
-            Types.Squares.g6   => nameof(Types.Squares.a1),
-            Types.Squares.h6   => nameof(Types.Squares.a1),
-            Types.Squares.a7   => nameof(Types.Squares.a1),
-            Types.Squares.b7   => nameof(Types.Squares.a1),
+            Types.Squares.c5   => nameof(Types.Squares.c5),
+            Types.Squares.d5   => nameof(Types.Squares.d5),
+            Types.Squares.e5   => nameof(Types.Squares.e5),
+            Types.Squares.f5   => nameof(Types.Squares.f5),
+            Types.Squares.g5   => nameof(Types.Squares.g5),
+            Types.Squares.h5   => nameof(Types.Squares.h5),
+            Types.Squares.a6   => nameof(Types.Squares.a6),
+            Types.Squares.b6   => nameof(Types.Squares.b6),
+            Types.Squares.c6   => nameof(Types.Squares.c6),
+            Types.Squares.d6   => nameof(Types.Squares.d6),
+            Types.Squares.e6   => nameof(Types.Squares.e6),
+            Types.Squares.f6   => nameof(Types.Squares.f6),
+            Types.Squares.g6   => nameof(Types.Squares.g6),
+            Types.Squares.h6   => nameof(Types.Squares.h6),
+            Types.Squares.a7   => nameof(Types.Squares.a7),
+            Types.Squares.b7   => nameof(Types.Squares.b7),
             Types.Squares.c7   => nameof(Types.Squares.c7),
             Types.Squares.d7   => nameof(Types.Squares.d7),
             Types.Squares.e7   => nameof(Types.Squares.e7),
@@ -94,7 +95,7 @@ private static string GetNameOf(Squares sq)
             Types.Squares.f8   => nameof(Types.Squares.f8),
             Types.Squares.g8   => nameof(Types.Squares.g8),
             Types.Squares.h8   => nameof(Types.Squares.h8),
-            Types.Squares.none => nameof(Types.Squares.a1),
+            Types.Squares.none => nameof(Types.Squares.none),
             _                  => throw new ArgumentOutOfRangeException()
         };
     }
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
index 3ad6a3a2..0bd4ac14 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
@@ -42,12 +42,12 @@ public PgnTests()
                 .AddPgnParser(static () => false)
                 .BuildServiceProvider();
     }
-    
+
     [Fact]
     public async Task ParseFile_WithTestContent_ReturnsCorrectGamesAndMoves()
     {
         var parser = _serviceProvider.GetRequiredService<IPgnParser>();
-        
+
         var games = new List<PgnGame>();
 
         await foreach (var game in parser.ParseFile(SampleFilePath))
@@ -58,8 +58,9 @@ public async Task ParseFile_WithTestContent_ReturnsCorrectGamesAndMoves()
 
         var g = JsonSerializer.Serialize(game1);
 
+        Assert.NotEmpty(g);
         Assert.Equal(ExpectedGameCount, games.Count);
-        
+
         Assert.Equal("Test event", game1.Tags["Event"]);
         Assert.Equal(3, game1.Moves.Count);
         Assert.Equal("e4", game1.Moves[0].WhiteMove);
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
index 8b0d1f35..8bf01273 100644
--- a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
@@ -30,7 +30,7 @@ namespace Rudzoft.ChessLib.PGN;
 
 public sealed class NonRegexPgnParser : IPgnParser
 {
-    private static readonly char[] Separators = { ' ', '\t' };
+    private static readonly char[] Separators = [' ', '\t'];
 
     public async IAsyncEnumerable<PgnGame> ParseFile(
         string pgnFile,
@@ -78,9 +78,9 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
                              word.Contains('*'))
                     {
                         yield return new(currentGameTags, currentGameMoves);
-                        currentGameTags = new();
-                        currentGameMoves = new();
-                        inMoveSection = false;
+                        currentGameTags  = new();
+                        currentGameMoves = [];
+                        inMoveSection    = false;
                     }
                 }
             }
@@ -88,15 +88,15 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
             {
                 if (!line.StartsWith('[') || !line.EndsWith(']') || !line.Contains('"'))
                     continue;
-                
+
                 var firstSpaceIndex = line.IndexOf(' ');
                 var firstQuoteIndex = line.IndexOf('"');
-                var lastQuoteIndex = line.LastIndexOf('"');
+                var lastQuoteIndex  = line.LastIndexOf('"');
 
                 if (firstSpaceIndex <= 0 || firstQuoteIndex <= firstSpaceIndex
                                          || lastQuoteIndex <= firstQuoteIndex)
                     continue;
-                    
+
                 var tagName = line.Substring(1, firstSpaceIndex - 1).Trim();
                 var tagValue = line
                     .Substring(firstQuoteIndex + 1, lastQuoteIndex - firstQuoteIndex - 1)
diff --git a/src/Rudzoft.ChessLib.PGN/PgnParserFactory.cs b/src/Rudzoft.ChessLib.PGN/PgnParserServiceCollectionExtensions.cs
similarity index 96%
rename from src/Rudzoft.ChessLib.PGN/PgnParserFactory.cs
rename to src/Rudzoft.ChessLib.PGN/PgnParserServiceCollectionExtensions.cs
index 1d3dd12b..6bb26b87 100644
--- a/src/Rudzoft.ChessLib.PGN/PgnParserFactory.cs
+++ b/src/Rudzoft.ChessLib.PGN/PgnParserServiceCollectionExtensions.cs
@@ -28,7 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.PGN;
 
-public static class PgnParserFactory
+public static class PgnParserServiceCollectionExtensions
 {
     public static IServiceCollection AddPgnParser(this IServiceCollection services, Func<bool>? useRegex = null)
     {
diff --git a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
index b7f3560c..f1dc57f3 100644
--- a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
+++ b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
@@ -3,6 +3,8 @@
     <PropertyGroup>
         <Nullable>enable</Nullable>
         <PublishAot>true</PublishAot>
+        <ServerGarbageCollection>true</ServerGarbageCollection>
+        <GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
     </PropertyGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs
index 05b53266..4d560722 100644
--- a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs
@@ -62,6 +62,9 @@ public void AlignedSimpleTotal()
                     actual += s1.Aligned(s2, s3).AsByte();
                 }
             }
+
+            var s = bb.ToString();
+            Assert.NotNull(s);
         }
 
         Assert.Equal(expected, actual);
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 13ee6324..b67d5f06 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -95,7 +95,8 @@ public BoardTestsTheoryData(string[] fens, PieceTypes[] pts, Player[] players, i
         }
     }
 
-    private static readonly string[] Fens = {
+    private static readonly string[] Fens =
+    [
         "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
         "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
         "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
@@ -119,27 +120,30 @@ public BoardTestsTheoryData(string[] fens, PieceTypes[] pts, Player[] players, i
         "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
         "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
         "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-    };
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53"
+    ];
 
-    private static readonly PieceTypes[] PieceType = {
+    private static readonly PieceTypes[] PieceType =
+    [
         PieceTypes.Pawn, PieceTypes.Pawn, PieceTypes.Knight, PieceTypes.Knight, PieceTypes.Bishop, PieceTypes.Bishop,
         PieceTypes.Rook, PieceTypes.Rook, PieceTypes.Queen, PieceTypes.Queen, PieceTypes.King, PieceTypes.King,
         PieceTypes.Pawn, PieceTypes.Pawn, PieceTypes.Knight, PieceTypes.Knight, PieceTypes.Bishop, PieceTypes.Bishop,
-        PieceTypes.Rook, PieceTypes.Rook, PieceTypes.Queen, PieceTypes.Queen, PieceTypes.King, PieceTypes.King,
-    };
+        PieceTypes.Rook, PieceTypes.Rook, PieceTypes.Queen, PieceTypes.Queen, PieceTypes.King, PieceTypes.King
+    ];
 
-    private static readonly Player[] Player = {
-        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
+    private static readonly Player[] Player =
+    [
         Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
         Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
         Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
-    };
+        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black
+    ];
 
-    private static readonly int[] ExpectedCount = {
+    private static readonly int[] ExpectedCount =
+    [
         8, 8, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
         4, 3, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1
-    };
+    ];
 
     public static readonly BoardTestsTheoryData TheoryData = new(Fens, PieceType, Player, ExpectedCount);
 
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
index 916ce33f..5ea26211 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
@@ -31,14 +31,14 @@ public sealed class PerftOneTest : PerftVerify
     private static readonly string[] Fens = Enumerable.Repeat(Fen.Fen.StartPositionFen, 6).ToArray();
 
     private static readonly int[] Depths =
-    {
+    [
         1, 2, 3, 4, 5, 6
-    };
+    ];
 
     private static readonly ulong[] Results =
-    {
+    [
         20UL, 400UL, 8_902UL, 197_281UL, 4_865_609UL, 119_060_324UL
-    };
+    ];
 
     public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
@@ -55,14 +55,14 @@ public sealed class PerftTwoTest : PerftVerify
     private static readonly string[] Fens = Enumerable.Repeat(Fen, 5).ToArray();
 
     private static readonly int[] Depths =
-    {
+    [
         1, 2, 3, 4, 5
-    };
+    ];
 
     private static readonly ulong[] Results =
-    {
+    [
         48UL, 2_039UL, 97_862UL, 4_085_603UL, 193_690_690UL
-    };
+    ];
 
     public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
@@ -79,14 +79,14 @@ public sealed class PerftThreeTest : PerftVerify
     private static readonly string[] Fens = Enumerable.Repeat(Fen, 7).ToArray();
 
     private static readonly int[] Depths =
-    {
+    [
         1, 2, 3, 4, 5, 6, 7
-    };
+    ];
 
     private static readonly ulong[] Results =
-    {
+    [
         14UL, 191UL, 2_812UL, 43_238UL, 674_624UL, 11_030_083UL, 178_633_661UL
-    };
+    ];
 
     public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
@@ -103,14 +103,14 @@ public sealed class PerftFourTest : PerftVerify
     private static readonly string[] Fens = Enumerable.Repeat(Fen, 5).ToArray();
 
     private static readonly int[] Depths =
-    {
+    [
         1, 2, 3, 4, 5
-    };
+    ];
 
     private static readonly ulong[] Results =
-    {
+    [
         6UL, 264UL, 9_467UL, 422_333UL, 15_833_292UL
-    };
+    ];
 
     public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
@@ -127,14 +127,14 @@ public sealed class PerftFiveTest : PerftVerify
     private static readonly string[] Fens = Enumerable.Repeat(Fen, 5).ToArray();
 
     private static readonly int[] Depths =
-    {
+    [
         1, 2, 3, 4, 5
-    };
+    ];
 
     private static readonly ulong[] Results =
-    {
+    [
         46UL, 2_079UL, 89_890UL, 3_894_594UL, 164_075_551UL
-    };
+    ];
 
     public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
@@ -147,7 +147,7 @@ public void Perft(string fen, int depth, ulong expected)
 public sealed class TalkChessPerftTests : PerftVerify
 {
     private static readonly string[] Fens =
-    {
+    [
         "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1",         //--Illegal ep move #1
         "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1",        //--Illegal ep move #2
         "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1",       //--EP Capture Checks Opponent
@@ -161,22 +161,22 @@ public sealed class TalkChessPerftTests : PerftVerify
         "8/P1k5/K7/8/8/8/8/8 w - - 0 1",             //--Under Promote to give check
         "K1k5/8/P7/8/8/8/8/8 w - - 0 1",             //--Self Stalemate
         "8/k1P5/8/1K6/8/8/8/8 w - - 0 1",            //--Stalemate & Checkmate
-        "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1",         //--Stalemate & Checkmate
-    };
+        "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1"          //--Stalemate & Checkmate
+    ];
 
     private static readonly int[] Depths =
-    {
+    [
         6, 6, 6, 6, 6,
         4, 4, 6, 5, 6,
         6, 6, 7, 4
-    };
+    ];
 
     private static readonly ulong[] Results =
-    {
+    [
         1_134_888UL, 1_015_133UL, 1_440_467UL, 661_072UL, 803_711UL,
         1_274_206UL, 1_720_476UL, 3_821_001UL, 1_004_658UL, 217_342UL,
         92_683UL, 2_217UL, 567_584UL, 23_527UL
-    };
+    ];
 
     public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs
index 2a45ace4..eef492d7 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs
@@ -83,8 +83,8 @@ public enum EBands
 
     protected const ulong PawnDelta = 0x18181818181800;
 
-    protected static readonly BitBoard[] Bands = { Alpha, Beta, Gamma, Delta };
+    protected static readonly BitBoard[] Bands = [Alpha, Beta, Gamma, Delta];
 
     protected static readonly BitBoard[] EEBands =
-        { 0xff818181818181ff, 0x7e424242427e00, 0x3c24243c0000, 0x1818000000 };
+        [0xff818181818181ff, 0x7e424242427e00, 0x3c24243c0000, 0x1818000000];
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
index 5e9012d4..910df670 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
@@ -32,16 +32,16 @@ namespace Rudzoft.ChessLib.Test.PiecesTests;
 public sealed class RegularMobilityFixture
 {
     // special case with alpha and beta bands in the corners are taken care of
-    public int[] KnightExpected { get; } = { 4, 6, 8, 8 };
+    public int[] KnightExpected { get; } = [4, 6, 8, 8];
 
     // special case with alpha band is taken care of
-    public int[] KingExpected { get; } = { 5, 8, 8, 8 };
+    public int[] KingExpected { get; } = [5, 8, 8, 8];
 
     public BitBoard BoardCorners { get; } = Square.A1 | Square.A8 | Square.H1 | Square.H8;
 
     // pawn = 0 (N/A for now), knight = 1, king = 2
     public Func<Square, BitBoard>[] RegAttacks { get; } =
-    {
+    [
         static _ => BitBoard.Empty, BitBoards.KnightAttacks, BitBoards.KingAttacks
-    };
+    ];
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
index d476b2d4..faca0fc2 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
@@ -31,9 +31,9 @@ namespace Rudzoft.ChessLib.Test.PiecesTests;
 // ReSharper disable once ClassNeverInstantiated.Global
 public sealed class SliderMobilityFixture
 {
-    public int[] BishopExpected { get; } = { 7, 9, 11, 13 };
+    public int[] BishopExpected { get; } = [7, 9, 11, 13];
 
-    public int[] RookExpected { get; } = { 14, 14, 14, 14 };
+    public int[] RookExpected { get; } = [14, 14, 14, 14];
 
     public BitBoard SliderAttacks(PieceTypes pt, Square sq, in BitBoard occ)
     {
diff --git a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
index 21276ee6..0e4baa4f 100644
--- a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
@@ -49,7 +49,7 @@ public void PieceSize()
     }
 
     [Fact]
-    public unsafe void SquareSize()
+    public void SquareSize()
     {
         const int expected = 1;
         var actual = Unsafe.SizeOf<Square>();
@@ -57,7 +57,7 @@ public unsafe void SquareSize()
     }
 
     [Fact]
-    public unsafe void RankSize()
+    public void RankSize()
     {
         const int expected = 4;
         var actual = Unsafe.SizeOf<Rank>();
diff --git a/src/Rudzoft.ChessLib.WebApi/Program.cs b/src/Rudzoft.ChessLib.WebApi/Program.cs
index cf8973fe..2acb31d6 100644
--- a/src/Rudzoft.ChessLib.WebApi/Program.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Program.cs
@@ -11,7 +11,7 @@
 builder.Services.AddEndpointsApiExplorer();
 builder.Services.AddSwaggerGen();
 
-builder.Services.AddChessLib(null)
+builder.Services.AddChessLib()
     .AddTransient<IMoveGeneratorService, MoveGeneratorService>();
 
 var app = builder.Build();
diff --git a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
index dd211f70..ba4d0baa 100644
--- a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
@@ -8,7 +8,7 @@ public sealed class MoveGeneratorService : IMoveGeneratorService
     private readonly ILogger<IMoveGeneratorService> _logger;
     private readonly IPosition _position;
 
-    public MoveGeneratorService(ILogger<IMoveGeneratorService> logger, IPosition pos)
+    public MoveGeneratorService(ILogger<MoveGeneratorService> logger, IPosition pos)
     {
         _logger = logger;
         _position = pos;
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index b6a3e46d..cc3d35df 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -54,13 +54,13 @@ public Board()
         _pieceList = new Square[Types.Square.Count][];
         for (var i = 0; i < _pieceList.Length; i++)
         {
-            _pieceList[i] = new[]
-            {
+            _pieceList[i] =
+            [
                 Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None,
                 Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None,
                 Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None,
                 Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None
-            };
+            ];
         }
 
         _index = new int[Types.Square.Count];
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/Rudzoft.ChessLib/Cuckoo.cs
index 0a8ec8a9..6b588f1f 100644
--- a/src/Rudzoft.ChessLib/Cuckoo.cs
+++ b/src/Rudzoft.ChessLib/Cuckoo.cs
@@ -38,14 +38,14 @@ namespace Rudzoft.ChessLib;
 public sealed class Cuckoo : ICuckoo
 {
     private readonly HashKey[] _cuckooKeys;
-    private readonly Move[] _cuckooMoves;
+    private readonly Move[]    _cuckooMoves;
 
     public Cuckoo(IZobrist zobrist)
     {
-        _cuckooKeys = new HashKey[8192];
+        _cuckooKeys  = new HashKey[8192];
         _cuckooMoves = new Move[8192];
         var count = 0;
-        
+
         foreach (var pc in Piece.All.AsSpan())
         {
             var bb = BitBoards.AllSquares;
@@ -58,11 +58,11 @@ public Cuckoo(IZobrist zobrist)
                         continue;
 
                     var move = Move.Create(sq1, sq2);
-                    var key = zobrist.Psq(sq1, pc) ^ zobrist.Psq(sq2, pc) ^ zobrist.Side();
-                    var j = CuckooHashOne(in key);
+                    var key  = zobrist.Psq(sq1, pc) ^ zobrist.Psq(sq2, pc) ^ zobrist.Side();
+                    var j    = CuckooHashOne(in key);
                     do
                     {
-                        (_cuckooKeys[j], key) = (key, _cuckooKeys[j]);
+                        (_cuckooKeys[j], key)   = (key, _cuckooKeys[j]);
                         (_cuckooMoves[j], move) = (move, _cuckooMoves[j]);
 
                         // check for empty slot
@@ -88,8 +88,8 @@ public bool HashCuckooCycle(in IPosition pos, int end, int ply)
         if (end < 3)
             return false;
 
-        var state = pos.State;
-        var originalKey = state.PositionKey;
+        var state         = pos.State;
+        var originalKey   = state.PositionKey;
         var statePrevious = state.Previous;
 
         for (var i = 3; i <= end; i += 2)
@@ -97,12 +97,12 @@ public bool HashCuckooCycle(in IPosition pos, int end, int ply)
             statePrevious = statePrevious.Previous.Previous;
             var moveKey = originalKey ^ statePrevious.PositionKey;
 
-            var j = CuckooHashOne(in moveKey);
+            var j     = CuckooHashOne(in moveKey);
             var found = _cuckooKeys[j] == moveKey;
 
             if (!found)
             {
-                j = CuckooHashTwo(in moveKey);
+                j     = CuckooHashTwo(in moveKey);
                 found = _cuckooKeys[j] == moveKey;
             }
 
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 176aa4a3..ac19801f 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -48,8 +48,7 @@ public static IServiceCollection AddChessLib(
         IConfiguration configuration = null,
         string configurationFile = null)
     {
-        if (serviceCollection == null)
-            throw new ArgumentNullException(nameof(serviceCollection));
+        ArgumentNullException.ThrowIfNull(serviceCollection);
 
         if (configuration == null)
         {
diff --git a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
index 620405a5..5cb0ab3e 100644
--- a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
@@ -39,12 +39,13 @@ public static class PieceExtensions
 
     public const string BookPieceNames = "pPnNbBrRqQkK";
 
-    private static readonly string[] PieceStrings = { " ", "P", "N", "B", "R", "Q", "K", " ", " ", "p", "n", "b", "r", "q", "k" };
+    private static readonly string[] PieceStrings = [" ", "P", "N", "B", "R", "Q", "K", " ", " ", "p", "n", "b", "r", "q", "k"
+    ];
 
-    private static readonly string[] PieceNames = { "None", "Pawn", "Knight", "Bishop", "Rook", "Queen", "King" };
+    private static readonly string[] PieceNames = ["None", "Pawn", "Knight", "Bishop", "Rook", "Queen", "King"];
 
     private static readonly char[] PieceUnicodeChar =
-    {
+    [
         ' ',
         '\u2659',   //  ♙   U+2659  &#9817;
         '\u2658',   //  ♘   U+2658  &#9816;
@@ -61,7 +62,7 @@ public static class PieceExtensions
         '\u265B',   //  ♛   U+265B  &#9819;
         '\u265A',   //  ♚   U+265A  &#9818;
         ' '
-    };
+    ];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static char GetPieceChar(this Piece p) => PieceChars[p.AsInt()];
diff --git a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
index 2859980b..858a9938 100644
--- a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
@@ -50,7 +50,7 @@ public static Queue<string> Parse(this ReadOnlySpan<char> command, char separato
             if (i == command.Length - 1)
             {
                 // return last token.
-                q.Enqueue(new string(command.Slice(startIndex, i - startIndex + 1).TrimEnd(tokenizer)));
+                q.Enqueue(new(command.Slice(startIndex, i - startIndex + 1).TrimEnd(tokenizer)));
                 break;
             }
 
@@ -63,7 +63,7 @@ public static Queue<string> Parse(this ReadOnlySpan<char> command, char separato
                     continue;
 
                 // return token
-                q.Enqueue(new string(command[startIndex..i].TrimEnd(tokenizer)));
+                q.Enqueue(new(command[startIndex..i].TrimEnd(tokenizer)));
                 startIndex = i + 1;
             }
             else if (character == tokenizer)
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index bfcd38c1..2cfe71ba 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -39,9 +39,9 @@ public static class Fen
 
     public const int MaxFenLen = 128;
 
-    private const string FenRankRegexSnippet = @"[1-8KkQqRrBbNnPp]{1,8}";
+    private const string FenRankRegexSnippet = "[1-8KkQqRrBbNnPp]{1,8}";
 
-    private const string ValidChars = @"012345678pPnNbBrRqQkK/ w-abcdefgh";
+    private const string ValidChars = "012345678pPnNbBrRqQkK/ w-abcdefgh";
 
     private const char Space = ' ';
 
@@ -51,9 +51,12 @@ public static class Fen
 
     private const int SeparatorCount = 7;
 
-    private static readonly Lazy<Regex> ValidFenRegex = new(static () => new Regex(
-       string.Format(@"^ \s* {0}/{0}/{0}/{0}/{0}/{0}/{0}/{0} \s+ (?:w|b) \s+ (?:[KkQq]+|\-) \s+ (?:[a-h][1-8]|\-) \s+ \d+ \s+ \d+ \s* $", FenRankRegexSnippet),
-       RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.NonBacktracking | RegexOptions.ExplicitCapture));
+    private static readonly Regex ValidFenRegex = new(
+        string.Format(
+            """^ \s* {0}/{0}/{0}/{0}/{0}/{0}/{0}/{0} \s+ (?:w|b) \s+ (?:[KkQq]+|\-) \s+ (?:[a-h][1-8]|\-) \s+ \d+ \s+ \d+ \s* $""",
+            FenRankRegexSnippet),
+        RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline |
+        RegexOptions.NonBacktracking | RegexOptions.ExplicitCapture);
 
     /// <summary>
     /// Performs basic validation of FEN string.
@@ -80,7 +83,7 @@ public static void Validate(string fen)
         if (f.Length >= MaxFenLen)
             throw new InvalidFenException($"Invalid length for fen {fen}.");
 
-        if (!ValidFenRegex.Value.IsMatch(fen))
+        if (!ValidFenRegex.IsMatch(fen))
             throw new InvalidFenException($"Invalid format for fen {fen}.");
 
         CountValidity(f);
@@ -116,6 +119,7 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
             {
                 if (++pieceCount[0] > SeparatorCount)
                     throw new InvalidFenException($"Invalid fen (too many separators) {s.ToString()}");
+
                 continue;
             }
 
@@ -123,6 +127,7 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
             {
                 if (!char.IsBetween(t, '1', '8'))
                     throw new InvalidFenException($"Invalid fen (not a valid square jump) {s.ToString()}");
+
                 continue;
             }
 
@@ -143,18 +148,6 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
                     $"Invalid fen (piece limit exceeded for {pc}. index={i},limit={limit},count={pieceCount[pc.AsInt()]}) {s.ToString()}");
         }
 
-        // check for summed up values
-        static bool GetSpanSum(ReadOnlySpan<int> span, int limit)
-        {
-            var sum = 0;
-
-            ref var spanSpace = ref MemoryMarshal.GetReference(span);
-            for (var i = 0; i < span.Length; ++i)
-                sum += Unsafe.Add(ref spanSpace, i);
-
-            return sum <= limit;
-        }
-
         var whitePieces = pieceCount.Slice(1, 5);
 
         var valid = GetSpanSum(whitePieces, 15);
@@ -176,6 +169,18 @@ static bool GetSpanSum(ReadOnlySpan<int> span, int limit)
             throw new InvalidFenException($"Invalid half move count for fen {s.ToString()}");
 
         return true;
+
+        // check for summed up values
+        static bool GetSpanSum(ReadOnlySpan<int> span, int limit)
+        {
+            var sum = 0;
+
+            ref var spanSpace = ref MemoryMarshal.GetReference(span);
+            for (var i = 0; i < span.Length; ++i)
+                sum += Unsafe.Add(ref spanSpace, i);
+
+            return sum <= limit;
+        }
     }
 
     /// <summary>
diff --git a/src/Rudzoft.ChessLib/Fen/FenData.cs b/src/Rudzoft.ChessLib/Fen/FenData.cs
index 7d7f6f5f..bcbf7eea 100644
--- a/src/Rudzoft.ChessLib/Fen/FenData.cs
+++ b/src/Rudzoft.ChessLib/Fen/FenData.cs
@@ -40,15 +40,10 @@ namespace Rudzoft.ChessLib.Fen;
 public sealed class FenData : EventArgs, IFenData
 {
     private record struct SplitPoint(int Begin, int End);
-    
-    private readonly Queue<SplitPoint> _splitPoints;
 
-    private FenData()
-    {
-        _splitPoints = new(6);
-    }
+    private readonly Queue<SplitPoint> _splitPoints = new(6);
 
-    public FenData(ReadOnlyMemory<char> fen) : this()
+    public FenData(ReadOnlyMemory<char> fen)
     {
         Fen = fen;
         var start = 0;
@@ -71,7 +66,7 @@ public FenData(ReadOnlyMemory<char> fen) : this()
         _splitPoints.Enqueue(new(start, s.Length));
     }
 
-    public FenData(ReadOnlySpan<string> fen) : this()
+    public FenData(ReadOnlySpan<string> fen)
     {
         var sb = new StringBuilder(128);
 
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 6a3c5de4..7204d586 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -60,7 +60,7 @@ public Game(
         SearchParameters = searchParameters;
         Uci              = uci;
         Cpu              = cpu;
-        _perftTable      = new PerftTable();
+        _perftTable      = new();
     }
 
     public Action<IPieceSquare> PieceUpdated => _pos.PieceUpdated;
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
index 9ebfb833..21bad571 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
@@ -80,4 +80,4 @@ internal void save(uint k, Value v, Bound t, Depth d, Move m, int g, Value statV
     internal int generation() { return generation8; }
     internal Value static_value() { return staticValue; }
     internal Value static_value_margin() { return staticMargin; }
-};
+}
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 35c6296c..56289a1c 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -35,10 +35,10 @@ namespace Rudzoft.ChessLib.MoveGeneration;
 public static class MoveGenerator
 {
     private static readonly (CastleRight, CastleRight)[] CastleSideRights =
-    {
+    [
         (CastleRight.WhiteKing, CastleRight.WhiteQueen),
         (CastleRight.BlackKing, CastleRight.BlackQueen)
-    };
+    ];
 
     public static int Generate(
         in IPosition pos,
diff --git a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
index c9501334..2013ad57 100644
--- a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
@@ -40,8 +40,8 @@ public sealed class MoveNotation : IMoveNotation
 
     private MoveNotation(IPosition pos)
     {
-        _notations = new INotation[]
-        {
+        _notations =
+        [
             new SanNotation(pos),
             new FanNotation(pos),
             new LanNotation(pos),
@@ -52,7 +52,7 @@ private MoveNotation(IPosition pos)
             new CoordinateNotation(pos),
             new IccfNotation(pos),
             new UciNotation(pos)
-        };
+        ];
     }
 
     public static IMoveNotation Create(IPosition pos)
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
index 57eda35f..40d73b66 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
@@ -61,6 +61,6 @@ public override string Convert(Move move)
             re[i++] = ')';
         }
 
-        return new string(re[..i]);
+        return new(re[..i]);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
index 1fc07cba..dda0e394 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
@@ -91,6 +91,6 @@ public override string Convert(Move move)
         if (Pos.InCheck)
             re[i++] = GetCheckChar();
 
-        return new string(re[..i]);
+        return new(re[..i]);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
index c3b5cf42..67145b02 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
@@ -93,6 +93,6 @@ public override string Convert(Move move)
         if (Pos.InCheck)
             re[i++] = GetCheckChar();
 
-        return new string(re[..i]);
+        return new(re[..i]);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
index 488e5d50..4a033c05 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
@@ -94,6 +94,6 @@ public override string Convert(Move move)
         if (Pos.InCheck)
             re[i++] = GetCheckChar();
 
-        return new string(re[..i]);
+        return new(re[..i]);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
index 058f1320..5f6a6c11 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
@@ -89,6 +89,6 @@ public override string Convert(Move move)
         if (GivesCheck(move))
             re[i++] = '+';
 
-        return new string(re[..i]);
+        return new(re[..i]);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
index 2821570d..1fb0986c 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
@@ -57,6 +57,6 @@ public override string Convert(Move move)
         else if (move.IsCastleMove())
             re[i++] = 'c';
 
-        return new string(re[..i]);
+        return new(re[..i]);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index 1d56a522..479f5143 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -37,12 +37,12 @@ namespace Rudzoft.ChessLib.Polyglot;
 public sealed class PolyglotBook : IPolyglotBook
 {
     private static readonly CastleRight[] CastleRights =
-    {
+    [
         CastleRight.WhiteKing,
         CastleRight.WhiteQueen,
         CastleRight.BlackKing,
         CastleRight.BlackQueen
-    };
+    ];
 
     private readonly FileStream _fileStream;
     private readonly BinaryReader _binaryReader;
@@ -53,21 +53,21 @@ public sealed class PolyglotBook : IPolyglotBook
 
     private PolyglotBook(ObjectPool<IMoveList> pool)
     {
-        _entrySize = Unsafe.SizeOf<PolyglotBookEntry>();
-        _rnd = new Random(DateTime.Now.Millisecond);
+        _entrySize    = Unsafe.SizeOf<PolyglotBookEntry>();
+        _rnd          = new(DateTime.Now.Millisecond);
         _moveListPool = pool;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal static PolyglotBook Create(ObjectPool<IMoveList> pool)
     {
-        return new PolyglotBook(pool);
+        return new(pool);
     }
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal static PolyglotBook Create(ObjectPool<IMoveList> pool, string path, string file)
     {
-        return new PolyglotBook(pool)
+        return new(pool)
         {
             BookFile = Path.Combine(path, file)
         };
@@ -83,7 +83,7 @@ public string BookFile
             if (_bookFilePath == value)
                 return;
             _bookFilePath = value;
-            _fileStream = new FileStream(value, FileMode.Open, FileAccess.Read);
+            _fileStream = new(value, FileMode.Open, FileAccess.Read);
             _binaryReader = BitConverter.IsLittleEndian
                 ? new ReverseEndianBinaryStreamReader(_fileStream)
                 : new BinaryReader(_fileStream);
@@ -160,7 +160,7 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
         var moves = ml.Get();
 
         var mm = SelectMove(in pos, from, to, move.MoveType(), moves);
-        
+
         _moveListPool.Return(ml);
 
         return mm;
@@ -179,14 +179,14 @@ private static Move SelectMove(in IPosition pos, Square polyFrom, Square polyTo,
             var promotionMatches = m.IsPromotionMove()
                 ? polyType == MoveTypes.Promotion
                 : polyType != MoveTypes.Promotion;
-            
+
             if (promotionMatches && !IsInCheck(pos, m))
                 return m;
         }
 
         return Move.EmptyMove;
     }
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static bool IsInCheck(IPosition pos, Move m)
     {
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 8b97b019..01f02d54 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -325,7 +325,7 @@ internal static class PolyglotBookZobrist
     };
 
     private static readonly ulong[] CastleKeys =
-    {
+    [
         0UL,
         0x31D71DCE64B2C310UL, // white short
         0xF165B587DF898190UL, // white long
@@ -335,18 +335,18 @@ internal static class PolyglotBookZobrist
         0UL,
         0UL,
         0x1EF6E6DBB1961EC9UL // black long
-    };
+    ];
 
     private static readonly ulong[] EnPassantKeys =
-    {
+    [
         0x70CC73D90BC26E24UL, 0xE21A6B35DF0C3AD7UL, 0x003A93D8B2806962UL, 0x1C99DED33CB890A1UL,
         0xCF3145DE0ADD4289UL, 0xD0E4427A5514FB72UL, 0x77C621CC9FB3A483UL, 0x67A34DAC4356550BUL
-    };
+    ];
 
     private const ulong TurnKey = 0xF8D626AAAF278509UL;
 
     // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
-    private static readonly int[] PieceMapping = { -1, 1, 3, 5, 7, 9, 11, -1, -1, 0, 2, 4, 6, 8, 10 };
+    private static readonly int[] PieceMapping = [-1, 1, 3, 5, 7, 9, 11, -1, -1, 0, 2, 4, 6, 8, 10];
 
     internal static ulong Psq(Piece pc, Square sq)
     {
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 5112b924..cdd4f74a 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Buffers;
 using System.Collections;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
@@ -72,16 +73,16 @@ public Position(
         _castlingRightsMask = new CastleRight[Square.Count];
         _castlingRightsMask.Fill(CastleRight.None);
         _castlingRookSquare = new Square[CastleRight.Count];
-        _nonPawnMaterial = new[] { Value.ValueZero, Value.ValueZero };
-        _outputObjectPool = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
-        _moveListPool = moveListPool;
-        _positionValidator = positionValidator;
-        Board = board;
-        IsProbing = true;
-        Values = values;
-        Zobrist = zobrist;
-        _cuckoo = cuckoo;
-        State = new();
+        _nonPawnMaterial    = [Value.ValueZero, Value.ValueZero];
+        _outputObjectPool   = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
+        _moveListPool       = moveListPool;
+        _positionValidator  = positionValidator;
+        Board               = board;
+        IsProbing           = true;
+        Values              = values;
+        Zobrist             = zobrist;
+        _cuckoo             = cuckoo;
+        State               = new();
         Clear();
     }
 
@@ -422,10 +423,10 @@ public HashKey GetKey()
 
         if (State.EnPassantSquare != Square.None)
             result ^= Zobrist.EnPassant(State.EnPassantSquare);
-        
+
         if (_sideToMove == Player.Black)
             result ^= Zobrist.Side();
-        
+
         return result;
     }
 
@@ -998,12 +999,12 @@ public bool SeeGe(Move m, Value threshold)
         var (from, to) = m;
 
         var swap = Values.GetPieceValue(GetPiece(to), Phases.Mg) - threshold;
-        
+
         if (swap < Value.ValueZero)
             return false;
 
         swap = Values.GetPieceValue(GetPiece(from), Phases.Mg) - swap;
-        
+
         if (swap <= Value.ValueZero)
             return true;
 
@@ -1159,12 +1160,12 @@ private void SetupEnPassant(ReadOnlySpan<char> fenChunk)
             State.EnPassantSquare = Square.None;
     }
 
-    private void SetupMoveNumber(IFenData fenData)
+    private void SetupMoveNumber(ReadOnlySpan<char> fen, in Range halfMove, in Range moveNumber)
     {
         var moveNum = 0;
         var halfMoveNum = 0;
 
-        var chunk = fenData.Chunk();
+        var chunk = fen[halfMove];
 
         if (!chunk.IsEmpty)
         {
@@ -1172,7 +1173,7 @@ private void SetupMoveNumber(IFenData fenData)
                 halfMoveNum = 0;
 
             // half move number
-            chunk = fenData.Chunk();
+            chunk = fen[moveNumber];
 
             Maths.ToIntegral(chunk, out moveNum);
 
@@ -1186,7 +1187,7 @@ private void SetupMoveNumber(IFenData fenData)
 
     /// <summary>
     /// Apply FEN to this position.
-    /// 
+    ///
     /// </summary>
     /// <param name="fenData">The fen data to set</param>
     /// <param name="chessMode">The chess mode to apply</param>
@@ -1205,11 +1206,20 @@ public IPosition Set(in FenData fenData, ChessMode chessMode, in State state, bo
 
         CopyState(in state);
 
-        SetupPieces(fenData.Chunk());
-        SetupPlayer(fenData.Chunk());
-        SetupCastle(fenData.Chunk());
-        SetupEnPassant(fenData.Chunk());
-        SetupMoveNumber(fenData);
+        const StringSplitOptions splitOptions = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries;
+
+        var         fen        = fenData.Fen.Span;
+        Span<Range> ranges     = stackalloc Range[7];
+        var         splitCount = fen.Split(ranges, ' ', splitOptions);
+
+        if (splitCount < 4)
+            throw new InvalidFenException("Invalid FEN string");
+
+        SetupPieces(fen[ranges[0]]);
+        SetupPlayer(fen[ranges[1]]);
+        SetupCastle(fen[ranges[2]]);
+        SetupEnPassant(fen[ranges[3]]);
+        SetupMoveNumber(fen, in ranges[4], in ranges[5]);
 
         SetState(State);
 
@@ -1343,7 +1353,7 @@ public override string ToString()
 
             for (var file = Files.FileA; file <= Files.FileH; file++)
             {
-                var piece = GetPiece(new(rank, file));
+                var piece      = GetPiece(new(rank, file));
                 row[rowIndex++] = splitter;
                 row[rowIndex++] = space;
                 row[rowIndex++] = piece.GetPieceChar();
@@ -1473,7 +1483,7 @@ private void CopyState(in State newState)
     {
         State = State.CopyTo(newState);
     }
-    
+
     private void SetState(State state)
     {
         state.MaterialKey = HashKey.Empty;
@@ -1504,7 +1514,7 @@ private void SetState(State state)
                 var val = Values.GetPieceValue(pc.Type(), Phases.Mg).AsInt() * Board.PieceCount(pc);
                 _nonPawnMaterial[pc.ColorOf().Side] += val;
             }
-            
+
             for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
                 state.MaterialKey ^= Zobrist.Psq(cnt, pc);
         }
@@ -1611,9 +1621,9 @@ private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
 
         // En-passant attackers
         var attackers = Pieces(PieceTypes.Pawn, us) & epSquare.PawnAttack(them);
-        
+
         Debug.Assert(attackers.Count <= 2);
-        
+
         if (attackers.IsEmpty)
             return false;
 
@@ -1623,9 +1633,9 @@ private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
         var ksq = Board.Square(PieceTypes.King, us);
         var bq = Pieces(PieceTypes.Bishop, PieceTypes.Queen, them) & GetAttacks(ksq, PieceTypes.Bishop);
         var rq = Pieces(PieceTypes.Rook, PieceTypes.Queen, them) & GetAttacks(ksq, PieceTypes.Rook);
-        
+
         var mocc = (Pieces() ^ cap) | epSquare;
-        
+
         while (attackers) {
             var sq = BitBoards.PopLsb(ref attackers);
             var amocc =  mocc ^ sq;
@@ -1635,7 +1645,7 @@ private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
                 (rq.IsEmpty || (rq & GetAttacks(ksq, PieceTypes.Rook, in amocc)).IsEmpty))
                 return true;
         }
-        
+
         return false;
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
index 34e78282..b993f74b 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
@@ -39,7 +39,7 @@ public sealed class HiResTimer : IHiResTimer, IEquatable<HiResTimer>
     public static readonly double Frequency = Stopwatch.Frequency;
 
     private static readonly TimeSpan RestartThreshold = TimeSpan.FromHours(1);
-    
+
     private readonly object _intervalLock;
 
     private float _interval;
@@ -48,17 +48,17 @@ public sealed class HiResTimer : IHiResTimer, IEquatable<HiResTimer>
 
     private Task _executer;
 
-    public HiResTimer() => _intervalLock = new object();
+    public HiResTimer() => _intervalLock = new();
 
     public HiResTimer(int id)
         : this(1f, id, null) { }
 
     public HiResTimer(float interval, int id, Action<HiResTimerArgs> elapsed)
     {
-        _intervalLock = new object();
-        Interval = interval;
-        Elapsed = elapsed;
-        Id = id;
+        _intervalLock = new();
+        Interval      = interval;
+        Elapsed       = elapsed;
+        Id            = id;
     }
 
     /// <summary>
@@ -112,7 +112,7 @@ public void Start()
 
         Debug.Print($"Timer Start on thread {Environment.CurrentManagedThreadId}");
 
-        _cancellationTokenSource = new CancellationTokenSource();
+        _cancellationTokenSource = new();
         _cancellationTokenSource.Token.ThrowIfCancellationRequested();
         _executer = Task.Run(() => ExecuteTimer(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
     }
@@ -200,7 +200,7 @@ private void ExecuteTimer(CancellationToken cancellationToken)
             if (Elapsed != null)
             {
                 var delay = elapsed - nextTrigger;
-                Elapsed(new HiResTimerArgs(delay, Id));
+                Elapsed(new(delay, Id));
             }
 
             if (cancellationToken.IsCancellationRequested)
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs b/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
index d789ad46..4794cfe3 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
@@ -28,7 +28,7 @@ namespace Rudzoft.ChessLib.Protocol.UCI;
 
 public sealed class InputOutput : IInputOutput
 {
-    private static readonly char[] SplitChar = { ' ' };
+    private static readonly char[] SplitChar = [' '];
 
     private readonly Mutex _mutex;
 
@@ -37,9 +37,9 @@ public sealed class InputOutput : IInputOutput
 
     public InputOutput()
     {
-        _mutex = new Mutex();
+        _mutex       = new();
         LastLineRead = string.Empty;
-        _words = Array.Empty<string>();
+        _words       = Array.Empty<string>();
     }
 
     public TextReader Input { get; set; }
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
index 49e81173..005b500a 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
@@ -35,7 +35,7 @@ public sealed class MovesToGoModel : EventArgs, IMovesToGoModel
 
     public MovesToGoModel(ISearchParameters original)
     {
-        _time = new[] { original.WhiteTimeMilliseconds, original.BlackTimeMilliseconds };
+        _time     = [original.WhiteTimeMilliseconds, original.BlackTimeMilliseconds];
         MovesToGo = original.MovesToGo;
     }
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
index 46e674a4..259cc104 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
@@ -31,8 +31,8 @@ namespace Rudzoft.ChessLib.Protocol.UCI;
 
 public sealed class Option : IOption
 {
-    private static readonly string[] BoolStrings = { "false", "true" };
-    
+    private static readonly string[] BoolStrings = ["false", "true"];
+
     private string _currentValue;
 
     public Option()
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index 63c4fdce..c48d06aa 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -33,20 +33,14 @@ namespace Rudzoft.ChessLib.Protocol.UCI;
 /// <summary>
 /// Contains the information related to search parameters for a UCI chess engine.
 /// </summary>
-public sealed class SearchParameters : ISearchParameters
+[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
+public sealed class SearchParameters() : ISearchParameters
 {
     private static readonly Clock ZeroClock = new(0UL, 0UL);
-    
-    private readonly Clock[] _clock;
-    private ulong _movesToGo;
-    private ulong _moveTime;
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public SearchParameters()
-    {
-        _clock = new Clock[Player.Count];
-        SearchMoves = new List<Move>();
-    }
+    private readonly Clock[] _clock = new Clock[Player.Count];
+    private          ulong   _movesToGo;
+    private          ulong   _moveTime;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public SearchParameters(bool infinite)
@@ -78,7 +72,7 @@ public SearchParameters(ulong whiteTimeMilliseconds, ulong blackTimeMilliseconds
         MoveTime = moveTime;
     }
 
-    public List<Move> SearchMoves { get; }
+    public List<Move> SearchMoves { get; } = [];
 
     public bool Infinite { get; set; }
 
@@ -135,7 +129,7 @@ public bool UseTimeManagement()
     {
         return _clock[Player.White.Side].Time != 0 && _clock[Player.Black.Side].Time != 0;
     }
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public ulong Time(Player p) => _clock[p.Side].Time;
 
@@ -155,7 +149,7 @@ public override string ToString()
     {
         Span<char> s = stackalloc char[128];
         TryFormat(s, out var written);
-        return new string(s[..written]);
+        return new(s[..written]);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index 3cd47638..c8553b65 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -93,6 +93,7 @@ public bool TryGetOption(string name, out IOption option)
     public ulong Nps(in ulong nodes, in TimeSpan time)
         => (ulong)(nodes * 1000.0 / time.Milliseconds);
 
+    [SkipLocalsInit]
     public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
     {
         var ml = MoveListPool.Get();
@@ -102,12 +103,14 @@ public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
         var moves = ml.Get();
 
         var m = Move.EmptyMove;
-        
+
+        Span<char> s = stackalloc char[5];
         foreach (var move in moves)
         {
-            if (!uciMove.Equals(move.Move.ToString(), StringComparison.InvariantCultureIgnoreCase))
+            if (move.Move.TryFormat(s, out var written)
+                && !uciMove.Equals(s[..written] ,StringComparison.Ordinal))
                 continue;
-            
+
             m = move.Move;
             break;
         }
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index 3de21f7f..a8392dfd 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -62,9 +62,9 @@ public sealed class State : IEquatable<State>
     /// </summary>
     public BitBoard Checkers { get; set; } = BitBoard.Empty;
 
-    public BitBoard[] BlockersForKing { get; } = { BitBoard.Empty, BitBoard.Empty };
+    public BitBoard[] BlockersForKing { get; } = [BitBoard.Empty, BitBoard.Empty];
 
-    public BitBoard[] Pinners { get; } = { BitBoard.Empty, BitBoard.Empty };
+    public BitBoard[] Pinners { get; } = [BitBoard.Empty, BitBoard.Empty];
 
     public BitBoard[] CheckedSquares { get; private set; } = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
 
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs b/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
index 4a4b7303..c4499683 100644
--- a/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
+++ b/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
@@ -33,7 +33,7 @@ namespace Rudzoft.ChessLib.Tables.KillerMoves;
 public sealed class KillerMoves : IKillerMoves
 {
     private static readonly PieceSquare EmptyPieceSquare = new(Piece.EmptyPiece, Square.None);
-    
+
     private readonly PieceSquare[][] _killerMoves;
     private readonly int _maxDepth;
 
@@ -44,7 +44,7 @@ private KillerMoves(int maxDepth)
         _maxDepth = maxDepth + 1;
         _killerMoves = new PieceSquare[_maxDepth][];
         for (var i = 0; i < _killerMoves.Length; ++i)
-            _killerMoves[i] = new[] { EmptyPieceSquare, EmptyPieceSquare };
+            _killerMoves[i] = [EmptyPieceSquare, EmptyPieceSquare];
     }
 
     private IKillerMoves Initialize()
@@ -72,7 +72,7 @@ public void UpdateValue(int depth, Move m, Piece fromPc)
         // Shift killer move.
         _killerMoves[depth][1] = _killerMoves[depth][0];
         // Update killer move.
-        _killerMoves[depth][0] = new PieceSquare(fromPc, m.ToSquare());
+        _killerMoves[depth][0] = new(fromPc, m.ToSquare());
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index e21a2ea5..41adbb9f 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -31,21 +31,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Diagnostics;
 using System.Numerics;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 
 namespace Rudzoft.ChessLib.Types;
 
 public static class BitBoards
 {
     internal const ulong One = 0x1ul;
-    
+
     public static readonly BitBoard WhiteArea = new(0x00000000FFFFFFFFUL);
 
     public static readonly BitBoard BlackArea = ~WhiteArea;
 
     private static readonly BitBoard LightSquares = new(0x55AA55AA55AA55AAUL);
 
-    private static readonly BitBoard[] ColorsBB = { LightSquares, ~LightSquares };
+    private static readonly BitBoard[] ColorsBB = [LightSquares, ~LightSquares];
 
     private static readonly BitBoard FileABB = new(0x0101010101010101UL);
 
@@ -64,10 +63,10 @@ public static class BitBoards
     private static readonly BitBoard FileHBB = new(0x8080808080808080UL);
 
     private static readonly BitBoard[] FilesBB =
-    {
+    [
         FileABB, FileBBB, FileCBB, FileDBB,
         FileEBB, FileFBB, FileGBB, FileHBB
-    };
+    ];
 
     private static readonly BitBoard Rank1BB = new(0x00000000000000ffUL);
 
@@ -86,10 +85,10 @@ public static class BitBoards
     private static readonly BitBoard Rank8BB = new(0xff00000000000000UL);
 
     private static readonly BitBoard[] RanksBB =
-    {
+    [
         Rank1BB, Rank2BB, Rank3BB, Rank4BB,
         Rank5BB, Rank6BB, Rank7BB, Rank8BB
-    };
+    ];
 
     public static readonly BitBoard EmptyBitBoard = new(ulong.MinValue);
 
@@ -108,12 +107,12 @@ public static class BitBoards
     // A1..H8 | H1..A8
     public static readonly BitBoard DiagonalBB = new(0x8142241818244281UL);
 
-    private static readonly BitBoard[] PromotionRanks = { Rank8BB, Rank1BB };
+    private static readonly BitBoard[] PromotionRanks = [Rank8BB, Rank1BB];
 
     public static readonly BitBoard PromotionRanksBB = Rank1BB | Rank8BB;
 
     internal static readonly BitBoard[] BbSquares =
-    {
+    [
         0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000,
         0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000, 0x4000000, 0x8000000,
         0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x100000000, 0x200000000, 0x400000000, 0x800000000,
@@ -124,7 +123,7 @@ public static class BitBoards
         0x100000000000000,
         0x200000000000000, 0x400000000000000, 0x800000000000000, 0x1000000000000000, 0x2000000000000000,
         0x4000000000000000, 0x8000000000000000
-    };
+    ];
 
     public static readonly BitBoard CornerA1 = Square.A1 | Square.B1 | Square.A2 | Square.B2;
 
@@ -134,15 +133,15 @@ public static class BitBoards
 
     public static readonly BitBoard CornerH8 = Square.H8 | Square.G8 | Square.H7 | Square.G7;
 
-    private static readonly BitBoard[] Ranks1 = { Rank1BB, Rank8BB };
+    private static readonly BitBoard[] Ranks1 = [Rank1BB, Rank8BB];
 
-    private static readonly BitBoard[] Ranks6And7BB = { Rank6BB | Rank7BB, Rank2BB | Rank3BB };
+    private static readonly BitBoard[] Ranks6And7BB = [Rank6BB | Rank7BB, Rank2BB | Rank3BB];
 
-    private static readonly BitBoard[] Ranks7And8BB = { Rank7BB | Rank8BB, Rank1BB | Rank2BB };
+    private static readonly BitBoard[] Ranks7And8BB = [Rank7BB | Rank8BB, Rank1BB | Rank2BB];
 
-    private static readonly BitBoard[] Ranks3BB = { Rank3BB, Rank6BB };
+    private static readonly BitBoard[] Ranks3BB = [Rank3BB, Rank6BB];
 
-    private static readonly BitBoard[] Ranks7BB = { Rank7BB, Rank2BB };
+    private static readonly BitBoard[] Ranks7BB = [Rank7BB, Rank2BB];
 
     /// <summary>
     /// PseudoAttacks are just that, full attack range for all squares for all pieces. The pawns
@@ -166,10 +165,10 @@ public static class BitBoards
     private static readonly BitBoard[][] LineBB = new BitBoard[Square.Count][];
 
     private static readonly BitBoard[] AdjacentFilesBB =
-    {
+    [
         FileBBB, FileABB | FileCBB, FileBBB | FileDBB, FileCBB | FileEBB, FileDBB | FileFBB, FileEBB | FileGBB,
         FileFBB | FileHBB, FileGBB
-    };
+    ];
 
     private static readonly int[][] SquareDistance = new int[Square.Count][]; // chebyshev distance
 
@@ -177,9 +176,7 @@ public static class BitBoards
 
     private static readonly BitBoard[] SlotFileBB;
 
-    private static readonly Direction[] PawnPushDirections = { Direction.North, Direction.South };
-
-    private static readonly Dictionary<Direction, Func<BitBoard, BitBoard>> ShiftFuncs = MakeShiftFuncs();
+    private static readonly Direction[] PawnPushDirections = [Direction.North, Direction.South];
 
     private static readonly Func<BitBoard, BitBoard>[] FillFuncs = MakeFillFuncs();
 
@@ -215,11 +212,6 @@ static BitBoards()
         for (var i = 0; i < SquareDistance.Length; i++)
             DistanceRingBB[i] = new BitBoard[8];
 
-        // local helper functions to calculate distance
-        static int distance(int x, int y) => Math.Abs(x - y);
-        static int distanceFile(Square x, Square y) => distance(x.File.AsInt(), y.File.AsInt());
-        static int distanceRank(Square x, Square y) => distance(x.Rank.AsInt(), y.Rank.AsInt());
-
         // ForwardRanksBB population loop idea from sf
         for (var r = Rank.Rank1; r <= Rank.Rank8; r++)
         {
@@ -285,17 +277,24 @@ static BitBoards()
                                          GetAttacks(s2, validMagicPiece, BbSquares[sq]);
                 }
             }
-            
+
             // Compute KingRings
             InitializeKingRing(s1, sq, file);
         }
 
-        SlotFileBB = new[]
-        {
+        SlotFileBB =
+        [
             FileEBB | FileFBB | FileGBB | FileHBB, // King
             FileABB | FileBBB | FileCBB | FileDBB, // Queen
             FileCBB | FileDBB | FileEBB | FileFBB  // Center
-        };
+        ];
+
+        return;
+
+        static int distanceRank(Square x, Square y) => distance(x.Rank.AsInt(), y.Rank.AsInt());
+        static int distanceFile(Square x, Square y) => distance(x.File.AsInt(), y.File.AsInt());
+        // local helper functions to calculate distance
+        static int distance(int x, int y) => Math.Abs(x - y);
     }
 
     private static BitBoard ComputeKnightAttack(in BitBoard bb)
@@ -506,41 +505,43 @@ public static string Stringify(in BitBoard bb, string title = "")
     {
         const string line   = "+---+---+---+---+---+---+---+---+---+";
         const string bottom = "|   | A | B | C | D | E | F | G | H |";
+
         Span<char> span = stackalloc char[768];
+
         line.CopyTo(span);
         var idx = line.Length;
         span[idx++] = '\n';
-        foreach (var c in title.Length > 64 ? title.AsSpan()[..64] : title.AsSpan())
-            span[idx++] = c;
 
+        var t = title.Length > 64 ? title.AsSpan()[..64] : title.AsSpan();
+        t.CopyTo(span[idx..]);
+
+        Span<char> rank = stackalloc char[4] { '|', ' ', ' ', ' ' };
         for (var r = Ranks.Rank8; r >= Ranks.Rank1; --r)
         {
-            span[idx++] = '|';
-            span[idx++] = ' ';
-            span[idx++] = (char)('0' + (int)r + 1);
-            span[idx++] = ' ';
+            rank[2]     = (char)('0' + (int)r + 1);
+            rank.CopyTo(span[idx..]);
+            idx += rank.Length;
             for (var f = Files.FileA; f <= Files.FileH; ++f)
             {
-                span[idx++] = '|';
-                span[idx++] = ' ';
-                span[idx++] = (bb & new Square(r, f)).IsEmpty ? ' ' : 'X';
-                span[idx++] = ' ';
+                rank[2]     = (bb & new Square(r, f)).IsEmpty ? ' ' : 'X';
+                rank.CopyTo(span[idx..]);
+                idx += rank.Length;
             }
 
             span[idx++] = '|';
             span[idx++] = '\n';
-            foreach (var c in line)
-                span[idx++] = c;
+            line.CopyTo(span[idx..]);
+            idx += line.Length;
             span[idx++] = '\n';
         }
 
-        foreach (var c in bottom)
-            span[idx++] = c;
+        bottom.CopyTo(span[idx..]);
+        idx += bottom.Length;
 
         span[idx++] = '\n';
 
-        foreach (var c in line)
-            span[idx++] = c;
+        line.CopyTo(span[idx..]);
+        idx += line.Length;
 
         span[idx] = '\n';
 
@@ -623,12 +624,33 @@ public static BitBoard SouthFill(this BitBoard bb)
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     public static BitBoard Shift(this in BitBoard bb, Direction d)
     {
-        ref var func = ref CollectionsMarshal.GetValueRefOrNullRef(ShiftFuncs, d);
-
-        if (Unsafe.IsNullRef(ref func))
-            throw new ArgumentException("Invalid shift argument.", nameof(d));
-
-        return func(bb);
+        if (d == Direction.North)
+            return bb.NorthOne();
+        if (d == Direction.South)
+            return bb.SouthOne();
+        if (d == Direction.SouthEast)
+            return bb.SouthEastOne();
+        if (d == Direction.SouthWest)
+            return bb.SouthWestOne();
+        if (d == Direction.NorthEast)
+            return bb.NorthEastOne();
+        if (d == Direction.NorthWest)
+            return bb.NorthWestOne();
+        if (d == Direction.NorthDouble)
+            return bb.NorthOne().NorthOne();
+        if (d == Direction.SouthDouble)
+            return bb.SouthOne().SouthOne();
+        if (d == Direction.East)
+            return bb.EastOne();
+        if (d == Direction.West)
+            return bb.WestOne();
+        if (d == Direction.NorthFill)
+            return bb.NorthFill();
+        if (d == Direction.SouthFill)
+            return bb.SouthFill();
+        if (d == Direction.None)
+            return bb;
+        throw new ArgumentException("Invalid shift argument.", nameof(d));
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -729,29 +751,7 @@ public static BitBoard MakeBitboard(params Square[] sqs)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool MoreThanOne(in BitBoard bb) => (bb.Value & (bb.Value - 1)) != 0;
 
-    /// <summary>
-    /// Helper method to generate shift function dictionary for all directions.
-    /// </summary>
-    /// <returns>The generated shift dictionary</returns>
-    private static Dictionary<Direction, Func<BitBoard, BitBoard>> MakeShiftFuncs()
-        => new(13)
-        {
-            { Direction.None, static board => board },
-            { Direction.North, static board => board.NorthOne() },
-            { Direction.NorthDouble, static board => board.NorthOne().NorthOne() },
-            { Direction.NorthEast, static board => board.NorthEastOne() },
-            { Direction.NorthWest, static board => board.NorthWestOne() },
-            { Direction.NorthFill, static board => board.NorthFill() },
-            { Direction.South, static board => board.SouthOne() },
-            { Direction.SouthDouble, static board => board.SouthOne().SouthOne() },
-            { Direction.SouthEast, static board => board.SouthEastOne() },
-            { Direction.SouthWest, static board => board.SouthWestOne() },
-            { Direction.SouthFill, static board => board.SouthFill() },
-            { Direction.East, static board => board.EastOne() },
-            { Direction.West, static board => board.WestOne() }
-        };
-
-    private static Func<BitBoard, BitBoard>[] MakeFillFuncs() => new[] { NorthFill, SouthFill };
+    private static Func<BitBoard, BitBoard>[] MakeFillFuncs() => [NorthFill, SouthFill];
 
     private static BitBoard GetAttacks(this in Square sq, PieceTypes pt, in BitBoard occ = default)
     {
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index 94d4b361..9d3a5074 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -86,14 +86,14 @@ public static class CastleExtensions
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static CastleRights MakeCastleRights(this CastleRights cs, Player p)
     {
+        var isQueen = cs == CastleRights.Queen;
         if (p.IsWhite)
-            return cs == CastleRights.Queen
+            return isQueen
                 ? CastleRights.WhiteQueen
                 : CastleRights.WhiteKing;
-        else
-            return cs == CastleRights.Queen
-                ? CastleRights.BlackQueen
-                : CastleRights.BlackKing;
+        return isQueen
+            ? CastleRights.BlackQueen
+            : CastleRights.BlackKing;
     }
 }
 
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index 18ebbbf4..5d49a7bf 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -53,24 +53,21 @@ public static class FilesExtensions
 }
 
 public readonly record struct File(Files Value)
-    : IComparable<File>, ISpanFormattable, IValidationType
-#if NET7_0_OR_GREATER 
-        , IMinMaxValue<File>
-#endif
+    : IComparable<File>, ISpanFormattable, IValidationType , IMinMaxValue<File>
 {
-    private static readonly string[] FileStrings = { "a", "b", "c", "d", "e", "f", "g", "h" };
-
-    public static File FileA { get; } = new(Files.FileA);
-    public static File FileB { get; } = new(Files.FileB);
-    public static File FileC { get; } = new(Files.FileC);
-    public static File FileD { get; } = new(Files.FileD);
-    public static File FileE { get; } = new(Files.FileE);
-    public static File FileF { get; } = new(Files.FileF);
-    public static File FileG { get; } = new(Files.FileG);
-    public static File FileH { get; } = new(Files.FileH);
-    public static File[] AllFiles { get; } = { FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH };
-    public static File MaxValue => FileH;
-    public static File MinValue => FileA;
+    private static readonly string[] FileStrings = ["a", "b", "c", "d", "e", "f", "g", "h"];
+
+    public static File   FileA    { get; } = new(Files.FileA);
+    public static File   FileB    { get; } = new(Files.FileB);
+    public static File   FileC    { get; } = new(Files.FileC);
+    public static File   FileD    { get; } = new(Files.FileD);
+    public static File   FileE    { get; } = new(Files.FileE);
+    public static File   FileF    { get; } = new(Files.FileF);
+    public static File   FileG    { get; } = new(Files.FileG);
+    public static File   FileH    { get; } = new(Files.FileH);
+    public static File[] AllFiles { get; } = [FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH];
+    public static File   MaxValue => FileH;
+    public static File   MinValue => FileA;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public File(int file) : this((Files)file)
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 9cd67852..aefcde62 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -24,9 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-#if NET7_0_OR_GREATER
 using System.Numerics;
-#endif
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Extensions;
 
@@ -82,10 +80,7 @@ public static bool InBetween(this PieceTypes v, PieceTypes min, PieceTypes max)
 /// <summary>
 /// Piece. Contains the piece type which indicate what type and color the piece is
 /// </summary>
-public readonly record struct Piece(Pieces Value)
-#if NET7_0_OR_GREATER
-    : IMinMaxValue<Piece>
-#endif
+public readonly record struct Piece(Pieces Value) : IMinMaxValue<Piece>
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private Piece(int pc) : this((Pieces)pc) { }
@@ -114,10 +109,10 @@ private Piece(Piece pc) : this(pc.Value) { }
     public static Piece BlackKing { get; } = new(Pieces.BlackKing);
 
     public static Piece[] All { get; } =
-    {
+    [
         WhitePawn, WhiteKnight, WhiteBishop, WhiteRook, WhiteQueen, WhiteKing,
         BlackPawn, BlackKnight, BlackBishop, BlackRook, BlackQueen, BlackKing
-    };
+    ];
 
     public static Piece MaxValue => WhitePawn;
 
@@ -128,14 +123,14 @@ private Piece(Piece pc) : this(pc.Value) { }
     public static Range BlackPieces => new(6, 11);
     
     public static PieceTypes[] AllTypes { get; } =
-    {
+    [
         PieceTypes.Pawn,
         PieceTypes.Knight,
         PieceTypes.Bishop,
         PieceTypes.Rook,
         PieceTypes.Queen,
         PieceTypes.King
-    };
+    ];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Piece(char value) => new(GetPiece(value));
diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Player.cs
index 5eb7b59c..22161685 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Player.cs
@@ -43,26 +43,23 @@ public enum PlayerTypes
     Human = 1
 }
 
-public readonly record struct Player(byte Side) : ISpanFormattable
-#if NET7_0_OR_GREATER
-    , IMinMaxValue<Player>
-#endif
+public readonly record struct Player(byte Side) : ISpanFormattable, IMinMaxValue<Player>
 {
-    private static readonly Direction[] PawnPushDist = { Direction.North, Direction.South };
+    private static readonly Direction[] PawnPushDist = [Direction.North, Direction.South];
 
-    private static readonly Direction[] PawnDoublePushDist = { Direction.NorthDouble, Direction.SouthDouble };
+    private static readonly Direction[] PawnDoublePushDist = [Direction.NorthDouble, Direction.SouthDouble];
 
-    private static readonly Direction[] PawnWestAttackDist = { Direction.NorthEast, Direction.SouthEast };
+    private static readonly Direction[] PawnWestAttackDist = [Direction.NorthEast, Direction.SouthEast];
 
-    private static readonly Direction[] PawnEastAttackDist = { Direction.NorthWest, Direction.SouthWest };
+    private static readonly Direction[] PawnEastAttackDist = [Direction.NorthWest, Direction.SouthWest];
 
-    private static readonly string[] PlayerColors = { "White", "Black" };
+    private static readonly string[] PlayerColors = ["White", "Black"];
 
-    private static readonly char[] PlayerFen = { 'w', 'b' };
+    private static readonly char[] PlayerFen = ['w', 'b'];
 
-    private static readonly Func<BitBoard, BitBoard>[] PawnPushModifiers = { BitBoards.NorthOne, BitBoards.SouthOne };
+    private static readonly Func<BitBoard, BitBoard>[] PawnPushModifiers = [BitBoards.NorthOne, BitBoards.SouthOne];
 
-    private static readonly int[] ScoreSign = { 1, -1 };
+    private static readonly int[] ScoreSign = [1, -1];
 
     public Player(Player p)
         : this(p.Side) { }
@@ -73,15 +70,15 @@ public Player(Players p)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Deconstruct(out byte side) => side = Side;
     
-    public bool IsWhite => Side == byte.MinValue;
-    public bool IsBlack => Side != byte.MinValue;
-    public int Sign => ScoreSign[Side];
-    public char Fen => PlayerFen[Side];
-    public static Player White { get; } = new(Players.White);
-    public static Player Black { get; } = new(Players.Black);
-    public static Player[] AllPlayers { get; } = { White, Black };
-    public static Player MaxValue => White;
-    public static Player MinValue => Black;
+    public        bool     IsWhite    => Side == byte.MinValue;
+    public        bool     IsBlack    => Side != byte.MinValue;
+    public        int      Sign       => ScoreSign[Side];
+    public        char     Fen        => PlayerFen[Side];
+    public static Player   White      { get; } = new(Players.White);
+    public static Player   Black      { get; } = new(Players.Black);
+    public static Player[] AllPlayers { get; } = [White, Black];
+    public static Player   MaxValue   => White;
+    public static Player   MinValue   => Black;
 
     public const int Count = (int)Players.PlayerNb;
 
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index 47e703c9..d956777c 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -53,7 +53,7 @@ public static class RanksExtensions
 
 public readonly record struct Rank(Ranks Value) : ISpanFormattable, IValidationType
 {
-    private static readonly string[] RankStrings = { "1", "2", "3", "4", "5", "6", "7", "8" };
+    private static readonly string[] RankStrings = ["1", "2", "3", "4", "5", "6", "7", "8"];
 
     public static Rank Rank1 { get; } = new(Ranks.Rank1);
     public static Rank Rank2 { get; } = new(Ranks.Rank2);
@@ -64,9 +64,9 @@ public readonly record struct Rank(Ranks Value) : ISpanFormattable, IValidationT
     public static Rank Rank7 { get; } = new(Ranks.Rank7);
     public static Rank Rank8 { get; } = new(Ranks.Rank8);
 
-    public static Rank[] PawnRanks { get; } = { Rank2, Rank3, Rank4, Rank5, Rank6, Rank7 };
+    public static Rank[] PawnRanks { get; } = [Rank2, Rank3, Rank4, Rank5, Rank6, Rank7];
 
-    public static Rank[] All { get; } = { Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8 };
+    public static Rank[] All { get; } = [Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Rank(int rank) : this((Ranks)rank) { }
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index 89b1c2db..764b3e52 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -41,16 +41,16 @@ public struct Score : IEquatable<Score>
     private Score(Vector2 value) => _data = value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private Score(int value) => _data = new Vector2(value, 0);
+    private Score(int value) => _data = new(value, 0);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Score(float mg, float eg) => _data = new Vector2(mg, eg);
+    public Score(float mg, float eg) => _data = new(mg, eg);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Score(int mg, int eg) => _data = new Vector2(mg, eg);
+    public Score(int mg, int eg) => _data = new(mg, eg);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Score(Value mg, Value eg) => _data = new Vector2((int)mg.Raw, (int)eg.Raw);
+    public Score(Value mg, Value eg) => _data = new((int)mg.Raw, (int)eg.Raw);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Score(Score s) => _data = s._data;
@@ -82,7 +82,7 @@ public struct Score : IEquatable<Score>
     public static Score operator -(Score s1, Score s2) => new(Vector2.Subtract(s1._data, s2._data));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Score operator +(Score s, int v) => new(Vector2.Add(s._data, new Vector2(v, v)));
+    public static Score operator +(Score s, int v) => new(Vector2.Add(s._data, new(v, v)));
 
     public static readonly Score Zero = new();
 
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index 5b33fbd8..e4839bb0 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -188,8 +188,8 @@ public void Deconstruct(out Rank r, out File f)
 
     public const int Count = 64;
 
-    public static Square[] All => new[]
-    {
+    public static Square[] All =>
+    [
         A1, A2, A3, A4, A5, A6, A7, A8,
         B1, B2, B3, B4, B5, B6, B7, B8,
         C1, C2, C3, C4, C5, C6, C7, C8,
@@ -197,8 +197,8 @@ public void Deconstruct(out Rank r, out File f)
         E1, E2, E3, E4, E5, E6, E7, E8,
         F1, F2, F3, F4, F5, F6, F7, F8,
         G1, G2, G3, G4, G5, G6, G7, G8,
-        H1, H2, H3, H4, H5, H6, H7, H8,
-    };
+        H1, H2, H3, H4, H5, H6, H7, H8
+    ];
 
     public static readonly Range WhiteSide = new(0, 32);
 
diff --git a/src/Rudzoft.ChessLib/Types/StateStack.cs b/src/Rudzoft.ChessLib/Types/StateStack.cs
index 9621daa7..5d94e110 100644
--- a/src/Rudzoft.ChessLib/Types/StateStack.cs
+++ b/src/Rudzoft.ChessLib/Types/StateStack.cs
@@ -42,7 +42,7 @@ public StateStack(int size)
         Count = 0;
         _stack = new State[size];
         for (var i = 0; i < _stack.Length; i++)
-            Push(new State());
+            Push(new());
     }
 
     /// <summary>
diff --git a/src/Rudzoft.ChessLib/Types/Value.cs b/src/Rudzoft.ChessLib/Types/Value.cs
index 2a7929e8..22128f98 100644
--- a/src/Rudzoft.ChessLib/Types/Value.cs
+++ b/src/Rudzoft.ChessLib/Types/Value.cs
@@ -101,7 +101,7 @@ public Value(int value) : this((DefaultPieceValues)value) { }
 
     public static bool operator false(Value value) => value.Raw <= 0;
 
-    public Value ForColor(Player p) => p.IsWhite ? this : new Value(-(int)Raw);
+    public Value ForColor(Player p) => p.IsWhite ? this : new(-(int)Raw);
 
     public bool Equals(Value other) => Raw == other.Raw;
 
diff --git a/src/Rudzoft.ChessLib/Types/Values.cs b/src/Rudzoft.ChessLib/Types/Values.cs
index 73a89203..cd75bef3 100644
--- a/src/Rudzoft.ChessLib/Types/Values.cs
+++ b/src/Rudzoft.ChessLib/Types/Values.cs
@@ -80,8 +80,10 @@ public Values()
         for (var index = 0; index < _defaults.Length; index++)
             _defaults[index] = new DefaultPieceValues[6];
 
-        _defaults[0] = new[] { DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueMg, DefaultPieceValues.KnightValueMg, DefaultPieceValues.BishopValueMg, DefaultPieceValues.RookValueMg, DefaultPieceValues.QueenValueMg };
-        _defaults[1] = new[] { DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueEg, DefaultPieceValues.KnightValueEg, DefaultPieceValues.BishopValueEg, DefaultPieceValues.RookValueEg, DefaultPieceValues.QueenValueEg };
+        _defaults[0] = [DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueMg, DefaultPieceValues.KnightValueMg, DefaultPieceValues.BishopValueMg, DefaultPieceValues.RookValueMg, DefaultPieceValues.QueenValueMg
+        ];
+        _defaults[1] = [DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueEg, DefaultPieceValues.KnightValueEg, DefaultPieceValues.BishopValueEg, DefaultPieceValues.RookValueEg, DefaultPieceValues.QueenValueEg
+        ];
 
         SetDefaults();
     }
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index e4089e9b..d3219162 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -106,7 +106,7 @@ private static IEnumerable<string> ValidateBasic(IPosition pos)
             yield return $"{nameof(pos.EnPassantSquare)} square is not on relative rank 6";
     }
 
-    private IEnumerable<string> ValidateCastleling(IPosition pos)
+    private static IEnumerable<string> ValidateCastleling(IPosition pos)
     {
         var crs = new[] { CastleRight.None, CastleRight.None };
 
diff --git a/src/Rudzoft.Perft/Parsers/EpdParser.cs b/src/Rudzoft.Perft/Parsers/EpdParser.cs
index 1bb3ec8a..60ce547a 100644
--- a/src/Rudzoft.Perft/Parsers/EpdParser.cs
+++ b/src/Rudzoft.Perft/Parsers/EpdParser.cs
@@ -46,7 +46,7 @@ public async IAsyncEnumerable<IEpdSet> Parse(string epdFile)
         var idSet = false;
         var epdSet = false;
         var perftData = new List<string>(16);
-        while (await sr.ReadLineAsync().ConfigureAwait(false) is { } s)
+        while (await sr.ReadLineAsync().ConfigureAwait(ConfigureAwaitOptions.None) is { } s)
         {
             // skip comments
             if (s.Length < 4 || s[0] == '#')
diff --git a/src/Rudzoft.Perft/Services/PerftRunner.cs b/src/Rudzoft.Perft/Services/PerftRunner.cs
index ae6dd517..2230882c 100644
--- a/src/Rudzoft.Perft/Services/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Services/PerftRunner.cs
@@ -74,13 +74,13 @@ public PerftRunner(
         ObjectPool<PerftResult> resultPool,
         IUci uci)
     {
-        _epdParser = parser;
-        _perft = perft;
+        _epdParser                =   parser;
+        _perft                    =   perft;
         _perft.BoardPrintCallback ??= s => Log.Information("Board:\n{Board}", s);
-        _transpositionTable = transpositionTable;
-        _resultPool = resultPool;
-        _uci = uci;
-        _runners = new Func<CancellationToken, IAsyncEnumerable<PerftPosition>>[] { ParseEpd, ParseFen };
+        _transpositionTable       =   transpositionTable;
+        _resultPool               =   resultPool;
+        _uci                      =   uci;
+        _runners                  =   [ParseEpd, ParseFen];
 
         configuration.Bind("TranspositionTable", TranspositionTableOptions);
 
@@ -102,12 +102,12 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
         if (TranspositionTableOptions is TTOptions { Use: true } ttOptions)
             _transpositionTable.SetSize(ttOptions.Size);
 
-        var errors = 0;
+        var errors      = 0;
         var runnerIndex = (Options is FenOptions).AsByte();
         _usingEpd = runnerIndex == 0;
         var positions = _runners[runnerIndex].Invoke(cancellationToken);
 
-        _perft.Positions = new();
+        _perft.Positions = [];
 
         GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;
 
@@ -124,7 +124,7 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
             }
             catch (AggregateException e)
             {
-                Log.Error(e.GetBaseException(), "Cancel requested.");
+                Log.Error(e.GetBaseException(), "Cancel requested");
                 Interlocked.Increment(ref errors);
                 break;
             }
@@ -144,19 +144,24 @@ private static void InternalRunArgumentCheck(IPerftOptions options)
     private IAsyncEnumerable<PerftPosition> ParseEpd(CancellationToken cancellationToken)
     {
         var options = Options as EpdOptions;
-        return ParseEpd(options);
+        return ParseEpd(options, cancellationToken);
     }
 
-    private async IAsyncEnumerable<PerftPosition> ParseEpd(EpdOptions options)
+    private async IAsyncEnumerable<PerftPosition> ParseEpd(
+        EpdOptions options,
+        [EnumeratorCancellation] CancellationToken cancellationToken)
     {
         foreach (var epd in options.Epds)
         {
             var start = Stopwatch.GetTimestamp();
 
             var parsedCount = 0L;
-            
-            await foreach(var epdPos in _epdParser.Parse(epd))
+
+            await foreach (var epdPos in _epdParser.Parse(epd).WithCancellation(cancellationToken))
             {
+                if (cancellationToken.IsCancellationRequested)
+                    yield break;
+
                 parsedCount++;
 
                 var perftPosition = PerftPositionFactory.Create(epdPos.Id, epdPos.Epd, epdPos.Perft);
@@ -172,11 +177,13 @@ private async IAsyncEnumerable<PerftPosition> ParseEpd(EpdOptions options)
     private IAsyncEnumerable<PerftPosition> ParseFen(CancellationToken cancellationToken)
     {
         var options = Options as FenOptions;
-        return ParseFen(options);
+        return ParseFen(options, cancellationToken);
     }
 
 #pragma warning disable 1998
-    private static async IAsyncEnumerable<PerftPosition> ParseFen(FenOptions options)
+    private static async IAsyncEnumerable<PerftPosition> ParseFen(
+        FenOptions options,
+        [EnumeratorCancellation] CancellationToken cancellationToken)
 #pragma warning restore 1998
     {
         const ulong zero = ulong.MinValue;
@@ -187,7 +194,11 @@ private static async IAsyncEnumerable<PerftPosition> ParseFen(FenOptions options
             options.Fens.Select(f => PerftPositionFactory.Create(Guid.NewGuid().ToString(), f, depths));
 
         foreach (var perftPosition in perftPositions)
+        {
+            if (cancellationToken.IsCancellationRequested)
+                yield break;
             yield return perftPosition;
+        }
     }
 
     private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken)
@@ -217,7 +228,7 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
             var start = Stopwatch.GetTimestamp();
 
             var perftResult = await _perft.DoPerftAsync(depth).ConfigureAwait(false);
-            var elapsedMs = Stopwatch.GetElapsedTime(start);
+            var elapsedMs   = Stopwatch.GetElapsedTime(start);
 
             ComputeResults(in perftResult, depth, in expected, in elapsedMs, result);
 
@@ -226,7 +237,7 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
             if (baseFileName.IsNullOrEmpty())
                 continue;
 
-            await WriteOutput(result, baseFileName, cancellationToken);
+            await WriteOutput(result, baseFileName, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None);
         }
 
         Log.Information("{Info} parsing complete. Encountered {Errors} errors", _usingEpd ? "EPD" : "FEN", errors);
@@ -236,32 +247,32 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
         return result;
     }
 
-    private static async ValueTask WriteOutput(
+    private static async Task WriteOutput(
         IPerftResult result,
         string baseFileName,
         CancellationToken cancellationToken)
     {
-        // ReSharper disable once MethodHasAsyncOverload
-        var contents = JsonSerializer.Serialize(result);
-        var outputFileName = $"{baseFileName}{result.Depth}].json";
-        await File.WriteAllTextAsync(
-                outputFileName,
-                contents,
-                cancellationToken)
-            .ConfigureAwait(false);
+        var outputFileName = Path.Combine(Environment.CurrentDirectory, $"{baseFileName}{result.Depth}].json");
+        await using var outStream = File.OpenWrite(outputFileName);
+        await JsonSerializer.SerializeAsync(outStream, result, cancellationToken: cancellationToken);
     }
 
-    private void ComputeResults(in ulong result, int depth, in ulong expected, in TimeSpan elapsedMs, IPerftResult results)
+    private void ComputeResults(
+        in ulong result,
+        int depth,
+        in ulong expected,
+        in TimeSpan elapsedMs,
+        IPerftResult results)
     {
         // compute results
         results.Result = result;
-        results.Depth = depth;
+        results.Depth  = depth;
         // add 1 to avoid potential dbz
-        results.Elapsed = elapsedMs.Add(TimeSpan.FromMicroseconds(1));
-        results.Nps = _uci.Nps(in result, results.Elapsed);
+        results.Elapsed       = elapsedMs.Add(TimeSpan.FromMicroseconds(1));
+        results.Nps           = _uci.Nps(in result, results.Elapsed);
         results.CorrectResult = expected;
-        results.Passed = expected == result;
-        results.TableHits = _transpositionTable.Hits;
+        results.Passed        = expected == result;
+        results.TableHits     = _transpositionTable.Hits;
     }
 
     private int LogResults(IPerftResult result)

From 8512d69b15891bb6048d91ca3adffc4a3988ba5b Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 11 Jan 2024 23:45:09 +0100
Subject: [PATCH 072/119] simplify move list properties

---
 src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index e411400d..df385828 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -42,17 +42,11 @@ public sealed class MoveList : IMoveList
     private readonly ValMove[] _moves = new ValMove[218];
     private int _cur;
 
-    int IReadOnlyCollection<ValMove>.Count
-    {
-        get => Length;
-    }
+    int IReadOnlyCollection<ValMove>.Count => Length;
 
     public int Length { get; private set; }
 
-    public ref ValMove CurrentMove
-    {
-        get => ref _moves[_cur];
-    }
+    public ref ValMove CurrentMove => ref _moves[_cur];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static MoveList operator ++(MoveList moveList)

From 71ccd00551b13a7e97f40f55d91c38662e53404f Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 11 Jan 2024 23:50:20 +0100
Subject: [PATCH 073/119] update notation to be more compatible with DI

---
 .../PgnMoveNotationTests/SanToMoveTests.cs    |  37 ++----
 .../NotationTests/FanTests.cs                 |  74 +++++-------
 .../NotationTests/IccfTests.cs                |  51 +++------
 .../NotationTests/RanTests.cs                 |  50 +++------
 .../NotationTests/SanTests.cs                 | 106 ++++++++----------
 .../ChessLibServiceCollectionExtensions.cs    |  77 ++++++++-----
 src/Rudzoft.ChessLib/Notation/MoveNotation.cs |  37 +++---
 .../Notation/NotationToMove.cs                |  33 +++---
 .../Notation/Notations/CoordinateNotation.cs  |  14 +--
 .../Notation/Notations/FanNotation.cs         |  22 ++--
 .../Notation/Notations/INotation.cs           |   2 +-
 .../Notation/Notations/IccfNotation.cs        |  24 ++--
 .../Notation/Notations/LanNotation.cs         |  21 ++--
 .../Notation/Notations/Notation.cs            |  47 ++++----
 .../Notation/Notations/RanNotation.cs         |  21 ++--
 .../Notation/Notations/SanNotation.cs         |  36 +++---
 .../Notation/Notations/SmithNotation.cs       |  20 ++--
 .../Notation/Notations/UciNotation.cs         |  16 ++-
 18 files changed, 315 insertions(+), 373 deletions(-)

diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
index 70af2328..2479db77 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
@@ -25,15 +25,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
-using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.PGN.Test.PgnMoveNotationTests;
 
@@ -47,21 +44,7 @@ public sealed class SanToMoveTests
     public SanToMoveTests()
     {
         _serviceProvider = new ServiceCollection()
-            .AddTransient<IBoard, Board>()
-            .AddSingleton<IValues, Values>()
-            .AddSingleton<IZobrist, Zobrist>()
-            .AddSingleton<ICuckoo, Cuckoo>()
-            .AddSingleton<IRKiss, RKiss>()
-            .AddSingleton<IPositionValidator, PositionValidator>()
-            .AddTransient<IPosition, Position>()
-            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
-            .AddSingleton<INotationToMove, NotationToMove>()
-            .AddSingleton(static serviceProvider =>
-            {
-                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
-                return provider.Create(policy);
-            })
+            .AddChessLib()
             .AddPgnParser()
             .BuildServiceProvider();
     }
@@ -73,7 +56,7 @@ public async Task BasicSanConvert()
         var fenData = new FenData(Fen.Fen.StartPositionFen);
         var state = new State();
         pos.Set(in fenData, ChessMode.Normal, in state);
-        
+
         var games = new List<PgnGame>();
         var parser = _serviceProvider.GetRequiredService<IPgnParser>();
 
@@ -89,8 +72,8 @@ public async Task BasicSanConvert()
                 sanMoves.Add(pgnMove.BlackMove);
         }
 
-        var moveNotation = MoveNotation.Create(pos);
-        var notation = moveNotation.ToNotation(MoveNotations.San);
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(MoveNotations.San);
         var converter = _serviceProvider.GetRequiredService<INotationToMove>();
 
         var chessMoves = sanMoves
@@ -99,7 +82,7 @@ public async Task BasicSanConvert()
 
         foreach (var move in chessMoves)
             pos.MakeMove(move, in state);
-        
+
         Assert.Equal(ExpectedGameCount, games.Count);
         Assert.NotEmpty(games);
         Assert.Equal(sanMoves.Count - 1, pos.Ply);
@@ -112,7 +95,7 @@ public async Task AllAtOnceConvert()
         var fenData = new FenData(Fen.Fen.StartPositionFen);
         var state = new State();
         pos.Set(in fenData, ChessMode.Normal, in state);
-        
+
         var games = new List<PgnGame>();
         var parser = _serviceProvider.GetRequiredService<IPgnParser>();
 
@@ -128,9 +111,9 @@ public async Task AllAtOnceConvert()
                 sanMoves.Add(pgnMove.BlackMove);
         }
 
-        var moveNotation = MoveNotation.Create(pos);
-        var notation = moveNotation.ToNotation(MoveNotations.San);
-        var converter = _serviceProvider.GetRequiredService<INotationToMove>();
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(MoveNotations.San);
+        var converter    = _serviceProvider.GetRequiredService<INotationToMove>();
 
         var actualMoves = converter.FromNotation(pos, sanMoves, notation);
 
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index 23e0a89b..f052988a 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -25,79 +25,62 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
-using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
 
 public sealed class FanTests
 {
-    private readonly IServiceProvider _serviceProvider;
-
-    public FanTests()
-    {
-        _serviceProvider = new ServiceCollection()
-            .AddTransient<IBoard, Board>()
-            .AddSingleton<IValues, Values>()
-            .AddSingleton<IRKiss, RKiss>()
-            .AddSingleton<IZobrist, Zobrist>()
-            .AddSingleton<ICuckoo, Cuckoo>()
-            .AddSingleton<IPositionValidator, PositionValidator>()
-            .AddTransient<IPosition, Position>()
-            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
-            .AddSingleton(static serviceProvider =>
-            {
-                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
-                return provider.Create(policy);
-            })
-            .BuildServiceProvider();
-    }
+    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
+                                                         .AddChessLib()
+                                                         .BuildServiceProvider();
 
     [Theory]
     [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.Fan, PieceTypes.Knight, Squares.d2, Squares.f2,
         Squares.e4)]
-    public void FanRankAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
-        Squares fromSqTwo, Squares toSq)
+    public void FanRankAmbiguities(
+        string fen,
+        MoveNotations moveNotation,
+        PieceTypes movingPt,
+        Squares fromSqOne,
+        Squares fromSqTwo,
+        Squares toSq)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
-        
+
         var pc = movingPt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
-        var to = new Square(toSq);
+        var to      = new Square(toSq);
 
         Assert.True(fromOne.IsOk);
         Assert.True(fromTwo.IsOk);
         Assert.True(to.IsOk);
 
         var pieceChar = pc.GetUnicodeChar();
-        var toString = to.ToString();
+        var toString  = to.ToString();
 
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var notation = MoveNotation.Create(pos);
+        var notation = _serviceProvider.GetRequiredService<IMoveNotation>();
 
         var expectedOne = $"{pieceChar}{fromOne.FileChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.FileChar}{toString}";
 
-        var actualOne = notation.ToNotation(moveNotation).Convert(moveOne);
-        var actualTwo = notation.ToNotation(moveNotation).Convert(moveTwo);
+        var actualOne = notation.ToNotation(moveNotation).Convert(pos, moveOne);
+        var actualTwo = notation.ToNotation(moveNotation).Convert(pos, moveTwo);
 
         Assert.Equal(expectedOne, actualOne);
         Assert.Equal(expectedTwo, actualTwo);
@@ -106,39 +89,44 @@ public void FanRankAmbiguities(string fen, MoveNotations moveNotation, PieceType
     [Theory]
     [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.Fan, PieceTypes.Knight, Squares.d2, Squares.d4,
         Squares.f3)]
-    public void FanFileAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
-        Squares fromSqTwo, Squares toSq)
+    public void FanFileAmbiguities(
+        string fen,
+        MoveNotations moveNotation,
+        PieceTypes movingPt,
+        Squares fromSqOne,
+        Squares fromSqTwo,
+        Squares toSq)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
-        
+
         var pc = movingPt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
-        var to = new Square(toSq);
+        var to      = new Square(toSq);
 
         Assert.True(fromOne.IsOk);
         Assert.True(fromTwo.IsOk);
         Assert.True(to.IsOk);
 
         var pieceChar = pc.GetUnicodeChar();
-        var toString = to.ToString();
+        var toString  = to.ToString();
 
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var notation = MoveNotation.Create(pos);
+        var notation = _serviceProvider.GetRequiredService<IMoveNotation>();
 
         var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
 
-        var actualOne = notation.ToNotation(moveNotation).Convert(moveOne);
-        var actualTwo = notation.ToNotation(moveNotation).Convert(moveTwo);
+        var actualOne = notation.ToNotation(moveNotation).Convert(pos, moveOne);
+        var actualTwo = notation.ToNotation(moveNotation).Convert(pos, moveTwo);
 
         Assert.Equal(expectedOne, actualOne);
         Assert.Equal(expectedTwo, actualTwo);
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index 5fec6213..73531d2a 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -25,61 +25,41 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
-using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
 
 public sealed class IccfTests
 {
-    private readonly IServiceProvider _serviceProvider;
-
-    public IccfTests()
-    {
-        _serviceProvider = new ServiceCollection()
-            .AddTransient<IBoard, Board>()
-            .AddSingleton<IValues, Values>()
-            .AddSingleton<IRKiss, RKiss>()
-            .AddSingleton<IZobrist, Zobrist>()
-            .AddSingleton<ICuckoo, Cuckoo>()
-            .AddSingleton<IPositionValidator, PositionValidator>()
-            .AddTransient<IPosition, Position>()
-            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
-            .AddSingleton(static serviceProvider =>
-            {
-                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
-                return provider.Create(policy);
-            })
-            .BuildServiceProvider();
-    }
+    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
+                                                         .AddChessLib()
+                                                         .BuildServiceProvider();
 
     [Fact]
     public void IccfRegularMove()
     {
-        const string fen = "8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1";
-        const MoveNotations notation = MoveNotations.ICCF;
-        const string expectedPrimary = "4263";
+        const string        fen             = "8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1";
+        const MoveNotations moveNotations   = MoveNotations.ICCF;
+        const string        expectedPrimary = "4263";
 
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
         var w1 = Move.Create(Squares.d2, Squares.f3);
 
-        var moveNotation = MoveNotation.Create(pos);
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
 
-        var actualPrimary = moveNotation.ToNotation(notation).Convert(w1);
+        var actualPrimary = notation.Convert(pos, w1);
 
         Assert.Equal(expectedPrimary, actualPrimary);
     }
@@ -91,20 +71,21 @@ public void IccfRegularMove()
     [InlineData("8/1P4k1/8/8/8/8/1K6/8 w - - 0 1", PieceTypes.Knight, "27284")]
     public void IccfPromotionMove(string fen, PieceTypes promoPt, string expected)
     {
-        const MoveNotations notation = MoveNotations.ICCF;
+        const MoveNotations moveNotations = MoveNotations.ICCF;
 
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
         var w1 = Move.Create(Squares.b7, Squares.b8, MoveTypes.Promotion, promoPt);
 
-        var moveNotation = MoveNotation.Create(pos);
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
 
-        var actual = moveNotation.ToNotation(notation).Convert(w1);
+        var actual = notation.Convert(pos, w1);
 
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index 1d449514..007e22e7 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -25,42 +25,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
-using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
 
 public sealed class RanTests
 {
-    private readonly IServiceProvider _serviceProvider;
-
-    public RanTests()
-    {
-        _serviceProvider = new ServiceCollection()
-            .AddTransient<IBoard, Board>()
-            .AddSingleton<IValues, Values>()
-            .AddSingleton<IRKiss, RKiss>()
-            .AddSingleton<IZobrist, Zobrist>()
-            .AddSingleton<ICuckoo, Cuckoo>()
-            .AddSingleton<IPositionValidator, PositionValidator>()
-            .AddTransient<IPosition, Position>()
-            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
-            .AddSingleton(static serviceProvider =>
-            {
-                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
-                return provider.Create(policy);
-            })
-            .BuildServiceProvider();
-    }
+    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
+                                                         .AddChessLib()
+                                                         .BuildServiceProvider();
 
     [Theory]
     [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.Ran, PieceTypes.Knight, Squares.d2, Squares.d4,
@@ -71,13 +49,18 @@ public RanTests()
         Squares.f3)]
     [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.Lan, PieceTypes.Knight, Squares.d2, Squares.f2,
         Squares.e4)]
-    public void RanLanAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
-        Squares fromSqTwo, Squares toSq)
+    public void RanLanAmbiguities(
+        string fen,
+        MoveNotations moveNotations,
+        PieceTypes movingPt,
+        Squares fromSqOne,
+        Squares fromSqTwo,
+        Squares toSq)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
@@ -85,25 +68,26 @@ public void RanLanAmbiguities(string fen, MoveNotations moveNotation, PieceTypes
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
-        var to = new Square(toSq);
+        var to      = new Square(toSq);
 
         Assert.True(fromOne.IsOk);
         Assert.True(fromTwo.IsOk);
         Assert.True(to.IsOk);
 
         var pieceChar = pc.GetPieceChar();
-        var toString = to.ToString();
+        var toString  = to.ToString();
 
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var notation = MoveNotation.Create(pos);
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
 
         var expectedOne = $"{pieceChar}{fromOne}-{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo}-{toString}";
 
-        var actualOne = notation.ToNotation(moveNotation).Convert(moveOne);
-        var actualTwo = notation.ToNotation(moveNotation).Convert(moveTwo);
+        var actualOne = notation.Convert(pos, moveOne);
+        var actualTwo = notation.Convert(pos, moveTwo);
 
         Assert.Equal(expectedOne, actualOne);
         Assert.Equal(expectedTwo, actualTwo);
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index f921f1dd..ea67a41f 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -25,54 +25,33 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
-using Rudzoft.ChessLib.Validation;
 
 namespace Rudzoft.ChessLib.Test.NotationTests;
 
 public sealed class SanTests
 {
-    private readonly IServiceProvider _serviceProvider;
-
-    public SanTests()
-    {
-        _serviceProvider = new ServiceCollection()
-            .AddTransient<IBoard, Board>()
-            .AddSingleton<IValues, Values>()
-            .AddSingleton<IRKiss, RKiss>()
-            .AddSingleton<IZobrist, Zobrist>()
-            .AddSingleton<ICuckoo, Cuckoo>()
-            .AddSingleton<IPositionValidator, PositionValidator>()
-            .AddTransient<IPosition, Position>()
-            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
-            .AddSingleton(static serviceProvider =>
-            {
-                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
-                return provider.Create(policy);
-            })
-            .BuildServiceProvider();
-    }
+    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
+                                                         .AddChessLib()
+                                                         .BuildServiceProvider();
 
     [Theory]
     [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.San, PieceTypes.Knight, Squares.d2, Squares.f2,
         Squares.e4)]
-    public void SanRankAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
+    public void SanRankAmbiguities(
+        string fen, MoveNotations moveNotations, PieceTypes movingPt, Squares fromSqOne,
         Squares fromSqTwo, Squares toSq)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
@@ -80,25 +59,26 @@ public void SanRankAmbiguities(string fen, MoveNotations moveNotation, PieceType
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
-        var to = new Square(toSq);
+        var to      = new Square(toSq);
 
         Assert.True(fromOne.IsOk);
         Assert.True(fromTwo.IsOk);
         Assert.True(to.IsOk);
 
         var pieceChar = pc.GetPieceChar();
-        var toString = to.ToString();
+        var toString  = to.ToString();
 
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var ambiguity = MoveNotation.Create(pos);
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
 
         var expectedOne = $"{pieceChar}{fromOne.FileChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.FileChar}{toString}";
 
-        var actualOne = ambiguity.ToNotation(moveNotation).Convert(moveOne);
-        var actualTwo = ambiguity.ToNotation(moveNotation).Convert(moveTwo);
+        var actualOne = notation.Convert(pos, moveOne);
+        var actualTwo = notation.Convert(pos, moveTwo);
 
         Assert.Equal(expectedOne, actualOne);
         Assert.Equal(expectedTwo, actualTwo);
@@ -107,13 +87,14 @@ public void SanRankAmbiguities(string fen, MoveNotations moveNotation, PieceType
     [Theory]
     [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.San, PieceTypes.Knight, Squares.d2, Squares.d4,
         Squares.f3)]
-    public void SanFileAmbiguities(string fen, MoveNotations moveNotation, PieceTypes movingPt, Squares fromSqOne,
+    public void SanFileAmbiguities(
+        string fen, MoveNotations moveNotations, PieceTypes movingPt, Squares fromSqOne,
         Squares fromSqTwo, Squares toSq)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
@@ -121,25 +102,26 @@ public void SanFileAmbiguities(string fen, MoveNotations moveNotation, PieceType
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
-        var to = new Square(toSq);
+        var to      = new Square(toSq);
 
         Assert.True(fromOne.IsOk);
         Assert.True(fromTwo.IsOk);
         Assert.True(to.IsOk);
 
         var pieceChar = pc.GetPieceChar();
-        var toString = to.ToString();
+        var toString  = to.ToString();
 
         var moveOne = Move.Create(fromOne, to);
         var moveTwo = Move.Create(fromTwo, to);
 
-        var notation = MoveNotation.Create(pos);
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
 
         var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
         var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
 
-        var actualOne = notation.ToNotation(moveNotation).Convert(moveOne);
-        var actualTwo = notation.ToNotation(moveNotation).Convert(moveTwo);
+        var actualOne = notation.Convert(pos, moveOne);
+        var actualTwo = notation.Convert(pos, moveTwo);
 
         Assert.Equal(expectedOne, actualOne);
         Assert.Equal(expectedTwo, actualTwo);
@@ -150,33 +132,34 @@ public void RookSanAmbiguity()
     {
         // Tests rook ambiguity notation for white rooks @ e1 and g2. Original author : johnathandavis
 
-        const string fen = "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53";
-        const MoveNotations notation = MoveNotations.San;
-        var expectedNotations = new[] { "Ree2", "Rge2" };
+        const string        fen               = "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53";
+        const MoveNotations moveNotations     = MoveNotations.San;
+        var                 expectedNotations = new[] { "Ree2", "Rge2" };
 
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var sideToMove = pos.SideToMove;
+        var sideToMove  = pos.SideToMove;
         var targetPiece = PieceTypes.Rook.MakePiece(sideToMove);
 
-        var moveNotation = MoveNotation.Create(pos);
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
 
         var sanMoves = pos
-            .GenerateMoves()
-            .Select(static em => em.Move)
-            .Where(m => pos.GetPiece(m.FromSquare()) == targetPiece)
-            .Select(m => moveNotation.ToNotation(notation).Convert(m))
-            .ToArray();
+                       .GenerateMoves()
+                       .Select(static em => em.Move)
+                       .Where(m => pos.GetPiece(m.FromSquare()) == targetPiece)
+                       .Select(m => notation.Convert(pos, m))
+                       .ToArray();
 
         foreach (var notationResult in expectedNotations)
             Assert.Contains(sanMoves, s => s == notationResult);
     }
-    
+
     [Theory]
     [InlineData("2rr2k1/p3ppbp/b1n3p1/2p1P3/5P2/2N3P1/PP2N1BP/3R1RK1 w - - 2 18", "Rxd8+")]
     public void SanCaptureWithCheck(string fen, string expected)
@@ -186,38 +169,41 @@ public void SanCaptureWithCheck(string fen, string expected)
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var notation = MoveNotation.Create(pos);
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(MoveNotations.San);
 
-        var move = Move.Create(Squares.d1, Squares.d8, MoveTypes.Normal);
-        var sanMove = notation.ToNotation(MoveNotations.San).Convert(move);
+        var move    = Move.Create(Squares.d1, Squares.d8, MoveTypes.Normal);
+        var sanMove = notation.Convert(pos, move);
 
         // Capturing a piece with check
         Assert.Equal(sanMove, expected);
     }
-    
+
     [Theory]
     [InlineData("2rR2k1/p3ppbp/b1n3p1/2p1P3/5P2/2N3P1/PP2N1BP/5RK1 b - - 0 36", "Nxd8", "Rxd8", "Bf8")]
     public void SanRecaptureNotCheckmate(string fen, params string[] expected)
     {
         // author: skotz
-        
+
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
-        var state = new State();
+        var state   = new State();
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var notation = MoveNotation.Create(pos);
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(MoveNotations.San);
+
         var allMoves = pos.GenerateMoves().Get();
 
         foreach (var move in allMoves)
         {
-            var sanMove = notation.ToNotation(MoveNotations.San).Convert(move);
+            var sanMove = notation.Convert(pos, move);
 
             // Recapturing a piece to remove the check
             Assert.Contains(sanMove, expected);
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index ac19801f..15f6b6d6 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -33,6 +33,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.Notation;
+using Rudzoft.ChessLib.Notation.Notations;
 using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
@@ -60,55 +62,57 @@ public static IServiceCollection AddChessLib(
         serviceCollection.AddOptions<TranspositionTableConfiguration>().Configure<IConfiguration>(
             static (settings, configuration)
                 => configuration
-                    .GetSection(TranspositionTableConfiguration.Section)
-                    .Bind(settings));
+                   .GetSection(TranspositionTableConfiguration.Section)
+                   .Bind(settings));
 
         // TODO : Add configuration "manually" to avoid IL2026 warning
         serviceCollection.AddOptions<PolyglotBookConfiguration>().Configure<IConfiguration>(
             static (settings, configuration)
                 => configuration
-                    .GetSection(PolyglotBookConfiguration.Section)
-                    .Bind(settings));
+                   .GetSection(PolyglotBookConfiguration.Section)
+                   .Bind(settings));
 
         serviceCollection.TryAddSingleton<ITranspositionTable, TranspositionTable>();
         serviceCollection.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
         serviceCollection.TryAddSingleton(static serviceProvider =>
         {
             var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-            var policy = new MoveListPolicy();
+            var policy   = new MoveListPolicy();
             return provider.Create(policy);
         });
 
-        return serviceCollection.AddSingleton(static sp =>
-            {
-                var pool = sp.GetRequiredService<ObjectPool<IMoveList>>();
-                IUci uci = new Uci(pool);
-                uci.Initialize();
-                return uci;
-            })
-            .AddSingleton<ICuckoo, Cuckoo>()
-            .AddSingleton<IRKiss, RKiss>()
-            .AddSingleton<IZobrist, Zobrist>()
-            .AddTransient<IKillerMovesFactory, KillerMovesFactory>()
-            .AddSingleton<ISearchParameters, SearchParameters>()
-            .AddSingleton<IValues, Values>()
-            .AddSingleton<IKpkBitBase, KpkBitBase>()
-            .AddTransient<IBoard, Board>()
-            .AddSingleton<IPositionValidator, PositionValidator>()
-            .AddTransient<IPosition, Position>()
-            .AddTransient<IGame, Game>()
-            .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>()
-            .AddSingleton<ICpu, Cpu>();
+        return serviceCollection
+               .AddSingleton(static sp =>
+               {
+                   var  pool = sp.GetRequiredService<ObjectPool<IMoveList>>();
+                   IUci uci  = new Uci(pool);
+                   uci.Initialize();
+                   return uci;
+               })
+               .AddSingleton<ICuckoo, Cuckoo>()
+               .AddSingleton<IRKiss, RKiss>()
+               .AddSingleton<IZobrist, Zobrist>()
+               .AddTransient<IKillerMovesFactory, KillerMovesFactory>()
+               .AddSingleton<ISearchParameters, SearchParameters>()
+               .AddSingleton<IValues, Values>()
+               .AddSingleton<IKpkBitBase, KpkBitBase>()
+               .AddTransient<IBoard, Board>()
+               .AddSingleton<IPositionValidator, PositionValidator>()
+               .AddTransient<IPosition, Position>()
+               .AddTransient<IGame, Game>()
+               .AddSingleton<IPolyglotBookFactory, PolyglotBookFactory>()
+               .AddSingleton<ICpu, Cpu>()
+               .AddNotationServices();
     }
 
     private static IConfigurationRoot LoadConfiguration(string file)
     {
-        var configurationFile = string.IsNullOrWhiteSpace(file) ? "chesslib.json" : file;
+        var configurationFile     = string.IsNullOrWhiteSpace(file) ? "chesslib.json" : file;
         var configurationFilePath = Path.Combine(AppContext.BaseDirectory, configurationFile);
         return new ConfigurationBuilder()
-            .AddJsonFile(configurationFilePath, optional: true, reloadOnChange: true)
-            .AddEnvironmentVariables()
-            .Build();
+               .AddJsonFile(configurationFilePath, optional: true, reloadOnChange: true)
+               .AddEnvironmentVariables()
+               .Build();
     }
 
     public static void AddFactory<TService, TImplementation>(this IServiceCollection services)
@@ -119,4 +123,19 @@ public static void AddFactory<TService, TImplementation>(this IServiceCollection
         services.AddSingleton<Func<TService>>(static x => () => x.GetService<TService>()!);
         services.AddSingleton<IServiceFactory<TService>, ServiceFactory<TService>>();
     }
+
+    private static IServiceCollection AddNotationServices(this IServiceCollection services)
+    {
+        return services
+               .AddSingleton<INotationToMove, NotationToMove>()
+               .AddSingleton<IMoveNotation, MoveNotation>()
+               .AddKeyedSingleton<INotation, CoordinateNotation>(MoveNotations.Coordinate)
+               .AddKeyedSingleton<INotation, FanNotation>(MoveNotations.Fan)
+               .AddKeyedSingleton<INotation, IccfNotation>(MoveNotations.ICCF)
+               .AddKeyedSingleton<INotation, LanNotation>(MoveNotations.Lan)
+               .AddKeyedSingleton<INotation, RanNotation>(MoveNotations.Ran)
+               .AddKeyedSingleton<INotation, SanNotation>(MoveNotations.San)
+               .AddKeyedSingleton<INotation, SmithNotation>(MoveNotations.Smith)
+               .AddKeyedSingleton<INotation, UciNotation>(MoveNotations.Uci);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
index 2013ad57..44657188 100644
--- a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/MoveNotation.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.DependencyInjection;
 using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Notation.Notations;
 
@@ -34,29 +35,21 @@ namespace Rudzoft.ChessLib.Notation;
 /// Constructs string representation of a move based on specified move notation type.
 /// See https://en.wikipedia.org/wiki/Chess_notation
 /// </summary>
-public sealed class MoveNotation : IMoveNotation
+public sealed class MoveNotation(IServiceProvider sp) : IMoveNotation
 {
-    private readonly INotation[] _notations;
-
-    private MoveNotation(IPosition pos)
-    {
-        _notations =
-        [
-            new SanNotation(pos),
-            new FanNotation(pos),
-            new LanNotation(pos),
-            new RanNotation(pos),
-            null, // Cran
-            new SmithNotation(pos),
-            null, // Descriptive
-            new CoordinateNotation(pos),
-            new IccfNotation(pos),
-            new UciNotation(pos)
-        ];
-    }
-
-    public static IMoveNotation Create(IPosition pos)
-        => new MoveNotation(pos);
+    private readonly INotation[] _notations =
+    [
+        sp.GetKeyedService<INotation>(MoveNotations.San),
+        sp.GetKeyedService<INotation>(MoveNotations.Fan),
+        sp.GetKeyedService<INotation>(MoveNotations.Lan),
+        sp.GetKeyedService<INotation>(MoveNotations.Ran),
+        null, // Cran
+        sp.GetKeyedService<INotation>(MoveNotations.Smith),
+        null, // Descriptive
+        sp.GetKeyedService<INotation>(MoveNotations.Coordinate),
+        sp.GetKeyedService<INotation>(MoveNotations.ICCF),
+        sp.GetKeyedService<INotation>(MoveNotations.Uci)
+    ];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public INotation ToNotation(MoveNotations moveNotation = MoveNotations.Fan)
diff --git a/src/Rudzoft.ChessLib/Notation/NotationToMove.cs b/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
index 99640fe8..8a70a160 100644
--- a/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
+++ b/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
@@ -31,18 +31,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation;
 
-public sealed class NotationToMove : INotationToMove
+public sealed class NotationToMove(ObjectPool<IMoveList> moveListPool) : INotationToMove
 {
-    private readonly ObjectPool<IMoveList> _moveListPool;
-
-    public NotationToMove(ObjectPool<IMoveList> moveListPool)
-    {
-        _moveListPool = moveListPool;
-    }
-
     public IReadOnlyList<Move> FromNotation(IPosition pos, IEnumerable<string> notationalMoves, INotation notation)
     {
-        var moveList = _moveListPool.Get();
+        var moveList = moveListPool.Get();
         var result = new List<Move>();
 
         var validNotationalMoves = notationalMoves
@@ -53,28 +46,28 @@ public IReadOnlyList<Move> FromNotation(IPosition pos, IEnumerable<string> notat
         foreach (var notationalMove in validNotationalMoves)
         {
             moveList.Generate(pos);
-            
+
             var moves = moveList.Get();
-            
+
             if (moves.IsEmpty)
                 break;
 
             foreach (var move in moves)
             {
-                var notatedMove = notation.Convert(move);
+                var notatedMove = notation.Convert(pos, move);
                 if (string.IsNullOrWhiteSpace(notatedMove))
                     continue;
 
                 if (!notationalMove.Equals(notatedMove))
                     continue;
-                
+
                 pos.MakeMove(move.Move, in state);
                 result.Add(move);
                 break;
             }
         }
 
-        _moveListPool.Return(moveList);
+        moveListPool.Return(moveList);
 
         return result;
     }
@@ -84,27 +77,27 @@ public Move FromNotation(IPosition pos, ReadOnlySpan<char> notatedMove, INotatio
         if (notatedMove.IsEmpty || notatedMove[0] == '*')
             return Move.EmptyMove;
 
-        var moveList = _moveListPool.Get();
+        var moveList = moveListPool.Get();
         moveList.Generate(in pos);
         var moves = moveList.Get();
 
         var m = Move.EmptyMove;
-        
+
         foreach (var move in moves)
         {
-            var san = notation.Convert(move);
+            var san = notation.Convert(pos, move);
 
             if (string.IsNullOrWhiteSpace(san))
                 continue;
-            
+
             if (!notatedMove.Equals(san.AsSpan(), StringComparison.InvariantCulture))
                 continue;
-            
+
             m = move;
             break;
         }
 
-        _moveListPool.Return(moveList);
+        moveListPool.Return(moveList);
 
         return m;
     }
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
index 40d73b66..f3ab28b1 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
@@ -25,25 +25,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class CoordinateNotation : Notation
+public sealed class CoordinateNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
 {
-    public CoordinateNotation(IPosition pos) : base(pos)
-    {
-    }
-
     [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string Convert(Move move)
+    public override string Convert(IPosition pos, Move move)
     {
         var (from, to) = move;
 
         Span<char> re = stackalloc char[8];
-        var i = 0;
+        var        i  = 0;
 
         re[i++] = char.ToUpper(from.FileChar);
         re[i++] = from.RankChar;
@@ -51,7 +49,7 @@ public override string Convert(Move move)
         re[i++] = char.ToUpper(to.FileChar);
         re[i++] = to.RankChar;
 
-        var captured = Pos.GetPiece(to);
+        var captured = pos.GetPiece(to);
 
         // ReSharper disable once InvertIf
         if (captured != Piece.EmptyPiece)
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
index dda0e394..4c22b978 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
@@ -25,17 +25,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class FanNotation : Notation
+public sealed class FanNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
 {
-    public FanNotation(IPosition pos) : base(pos)
-    {
-    }
-
     /// <summary>
     /// <para>Converts a move to FAN notation.</para>
     /// </summary>
@@ -43,14 +41,14 @@ public FanNotation(IPosition pos) : base(pos)
     /// <returns>FAN move string</returns>
     [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string Convert(Move move)
+    public override string Convert(IPosition pos, Move move)
     {
         var (from, to, type) = move;
 
         if (move.IsCastleMove())
             return CastleExtensions.GetCastleString(to, from);
 
-        var pc = Pos.MovedPiece(move);
+        var pc = pos.MovedPiece(move);
         var pt = pc.Type();
 
         Span<char> re = stackalloc char[6];
@@ -59,7 +57,7 @@ public override string Convert(Move move)
         if (pt != PieceTypes.Pawn)
         {
             re[i++] = pc.GetUnicodeChar();
-            foreach (var c in Disambiguation(move, from))
+            foreach (var c in Disambiguation(pos, move, from))
                 re[i++] = c;
         }
 
@@ -71,7 +69,7 @@ public override string Convert(Move move)
         }
         else
         {
-            if (Pos.GetPiece(to) != Piece.EmptyPiece)
+            if (pos.GetPiece(to) != Piece.EmptyPiece)
             {
                 if (pt == PieceTypes.Pawn)
                     re[i++] = from.FileChar;
@@ -85,11 +83,11 @@ public override string Convert(Move move)
         if (type == MoveTypes.Promotion)
         {
             re[i++] = '=';
-            re[i++] = move.PromotedPieceType().MakePiece(Pos.SideToMove).GetUnicodeChar();
+            re[i++] = move.PromotedPieceType().MakePiece(pos.SideToMove).GetUnicodeChar();
         }
 
-        if (Pos.InCheck)
-            re[i++] = GetCheckChar();
+        if (pos.InCheck)
+            re[i++] = GetCheckChar(pos);
 
         return new(re[..i]);
     }
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/INotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/INotation.cs
index 16d7c631..51254aa9 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/INotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/INotation.cs
@@ -85,5 +85,5 @@ public enum MoveNotations
 
 public interface INotation
 {
-    string Convert(Move move);
+    string Convert(IPosition pos, Move move);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index 4418fd50..0cd847f1 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -25,24 +25,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class IccfNotation : Notation
+public sealed class IccfNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
 {
-    public IccfNotation(IPosition pos) : base(pos)
-    {
-    }
-
+    /// <summary>
+    /// <para>Converts a move to ICCF notation.</para>
+    /// </summary>
+    /// <param name="pos">The current position</param>
+    /// <param name="move">The move to convert</param>
+    /// <returns>ICCF move string</returns>
     [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string Convert(Move move)
+    public override string Convert(IPosition pos, Move move)
     {
         var (from, to) = move;
 
         Span<char> re = stackalloc char[5];
-        var i = 0;
+        var        i  = 0;
 
         re[i++] = (char)('1' + from.File.AsInt());
         re[i++] = (char)('1' + from.Rank.AsInt());
@@ -55,11 +59,11 @@ public override string Convert(Move move)
             // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
             var c = move.PromotedPieceType() switch
             {
-                PieceTypes.Queen => 1,
-                PieceTypes.Rook => 2,
+                PieceTypes.Queen  => 1,
+                PieceTypes.Rook   => 2,
                 PieceTypes.Bishop => 3,
                 PieceTypes.Knight => 4,
-                var _ => throw new NotImplementedException()
+                var _             => throw new NotImplementedException()
             };
 
             re[i++] = (char)('0' + c);
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
index 67145b02..09e043a8 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
@@ -25,25 +25,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class LanNotation : Notation
+public sealed class LanNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
 {
-    public LanNotation(IPosition pos) : base(pos)
-    {
-    }
-
     /// <summary>
     /// <para>Converts a move to LAN notation.</para>
     /// </summary>
+    /// <param name="pos" >The current position</param>
     /// <param name="move">The move to convert</param>
     /// <returns>LAN move string</returns>
     [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string Convert(Move move)
+    public override string Convert(IPosition pos, Move move)
     {
         var (from, to) = move;
 
@@ -53,7 +52,7 @@ public override string Convert(Move move)
         Span<char> re = stackalloc char[6];
         var i = 0;
 
-        var pt = Pos.GetPieceType(from);
+        var pt = pos.GetPieceType(from);
 
         if (pt != PieceTypes.Pawn)
             re[i++] = pt.GetPieceChar();
@@ -69,7 +68,7 @@ public override string Convert(Move move)
         }
         else
         {
-            var capturedPiece = Pos.GetPiece(to);
+            var capturedPiece = pos.GetPiece(to);
             if (capturedPiece != Piece.EmptyPiece)
             {
                 if (pt == PieceTypes.Pawn)
@@ -87,11 +86,11 @@ public override string Convert(Move move)
         if (move.IsPromotionMove())
         {
             re[i++] = '=';
-            re[i++] = move.PromotedPieceType().MakePiece(Pos.SideToMove).GetUnicodeChar();
+            re[i++] = move.PromotedPieceType().MakePiece(pos.SideToMove).GetUnicodeChar();
         }
 
-        if (Pos.InCheck)
-            re[i++] = GetCheckChar();
+        if (pos.InCheck)
+            re[i++] = GetCheckChar(pos);
 
         return new(re[..i]);
     }
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
index 3cb47515..e0325718 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
@@ -25,37 +25,40 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public abstract class Notation : INotation
+public abstract class Notation(ObjectPool<IMoveList> moveLists) : INotation
 {
-    protected readonly IPosition Pos;
+    protected readonly ObjectPool<IMoveList> MoveLists = moveLists;
 
-    protected Notation(IPosition pos) => Pos = pos;
-
-    public abstract string Convert(Move move);
+    public abstract string Convert(IPosition pos, Move move);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    protected char GetCheckChar() =>
-        Pos.GenerateMoves().Length switch
+    protected char GetCheckChar(IPosition pos)
+    {
+        var ml = MoveLists.Get();
+        ml.Generate(pos);
+        var count = ml.Get().Length;
+        MoveLists.Return(ml);
+
+        return count switch
         {
-            0 => '+',
+            0     => '+',
             var _ => '#'
         };
+    }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    protected bool GivesCheck(Move move) => Pos.GivesCheck(move);
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private MoveAmbiguities Ambiguity(Square from, BitBoard similarTypeAttacks)
+    private static MoveAmbiguities Ambiguity(IPosition pos, Square from, BitBoard similarTypeAttacks)
     {
         var ambiguity = MoveAmbiguities.None;
-        var c = Pos.SideToMove;
-        var pinned = Pos.PinnedPieces(c);
+        var c         = pos.SideToMove;
+        var pinned    = pos.PinnedPieces(c);
 
         while (similarTypeAttacks)
         {
@@ -64,11 +67,11 @@ private MoveAmbiguities Ambiguity(Square from, BitBoard similarTypeAttacks)
             if (pinned & square)
                 continue;
 
-            if (Pos.GetPieceType(from) != Pos.GetPieceType(square))
+            if (pos.GetPieceType(from) != pos.GetPieceType(square))
                 continue;
 
             // ReSharper disable once InvertIf
-            if (Pos.Pieces(c) & square)
+            if (pos.Pieces(c) & square)
             {
                 if (square.File == from.File)
                     ambiguity |= MoveAmbiguities.File;
@@ -90,10 +93,10 @@ private MoveAmbiguities Ambiguity(Square from, BitBoard similarTypeAttacks)
     /// <param name="move">The move to check</param>
     /// <param name="from">The from square</param>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    protected IEnumerable<char> Disambiguation(Move move, Square from)
+    protected static IEnumerable<char> Disambiguation(IPosition pos, Move move, Square from)
     {
-        var similarAttacks = GetSimilarAttacks(move);
-        var ambiguity = Ambiguity(from, similarAttacks);
+        var similarAttacks = GetSimilarAttacks(pos, move);
+        var ambiguity = Ambiguity(pos, from, similarAttacks);
 
         if (!ambiguity.HasFlagFast(MoveAmbiguities.Move))
             yield break;
@@ -115,15 +118,15 @@ protected IEnumerable<char> Disambiguation(Move move, Square from)
     /// <param name="move">The move to get similar attacks from</param>
     /// <returns>Squares for all similar attacks without the moves from square</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private BitBoard GetSimilarAttacks(Move move)
+    private static BitBoard GetSimilarAttacks(IPosition pos, Move move)
     {
         var from = move.FromSquare();
-        var pt = Pos.GetPieceType(from);
+        var pt = pos.GetPieceType(from);
 
         return pt switch
         {
             PieceTypes.Pawn or PieceTypes.King => BitBoard.Empty,
-            var _ => Pos.GetAttacks(move.ToSquare(), pt, Pos.Pieces()) ^ from
+            var _ => pos.GetAttacks(move.ToSquare(), pt, pos.Pieces()) ^ from
         };
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
index 4a033c05..6abedbd8 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
@@ -25,25 +25,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class RanNotation : Notation
+public sealed class RanNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
 {
-    public RanNotation(IPosition pos) : base(pos)
-    {
-    }
-
     /// <summary>
     /// <para>Converts a move to RAN notation.</para>
     /// </summary>
+    /// <param name="pos">The current position</param>
     /// <param name="move">The move to convert</param>
     /// <returns>RAN move string</returns>
     [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string Convert(Move move)
+    public override string Convert(IPosition pos, Move move)
     {
         var (from, to) = move;
 
@@ -53,7 +52,7 @@ public override string Convert(Move move)
         Span<char> re = stackalloc char[6];
         var i = 0;
 
-        var pt = Pos.GetPieceType(from);
+        var pt = pos.GetPieceType(from);
 
         if (pt != PieceTypes.Pawn)
             re[i++] = pt.GetPieceChar();
@@ -69,7 +68,7 @@ public override string Convert(Move move)
         }
         else
         {
-            var capturedPiece = Pos.GetPiece(to);
+            var capturedPiece = pos.GetPiece(to);
             if (capturedPiece != Piece.EmptyPiece)
             {
                 if (pt == PieceTypes.Pawn)
@@ -88,11 +87,11 @@ public override string Convert(Move move)
         if (move.IsPromotionMove())
         {
             re[i++] = '=';
-            re[i++] = move.PromotedPieceType().MakePiece(Pos.SideToMove).GetUnicodeChar();
+            re[i++] = move.PromotedPieceType().MakePiece(pos.SideToMove).GetUnicodeChar();
         }
 
-        if (Pos.InCheck)
-            re[i++] = GetCheckChar();
+        if (pos.InCheck)
+            re[i++] = GetCheckChar(pos);
 
         return new(re[..i]);
     }
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
index 5f6a6c11..7dee0470 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
@@ -25,26 +25,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class SanNotation : Notation
+public sealed class SanNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
 {
-    public SanNotation(IPosition pos) : base(pos)
-    {
-    }
-
     /// <summary>
     /// <para>Converts a move to SAN notation.</para>
     /// </summary>
+    /// <param name="pos">The current position</param>
     /// <param name="move">The move to convert</param>
     /// <returns>SAN move string</returns>
     [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string Convert(Move move)
+    public override string Convert(IPosition pos, Move move)
     {
         var (from, to) = move;
 
@@ -52,14 +50,14 @@ public override string Convert(Move move)
             return CastleExtensions.GetCastleString(to, from);
 
         Span<char> re = stackalloc char[6];
-        var i = 0;
+        var        i  = 0;
 
-        var pt = Pos.GetPieceType(from);
+        var pt = pos.GetPieceType(from);
 
         if (pt != PieceTypes.Pawn)
         {
-            re[i++] = Pos.GetPiece(from).GetPgnChar();
-            foreach (var amb in Disambiguation(move, from))
+            re[i++] = pos.GetPiece(from).GetPgnChar();
+            foreach (var amb in Disambiguation(pos, move, from))
                 re[i++] = amb;
         }
 
@@ -69,7 +67,7 @@ public override string Convert(Move move)
             re[i++] = 'p';
             re[i++] = from.FileChar;
         }
-        else if (Pos.GetPiece(to) != Piece.EmptyPiece)
+        else if (pos.GetPiece(to) != Piece.EmptyPiece)
         {
             if (pt == PieceTypes.Pawn)
                 re[i++] = from.FileChar;
@@ -82,11 +80,19 @@ public override string Convert(Move move)
         if (move.IsPromotionMove())
         {
             re[i++] = '=';
-            re[i++] = move.PromotedPieceType().MakePiece(Pos.SideToMove).GetPgnChar();
-        } else if (Pos.InCheck && Pos.GenerateMoves().Get().IsEmpty)
-            re[i++] = '#';
+            re[i++] = move.PromotedPieceType().MakePiece(pos.SideToMove).GetPgnChar();
+        }
+        else if (pos.InCheck)
+        {
+            var ml = MoveLists.Get();
+            ml.Generate(pos);
+            var count = ml.Get().Length;
+            MoveLists.Return(ml);
+            if (count == 0)
+                re[i++] = '#';
+        }
 
-        if (GivesCheck(move))
+        if (pos.GivesCheck(move))
             re[i++] = '+';
 
         return new(re[..i]);
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
index 1fb0986c..aef475e8 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
@@ -25,32 +25,36 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class SmithNotation : Notation
+public sealed class SmithNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
 {
-    public SmithNotation(IPosition pos) : base(pos)
-    {
-    }
-
+    /// <summary>
+    /// <para>Converts a move to Smith notation.</para>
+    /// </summary>
+    /// <param name="pos">The current position</param>
+    /// <param name="move">The move to convert</param>
+    /// <returns>Smith move string</returns>
     [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string Convert(Move move)
+    public override string Convert(IPosition pos, Move move)
     {
         var (from, to) = move;
 
         Span<char> re = stackalloc char[5];
-        var i = 0;
+        var        i  = 0;
 
         re[i++] = from.FileChar;
         re[i++] = from.RankChar;
         re[i++] = to.FileChar;
         re[i++] = to.RankChar;
 
-        var captured = Pos.GetPiece(to);
+        var captured = pos.GetPiece(to);
 
         if (captured != Piece.EmptyPiece)
             re[i++] = captured.GetPieceChar();
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
index 479ddcf6..5dfdb669 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
@@ -25,17 +25,21 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using System.Runtime.CompilerServices;
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class UciNotation : Notation
+public sealed class UciNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
 {
-    public UciNotation(IPosition pos) : base(pos)
-    {
-    }
-
+    /// <summary>
+    /// <para>Converts a move to UCI notation.</para>
+    /// </summary>
+    /// <param name="pos">The current position</param>
+    /// <param name="move">The move to convert</param>
+    /// <returns>UCI move string</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public override string Convert(Move move)
+    public override string Convert(IPosition pos, Move move)
         => move.ToString();
 }
\ No newline at end of file

From 39b9f75eb0548cdcfd60355a4f3618b36e86920c Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 8 Feb 2024 00:07:50 +0100
Subject: [PATCH 074/119] adopted new magic bb implementation (from portfish)

---
 src/Rudzoft.ChessLib/Types/BitBoards.cs     |  39 +-
 src/Rudzoft.ChessLib/Types/MagicBB.cs       | 434 ++++++++------------
 src/Rudzoft.ChessLib/Types/PextBitBoards.cs |   6 -
 3 files changed, 191 insertions(+), 288 deletions(-)
 delete mode 100644 src/Rudzoft.ChessLib/Types/PextBitBoards.cs

diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 41adbb9f..66291080 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -206,12 +206,6 @@ static BitBoards()
         for (var i = 0; i < LineBB.Length; i++)
             LineBB[i] = new BitBoard[Square.Count];
 
-        for (var i = 0; i < SquareDistance.Length; i++)
-            SquareDistance[i] = new int[Square.Count];
-
-        for (var i = 0; i < SquareDistance.Length; i++)
-            DistanceRingBB[i] = new BitBoard[8];
-
         // ForwardRanksBB population loop idea from sf
         for (var r = Rank.Rank1; r <= Rank.Rank8; r++)
         {
@@ -233,6 +227,19 @@ static BitBoards()
             }
         }
 
+        // have to compute here before we access the BitBoards
+        for (var s1 = Squares.a1; s1 <= Squares.h8; s1++)
+        {
+            SquareDistance[s1.AsInt()] = new int[64];
+            DistanceRingBB[s1.AsInt()] = new BitBoard[8];
+            for (var s2 = Squares.a1; s2 <= Squares.h8; s2++)
+            {
+                var dist = Math.Max(distanceFile(s1, s2), distanceRank(s1, s2));
+                SquareDistance[s1.AsInt()][s2.AsInt()] = dist;
+                DistanceRingBB[s1.AsInt()][dist] |= s2;
+            }
+        }
+
         // mini local helpers
 
         Span<PieceTypes> validMagicPieces = stackalloc PieceTypes[] { PieceTypes.Bishop, PieceTypes.Rook };
@@ -246,16 +253,6 @@ static BitBoards()
 
             var file = s1.File;
 
-            var bb2 = AllSquares & ~s1;
-            // distance computation
-            while (bb2)
-            {
-                var s2 = PopLsb(ref bb2);
-                var dist = Math.Max(distanceFile(s1, s2), distanceRank(s1, s2));
-                SquareDistance[sq][s2.AsInt()] = dist;
-                DistanceRingBB[sq][dist] |= s2;
-            }
-
             InitializePseudoAttacks(s1);
 
             // Compute lines and betweens
@@ -282,6 +279,7 @@ static BitBoards()
             InitializeKingRing(s1, sq, file);
         }
 
+
         SlotFileBB =
         [
             FileEBB | FileFBB | FileGBB | FileHBB, // King
@@ -353,6 +351,15 @@ private static void InitializeKingRing(Square s1, int sq, File file)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard FileBB(this File f) => FilesBB[f.AsInt()];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static BitBoard FileBB(File first, File last)
+    {
+        var b = EmptyBitBoard;
+        for (var f = first; f <= last; ++f)
+            b |= f.BitBoardFile();
+        return b;
+    }
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard RankBB(this Rank r) => RanksBB[r.AsInt()];
 
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index 80a523d6..d75a9ef9 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -1,306 +1,208 @@
-/*
- *Copyright (C) 2007 Pradyumna Kannan.
- *
- *This code is provided 'as-is', without any expressed or implied warranty.
- *In no event will the authors be held liable for any damages arising from
- *the use of this code. Permission is granted to anyone to use this
- *code for any purpose, including commercial applications, and to alter
- *it and redistribute it freely, subject to the following restrictions:
- *
- *1. The origin of this code must not be misrepresented; you must not
- *claim that you wrote the original code. If you use this code in a
- *product, an acknowledgment in the product documentation would be
- *appreciated but is not required.
- *
- *2. Altered source versions must be plainly marked as such, and must not be
- *misrepresented as being the original code.
- *
- *3. This notice may not be removed or altered from any source distribution.
- *
- * -------------------------------------------------------------------------
- * Converted from C to C# code by Rudy Alex Kohn, 2017 - for use in MUCI.
- * The same conditions as described above is *STILL* in effect.
- *
- * Original files: magicmoves.c and magicmoves.h
- * Conditional compile paths were removed.
- * The original algorithm and data were not changed in any way.
- *
- * WHATEVER YOU DO, DO NOT ALTER ANYTHING IN HERE!
- *
- */
-
+using System.Numerics;
 using System.Runtime.CompilerServices;
+using Rudzoft.ChessLib.Hash;
 
 namespace Rudzoft.ChessLib.Types;
 
-// ReSharper disable once InconsistentNaming
 public static class MagicBB
 {
-    private const ulong One = 1UL;
+    private static readonly BitBoard[]   RMasks   = new BitBoard[64];
+    private static readonly BitBoard[]   RMagics  = new BitBoard[64];
+    private static readonly BitBoard[][] RAttacks = new BitBoard[64][];
+    private static readonly int[]        RShifts  = new int[64];
+
+    private static readonly BitBoard[]   BMasks   = new BitBoard[64];
+    private static readonly BitBoard[]   BMagics  = new BitBoard[64];
+    private static readonly BitBoard[][] BAttacks = new BitBoard[64][];
+    private static readonly int[]        BShifts  = new int[64];
+
+    private static readonly int[][] MagicBoosters =
+    [
+        [3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996],
+        [1059, 3608, 605, 3234, 3326, 38, 2029, 3043]
+    ];
+
+    private static readonly  byte[]  BitCount8Bit   = new byte[256];
+
+    static MagicBB()
+    {
+        for (ulong b = 0; b < 256; b++)
+            BitCount8Bit[b] = (byte)popcount_1s_Max15(b);
 
-    private const ulong Ff = 0xFFUL;
+        Direction[] RDeltas = [Direction.North, Direction.East, Direction.South, Direction.West];
+        Direction[] BDeltas = [Direction.NorthEast, Direction.SouthEast, Direction.SouthWest, Direction.NorthWest];
 
-    private const int MagicBishopDbLength = 512;
+        var occupancy = new BitBoard[4096];
+        var reference = new BitBoard[4096];
+        var rk = new RKiss();
 
-    private const int MagicRookDbLength = 4096;
+        init_magics(PieceTypes.Rook, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index, occupancy, reference, rk);
+        init_magics(PieceTypes.Bishop, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index, occupancy, reference, rk);
 
-    private static readonly BitBoard[][] MagicBishopDb = new BitBoard[Square.Count][];
+    }
 
-    private static readonly Memory<ulong> BishopMagics = new[]
+#if AGGR_INLINE
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+    internal static int popcount_1s_Max15(in ulong b)
     {
-        0x0002020202020200UL, 0x0002020202020000UL, 0x0004010202000000UL, 0x0004040080000000UL,
-        0x0001104000000000UL, 0x0000821040000000UL, 0x0000410410400000UL, 0x0000104104104000UL,
-        0x0000040404040400UL, 0x0000020202020200UL, 0x0000040102020000UL, 0x0000040400800000UL,
-        0x0000011040000000UL, 0x0000008210400000UL, 0x0000004104104000UL, 0x0000002082082000UL,
-        0x0004000808080800UL, 0x0002000404040400UL, 0x0001000202020200UL, 0x0000800802004000UL,
-        0x0000800400A00000UL, 0x0000200100884000UL, 0x0000400082082000UL, 0x0000200041041000UL,
-        0x0002080010101000UL, 0x0001040008080800UL, 0x0000208004010400UL, 0x0000404004010200UL,
-        0x0000840000802000UL, 0x0000404002011000UL, 0x0000808001041000UL, 0x0000404000820800UL,
-        0x0001041000202000UL, 0x0000820800101000UL, 0x0000104400080800UL, 0x0000020080080080UL,
-        0x0000404040040100UL, 0x0000808100020100UL, 0x0001010100020800UL, 0x0000808080010400UL,
-        0x0000820820004000UL, 0x0000410410002000UL, 0x0000082088001000UL, 0x0000002011000800UL,
-        0x0000080100400400UL, 0x0001010101000200UL, 0x0002020202000400UL, 0x0001010101000200UL,
-        0x0000410410400000UL, 0x0000208208200000UL, 0x0000002084100000UL, 0x0000000020880000UL,
-        0x0000001002020000UL, 0x0000040408020000UL, 0x0004040404040000UL, 0x0002020202020000UL,
-        0x0000104104104000UL, 0x0000002082082000UL, 0x0000000020841000UL, 0x0000000000208800UL,
-        0x0000000010020200UL, 0x0000000404080200UL, 0x0000040404040400UL, 0x0002020202020200UL
-    };
-
-    private static readonly Memory<ulong> BishopMask = new[]
+#if X64
+        b -= (b >> 1) & 0x5555555555555555UL;
+        b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL);
+        return (int)((b * 0x1111111111111111UL) >> 60);
+#else
+        uint w = (uint)(b >> 32), v = (uint)(b);
+        v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
+        w -= (w >> 1) & 0x55555555;
+        v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
+        w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
+        v += w; // 0-8 in 4 bits
+        v *= 0x11111111;
+        return (int)(v >> 28);
+#endif
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static BitBoard RookAttacks(this Square s, in BitBoard occ)
     {
-        0x0040201008040200UL, 0x0000402010080400UL, 0x0000004020100A00UL, 0x0000000040221400UL,
-        0x0000000002442800UL, 0x0000000204085000UL, 0x0000020408102000UL, 0x0002040810204000UL,
-        0x0020100804020000UL, 0x0040201008040000UL, 0x00004020100A0000UL, 0x0000004022140000UL,
-        0x0000000244280000UL, 0x0000020408500000UL, 0x0002040810200000UL, 0x0004081020400000UL,
-        0x0010080402000200UL, 0x0020100804000400UL, 0x004020100A000A00UL, 0x0000402214001400UL,
-        0x0000024428002800UL, 0x0002040850005000UL, 0x0004081020002000UL, 0x0008102040004000UL,
-        0x0008040200020400UL, 0x0010080400040800UL, 0x0020100A000A1000UL, 0x0040221400142200UL,
-        0x0002442800284400UL, 0x0004085000500800UL, 0x0008102000201000UL, 0x0010204000402000UL,
-        0x0004020002040800UL, 0x0008040004081000UL, 0x00100A000A102000UL, 0x0022140014224000UL,
-        0x0044280028440200UL, 0x0008500050080400UL, 0x0010200020100800UL, 0x0020400040201000UL,
-        0x0002000204081000UL, 0x0004000408102000UL, 0x000A000A10204000UL, 0x0014001422400000UL,
-        0x0028002844020000UL, 0x0050005008040200UL, 0x0020002010080400UL, 0x0040004020100800UL,
-        0x0000020408102000UL, 0x0000040810204000UL, 0x00000A1020400000UL, 0x0000142240000000UL,
-        0x0000284402000000UL, 0x0000500804020000UL, 0x0000201008040200UL, 0x0000402010080400UL,
-        0x0002040810204000UL, 0x0004081020400000UL, 0x000A102040000000UL, 0x0014224000000000UL,
-        0x0028440200000000UL, 0x0050080402000000UL, 0x0020100804020000UL, 0x0040201008040200UL
-    };
-
-    private static readonly BitBoard[][] MagicRookDb = new BitBoard[Square.Count][];
-
-    private static readonly Memory<ulong> RookMagics = new[]
+        return RAttacks[s.AsInt()][((occ & RMasks[s.AsInt()]).Value * RMagics[s.AsInt()].Value) >> RShifts[s.AsInt()]];
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static BitBoard BishopAttacks(this Square s, in BitBoard occ)
     {
-        0x0080001020400080UL, 0x0040001000200040UL, 0x0080081000200080UL, 0x0080040800100080UL,
-        0x0080020400080080UL, 0x0080010200040080UL, 0x0080008001000200UL, 0x0080002040800100UL,
-        0x0000800020400080UL, 0x0000400020005000UL, 0x0000801000200080UL, 0x0000800800100080UL,
-        0x0000800400080080UL, 0x0000800200040080UL, 0x0000800100020080UL, 0x0000800040800100UL,
-        0x0000208000400080UL, 0x0000404000201000UL, 0x0000808010002000UL, 0x0000808008001000UL,
-        0x0000808004000800UL, 0x0000808002000400UL, 0x0000010100020004UL, 0x0000020000408104UL,
-        0x0000208080004000UL, 0x0000200040005000UL, 0x0000100080200080UL, 0x0000080080100080UL,
-        0x0000040080080080UL, 0x0000020080040080UL, 0x0000010080800200UL, 0x0000800080004100UL,
-        0x0000204000800080UL, 0x0000200040401000UL, 0x0000100080802000UL, 0x0000080080801000UL,
-        0x0000040080800800UL, 0x0000020080800400UL, 0x0000020001010004UL, 0x0000800040800100UL,
-        0x0000204000808000UL, 0x0000200040008080UL, 0x0000100020008080UL, 0x0000080010008080UL,
-        0x0000040008008080UL, 0x0000020004008080UL, 0x0000010002008080UL, 0x0000004081020004UL,
-        0x0000204000800080UL, 0x0000200040008080UL, 0x0000100020008080UL, 0x0000080010008080UL,
-        0x0000040008008080UL, 0x0000020004008080UL, 0x0000800100020080UL, 0x0000800041000080UL,
-        0x0000102040800101UL, 0x0000102040008101UL, 0x0000081020004101UL, 0x0000040810002101UL,
-        0x0001000204080011UL, 0x0001000204000801UL, 0x0001000082000401UL, 0x0000002040810402UL
-    };
-
-    private static readonly Memory<ulong> RookMask = new[]
+        return BAttacks[s.AsInt()][((occ & BMasks[s.AsInt()]).Value * BMagics[s.AsInt()].Value) >> BShifts[s.AsInt()]];
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static BitBoard QueenAttacks(this Square s, in BitBoard occ)
     {
-        0x000101010101017EUL, 0x000202020202027CUL, 0x000404040404047AUL, 0x0008080808080876UL,
-        0x001010101010106EUL, 0x002020202020205EUL, 0x004040404040403EUL, 0x008080808080807EUL,
-        0x0001010101017E00UL, 0x0002020202027C00UL, 0x0004040404047A00UL, 0x0008080808087600UL,
-        0x0010101010106E00UL, 0x0020202020205E00UL, 0x0040404040403E00UL, 0x0080808080807E00UL,
-        0x00010101017E0100UL, 0x00020202027C0200UL, 0x00040404047A0400UL, 0x0008080808760800UL,
-        0x00101010106E1000UL, 0x00202020205E2000UL, 0x00404040403E4000UL, 0x00808080807E8000UL,
-        0x000101017E010100UL, 0x000202027C020200UL, 0x000404047A040400UL, 0x0008080876080800UL,
-        0x001010106E101000UL, 0x002020205E202000UL, 0x004040403E404000UL, 0x008080807E808000UL,
-        0x0001017E01010100UL, 0x0002027C02020200UL, 0x0004047A04040400UL, 0x0008087608080800UL,
-        0x0010106E10101000UL, 0x0020205E20202000UL, 0x0040403E40404000UL, 0x0080807E80808000UL,
-        0x00017E0101010100UL, 0x00027C0202020200UL, 0x00047A0404040400UL, 0x0008760808080800UL,
-        0x00106E1010101000UL, 0x00205E2020202000UL, 0x00403E4040404000UL, 0x00807E8080808000UL,
-        0x007E010101010100UL, 0x007C020202020200UL, 0x007A040404040400UL, 0x0076080808080800UL,
-        0x006E101010101000UL, 0x005E202020202000UL, 0x003E404040404000UL, 0x007E808080808000UL,
-        0x7E01010101010100UL, 0x7C02020202020200UL, 0x7A04040404040400UL, 0x7608080808080800UL,
-        0x6E10101010101000UL, 0x5E20202020202000UL, 0x3E40404040404000UL, 0x7E80808080808000UL
-    };
-
-    [SkipLocalsInit]
-    static MagicBB()
+        return RookAttacks(s, in occ) | BishopAttacks(s, in occ);
+    }
+
+    private static uint magic_index(PieceTypes pt, Square s, BitBoard occ)
     {
-        for (var i = 0; i < MagicBishopDb.Length; i++)
-            MagicBishopDb[i] = new BitBoard[MagicBishopDbLength];
-
-        for (var i = 0; i < MagicBishopDb.Length; ++i)
-            MagicRookDb[i] = new BitBoard[MagicRookDbLength];
-
-#pragma warning disable format // @formatter:off
-        Span<int> initMagicMovesDb = stackalloc int[] {
-            63,  0, 58,  1, 59, 47, 53,  2,
-            60, 39, 48, 27, 54, 33, 42,  3,
-            61, 51, 37, 40, 49, 18, 28, 20,
-            55, 30, 34, 11, 43, 14, 22,  4,
-            62, 57, 46, 52, 38, 26, 32, 41,
-            50, 36, 17, 19, 29, 10, 13, 21,
-            56, 45, 25, 31, 35, 16,  9, 12,
-            44, 24, 15,  8, 23,  7,  6,  5
-        };
-#pragma warning restore format // @formatter:on
-
-        Span<int> squares = stackalloc int[Square.Count];
-
-        for (var i = 0; i < squares.Length; ++i)
-        {
-            var numSquares = InitSquares(squares, BishopMask.Span[i], initMagicMovesDb);
-            for (var temp = ulong.MinValue; temp < One << numSquares; ++temp)
-            {
-                var occ = InitMagicMovesOccupancy(squares[..numSquares], in temp);
-                MagicBishopDb[i][occ * BishopMagics.Span[i] >> 55] = InitmagicmovesBmoves(i, in occ);
-            }
+        if (pt == PieceTypes.Rook)
+            return (uint)(((occ & RMasks[s.AsInt()]).Value * RMagics[s.AsInt()].Value) >> RShifts[s.AsInt()]);
 
-            numSquares = InitSquares(squares, RookMask.Span[i], initMagicMovesDb);
-            for (var temp = ulong.MinValue; temp < One << numSquares; ++temp)
-            {
-                var occ = InitMagicMovesOccupancy(squares[..numSquares], in temp);
-                MagicRookDb[i][occ * RookMagics.Span[i] >> 52] = InitmagicmovesRmoves(i, in occ);
-            }
-        }
+        return (uint)(((occ & BMasks[s.AsInt()]).Value * BMagics[s.AsInt()].Value) >> BShifts[s.AsInt()]);
     }
 
-    private static int InitSquares(Span<int> squares, ulong mask, ReadOnlySpan<int> bbInits)
+
+    // init_magics() computes all rook and bishop attacks at startup. Magic
+    // bitboards are used to look up attacks of sliding pieces. As a reference see
+    // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
+    // use the so called "fancy" approach.
+
+    private static void init_magics(
+        PieceTypes pt,
+        BitBoard[][] attacks,
+        BitBoard[] magics,
+        BitBoard[] masks,
+        int[] shifts,
+        Direction[] deltas,
+        Func<PieceTypes, Square, BitBoard, uint> index,
+        BitBoard[] occupancy,
+        BitBoard[] reference,
+        IRKiss rk)
     {
-        const ulong bitFactor = 0x07EDD5E59A4E28C2UL;
-        const int shift = 58;
-        var numSquares = 0;
-        while (mask != ulong.MinValue)
+        var rankMask = Rank.Rank1.RankBB() | Rank.Rank8.RankBB();
+        var fileMask = File.FileA.FileBB() | File.FileH.FileBB();
+
+        for (var s = Squares.a1; s <= Squares.h8; s++)
         {
-            var bit = (ulong)((long)mask & -(long)mask);
-            squares[numSquares++] = bbInits[(int)(bit * bitFactor >> shift)];
-            mask ^= bit;
-        }
+            var sq = new Square(s);
+            // Board edges are not considered in the relevant occupancies
+            var edges = (rankMask & ~sq.Rank) | (fileMask & ~sq.File);
+
+            // Given a square 's', the mask is the bitboard of sliding attacks from
+            // 's' computed on an empty board. The index must be big enough to contain
+            // all the attacks for each possible subset of the mask and so is 2 power
+            // the number of 1s of the mask. Hence we deduce the size of the shift to
+            // apply to the 64 or 32 bits word to get the index.
+            masks[s.AsInt()] = sliding_attack(deltas, s, 0) & ~edges;
+            shifts[s.AsInt()] = 64 - popcount_1s_Max15(masks[s.AsInt()].Value);
+
+            // Use Carry-Rippler trick to enumerate all subsets of masks[s] and
+            // store the corresponding sliding attack bitboard in reference[].
+            var b = BitBoard.Empty;
+            var size = 0;
+            do
+            {
+                occupancy[size] = b;
+                reference[size++] = sliding_attack(deltas, sq, b);
+                b = (b.Value - masks[s.AsInt()].Value) & masks[s.AsInt()];
+            } while (b.IsNotEmpty);
 
-        return numSquares;
-    }
+            // Set the offset for the table of the next square. We have individual
+            // table sizes for each square with "Fancy Magic Bitboards".
+            var booster = MagicBoosters[1][sq.Rank.AsInt()];
 
-    private static ulong InitMagicMovesOccupancy(ReadOnlySpan<int> squares, in ulong lineOccupancy)
-    {
-        var ret = ulong.MinValue;
-        for (var i = 0; i < squares.Length; ++i)
-            if ((lineOccupancy & One << i) != ulong.MinValue)
-                ret |= One << squares[i];
+            attacks[s.AsInt()] = new BitBoard[size];
 
-        return ret;
-    }
+            // Find a magic for square 's' picking up an (almost) random number
+            // until we find the one that passes the verification test.
+            int i;
+            do
+            {
+                magics[s.AsInt()] = pick_random(masks[s.AsInt()], rk, booster);
+                Array.Clear(attacks[s.AsInt()], 0, size);
+
+                // A good magic must map every possible occupancy to an index that
+                // looks up the correct sliding attack in the attacks[s] database.
+                // Note that we build up the database for square 's' as a side
+                // effect of verifying the magic.
+                for (i = 0; i < size; i++)
+                {
+                    var idx = index(pt, s, occupancy[i]);
 
-    public static BitBoard BishopAttacks(this Square square, in BitBoard occupied)
-        => MagicBishopDb[square.AsInt()][
-            (occupied.Value & BishopMask.Span[square.AsInt()]) * BishopMagics.Span[square.AsInt()] >> 55];
+                    var attack = attacks[s.AsInt()][idx];
 
-    public static BitBoard RookAttacks(this Square square, in BitBoard occupied)
-        => MagicRookDb[square.AsInt()][
-            (occupied.Value & RookMask.Span[square.AsInt()]) * RookMagics.Span[square.AsInt()] >> 52];
+                    if (attack.IsNotEmpty && attack != reference[i])
+                        break;
 
-    public static BitBoard QueenAttacks(this Square square, in BitBoard occupied)
-        => square.BishopAttacks(in occupied) | square.RookAttacks(in occupied);
+                    attacks[s.AsInt()][idx] = reference[i];
+                }
+            } while (i != size);
+        }
+    }
 
-    private static ulong InitmagicmovesRmoves(int square, in ulong occ)
+    private static BitBoard sliding_attack(IReadOnlyList<Direction> deltas, Square sq, BitBoard occupied)
     {
-        var ret = ulong.MinValue;
-        var rowBits = Ff << 8 * (square / 8);
+        var attack = BitBoard.Empty;
 
-        var bit = One << square;
-        do
+        for (var i = 0; i < 4; i++)
         {
-            bit <<= 8;
-            ret |= bit;
-        } while (bit != 0 && (bit & occ) == ulong.MinValue);
+            for (var s = sq + deltas[i]; s.IsOk && s.Distance(s - deltas[i]) == 1; s += deltas[i])
+            {
+                attack |= s;
 
-        bit = One << square;
-        do
-        {
-            bit >>= 8;
-            ret |= bit;
-        } while (bit != 0 && (bit & occ) == ulong.MinValue);
+                if (occupied.Contains(s))
+                    break;
+            }
+        }
 
-        bit = One << square;
-        do
-        {
-            bit <<= 1;
-            if ((bit & rowBits) != ulong.MinValue)
-                ret |= bit;
-            else
-                break;
-        } while ((bit & occ) == ulong.MinValue);
-
-        bit = One << square;
-        do
-        {
-            bit >>= 1;
-            if ((bit & rowBits) != ulong.MinValue)
-                ret |= bit;
-            else
-                break;
-        } while ((bit & occ) == ulong.MinValue);
-
-        return ret;
+        return attack;
     }
 
-    private static ulong InitmagicmovesBmoves(int square, in ulong occ)
+    private static BitBoard pick_random(BitBoard mask, IRKiss rk, int booster)
     {
-        var ret = ulong.MinValue;
-        var rowBits = Ff << 8 * (square / 8);
+        // Values s1 and s2 are used to rotate the candidate magic of a
+        // quantity known to be the optimal to quickly find the magics.
+        int s1 = booster        & 63;
+        var s2 = (booster >> 6) & 63;
 
-        var bit = One << square;
-        var bit2 = bit;
-        do
+        while (true)
         {
-            bit <<= 8 - 1;
-            bit2 >>= 1;
-            if ((bit2 & rowBits) != ulong.MinValue)
-                ret |= bit;
-            else
-                break;
-        } while (bit != 0 && (bit & occ) == ulong.MinValue);
-
-        bit = One << square;
-        bit2 = bit;
-        do
-        {
-            bit <<= 8 + 1;
-            bit2 <<= 1;
-            if ((bit2 & rowBits) != ulong.MinValue)
-                ret |= bit;
-            else
-                break;
-        } while (bit != 0 && (bit & occ) == ulong.MinValue);
-
-        bit = One << square;
-        bit2 = bit;
-        do
-        {
-            bit >>= 8 - 1;
-            bit2 <<= 1;
-            if ((bit2 & rowBits) != ulong.MinValue)
-                ret |= bit;
-            else
-                break;
-        } while (bit != 0 && (bit & occ) == ulong.MinValue);
-
-        bit = One << square;
-        bit2 = bit;
-        do
-        {
-            bit >>= 8 + 1;
-            bit2 >>= 1;
-            if ((bit2 & rowBits) != ulong.MinValue)
-                ret |= bit;
-            else
-                break;
-        } while (bit != 0 && (bit & occ) == ulong.MinValue);
-
-        return ret;
+            var magic = rk.Rand();
+            magic =  (magic >> s1) | (magic << (64 - s1));
+            magic &= rk.Rand();
+            magic =  (magic >> s2) | (magic << (64 - s2));
+            magic &= rk.Rand();
+
+            if (BitCount8Bit[((mask.Value * magic) >> 56)] >= 6)
+                return magic;
+        }
     }
+
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/PextBitBoards.cs b/src/Rudzoft.ChessLib/Types/PextBitBoards.cs
deleted file mode 100644
index 0e74ed65..00000000
--- a/src/Rudzoft.ChessLib/Types/PextBitBoards.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Rudzoft.ChessLib.Types;
-
-public class PextBitBoards
-{
-    
-}
\ No newline at end of file

From bc0c4dacf421702b01f0089adbe073322143e297 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 8 Feb 2024 00:09:49 +0100
Subject: [PATCH 075/119] updated fen generate function

---
 src/Rudzoft.ChessLib/Position.cs | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index cdd4f74a..168320a8 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Buffers;
 using System.Collections;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
@@ -359,9 +358,18 @@ public FenData GenerateFen()
         }
 
         fen[length++] = space;
-        length = fen.Append(State.ClockPly, length);
-        fen[length++] = space;
-        length = fen.Append(1 + (Ply - _sideToMove.IsBlack.AsByte() / 2), length);
+
+        Span<char> format = stackalloc char[1] { 'D' };
+
+        State.ClockPly.TryFormat(fen[length..], out var written, format);
+
+        length        += written;
+        fen[length++] =  space;
+
+        var ply = 1 + (Ply - _sideToMove.IsBlack.AsByte() / 2);
+        ply.TryFormat(fen[length..], out written, format);
+
+        length += written;
 
         return new(new string(fen[..length]));
     }

From b37d8c238e2f8975a453dccfdc32d54b7487e6a0 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 8 Feb 2024 00:11:15 +0100
Subject: [PATCH 076/119] minor updates + chase for weird table hash bug

---
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |  10 +-
 .../IPerft.cs                                 |   6 +-
 src/Rudzoft.ChessLib.Perft/Perft.cs           |  22 ++--
 .../Extensions/SpanExtensions.cs              |  18 ---
 src/Rudzoft.ChessLib/Game.cs                  |  97 ++++++++--------
 .../Transposition/TranspositionTable.cs       |   1 -
 src/Rudzoft.ChessLib/IGame.cs                 |   4 +-
 src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs     |   4 +-
 .../Protocol/UCI/SearchParameters.cs          |  41 +++----
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |  45 ++++++--
 .../Tables/Perft/IPerftTableEntry.cs          |   2 +-
 .../Tables/Perft/PerftTable.cs                |   8 +-
 src/Rudzoft.ChessLib/Types/MagicBB.cs         | 106 ++++++++++--------
 src/Rudzoft.ChessLib/Types/Square.cs          |   2 +-
 src/Rudzoft.Perft/Actors/PerftRunnerActor.cs  |   1 -
 src/Rudzoft.Perft/Models/IPerftResult.cs      |  20 ++--
 src/Rudzoft.Perft/Models/PerftResult.cs       |  20 ++--
 src/Rudzoft.Perft/Services/PerftRunner.cs     |   2 +-
 18 files changed, 210 insertions(+), 199 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index 61fd296f..f5304b5e 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -67,7 +67,7 @@ public void Setup()
         var tt = new TranspositionTable(options);
 
         var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
-        
+
         var uci = new Uci(moveListObjectPool);
         uci.Initialize();
         var cpu = new Cpu();
@@ -88,9 +88,9 @@ public void Setup()
     }
 
     [Benchmark]
-    public async Task<ulong> PerftIAsync()
+    public async Task<UInt128> PerftIAsync()
     {
-        var total = ulong.MinValue;
+        var total = UInt128.MinValue;
         for (var i = 0; i < N; i++)
             await foreach (var res in _perft.DoPerft(N).ConfigureAwait(false))
                 total += res;
@@ -98,9 +98,9 @@ public async Task<ulong> PerftIAsync()
     }
 
     [Benchmark]
-    public async Task<ulong> Perft()
+    public async Task<UInt128> Perft()
     {
-        var total = ulong.MinValue;
+        var total = UInt128.MinValue;
         for (var i = 0; i < N; ++i)
             total += await _perft.DoPerftAsync(N);
 
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
index d4629eb6..c216f294 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
@@ -34,13 +34,13 @@ public interface IPerft
 
     int Depth { get; set; }
 
-    ulong Expected { get; set; }
+    UInt128 Expected { get; set; }
 
     public IGame Game { get; set; }
 
-    IAsyncEnumerable<ulong> DoPerft(int depth);
+    IAsyncEnumerable<UInt128> DoPerft(int depth);
 
-    Task<ulong> DoPerftAsync(int depth);
+    Task<UInt128> DoPerftAsync(int depth);
 
     void ClearPositions();
 
diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/Rudzoft.ChessLib.Perft/Perft.cs
index d055fabd..9f68bfde 100644
--- a/src/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/Rudzoft.ChessLib.Perft/Perft.cs
@@ -50,35 +50,29 @@ Result	3	379.0 us	1.897 us	1.584 us
 Result	6	1,912,300.6 us	3,551.167 us	3,148.017 us
      */
 
-public sealed class Perft : IPerft
+public sealed class Perft(IGame game, IEnumerable<PerftPosition> positions) : IPerft
 {
-    public Perft(IGame game, IEnumerable<PerftPosition> positions)
-    {
-        Positions = positions.ToList();
-        Game = game;
-    }
-
     public Action<string>? BoardPrintCallback { get; set; }
 
     /// <summary>
     /// The positional data for the run
     /// </summary>
-    public List<PerftPosition> Positions { get; set; }
+    public List<PerftPosition> Positions { get; set; } = positions.ToList();
 
-    public IGame Game { get; set; }
-    public int Depth { get; set; }
-    public ulong Expected { get; set; }
+    public IGame   Game     { get; set; } = game;
+    public int     Depth    { get; set; }
+    public UInt128 Expected { get; set; }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
 #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
-    public async IAsyncEnumerable<ulong> DoPerft(int depth)
+    public async IAsyncEnumerable<UInt128> DoPerft(int depth)
 #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
     {
         if (Positions.Count == 0)
             yield break;
 
         var state = new State();
-        
+
         foreach (var fd in Positions.Select(static p => new FenData(p.Fen)))
         {
             Game.Pos.Set(in fd, ChessMode.Normal, in state);
@@ -87,7 +81,7 @@ public async IAsyncEnumerable<ulong> DoPerft(int depth)
         }
     }
 
-    public Task<ulong> DoPerftAsync(int depth)
+    public Task<UInt128> DoPerftAsync(int depth)
         => Task.Run(()
             => Game.Perft(depth));
 
diff --git a/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs b/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
index 8a52339b..bbc90109 100644
--- a/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
@@ -30,24 +30,6 @@ namespace Rudzoft.ChessLib.Extensions;
 
 public static class SpanExtensions
 {
-    /// <summary>
-    /// Appends an int to a span of char
-    /// </summary>
-    /// <param name="target">The target span</param>
-    /// <param name="v">The value to append</param>
-    /// <param name="targetIndex">The current index for target span</param>
-    /// <param name="capacity">Max size of "value".ToString(), defaults to 3</param>
-    /// <returns>New index after append</returns>
-    [SkipLocalsInit]
-    public static int Append(this Span<char> target, int v, int targetIndex, int capacity = 3)
-    {
-        Span<char> s = stackalloc char[capacity];
-        v.TryFormat(s, out var written);
-        for (var i = 0; i < written; ++i)
-            target[targetIndex++] = s[i];
-        return targetIndex;
-    }
-
     /// <summary>
     /// Appends an ulong to a span of char
     /// </summary>
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 7204d586..508dbe22 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -39,60 +39,47 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib;
 
-public sealed class Game : IGame
+public sealed class Game(
+    ITranspositionTable transpositionTable,
+    IUci uci,
+    ICpu cpu,
+    ISearchParameters searchParameters,
+    IPosition pos,
+    ObjectPool<IMoveList> moveListPool)
+    : IGame
 {
-    private readonly ObjectPool<IMoveList> _moveListPool;
-    private readonly IPosition             _pos;
-    private readonly PerftTable            _perftTable;
-
-    public Game(
-        ITranspositionTable transpositionTable,
-        IUci uci,
-        ICpu cpu,
-        ISearchParameters searchParameters,
-        IPosition pos,
-        ObjectPool<IMoveList> moveListPool)
-    {
-        _moveListPool = moveListPool;
-        _pos = pos;
-
-        Table            = transpositionTable;
-        SearchParameters = searchParameters;
-        Uci              = uci;
-        Cpu              = cpu;
-        _perftTable      = new();
-    }
+    private readonly PerftTable            _perftTable   = new();
 
-    public Action<IPieceSquare> PieceUpdated => _pos.PieceUpdated;
+    public Action<IPieceSquare> PieceUpdated => pos.PieceUpdated;
 
     public int MoveNumber => 1 + (Pos.Ply - Pos.SideToMove.IsBlack.AsByte() / 2);
 
     public BitBoard Occupied => Pos.Pieces();
 
-    public IPosition Pos => _pos;
+    public IPosition Pos => pos;
 
     public GameEndTypes GameEndType { get; set; }
 
-    public ITranspositionTable Table { get; }
+    public ITranspositionTable Table { get; } = transpositionTable;
 
-    public ISearchParameters SearchParameters { get; }
+    public ISearchParameters SearchParameters { get; } = searchParameters;
 
-    public IUci Uci { get; }
+    public IUci Uci { get; } = uci;
 
-    public ICpu Cpu { get; }
+    public ICpu Cpu { get; } = cpu;
 
-    public bool IsRepetition => _pos.IsRepetition;
+    public bool IsRepetition => pos.IsRepetition;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void NewGame(string fen = Fen.Fen.StartPositionFen)
     {
         var fenData = new FenData(fen);
         var state = new State();
-        _pos.Set(in fenData, ChessMode.Normal, state, true);
+        pos.Set(in fenData, ChessMode.Normal, state, true);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public FenData GetFen() => _pos.GenerateFen();
+    public FenData GetFen() => pos.GenerateFen();
 
     public void UpdateDrawTypes()
     {
@@ -102,40 +89,49 @@ public void UpdateDrawTypes()
         if (Pos.Rule50 >= 100)
             gameEndType |= GameEndTypes.FiftyMove;
 
-        var moveList = _moveListPool.Get();
-        moveList.Generate(in _pos);
+        var moveList = moveListPool.Get();
+        moveList.Generate(in pos);
 
         var moves = moveList.Get();
 
         if (moves.IsEmpty)
             gameEndType |= GameEndTypes.Pat;
 
-        _moveListPool.Return(moveList);
+        moveListPool.Return(moveList);
 
         GameEndType = gameEndType;
     }
 
     public override string ToString()
     {
-        return _pos.ToString() ?? string.Empty;
+        return pos.ToString() ?? string.Empty;
     }
 
     IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
-    public IEnumerator<Piece> GetEnumerator() => _pos.GetEnumerator();
+    public IEnumerator<Piece> GetEnumerator() => pos.GetEnumerator();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard OccupiedBySide(Player p) => _pos.Pieces(p);
+    public BitBoard OccupiedBySide(Player p) => pos.Pieces(p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Player CurrentPlayer() => _pos.SideToMove;
+    public Player CurrentPlayer() => pos.SideToMove;
 
-    public ulong Perft(int depth, bool root = true)
+    public UInt128 Perft(int depth, bool root = true)
     {
-        var tot = ulong.MinValue;
+        ref var table = ref _perftTable.TryGet(pos, depth, out var found);
+
+        if (found)
+        {
+            Console.WriteLine($"Found table: {depth} = {table.Count}");
+            return table.Count;
+        }
+
+        var tot = UInt128.MinValue;
+        var ml  = moveListPool.Get();
+        ml.Generate(in pos);
 
-        var ml = _moveListPool.Get();
-        ml.Generate(in _pos);
+        var state = new State();
 
         var moves = ml.Get();
         ref var movesSpace = ref MemoryMarshal.GetReference(moves);
@@ -149,16 +145,15 @@ public ulong Perft(int depth, bool root = true)
 
             var valMove = Unsafe.Add(ref movesSpace, i);
             var m = valMove.Move;
-            var state = new State();
 
-            _pos.MakeMove(m, in state);
+            pos.MakeMove(m, in state);
 
             if (depth <= 2)
             {
-                var ml2 = _moveListPool.Get();
-                ml2.Generate(in _pos);
+                var ml2 = moveListPool.Get();
+                ml2.Generate(in pos);
                 tot += (ulong)ml2.Length;
-                _moveListPool.Return(ml2);
+                moveListPool.Return(ml2);
             }
             else
             {
@@ -166,10 +161,14 @@ public ulong Perft(int depth, bool root = true)
                 tot += next;
             }
 
-            _pos.TakeMove(m);
+            pos.TakeMove(m);
         }
 
-        _moveListPool.Return(ml);
+        table.Key = pos.State.PositionKey;
+        table.Depth = depth;
+        table.Count = tot;
+
+        moveListPool.Return(ml);
 
         return tot;
     }
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index 81cd22d4..c9114d51 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Buffers;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Microsoft.Extensions.Options;
diff --git a/src/Rudzoft.ChessLib/IGame.cs b/src/Rudzoft.ChessLib/IGame.cs
index df9579f6..28cc3f90 100644
--- a/src/Rudzoft.ChessLib/IGame.cs
+++ b/src/Rudzoft.ChessLib/IGame.cs
@@ -46,7 +46,7 @@ public interface IGame : IEnumerable<Piece>
     bool IsRepetition { get; }
 
     public IUci Uci { get; }
-    
+
     public ICpu Cpu { get; }
 
     void NewGame(string fen = Fen.Fen.StartPositionFen);
@@ -61,5 +61,5 @@ public interface IGame : IEnumerable<Piece>
 
     Player CurrentPlayer();
 
-    ulong Perft(int depth, bool root = true);
+    UInt128 Perft(int depth, bool root = true);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
index 6d6eeabd..6c9939cc 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
@@ -43,9 +43,11 @@ public interface IUci
     void AddOption(string name, IOption option);
 
     bool TryGetOption(string name, out IOption option);
-    
+
     ulong Nps(in ulong nodes, in TimeSpan time);
 
+    ulong Nps(in UInt128 nodes, in TimeSpan time);
+
     Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove);
 
     IEnumerable<Move> MovesFromUci(IPosition pos, Stack<State> states, IEnumerable<string> moves);
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index c48d06aa..632635a1 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -157,16 +157,6 @@ public override string ToString()
 
     public void AddSearchMove(Move move) => SearchMoves.Add(move);
 
-    [SkipLocalsInit]
-    private static int ParseValue(int index, in ulong value, Span<char> target)
-    {
-        Span<char> number = stackalloc char[32];
-        value.TryFormat(number, out var numericWritten);
-        for (var i = 0; i < numericWritten; ++i)
-            target[index++] = number[i];
-        return index;
-    }
-
     public string ToString(string format, IFormatProvider formatProvider)
         => string.Format(formatProvider, format, ToString());
 
@@ -202,7 +192,9 @@ public bool TryFormat(
         destination[index++] = 'm';
         destination[index++] = 'e';
         destination[index++] = ' ';
-        index = ParseValue(index, _clock[Player.White.Side].Time, destination);
+
+        _clock[Player.White.Side].Time.TryFormat(destination[index..], out var written);
+        index += written;
 
         destination[index++] = ' ';
         destination[index++] = 'b';
@@ -211,20 +203,18 @@ public bool TryFormat(
         destination[index++] = 'm';
         destination[index++] = 'e';
         destination[index++] = ' ';
-        index = ParseValue(index, _clock[Player.Black.Side].Time, destination);
+
+        _clock[Player.Black.Side].Time.TryFormat(destination[index..], out written);
+        index += written;
 
         if (MoveTime > ulong.MinValue)
         {
-            destination[index++] = ' ';
-            destination[index++] = 'm';
-            destination[index++] = 'o';
-            destination[index++] = 'v';
-            destination[index++] = 'e';
-            destination[index++] = 't';
-            destination[index++] = 'i';
-            destination[index++] = 'm';
-            destination[index++] = 'e';
-            destination[index++] = ' ';
+            Span<char> moveTimeText = stackalloc char[]
+            {
+                ' ', 'm', 'o', 'v', 'e', 't', 'i', 'm', 'e', ' '
+            };
+            moveTimeText.TryCopyTo(destination[index..]);
+            index += moveTimeText.Length;
             index = destination.Append(in _moveTime, index);
         }
 
@@ -234,7 +224,9 @@ public bool TryFormat(
         destination[index++] = 'n';
         destination[index++] = 'c';
         destination[index++] = ' ';
-        index = ParseValue(index, _clock[Player.White.Side].Inc, destination);
+
+        _clock[Player.White.Side].Inc.TryFormat(destination[index..], out written);
+        index += written;
 
         destination[index++] = ' ';
         destination[index++] = 'b';
@@ -243,7 +235,8 @@ public bool TryFormat(
         destination[index++] = 'c';
         destination[index++] = ' ';
 
-        index = ParseValue(index, _clock[Player.Black.Side].Inc, destination);
+        _clock[Player.Black.Side].Inc.TryFormat(destination[index..], out written);
+        index += written;
 
         if (_movesToGo == ulong.MinValue)
         {
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index c8553b65..9f43cfc4 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -43,7 +43,7 @@ public class Uci : IUci
     private static readonly string[] OptionTypeStrings = Enum.GetNames(typeof(UciOptionType));
 
     protected readonly ObjectPool<IMoveList> MoveListPool;
-    
+
     private readonly ObjectPool<StringBuilder> _pvPool;
     private readonly Dictionary<string, IOption> _options;
 
@@ -80,8 +80,10 @@ public void Initialize(int maxThreads = 128)
         _options["UCI_Chess960"] = new Option("UCI_Chess960", _options.Count, false);
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void AddOption(string name, IOption option) => _options[name] = option;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool TryGetOption(string name, out IOption option)
     {
         ref var opt = ref CollectionsMarshal.GetValueRefOrNullRef(_options, name);
@@ -90,14 +92,23 @@ public bool TryGetOption(string name, out IOption option)
         return result;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public ulong Nps(in ulong nodes, in TimeSpan time)
         => (ulong)(nodes * 1000.0 / time.Milliseconds);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public ulong Nps(in UInt128 nodes, in TimeSpan time)
+    {
+        var t    = nodes * 1000;
+        var d = new UInt128(ulong.MinValue, (ulong)time.Milliseconds + 1);
+        return (ulong)(t / d);
+    }
+
     [SkipLocalsInit]
     public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
     {
         var ml = MoveListPool.Get();
-        
+
         ml.Generate(in pos);
 
         var moves = ml.Get();
@@ -114,7 +125,7 @@ public Move MoveFromUci(IPosition pos, ReadOnlySpan<char> uciMove)
             m = move.Move;
             break;
         }
-        
+
         MoveListPool.Return(ml);
 
         return m;
@@ -126,7 +137,7 @@ public IEnumerable<Move> MovesFromUci(IPosition pos, Stack<State> states, IEnume
         {
             if (move.IsNullMove())
                 continue;
-            
+
             var state = new State();
             states.Push(state);
             pos.MakeMove(move, in state);
@@ -134,21 +145,27 @@ public IEnumerable<Move> MovesFromUci(IPosition pos, Stack<State> states, IEnume
         }
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string UciOk() => "uciok";
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string ReadyOk() => "readyok";
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string CopyProtection(CopyProtections copyProtections)
         => $"copyprotection {copyProtections}";
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string BestMove(Move move, Move ponderMove)
         => !ponderMove.IsNullMove()
             ? $"bestmove {move} ponder {ponderMove}"
             : $"bestmove {move}";
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string CurrentMoveNum(int moveNumber, Move move, in ulong visitedNodes, in TimeSpan time)
         => $"info currmovenumber {moveNumber} currmove {move} nodes {visitedNodes} time {time.Milliseconds}";
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string Score(int value, int mateInMaxPly, int valueMate)
     {
         if (Math.Abs(value) >= mateInMaxPly)
@@ -160,12 +177,15 @@ public string Score(int value, int mateInMaxPly, int valueMate)
         return $"cp {ToCenti(value)}";
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string ScoreCp(int value)
         => $"info score cp {ToCenti(value)}";
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string Depth(int depth)
         => $"info depth {depth}";
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string Pv(
         int count,
         int score,
@@ -196,6 +216,7 @@ in ulong tableHits
         return result;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public string Fullness(int fullNess, in ulong tbHits, in ulong nodes, in TimeSpan time)
         =>
             $"info hashfull {fullNess} tbhits {tbHits} nodes {nodes} time {time.Milliseconds} nps {Nps(in nodes, in time)}";
@@ -237,13 +258,22 @@ public string MoveToString(Move m, ChessMode chessMode = ChessMode.Normal)
         foreach (var opt in _options.Values.Order(OptionComparer))
         {
             sb.AppendLine();
-            sb.Append("option name ").Append(opt.Name).Append(" type ").Append(OptionTypeStrings[(int)opt.Type]);
-            
+
+            var optionType = OptionTypeStrings[(int)opt.Type];
+
+            sb.Append("option name ")
+              .Append(opt.Name)
+              .Append(" type ")
+              .Append(optionType);
+
             if (opt.Type != UciOptionType.Button)
                 sb.Append(" default ").Append(opt.DefaultValue);
 
             if (opt.Type == UciOptionType.Spin)
-                sb.Append(" min ").Append(opt.Min).Append(" max ").Append(opt.Max);
+                sb.Append(" min ")
+                  .Append(opt.Min)
+                  .Append(" max ")
+                  .Append(opt.Max);
         }
 
         var result = sb.ToString();
@@ -251,5 +281,6 @@ public string MoveToString(Move m, ChessMode chessMode = ChessMode.Normal)
         return result;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static int ToCenti(int v) => v / 100;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs b/src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs
index 0f0e2206..52cf013e 100644
--- a/src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs
+++ b/src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs
@@ -2,6 +2,6 @@
 
 public interface IPerftTableEntry : ITableEntry
 {
-    public ulong Count { get; set; }
+    public UInt128 Count { get; set; }
     public int Depth { get; set; }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
index ed420542..aabaa738 100644
--- a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
@@ -33,10 +33,10 @@ public struct PerftTableEntry : IPerftTableEntry
 {
     public HashKey Key { get; set; }
     
-    public ulong Count { get; set; }
-    
+    public UInt128 Count { get; set; }
+
     public int Depth { get; set; }
-    
+
     public Score Evaluate(IPosition pos)
     {
         throw new NotImplementedException();
@@ -55,7 +55,7 @@ public PerftTable()
         Initialize(ElementSize, HashMemory, static key => new PerftTableEntry { Key = key, Count = ulong.MinValue, Depth = -1 });
         _mask = Count - 1;
     }
-    
+
     public ref IPerftTableEntry TryGet(in IPosition pos, int depth, out bool found)
     {
         var posKey = pos.State.PositionKey;
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index d75a9ef9..5c0455a4 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -1,4 +1,7 @@
-using System.Numerics;
+/*
+ * Adapted from PortFish.
+ * Minor modernizations by Rudy Alex Kohn
+ */
 using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Hash;
 
@@ -6,15 +9,15 @@ namespace Rudzoft.ChessLib.Types;
 
 public static class MagicBB
 {
-    private static readonly BitBoard[]   RMasks   = new BitBoard[64];
-    private static readonly BitBoard[]   RMagics  = new BitBoard[64];
+    private static readonly BitBoard[] RMasks = new BitBoard[64];
+    private static readonly BitBoard[] RMagics = new BitBoard[64];
     private static readonly BitBoard[][] RAttacks = new BitBoard[64][];
-    private static readonly int[]        RShifts  = new int[64];
+    private static readonly int[] RShifts = new int[64];
 
-    private static readonly BitBoard[]   BMasks   = new BitBoard[64];
-    private static readonly BitBoard[]   BMagics  = new BitBoard[64];
+    private static readonly BitBoard[] BMasks = new BitBoard[64];
+    private static readonly BitBoard[] BMagics = new BitBoard[64];
     private static readonly BitBoard[][] BAttacks = new BitBoard[64][];
-    private static readonly int[]        BShifts  = new int[64];
+    private static readonly int[] BShifts = new int[64];
 
     private static readonly int[][] MagicBoosters =
     [
@@ -22,44 +25,57 @@ public static class MagicBB
         [1059, 3608, 605, 3234, 3326, 38, 2029, 3043]
     ];
 
-    private static readonly  byte[]  BitCount8Bit   = new byte[256];
+    private static readonly byte[] BitCount8Bit = new byte[256];
 
+    [SkipLocalsInit]
     static MagicBB()
     {
-        for (ulong b = 0; b < 256; b++)
-            BitCount8Bit[b] = (byte)popcount_1s_Max15(b);
+        for (ulong b = 0; b <= byte.MaxValue; b++)
+            BitCount8Bit[b] = (byte)PopCountMax15(b);
 
-        Direction[] RDeltas = [Direction.North, Direction.East, Direction.South, Direction.West];
-        Direction[] BDeltas = [Direction.NorthEast, Direction.SouthEast, Direction.SouthWest, Direction.NorthWest];
+        Span<Direction> rookDeltas = stackalloc Direction[]
+            { Direction.North, Direction.East, Direction.South, Direction.West };
+
+        Span<Direction> bishopDeltas = stackalloc Direction[]
+            { Direction.NorthEast, Direction.SouthEast, Direction.SouthWest, Direction.NorthWest };
 
         var occupancy = new BitBoard[4096];
         var reference = new BitBoard[4096];
         var rk = new RKiss();
 
-        init_magics(PieceTypes.Rook, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index, occupancy, reference, rk);
-        init_magics(PieceTypes.Bishop, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index, occupancy, reference, rk);
-
+        InitMagics(
+            pt: PieceTypes.Rook,
+            attacks: RAttacks,
+            magics: RMagics,
+            masks: RMasks,
+            shifts: RShifts,
+            deltas: rookDeltas,
+            index: MagicIndex,
+            occupancy: occupancy,
+            reference: reference,
+            rk: rk
+        );
+
+        InitMagics(
+            pt: PieceTypes.Bishop,
+            attacks: BAttacks,
+            magics: BMagics,
+            masks: BMasks,
+            shifts: BShifts,
+            deltas: bishopDeltas,
+            index: MagicIndex,
+            occupancy: occupancy,
+            reference: reference,
+            rk: rk
+        );
     }
 
-#if AGGR_INLINE
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
-    internal static int popcount_1s_Max15(in ulong b)
+    private static int PopCountMax15(ulong b)
     {
-#if X64
         b -= (b >> 1) & 0x5555555555555555UL;
         b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL);
         return (int)((b * 0x1111111111111111UL) >> 60);
-#else
-        uint w = (uint)(b >> 32), v = (uint)(b);
-        v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
-        w -= (w >> 1) & 0x55555555;
-        v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
-        w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
-        v += w; // 0-8 in 4 bits
-        v *= 0x11111111;
-        return (int)(v >> 28);
-#endif
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -80,27 +96,25 @@ public static BitBoard QueenAttacks(this Square s, in BitBoard occ)
         return RookAttacks(s, in occ) | BishopAttacks(s, in occ);
     }
 
-    private static uint magic_index(PieceTypes pt, Square s, BitBoard occ)
+    private static uint MagicIndex(PieceTypes pt, Square s, BitBoard occ)
     {
-        if (pt == PieceTypes.Rook)
-            return (uint)(((occ & RMasks[s.AsInt()]).Value * RMagics[s.AsInt()].Value) >> RShifts[s.AsInt()]);
-
-        return (uint)(((occ & BMasks[s.AsInt()]).Value * BMagics[s.AsInt()].Value) >> BShifts[s.AsInt()]);
+        return pt == PieceTypes.Rook
+            ? (uint)(((occ & RMasks[s.AsInt()]).Value * RMagics[s.AsInt()].Value) >> RShifts[s.AsInt()])
+            : (uint)(((occ & BMasks[s.AsInt()]).Value * BMagics[s.AsInt()].Value) >> BShifts[s.AsInt()]);
     }
 
-
     // init_magics() computes all rook and bishop attacks at startup. Magic
     // bitboards are used to look up attacks of sliding pieces. As a reference see
     // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
     // use the so called "fancy" approach.
 
-    private static void init_magics(
+    private static void InitMagics(
         PieceTypes pt,
         BitBoard[][] attacks,
         BitBoard[] magics,
         BitBoard[] masks,
         int[] shifts,
-        Direction[] deltas,
+        ReadOnlySpan<Direction> deltas,
         Func<PieceTypes, Square, BitBoard, uint> index,
         BitBoard[] occupancy,
         BitBoard[] reference,
@@ -120,8 +134,8 @@ private static void init_magics(
             // all the attacks for each possible subset of the mask and so is 2 power
             // the number of 1s of the mask. Hence we deduce the size of the shift to
             // apply to the 64 or 32 bits word to get the index.
-            masks[s.AsInt()] = sliding_attack(deltas, s, 0) & ~edges;
-            shifts[s.AsInt()] = 64 - popcount_1s_Max15(masks[s.AsInt()].Value);
+            masks[s.AsInt()] = SlidingAttack(deltas, s, 0) & ~edges;
+            shifts[s.AsInt()] = 64 - PopCountMax15(masks[s.AsInt()].Value);
 
             // Use Carry-Rippler trick to enumerate all subsets of masks[s] and
             // store the corresponding sliding attack bitboard in reference[].
@@ -130,7 +144,7 @@ private static void init_magics(
             do
             {
                 occupancy[size] = b;
-                reference[size++] = sliding_attack(deltas, sq, b);
+                reference[size++] = SlidingAttack(deltas, sq, b);
                 b = (b.Value - masks[s.AsInt()].Value) & masks[s.AsInt()];
             } while (b.IsNotEmpty);
 
@@ -145,7 +159,7 @@ private static void init_magics(
             int i;
             do
             {
-                magics[s.AsInt()] = pick_random(masks[s.AsInt()], rk, booster);
+                magics[s.AsInt()] = PickRandom(masks[s.AsInt()], rk, booster);
                 Array.Clear(attacks[s.AsInt()], 0, size);
 
                 // A good magic must map every possible occupancy to an index that
@@ -155,7 +169,6 @@ private static void init_magics(
                 for (i = 0; i < size; i++)
                 {
                     var idx = index(pt, s, occupancy[i]);
-
                     var attack = attacks[s.AsInt()][idx];
 
                     if (attack.IsNotEmpty && attack != reference[i])
@@ -167,7 +180,7 @@ private static void init_magics(
         }
     }
 
-    private static BitBoard sliding_attack(IReadOnlyList<Direction> deltas, Square sq, BitBoard occupied)
+    private static BitBoard SlidingAttack(ReadOnlySpan<Direction> deltas, Square sq, BitBoard occupied)
     {
         var attack = BitBoard.Empty;
 
@@ -185,7 +198,7 @@ private static BitBoard sliding_attack(IReadOnlyList<Direction> deltas, Square s
         return attack;
     }
 
-    private static BitBoard pick_random(BitBoard mask, IRKiss rk, int booster)
+    private static BitBoard PickRandom(BitBoard mask, IRKiss rk, int booster)
     {
         // Values s1 and s2 are used to rotate the candidate magic of a
         // quantity known to be the optimal to quickly find the magics.
@@ -195,14 +208,13 @@ private static BitBoard pick_random(BitBoard mask, IRKiss rk, int booster)
         while (true)
         {
             var magic = rk.Rand();
-            magic =  (magic >> s1) | (magic << (64 - s1));
+            magic = (magic >> s1) | (magic << (64 - s1));
             magic &= rk.Rand();
-            magic =  (magic >> s2) | (magic << (64 - s2));
+            magic = (magic >> s2) | (magic << (64 - s2));
             magic &= rk.Rand();
 
             if (BitCount8Bit[((mask.Value * magic) >> 56)] >= 6)
                 return magic;
         }
     }
-
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index e4839bb0..259ed816 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -111,7 +111,7 @@ public void Deconstruct(out Rank r, out File f)
     public bool IsPromotionRank => (BitBoards.PromotionRanksBB & this).IsNotEmpty;
 
     public bool IsDark => (Player.Black.ColorBB() & this).IsNotEmpty;
-    
+
     public static Square None { get; } = new(Squares.none);
 
     public static Square A1 { get; } = new(Squares.a1);
diff --git a/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs b/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
index 6429a069..582888cf 100644
--- a/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
+++ b/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
@@ -1,7 +1,6 @@
 using System.Collections.Immutable;
 using Akka.Actor;
 using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
 using Rudzoft.Perft.Options;
 using Rudzoft.Perft.Services;
 
diff --git a/src/Rudzoft.Perft/Models/IPerftResult.cs b/src/Rudzoft.Perft/Models/IPerftResult.cs
index a7df8533..623203f7 100644
--- a/src/Rudzoft.Perft/Models/IPerftResult.cs
+++ b/src/Rudzoft.Perft/Models/IPerftResult.cs
@@ -28,16 +28,16 @@ namespace Rudzoft.Perft.Models;
 
 public interface IPerftResult
 {
-    string Id { get; set; }
-    string Fen { get; set; }
-    int Depth { get; set; }
-    ulong Result { get; set; }
-    ulong CorrectResult { get; set; }
-    TimeSpan Elapsed { get; set; }
-    ulong Nps { get; set; }
-    ulong TableHits { get; set; }
-    bool Passed { get; set; }
-    int Errors { get; set; }
+    string   Id            { get; set; }
+    string   Fen           { get; set; }
+    int      Depth         { get; set; }
+    UInt128  Result        { get; set; }
+    UInt128  CorrectResult { get; set; }
+    TimeSpan Elapsed       { get; set; }
+    ulong    Nps           { get; set; }
+    ulong    TableHits     { get; set; }
+    bool     Passed        { get; set; }
+    int      Errors        { get; set; }
 
     void Clear();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Models/PerftResult.cs b/src/Rudzoft.Perft/Models/PerftResult.cs
index 54a14a58..22864070 100644
--- a/src/Rudzoft.Perft/Models/PerftResult.cs
+++ b/src/Rudzoft.Perft/Models/PerftResult.cs
@@ -28,16 +28,16 @@ namespace Rudzoft.Perft.Models;
 
 public sealed class PerftResult : IPerftResult
 {
-    public string Id { get; set; }
-    public string Fen { get; set; }
-    public int Depth { get; set; }
-    public ulong Result { get; set; }
-    public ulong CorrectResult { get; set; }
-    public TimeSpan Elapsed { get; set; }
-    public ulong Nps { get; set; }
-    public ulong TableHits { get; set; }
-    public bool Passed { get; set; }
-    public int Errors { get; set; }
+    public string   Id            { get; set; }
+    public string   Fen           { get; set; }
+    public int      Depth         { get; set; }
+    public UInt128  Result        { get; set; }
+    public UInt128  CorrectResult { get; set; }
+    public TimeSpan Elapsed       { get; set; }
+    public ulong    Nps           { get; set; }
+    public ulong    TableHits     { get; set; }
+    public bool     Passed        { get; set; }
+    public int      Errors        { get; set; }
 
     public void Clear()
     {
diff --git a/src/Rudzoft.Perft/Services/PerftRunner.cs b/src/Rudzoft.Perft/Services/PerftRunner.cs
index 2230882c..fbad84d6 100644
--- a/src/Rudzoft.Perft/Services/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Services/PerftRunner.cs
@@ -258,7 +258,7 @@ private static async Task WriteOutput(
     }
 
     private void ComputeResults(
-        in ulong result,
+        in UInt128 result,
         int depth,
         in ulong expected,
         in TimeSpan elapsedMs,

From 5305f5891ab1c8a416d637f9482079e8820288b4 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 8 Feb 2024 00:12:29 +0100
Subject: [PATCH 077/119] update readme

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index cb181633..7be4f0c5 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ A C# chess data library with complete move generation and all needed custom type
 
 ## Requirements
 
-* .NET 7.0+
+* .NET 8.0+
 
 ## What is this for?
 
@@ -81,7 +81,7 @@ Yes you can, it is designed with that in mind.
 Perft console test program approximate timings to depth 6 for normal start position
 
 * AMD-FX 8350 = ~12.5 seconds. (without TT) (earlier version)
-* Intel i7-8086k = ~2.3 seconds
+* Intel i7-8086k = ~1.7 seconds
 
 ### Transposition Table
 

From aa413300c272b22c83f24afcb6774262c3e17ac6 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 07:08:12 +0100
Subject: [PATCH 078/119] update magicbb

---
 src/Rudzoft.ChessLib/Types/MagicBB.cs | 49 ++++++++++++++++-----------
 1 file changed, 30 insertions(+), 19 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index 5c0455a4..7a205e64 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -2,7 +2,10 @@
  * Adapted from PortFish.
  * Minor modernizations by Rudy Alex Kohn
  */
+
+using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Hash;
 
 namespace Rudzoft.ChessLib.Types;
@@ -30,6 +33,8 @@ public static class MagicBB
     [SkipLocalsInit]
     static MagicBB()
     {
+        var start = Stopwatch.GetTimestamp();
+
         for (ulong b = 0; b <= byte.MaxValue; b++)
             BitCount8Bit[b] = (byte)PopCountMax15(b);
 
@@ -43,6 +48,9 @@ static MagicBB()
         var reference = new BitBoard[4096];
         var rk = new RKiss();
 
+        ref var occupancyRef = ref MemoryMarshal.GetArrayDataReference(occupancy);
+        ref var referenceRef = ref MemoryMarshal.GetArrayDataReference(reference);
+
         InitMagics(
             pt: PieceTypes.Rook,
             attacks: RAttacks,
@@ -50,9 +58,8 @@ static MagicBB()
             masks: RMasks,
             shifts: RShifts,
             deltas: rookDeltas,
-            index: MagicIndex,
-            occupancy: occupancy,
-            reference: reference,
+            occupancyRef: ref occupancyRef,
+            referenceRef: ref referenceRef,
             rk: rk
         );
 
@@ -63,11 +70,13 @@ static MagicBB()
             masks: BMasks,
             shifts: BShifts,
             deltas: bishopDeltas,
-            index: MagicIndex,
-            occupancy: occupancy,
-            reference: reference,
+            occupancyRef: ref occupancyRef,
+            referenceRef: ref referenceRef,
             rk: rk
         );
+
+        var end = Stopwatch.GetElapsedTime(start);
+        Console.WriteLine($"MagicBB init took: {end}");
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -96,7 +105,8 @@ public static BitBoard QueenAttacks(this Square s, in BitBoard occ)
         return RookAttacks(s, in occ) | BishopAttacks(s, in occ);
     }
 
-    private static uint MagicIndex(PieceTypes pt, Square s, BitBoard occ)
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    private static uint MagicIndex(PieceTypes pt, Square s, in BitBoard occ)
     {
         return pt == PieceTypes.Rook
             ? (uint)(((occ & RMasks[s.AsInt()]).Value * RMagics[s.AsInt()].Value) >> RShifts[s.AsInt()])
@@ -115,9 +125,8 @@ private static void InitMagics(
         BitBoard[] masks,
         int[] shifts,
         ReadOnlySpan<Direction> deltas,
-        Func<PieceTypes, Square, BitBoard, uint> index,
-        BitBoard[] occupancy,
-        BitBoard[] reference,
+        ref BitBoard occupancyRef,
+        ref BitBoard referenceRef,
         IRKiss rk)
     {
         var rankMask = Rank.Rank1.RankBB() | Rank.Rank8.RankBB();
@@ -143,8 +152,9 @@ private static void InitMagics(
             var size = 0;
             do
             {
-                occupancy[size] = b;
-                reference[size++] = SlidingAttack(deltas, sq, b);
+                Unsafe.Add(ref occupancyRef, size) = b;
+                Unsafe.Add(ref referenceRef, size) = SlidingAttack(deltas, sq, b);
+                size++;
                 b = (b.Value - masks[s.AsInt()].Value) & masks[s.AsInt()];
             } while (b.IsNotEmpty);
 
@@ -168,13 +178,14 @@ private static void InitMagics(
                 // effect of verifying the magic.
                 for (i = 0; i < size; i++)
                 {
-                    var idx = index(pt, s, occupancy[i]);
+                    var idx = MagicIndex(pt, s, in Unsafe.Add(ref occupancyRef, i));
                     var attack = attacks[s.AsInt()][idx];
+                    ref var reference = ref Unsafe.Add(ref referenceRef, i);
 
-                    if (attack.IsNotEmpty && attack != reference[i])
+                    if (attack.IsNotEmpty && attack != reference)
                         break;
 
-                    attacks[s.AsInt()][idx] = reference[i];
+                    attacks[s.AsInt()][idx] = reference;
                 }
             } while (i != size);
         }
@@ -184,9 +195,9 @@ private static BitBoard SlidingAttack(ReadOnlySpan<Direction> deltas, Square sq,
     {
         var attack = BitBoard.Empty;
 
-        for (var i = 0; i < 4; i++)
+        foreach (var delta in deltas)
         {
-            for (var s = sq + deltas[i]; s.IsOk && s.Distance(s - deltas[i]) == 1; s += deltas[i])
+            for (var s = sq + delta; s.IsOk && s.Distance(s - delta) == 1; s += delta)
             {
                 attack |= s;
 
@@ -202,12 +213,12 @@ private static BitBoard PickRandom(BitBoard mask, IRKiss rk, int booster)
     {
         // Values s1 and s2 are used to rotate the candidate magic of a
         // quantity known to be the optimal to quickly find the magics.
-        int s1 = booster        & 63;
+        var s1 = booster & 63;
         var s2 = (booster >> 6) & 63;
 
         while (true)
         {
-            var magic = rk.Rand();
+            ulong magic = rk.Rand();
             magic = (magic >> s1) | (magic << (64 - s1));
             magic &= rk.Rand();
             magic = (magic >> s2) | (magic << (64 - s2));

From 34946b7970d82682868bfedc87aa8ab04a2dfae2 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 20:24:15 +0100
Subject: [PATCH 079/119] various

- killed off IMoveList + MoveListPolicy, MoveList now uses IResettable
- RKiss can now have it's seed modified
- Updated some dependencies
---
 .../FenBenchmark.cs                           |  7 +-
 .../MoveGeneratorBenchmark.cs                 | 12 ++-
 .../NumberCompactingBenchmark.cs              | 66 ++++++++++++++
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |  5 +-
 .../Rudzoft.ChessLib.Benchmark.csproj         |  2 +-
 .../PgnTests/PgnTests.cs                      | 11 +--
 .../Rudzoft.ChessLib.PGN.Test.csproj          | 10 +-
 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs |  1 -
 src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs    |  8 +-
 .../Rudzoft.ChessLib.PGN.csproj               |  1 +
 .../BoardTests/BoardTests.cs                  |  8 +-
 .../BookTests/PolyglotTests.cs                | 15 ++-
 .../CastleTests/BasicCastleTests.cs           |  4 +-
 .../EvaluationTests/KpkBitBaseTests.cs        |  6 +-
 .../FENTests/FenTests.cs                      |  6 +-
 .../FenceTests/FenceTests.cs                  |  4 +-
 .../GameplayTests/FoolsCheckMateTests.cs      |  4 +-
 .../MoveTests/MoveGen_49.cs                   |  7 +-
 .../MoveTests/MoveGeneratorTests.cs           |  3 +-
 .../MoveTests/MoveTests.cs                    |  4 +-
 src/Rudzoft.ChessLib.Test/NumberTests.cs      | 48 ++++++++++
 .../PerftTests/PerftTest.cs                   | 25 +++++
 .../PerftTests/PerftVerify.cs                 |  8 +-
 .../PiecesTests/PawnDoubleAttackTests.cs      | 10 +-
 .../PiecesTests/PieceAttacksRookTests.cs      | 10 +-
 .../PositionTests/EnPassantFenTests.cs        |  4 +-
 .../PositionTests/PositionTests.cs            | 12 +--
 .../PositionTests/ValidationTests.cs          |  6 +-
 .../ProtocolTests/OptionsTests.cs             | 17 ++--
 .../ProtocolTests/UciTests.cs                 |  5 +-
 .../Rudzoft.ChessLib.Test.csproj              |  8 +-
 .../ScoreTests/ScoreTests.cs                  | 45 ++++-----
 .../ZobristTests/ZobristHashTests.cs          |  6 +-
 .../Rudzoft.ChessLib.WebApi.csproj            |  8 +-
 .../ChessLibServiceCollectionExtensions.cs    |  5 +-
 .../Factories/PolyglotBookFactory.cs          |  4 +-
 src/Rudzoft.ChessLib/Game.cs                  | 32 ++-----
 src/Rudzoft.ChessLib/Hash/IRKiss.cs           |  2 +
 src/Rudzoft.ChessLib/Hash/RKiss.cs            | 12 ++-
 .../MoveGeneration/IMoveList.cs               | 52 -----------
 .../MoveGeneration/MoveGenerator.cs           |  4 +-
 .../MoveGeneration/MoveList.cs                | 10 +-
 .../Notation/NotationToMove.cs                |  2 +-
 .../Notation/Notations/CoordinateNotation.cs  |  6 +-
 .../Notation/Notations/FanNotation.cs         |  4 +-
 .../Notation/Notations/IccfNotation.cs        |  4 +-
 .../Notation/Notations/LanNotation.cs         |  4 +-
 .../Notation/Notations/Notation.cs            |  9 +-
 .../Notation/Notations/RanNotation.cs         |  4 +-
 .../Notation/Notations/SanNotation.cs         |  6 +-
 .../Notation/Notations/SmithNotation.cs       |  4 +-
 .../Notation/Notations/UciNotation.cs         |  2 +-
 .../ObjectPoolPolicies/MoveListPolicy.cs      | 41 ---------
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs | 17 ++--
 src/Rudzoft.ChessLib/Position.cs              | 91 ++++++++++++-------
 src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |  4 +-
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  3 +-
 .../Tables/Perft/PerftTable.cs                | 14 ++-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       |  2 -
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |  8 +-
 60 files changed, 411 insertions(+), 331 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Benchmark/NumberCompactingBenchmark.cs
 create mode 100644 src/Rudzoft.ChessLib.Test/NumberTests.cs
 delete mode 100644 src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
 delete mode 100644 src/Rudzoft.ChessLib/ObjectPoolPolicies/MoveListPolicy.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
index 35f9e2b4..5bc210a1 100644
--- a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
@@ -3,7 +3,6 @@
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -30,7 +29,9 @@ public void Setup()
         var zobrist = new Zobrist(rKiss);
         var cuckoo = new Cuckoo(zobrist);
         var validator = new PositionValidator(zobrist);
-        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        var provider = new DefaultObjectPoolProvider();
+        var policy = new DefaultPooledObjectPolicy<MoveList>();
+        var moveListObjectPool = provider.Create(policy);
         _pos = new Position(board, pieceValue, zobrist, cuckoo, validator, moveListObjectPool);
         _pos.Set(in fp, ChessMode.Normal, in state);
     }
@@ -52,4 +53,4 @@ public void GetFenOp()
             var fd = _pos.GenerateFen();
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
index 04ef1225..b68e1b05 100644
--- a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
@@ -2,7 +2,6 @@
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -23,10 +22,10 @@ public class MoveGeneratorBenchmark
         )
     ]
     public string _fen;
-    
+
     private IPosition _pos;
 
-    private ObjectPool<IMoveList> _objectPool;
+    private ObjectPool<MoveList> _objectPool;
 
     [GlobalSetup]
     public void Setup()
@@ -38,7 +37,10 @@ public void Setup()
         var zobrist = new Zobrist(rKiss);
         var cuckoo = new Cuckoo(zobrist);
         var validator = new PositionValidator(zobrist);
-        _objectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        var provider = new DefaultObjectPoolProvider();
+        var policy = new DefaultPooledObjectPolicy<MoveList>();
+
+        _objectPool = provider.Create(policy);
         _pos = new Position(board, pieceValue, zobrist, cuckoo, validator, _objectPool);
         _pos.Set(_fen, ChessMode.Normal, state);
     }
@@ -49,7 +51,7 @@ public int GenerateMovesNoPool()
         var moves = _pos.GenerateMoves();
         return moves.Length;
     }
-    
+
     [Benchmark]
     public int GenerateMovesWithPool()
     {
diff --git a/src/Rudzoft.ChessLib.Benchmark/NumberCompactingBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/NumberCompactingBenchmark.cs
new file mode 100644
index 00000000..c086dc1b
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Benchmark/NumberCompactingBenchmark.cs
@@ -0,0 +1,66 @@
+namespace Rudzoft.ChessLib.Benchmark;
+
+[MemoryDiagnoser]
+public class NumberCompactingBenchmark
+{
+    private const int numMask = 0b0000_0000_0000_0000_0000_0000_0000_1111;
+
+    private const string strNum = "12345678";
+    private const string strNum2 = "45678945";
+
+    [Benchmark(Baseline = true)]
+    public (string, string) Base()
+    {
+        var first = ulong.Parse(strNum);
+        var second = ulong.Parse(strNum2);
+        var compacted = (first << 32) | second;
+
+        var reverseNum = ((int)(compacted >> 32)).ToString();
+        var reverseNum2 = ((int)(compacted & 0xFFFFFFFF)).ToString();
+
+        return (reverseNum, reverseNum2);
+    }
+
+    [Benchmark]
+    public (string, string) InPlace()
+    {
+        var numSpan = strNum.AsSpan();
+
+        var result = Compact(numSpan, 0);
+        result |= Compact(strNum2.AsSpan(), 32);
+
+        Span<char> resultSpan = stackalloc char[8];
+        for (var i = 0; i < 8; i++)
+        {
+            var shift = i * 4;
+            var b = (byte)((result >> shift) & numMask);
+            resultSpan[i] = (char)(b + 48);
+        }
+
+        Span<char> resultSpan2 = stackalloc char[8];
+        for (var i = 0; i < 8; i++)
+        {
+            var shift = (i * 4) + 32;
+            var b = (byte)((result >> shift) & numMask);
+            resultSpan2[i] = (char)(b + 48);
+        }
+
+        return (resultSpan.ToString(), resultSpan2.ToString());
+    }
+
+    private static ulong Compact(ReadOnlySpan<char> numSpan, int delta)
+    {
+        var result = ulong.MinValue;
+
+        for (var index = 0; index < numSpan.Length; index++)
+        {
+            var shift = (index * 4) + delta;
+            var c = numSpan[index];
+            var b = (byte)c;
+            result |= (ulong)(b & numMask) << shift;
+        }
+
+        return result;
+    }
+
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index f5304b5e..32a666b4 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -29,7 +29,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Perft;
 using Rudzoft.ChessLib.Perft.Interfaces;
 using Rudzoft.ChessLib.Protocol.UCI;
@@ -66,7 +65,9 @@ public void Setup()
         var options = Options.Create(ttConfig);
         var tt = new TranspositionTable(options);
 
-        var moveListObjectPool = new DefaultObjectPool<IMoveList>(new MoveListPolicy());
+        var provider = new DefaultObjectPoolProvider();
+        var policy = new DefaultPooledObjectPolicy<MoveList>();
+        var moveListObjectPool = provider.Create(policy);
 
         var uci = new Uci(moveListObjectPool);
         uci.Initialize();
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 9342770e..fa67f879 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -13,7 +13,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
+        <PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
         <PackageReference Include="System.Runtime" Version="4.3.1"/>
     </ItemGroup>
 
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs b/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
index 0bd4ac14..247d7da6 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
+++ b/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
@@ -34,14 +34,9 @@ public sealed class PgnTests
     private const string SampleFilePath = "samples/sample.pgn";
     private const int ExpectedGameCount = 2;
 
-    private readonly IServiceProvider _serviceProvider;
-
-    public PgnTests()
-    {
-        _serviceProvider = new ServiceCollection()
-                .AddPgnParser(static () => false)
-                .BuildServiceProvider();
-    }
+    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
+                                                         .AddPgnParser(static () => true)
+                                                         .BuildServiceProvider();
 
     [Fact]
     public async Task ParseFile_WithTestContent_ReturnsCorrectGamesAndMoves()
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index cc90642f..2d71c538 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -7,14 +7,14 @@
 
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
-        <PackageReference Include="xunit" Version="2.6.1" />
-        <PackageReference Include="xunit.analyzers" Version="1.5.0" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
+        <PackageReference Include="xunit" Version="2.7.0" />
+        <PackageReference Include="xunit.analyzers" Version="1.11.0" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
-        <PackageReference Include="coverlet.collector" Version="6.0.0">
+        <PackageReference Include="coverlet.collector" Version="6.0.1">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
index 8bf01273..c02c8381 100644
--- a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
@@ -48,7 +48,6 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
             if (string.IsNullOrWhiteSpace(line))
             {
                 inMoveSection = currentGameTags.Count > 0;
-                continue;
             }
 
             if (inMoveSection)
diff --git a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
index 81b06237..dc67bd35 100644
--- a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
@@ -34,10 +34,10 @@ public sealed partial class RegexPgnParser : IPgnParser
     private const int DefaultTagCapacity = 7;
     private const int DefaultMoveListCapacity = 24;
 
-    [GeneratedRegex("""\[(?<tagName>\w+)\s+"(?<tagValue>[^"]+)"\]""",  RegexOptions.NonBacktracking)]
+    [GeneratedRegex("""\[(?<tagName>\w+)\s+"(?<tagValue>[^"]+)"\]""", RegexOptions.NonBacktracking)]
     private static partial Regex TagPairRegex();
 
-    [GeneratedRegex(@"(?<moveNumber>\d+)\.\s*(?<whiteMove>\S+)(\s+(?<blackMove>\S+))?",  RegexOptions.NonBacktracking)]
+    [GeneratedRegex(@"(?<moveNumber>\d+)\.\s*(?<whiteMove>\S+)(\s+(?<blackMove>\S+))?", RegexOptions.NonBacktracking)]
     private static partial Regex MoveTextRegex();
 
     private static readonly PgnMove EmptyPgnMove = new(0, string.Empty, string.Empty);
@@ -57,7 +57,7 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
 
         var line = await streamReader.ReadLineAsync(cancellationToken);
 
-        while (line != null)
+        while (line is not null)
         {
             if (line.AsSpan().IsWhiteSpace())
                 inMoveSection = currentGameTags.Count > 0;
@@ -88,7 +88,7 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
     private static bool IsPgnGameResult(ReadOnlySpan<char> line)
     {
         var trimmedLine = line.TrimEnd();
-        
+
         if (trimmedLine.EndsWith(stackalloc char[] { '*' }, StringComparison.InvariantCultureIgnoreCase))
             return true;
 
diff --git a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
index fcedd72c..468ff3fd 100644
--- a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
+++ b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
@@ -6,6 +6,7 @@
 
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.2" />
     </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index b67d5f06..9bd262a2 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -32,7 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -56,7 +56,7 @@ public BoardTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
@@ -78,12 +78,12 @@ public BoardTestsTheoryData(string[] fens, PieceTypes[] pts, Player[] players, i
             var ptsSpan = pts.AsSpan();
             var playersSpan = players.AsSpan();
             var expectedCountSpan = expectedCounts.AsSpan();
-            
+
             ref var fenSpace = ref MemoryMarshal.GetReference(fensSpan);
             ref var ptsSpace = ref MemoryMarshal.GetReference(ptsSpan);
             ref var playersSpace = ref MemoryMarshal.GetReference(playersSpan);
             ref var expectedCountSpace = ref MemoryMarshal.GetReference(expectedCountSpan);
-            
+
             for (var i = 0; i < fens.Length; ++i)
             {
                 var fen = Unsafe.Add(ref fenSpace, i);
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index 5bfbb437..ca5de126 100644
--- a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -33,7 +33,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
@@ -50,7 +49,7 @@ public PolyglotTests(BookFixture fixture)
     {
         var polyConfig = new PolyglotBookConfiguration { BookPath = string.Empty };
         var polyOptions = Options.Create(polyConfig);
-        
+
         _fixture = fixture;
         _serviceProvider = new ServiceCollection()
             .AddSingleton(polyOptions)
@@ -66,12 +65,12 @@ public PolyglotTests(BookFixture fixture)
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .AddSingleton(static sp =>
             {
-                var mlPool = sp.GetRequiredService<ObjectPool<IMoveList>>();
+                var mlPool = sp.GetRequiredService<ObjectPool<MoveList>>();
                 IUci uci = new Uci(mlPool);
                 uci.Initialize();
                 return uci;
@@ -108,12 +107,12 @@ public void BookFileLoadsCorrectly()
         const string fen = Fen.Fen.StartPositionFen;
 
         var bookPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
-        
+
         Assert.NotNull(bookPath);
         Assert.NotEmpty(bookPath);
-        
+
         var path = Path.Combine(bookPath, _fixture.BookFile);
-        
+
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
@@ -178,7 +177,7 @@ public void HashKeyFromInitialPositionIsComputedCorrectly(string[] uciMoves, ulo
         pos.Set(in fenData, ChessMode.Normal, state);
 
         var uci = _serviceProvider.GetRequiredService<IUci>();
-        
+
         var book = _serviceProvider
             .GetRequiredService<IPolyglotBookFactory>()
             .Create();
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
index 2df8b211..2e1d69b3 100644
--- a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
+++ b/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -53,7 +53,7 @@ public BasicCastleTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
index 852a3aae..6167f7f0 100644
--- a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
+++ b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
@@ -30,7 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Evaluation;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -55,7 +55,7 @@ public KpkBitBaseTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
@@ -79,7 +79,7 @@ public void KpkWin(string fen, Players rawPlayer, bool expected)
         var weakSide = ~strongSide;
 
         var kpkBitBase = _serviceProvider.GetRequiredService<IKpkBitBase>();
-        
+
         var strongKing = kpkBitBase.Normalize(pos, strongSide,  pos.GetPieceSquare(PieceTypes.King, strongSide));
         var strongPawn = kpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceTypes.Pawn, strongSide));
         var weakKing = kpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceTypes.King, weakSide));
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
index 159ab212..4ce28e98 100644
--- a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
@@ -30,7 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Exceptions;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -54,7 +54,7 @@ public FenTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
@@ -74,7 +74,7 @@ public void GetFen(string fen)
         var fenData = new FenData(fen);
         var state = new State();
         pos.Set(in fenData, ChessMode.Normal, state);
-        
+
         var actualFen = pos.GenerateFen().ToString();
         Assert.Equal(fen, actualFen);
     }
diff --git a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
index 15443f96..2aa56ddc 100644
--- a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
+++ b/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -54,7 +54,7 @@ public FenceTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
diff --git a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
index 183d32df..334cdfa9 100644
--- a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
+++ b/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -53,7 +53,7 @@ public FoolsCheckMateTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
index 07480079..b75ffb17 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
@@ -30,7 +30,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -54,19 +53,19 @@ public MoveGen_49()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
     }
-    
+
     [Fact]
     public void MoveListContainsMismatchedElement()
     {
         const string fen = "r3kb1r/p3pppp/p1n2n2/2pp1Q2/3P1B2/2P1PN2/Pq3PPP/RN2K2R w KQkq - 0 9";
 
         var pos = _serviceProvider.GetRequiredService<IPosition>();
-        
+
         var fd = new FenData(fen);
         var state = new State();
 
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
index 10a2cadf..b8f3a8d6 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
@@ -30,7 +30,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -54,7 +53,7 @@ public MoveGeneratorTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index 77fa31c9..57ec8d7b 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -31,7 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -55,7 +55,7 @@ public MoveTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
diff --git a/src/Rudzoft.ChessLib.Test/NumberTests.cs b/src/Rudzoft.ChessLib.Test/NumberTests.cs
new file mode 100644
index 00000000..97f168aa
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Test/NumberTests.cs
@@ -0,0 +1,48 @@
+using System.Runtime.CompilerServices;
+
+namespace Rudzoft.ChessLib.Test;
+
+public sealed class NumberTests
+{
+    [Fact]
+    [SkipLocalsInit]
+    public void NumberTest()
+    {
+        const int numMask = 0b0000_0000_0000_0000_0000_0000_0000_1111;
+
+        var strNum = "12345678";
+        var strNum2 = "45678945";
+
+        var compacted = (ulong.Parse(strNum) << 32) | ulong.Parse(strNum2);
+
+        var reverseNum = ((int)(compacted >> 32)).ToString();
+        var reverseNum2 = ((int)(compacted & 0xFFFFFFFF)).ToString();
+
+        Assert.Equal(strNum, reverseNum);
+        Assert.Equal(strNum2, reverseNum2);
+
+        var numSpan = strNum.AsSpan();
+
+        //101111101011110000011111111
+
+        var result = ulong.MinValue;
+
+        for (var index = 0; index < numSpan.Length; index++)
+        {
+            var shift = index * 4;
+            var c = numSpan[index];
+            var b = (byte)c;
+            result |= (ulong)(b & numMask) << shift;
+        }
+
+        Span<char> resultSpan = stackalloc char[8];
+        for (var i = 0; i < 8; i++)
+        {
+            var shift = i * 4;
+            var b = (byte)((result >> shift) & numMask);
+            resultSpan[i] = (char)(b + 48);
+        }
+
+        Assert.Equal(resultSpan.ToString(), strNum);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
index 5ea26211..4ea8acd0 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
@@ -180,6 +180,31 @@ public sealed class TalkChessPerftTests : PerftVerify
 
     public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
+    [Theory]
+    [MemberData(nameof(PerftTheoryData))]
+    public void Perft(string fen, int depth, ulong expected)
+        => AssertPerft(fen, depth, in expected);
+}
+
+public sealed class WeirdErrorFenTest : PerftVerify
+{
+    private static readonly string[] Fens =
+    [
+        "rnkq1bnr/p3ppp1/1ppp3p/3B4/6b1/2PQ3P/PP1PPP2/RNB1K1NR w KQkq - 0 1"
+    ];
+
+    private static readonly int[] Depths =
+    [
+        6
+    ];
+
+    private static readonly ulong[] Results =
+    [
+        840484313UL
+    ];
+
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
+
     [Theory]
     [MemberData(nameof(PerftTheoryData))]
     public void Perft(string fen, int depth, ulong expected)
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
index 0eaf8ea9..5d1c9d60 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
@@ -3,7 +3,7 @@
 using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -36,7 +36,7 @@ protected PerftVerify()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .AddSingleton<ITranspositionTable, TranspositionTable>()
@@ -49,11 +49,11 @@ protected void AssertPerft(string fen, int depth, in ulong expected)
         g.NewGame(fen);
 
         var initialZobrist = g.Pos.State.PositionKey;
-        
+
         var actual = g.Perft(depth);
 
         var afterZobrist = g.Pos.State.PositionKey;
-        
+
         Assert.Equal(expected, actual);
         Assert.Equal(initialZobrist, afterZobrist);
     }
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
index 20a6343f..2b552150 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -53,12 +53,12 @@ public PawnDoubleAttackTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
     }
-    
+
     [Fact]
     public void WhitePlayerPawnDoubleAttacksAttackSpan()
     {
@@ -66,9 +66,9 @@ public void WhitePlayerPawnDoubleAttacksAttackSpan()
 
         var fenData = new FenData(Fen.Fen.StartPositionFen);
         var state = new State();
-        
+
         pos.Set(in fenData, ChessMode.Normal, state);
-        
+
         var whitePawns = pos.Pieces(PieceTypes.Pawn, Player.White);
 
         var attackSpan = whitePawns.PawnDoubleAttacks(Player.White);
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
index 305a1fbc..b87d68db 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
@@ -27,7 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -50,13 +50,13 @@ public PieceAttacksRookTests()
             .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
             .AddSingleton(static serviceProvider =>
             {
-                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
-                return provider.Create(policy);
+            var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+            var policy = new DefaultPooledObjectPolicy<MoveList>();
+            return provider.Create(policy);
             })
             .BuildServiceProvider();
     }
-    
+
     /// <summary>
     /// Testing results of blocked rook attacks, they should always return 8 on the sides, and 14 in
     /// the corner
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
index 2875aa48..9063bf05 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -53,7 +53,7 @@ public EnPassantFenTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index 07841172..c568255e 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -53,7 +53,7 @@ public PositionTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
@@ -74,9 +74,9 @@ public void AddPieceTest()
         pos.AddPiece(Piece.WhitePawn, square);
 
         var actualFen = pos.FenNotation;
-        
+
         Assert.Equal(expectedFen, actualFen);
-        
+
         var piece = pos.GetPiece(square);
         Assert.Equal(Piece.WhitePawn, piece);
     }
@@ -109,7 +109,7 @@ public void KingBlocker()
 
         Assert.Equal(expectedSquare, actual);
     }
-    
+
     [Theory]
     [InlineData("KPK", "k7/8/8/8/8/8/8/KP6 w - - 0 10")]
     [InlineData("KNNK", "k7/8/8/8/8/8/8/KNN5 w - - 0 10")]
@@ -127,7 +127,7 @@ public void SetByCodeCreatesSameMaterialKey(string code, string fen)
 
         var posCode = pos.Set(code, Player.White, in state);
         var codeMaterialKey = posCode.State.MaterialKey;
-        
+
         Assert.Equal(materialKey, codeMaterialKey);
 
         var codeFen = pos.GenerateFen().ToString();
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index 2aba2acf..7c6e3d76 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Fen;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 
@@ -53,7 +53,7 @@ public ValidationTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
@@ -90,7 +90,7 @@ public void ValidateCastleling()
         // position only has pawns, rooks and kings
         const string fen = "r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w KQkq - 0 1";
         const PositionValidationTypes validationType = PositionValidationTypes.Castle;
-        
+
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
         var fenData = new FenData(fen);
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
index 5b8185a6..dcaae4b8 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 
 namespace Rudzoft.ChessLib.Test.ProtocolTests;
@@ -38,23 +37,23 @@ public sealed class OptionsTests
     [InlineData("Boolean Test", false, false, "option name Boolean Test type Check default false")]
     public void Boolean(string name, bool value, bool expected, string uciString)
     {
-        var policy = new MoveListPolicy();
-        var provider = new DefaultObjectPool<IMoveList>(policy);
+        var provider = new DefaultObjectPoolProvider();
+        var policy = new DefaultPooledObjectPolicy<MoveList>();
+
+        IUci uci = new Uci(provider.Create(policy));
 
-        IUci uci = new Uci(provider);
-        
         uci.Initialize();
-        
+
         var option = new Option(name, 0, value);
         uci.AddOption(name, option);
 
         var actual = option.GetBool();
-        
+
         Assert.Equal(expected, actual);
         Assert.Equal(expected, option);
-        
+
         var t = uci.ToString();
-        
+
         Assert.Contains(uciString, t);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
index fd529ca7..216711ea 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
@@ -32,7 +32,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
@@ -61,12 +60,12 @@ public UciTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .AddSingleton(static sp =>
             {
-                var pool = sp.GetRequiredService<ObjectPool<IMoveList>>();
+                var pool = sp.GetRequiredService<ObjectPool<MoveList>>();
                 IUci uci = new Uci(pool);
                 uci.Initialize();
                 return uci;
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index e3ac4f20..886a6b3a 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -24,10 +24,10 @@
 
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
-        <PackageReference Include="xunit" Version="2.6.1" />
-        <PackageReference Include="xunit.analyzers" Version="1.5.0" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
+        <PackageReference Include="xunit" Version="2.7.0" />
+        <PackageReference Include="xunit.analyzers" Version="1.11.0" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
             <PrivateAssets>all</PrivateAssets>
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
diff --git a/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs b/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
index 6962a4cf..438facf5 100644
--- a/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Numerics;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.ScoreTests;
@@ -35,26 +36,26 @@ public sealed class ScoreTests
     static ScoreTests()
     {
         TestValues = new Score[20];
-        TestValues[0] = new Score(35, 23);
-        TestValues[1] = new Score(-35, 23);
-        TestValues[2] = new Score(35, -23);
-        TestValues[3] = new Score(-35, -23);
-        TestValues[4] = new Score(350, 230);
-        TestValues[5] = new Score(3542, 234);
-        TestValues[6] = new Score(35, -223);
-        TestValues[7] = new Score(0, 0);
-        TestValues[8] = new Score(56, 23);
-        TestValues[9] = new Score(15423, -23);
-        TestValues[10] = new Score(2, 1);
-        TestValues[11] = new Score(425, 23);
-        TestValues[12] = new Score(35, 4223);
-        TestValues[13] = new Score(-35231, -24223);
-        TestValues[14] = new Score(-1, 1);
-        TestValues[15] = new Score(1, -1);
-        TestValues[16] = new Score(2, 23);
-        TestValues[17] = new Score(55, 23);
-        TestValues[18] = new Score(0, 23);
-        TestValues[19] = new Score(650, -2323);
+        TestValues[0] = new(35, 23);
+        TestValues[1] = new(-35, 23);
+        TestValues[2] = new(35, -23);
+        TestValues[3] = new(-35, -23);
+        TestValues[4] = new(350, 230);
+        TestValues[5] = new(3542, 234);
+        TestValues[6] = new(35, -223);
+        TestValues[7] = new(0, 0);
+        TestValues[8] = new(56, 23);
+        TestValues[9] = new(15423, -23);
+        TestValues[10] = new(2, 1);
+        TestValues[11] = new(425, 23);
+        TestValues[12] = new(35, 4223);
+        TestValues[13] = new(-35231, -24223);
+        TestValues[14] = new(-1, 1);
+        TestValues[15] = new(1, -1);
+        TestValues[16] = new(2, 23);
+        TestValues[17] = new(55, 23);
+        TestValues[18] = new(0, 23);
+        TestValues[19] = new(650, -2323);
     }
 
     [Fact]
@@ -65,8 +66,8 @@ public void ScoreMg()
         var result = TestValues
             .Aggregate(Score.Zero, static (current, expected) => current + expected);
 
-        Assert.Equal( expectedMg, result.Mg());
-        Assert.Equal( expectedEg, result.Eg());
+        Assert.Equal(expectedMg, result.Mg());
+        Assert.Equal(expectedEg, result.Eg());
     }
 
     [Theory]
diff --git a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
index 47092046..69d2c290 100644
--- a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
@@ -28,7 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
+using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
 using File = Rudzoft.ChessLib.Types.File;
@@ -53,7 +53,7 @@ public ZobristHashTests()
             .AddSingleton(static serviceProvider =>
             {
                 var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new MoveListPolicy();
+                var policy = new DefaultPooledObjectPolicy<MoveList>();
                 return provider.Create(policy);
             })
             .BuildServiceProvider();
@@ -105,7 +105,7 @@ public void AdvancedMoves(string fen, Squares from, Squares to, MoveTypes moveTy
         var states = new List<State> { new() };
 
         pos.Set(fen, ChessMode.Normal, states[stateIndex]);
-        
+
         var startKey = pos.State.PositionKey;
 
         var move = new Move(from, to, moveType);
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index c91f7731..e0fa21b2 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -6,14 +6,14 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.0">
+        <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.2">
             <PrivateAssets>all</PrivateAssets>
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
         <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.Options" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.OpenApi" Version="1.6.10"/>
+        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.2" />
+        <PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
+        <PackageReference Include="Microsoft.OpenApi" Version="1.6.13" />
         <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
         <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0"/>
         <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0"/>
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 15f6b6d6..3d087fda 100644
--- a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -35,7 +35,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Notation;
 using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.ObjectPoolPolicies;
 using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.ChessLib.Protocol.UCI;
 using Rudzoft.ChessLib.Types;
@@ -77,14 +76,14 @@ public static IServiceCollection AddChessLib(
         serviceCollection.TryAddSingleton(static serviceProvider =>
         {
             var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-            var policy   = new MoveListPolicy();
+            var policy = new DefaultPooledObjectPolicy<MoveList>();
             return provider.Create(policy);
         });
 
         return serviceCollection
                .AddSingleton(static sp =>
                {
-                   var  pool = sp.GetRequiredService<ObjectPool<IMoveList>>();
+                   var  pool = sp.GetRequiredService<ObjectPool<MoveList>>();
                    IUci uci  = new Uci(pool);
                    uci.Initialize();
                    return uci;
diff --git a/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs b/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
index 5b58f1c8..001d0884 100644
--- a/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
+++ b/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
@@ -35,9 +35,9 @@ namespace Rudzoft.ChessLib.Factories;
 public sealed class PolyglotBookFactory : IPolyglotBookFactory
 {
     private readonly string _path;
-    private readonly ObjectPool<IMoveList> _objectPool;
+    private readonly ObjectPool<MoveList> _objectPool;
 
-    public PolyglotBookFactory(IOptions<PolyglotBookConfiguration> options, ObjectPool<IMoveList> objectPool)
+    public PolyglotBookFactory(IOptions<PolyglotBookConfiguration> options, ObjectPool<MoveList> objectPool)
     {
         var config = options.Value;
         _path = string.IsNullOrWhiteSpace(config.BookPath) ? string.Empty : config.BookPath;
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 508dbe22..f4c3ad1a 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -34,7 +34,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.Hash.Tables.Transposition;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Protocol.UCI;
-using Rudzoft.ChessLib.Tables.Perft;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib;
@@ -45,10 +44,10 @@ public sealed class Game(
     ICpu cpu,
     ISearchParameters searchParameters,
     IPosition pos,
-    ObjectPool<IMoveList> moveListPool)
+    ObjectPool<MoveList> moveListPool)
     : IGame
 {
-    private readonly PerftTable            _perftTable   = new();
+    private readonly PertT            _perftTable   = new(8);
 
     public Action<IPieceSquare> PieceUpdated => pos.PieceUpdated;
 
@@ -119,30 +118,23 @@ public override string ToString()
 
     public UInt128 Perft(int depth, bool root = true)
     {
-        ref var table = ref _perftTable.TryGet(pos, depth, out var found);
-
-        if (found)
-        {
-            Console.WriteLine($"Found table: {depth} = {table.Count}");
-            return table.Count;
-        }
-
         var tot = UInt128.MinValue;
         var ml  = moveListPool.Get();
         ml.Generate(in pos);
-
         var state = new State();
 
         var moves = ml.Get();
         ref var movesSpace = ref MemoryMarshal.GetReference(moves);
-        for (var i = 0; i < moves.Length; ++i)
+
+        if (root && depth <= 1)
         {
-            if (root && depth <= 1)
-            {
-                tot++;
-                continue;
-            }
+            tot = (ulong)ml.Length;
+            moveListPool.Return(ml);
+            return tot;
+        }
 
+        for (var i = 0; i < moves.Length; ++i)
+        {
             var valMove = Unsafe.Add(ref movesSpace, i);
             var m = valMove.Move;
 
@@ -164,10 +156,6 @@ public UInt128 Perft(int depth, bool root = true)
             pos.TakeMove(m);
         }
 
-        table.Key = pos.State.PositionKey;
-        table.Depth = depth;
-        table.Count = tot;
-
         moveListPool.Return(ml);
 
         return tot;
diff --git a/src/Rudzoft.ChessLib/Hash/IRKiss.cs b/src/Rudzoft.ChessLib/Hash/IRKiss.cs
index d15e9421..3011fbe8 100644
--- a/src/Rudzoft.ChessLib/Hash/IRKiss.cs
+++ b/src/Rudzoft.ChessLib/Hash/IRKiss.cs
@@ -31,6 +31,8 @@ namespace Rudzoft.ChessLib.Hash;
 
 public interface IRKiss
 {
+    ulong Seed { get; set; }
+
     IEnumerable<ulong> Get(int count);
 
     ulong Rand();
diff --git a/src/Rudzoft.ChessLib/Hash/RKiss.cs b/src/Rudzoft.ChessLib/Hash/RKiss.cs
index ba0f3be2..489a9be0 100644
--- a/src/Rudzoft.ChessLib/Hash/RKiss.cs
+++ b/src/Rudzoft.ChessLib/Hash/RKiss.cs
@@ -42,7 +42,7 @@ public sealed class RKiss : IRKiss
     /// </summary>
     private const ulong DefaultRandomSeed = 1070372;
 
-    private ulong _s = DefaultRandomSeed;
+    public ulong Seed { get; set; } = DefaultRandomSeed;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public IEnumerable<ulong> Get(int count)
@@ -64,9 +64,11 @@ public ulong Sparse()
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private ulong Rand64()
     {
-        _s ^= _s >> 12;
-        _s ^= _s << 25;
-        _s ^= _s >> 27;
-        return _s * 2685821657736338717L;
+        var s = Seed;
+        s ^= s >> 12;
+        s ^= s << 25;
+        s ^= s >> 27;
+        Seed = s;
+        return s * 2685821657736338717L;
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
deleted file mode 100644
index ce3e8c9c..00000000
--- a/src/Rudzoft.ChessLib/MoveGeneration/IMoveList.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.MoveGeneration;
-
-public interface IMoveList : IReadOnlyCollection<ValMove>
-{
-    ValMove this[int index] { get; set; }
-
-    int Length { get; }
-    ref ValMove CurrentMove { get; }
-    void Add(in ValMove item);
-    void Add(Move item);
-
-    /// <summary>
-    /// Clears the move generated data
-    /// </summary>
-    void Clear();
-
-    bool Contains(in ValMove item);
-    bool Contains(Move move);
-    bool Contains(Square from, Square to);
-    bool ContainsType(MoveTypes type);
-    void Generate(in IPosition pos, MoveGenerationTypes types = MoveGenerationTypes.Legal);
-    ReadOnlySpan<ValMove> Get();
-}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 56289a1c..0f611020 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -498,11 +498,11 @@ private static int MakePromotions(
                 moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion);
         }
 
-        const MoveGenerationTypes noneQueenPromotion = MoveGenerationTypes.Quiets
+        const MoveGenerationTypes nonQueenPromotion = MoveGenerationTypes.Quiets
                                                        | MoveGenerationTypes.Evasions
                                                        | MoveGenerationTypes.NonEvasions;
 
-        if (!types.HasFlagFast(noneQueenPromotion))
+        if (!types.HasFlagFast(nonQueenPromotion))
             return index;
 
         moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Rook);
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index df385828..128087c4 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using System.Collections;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using Microsoft.Extensions.ObjectPool;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Types;
 
@@ -37,7 +38,7 @@ namespace Rudzoft.ChessLib.MoveGeneration;
  * It works through an index pointer,
  * which means that re-use is never actually clearing any data, just resetting the index.
  */
-public sealed class MoveList : IMoveList
+public sealed class MoveList : IReadOnlyCollection<ValMove>, IResettable
 {
     private readonly ValMove[] _moves = new ValMove[218];
     private int _cur;
@@ -137,4 +138,11 @@ public IEnumerator<ValMove> GetEnumerator()
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     IEnumerator IEnumerable.GetEnumerator()
         => GetEnumerator();
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool TryReset()
+    {
+        Clear();
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/NotationToMove.cs b/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
index 8a70a160..63bd94cc 100644
--- a/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
+++ b/src/Rudzoft.ChessLib/Notation/NotationToMove.cs
@@ -31,7 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation;
 
-public sealed class NotationToMove(ObjectPool<IMoveList> moveListPool) : INotationToMove
+public sealed class NotationToMove(ObjectPool<MoveList> moveListPool) : INotationToMove
 {
     public IReadOnlyList<Move> FromNotation(IPosition pos, IEnumerable<string> notationalMoves, INotation notation)
     {
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
index f3ab28b1..4fa9b663 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
@@ -32,8 +32,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class CoordinateNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
+public sealed class CoordinateNotation : Notation
 {
+    public CoordinateNotation(ObjectPool<MoveList> moveLists) : base(moveLists) { }
+
     [SkipLocalsInit]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string Convert(IPosition pos, Move move)
@@ -41,7 +43,7 @@ public override string Convert(IPosition pos, Move move)
         var (from, to) = move;
 
         Span<char> re = stackalloc char[8];
-        var        i  = 0;
+        var i = 0;
 
         re[i++] = char.ToUpper(from.FileChar);
         re[i++] = from.RankChar;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
index 4c22b978..8aede2f9 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
@@ -32,8 +32,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class FanNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
+public sealed class FanNotation : Notation
 {
+    public FanNotation(ObjectPool<MoveList> moveLists) : base(moveLists) { }
+
     /// <summary>
     /// <para>Converts a move to FAN notation.</para>
     /// </summary>
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index 0cd847f1..614c9bac 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -31,8 +31,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class IccfNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
+public sealed class IccfNotation : Notation
 {
+    public IccfNotation(ObjectPool<MoveList> moveLists) : base(moveLists) { }
+
     /// <summary>
     /// <para>Converts a move to ICCF notation.</para>
     /// </summary>
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
index 09e043a8..3bf63563 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
@@ -32,8 +32,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class LanNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
+public sealed class LanNotation : Notation
 {
+    public LanNotation(ObjectPool<MoveList> moveLists) : base(moveLists) { }
+
     /// <summary>
     /// <para>Converts a move to LAN notation.</para>
     /// </summary>
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
index e0325718..d2448a31 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
@@ -32,9 +32,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public abstract class Notation(ObjectPool<IMoveList> moveLists) : INotation
+public abstract class Notation : INotation
 {
-    protected readonly ObjectPool<IMoveList> MoveLists = moveLists;
+    protected readonly ObjectPool<MoveList> MoveLists;
+
+    protected Notation(ObjectPool<MoveList> moveLists)
+    {
+        MoveLists = moveLists;
+    }
 
     public abstract string Convert(IPosition pos, Move move);
 
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
index 6abedbd8..a063be92 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
@@ -32,8 +32,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class RanNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
+public sealed class RanNotation : Notation
 {
+    public RanNotation(ObjectPool<MoveList> moveLists) : base(moveLists) { }
+
     /// <summary>
     /// <para>Converts a move to RAN notation.</para>
     /// </summary>
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
index 7dee0470..6927dc27 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
@@ -32,8 +32,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class SanNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
+public sealed class SanNotation : Notation
 {
+    public SanNotation(ObjectPool<MoveList> moveLists) : base(moveLists) { }
+
     /// <summary>
     /// <para>Converts a move to SAN notation.</para>
     /// </summary>
@@ -50,7 +52,7 @@ public override string Convert(IPosition pos, Move move)
             return CastleExtensions.GetCastleString(to, from);
 
         Span<char> re = stackalloc char[6];
-        var        i  = 0;
+        var i = 0;
 
         var pt = pos.GetPieceType(from);
 
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
index aef475e8..fb7f86d2 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
@@ -32,8 +32,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class SmithNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
+public sealed class SmithNotation : Notation
 {
+    public SmithNotation(ObjectPool<MoveList> moveLists) : base(moveLists) { }
+
     /// <summary>
     /// <para>Converts a move to Smith notation.</para>
     /// </summary>
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
index 5dfdb669..123e2383 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
@@ -31,7 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Notation.Notations;
 
-public sealed class UciNotation(ObjectPool<IMoveList> moveLists) : Notation(moveLists)
+public sealed class UciNotation(ObjectPool<MoveList> moveLists) : Notation(moveLists)
 {
     /// <summary>
     /// <para>Converts a move to UCI notation.</para>
diff --git a/src/Rudzoft.ChessLib/ObjectPoolPolicies/MoveListPolicy.cs b/src/Rudzoft.ChessLib/ObjectPoolPolicies/MoveListPolicy.cs
deleted file mode 100644
index af915754..00000000
--- a/src/Rudzoft.ChessLib/ObjectPoolPolicies/MoveListPolicy.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Microsoft.Extensions.ObjectPool;
-using Rudzoft.ChessLib.MoveGeneration;
-
-namespace Rudzoft.ChessLib.ObjectPoolPolicies;
-
-public sealed class MoveListPolicy : IPooledObjectPolicy<IMoveList>
-{
-    public IMoveList Create() => new MoveList();
-
-    public bool Return(IMoveList obj)
-    {
-        obj.Clear();
-        return true;
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index 479f5143..c7f946b9 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -49,23 +49,23 @@ public sealed class PolyglotBook : IPolyglotBook
     private readonly string _bookFilePath;
     private readonly int _entrySize;
     private readonly Random _rnd;
-    private readonly ObjectPool<IMoveList> _moveListPool;
+    private readonly ObjectPool<MoveList> _moveListPool;
 
-    private PolyglotBook(ObjectPool<IMoveList> pool)
+    private PolyglotBook(ObjectPool<MoveList> pool)
     {
-        _entrySize    = Unsafe.SizeOf<PolyglotBookEntry>();
-        _rnd          = new(DateTime.Now.Millisecond);
+        _entrySize = Unsafe.SizeOf<PolyglotBookEntry>();
+        _rnd = new(DateTime.Now.Millisecond);
         _moveListPool = pool;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal static PolyglotBook Create(ObjectPool<IMoveList> pool)
+    internal static PolyglotBook Create(ObjectPool<MoveList> pool)
     {
         return new(pool);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal static PolyglotBook Create(ObjectPool<IMoveList> pool, string path, string file)
+    internal static PolyglotBook Create(ObjectPool<MoveList> pool, string path, string file)
     {
         return new(pool)
         {
@@ -92,7 +92,7 @@ public string BookFile
 
     public Move Probe(IPosition pos, bool pickBest = true)
     {
-        if (_bookFilePath.IsNullOrEmpty() || _fileStream == null ||_binaryReader == null)
+        if (_bookFilePath.IsNullOrEmpty() || _fileStream == null || _binaryReader == null)
             return Move.EmptyMove;
 
         var polyMove = ushort.MinValue;
@@ -166,7 +166,8 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
         return mm;
     }
 
-    private static Move SelectMove(in IPosition pos, Square polyFrom, Square polyTo, MoveTypes polyType, ReadOnlySpan<ValMove> moves)
+    private static Move SelectMove(
+        in IPosition pos, Square polyFrom, Square polyTo, MoveTypes polyType, ReadOnlySpan<ValMove> moves)
     {
         // Iterate all known moves for current position to find a match.
         foreach (var valMove in moves)
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 168320a8..4ce51a1a 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -55,7 +55,7 @@ public sealed class Position : IPosition
     private readonly Value[] _nonPawnMaterial;
     private readonly ICuckoo _cuckoo;
     private readonly ObjectPool<StringBuilder> _outputObjectPool;
-    private readonly ObjectPool<IMoveList> _moveListPool;
+    private readonly ObjectPool<MoveList> _moveListPool;
     private readonly IPositionValidator _positionValidator;
     private Player _sideToMove;
 
@@ -65,7 +65,7 @@ public Position(
         IZobrist zobrist,
         ICuckoo cuckoo,
         IPositionValidator positionValidator,
-        ObjectPool<IMoveList> moveListPool)
+        ObjectPool<MoveList> moveListPool)
     {
         _castleKingPath = new BitBoard[CastleRight.Count];
         _castleRookPath = new BitBoard[CastleRight.Count];
@@ -567,7 +567,7 @@ public bool IsLegal(Move m)
         // If the moving piece is a king, check whether the destination square is attacked by
         // the opponent.
         if (MovedPiece(m).Type() == PieceTypes.King)
-            return m.IsCastleMove() || (AttacksTo(to) & Board.Pieces(~us)).IsEmpty;
+            return (AttacksTo(to) & Board.Pieces(~us)).IsEmpty;
 
         // A non-king move is legal if and only if it is not pinned or it is moving along the
         // ray towards or away from the king.
@@ -579,22 +579,47 @@ private bool IsCastlelingMoveLegal(Move m, Square to, Square from, Player us)
         // After castling, the rook and king final positions are the same in Chess960 as
         // they would be in standard chess.
 
+        var them = ~us;
         var isKingSide = to > from;
-        to = (isKingSide ? Square.G1 : Square.C1).Relative(us);
-        var step = isKingSide ? Direction.West : Direction.East;
+        Direction step;
+
+        if (isKingSide)
+        {
+            to = Square.G1.Relative(us);
+            step = Directions.West;
+        }
+        else
+        {
+            to = Square.C1.Relative(us);
+            step = Directions.East;
+        }
 
         for (var s = to; s != from; s += step)
-            if (AttacksTo(s) & Board.Pieces(~us))
+            if (AttacksTo(s) & Board.Pieces(them))
                 return false;
 
         // In case of Chess960, verify that when moving the castling rook we do not discover
         // some hidden checker. For instance an enemy queen in SQ_A1 when castling rook is
         // in SQ_B1.
-        return ChessMode == ChessMode.Normal || (GetAttacks(
-                    to,
-                    PieceTypes.Rook,
-                    Board.Pieces() ^ m.ToSquare()) & Board.Pieces(~us, PieceTypes.Rook, PieceTypes.Queen)
-            ).IsEmpty;
+        if (ChessMode == ChessMode.Normal)
+            return true;
+
+        var attacks = GetAttacks(
+            sq: to,
+            pt: PieceTypes.Rook,
+            occ: Board.Pieces() ^ m.ToSquare()
+        );
+
+        var occupied = Board.Pieces(them, PieceTypes.Rook, PieceTypes.Queen);
+
+        return (attacks & occupied).IsEmpty;
+
+        static (Square, Direction) GetRookSquareAndDirection(Square toSq, Square fromSq, Player us)
+        {
+            return toSq > fromSq
+                ? (Square.G1.Relative(us), Direction.West)
+                : (Square.C1.Relative(us), Direction.East);
+        }
     }
 
     private bool IsEnPassantMoveLegal(Square to, Player us, Square from, Square ksq)
@@ -727,7 +752,8 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             Debug.Assert(pc == PieceTypes.King.MakePiece(us));
             Debug.Assert(capturedPiece == PieceTypes.Rook.MakePiece(us));
 
-            var (rookFrom, rookTo) = DoCastle(us, from, ref to, CastlePerform.Do);
+            DoCastle(us, from, ref to, out var rookFrom, out var rookTo, CastlePerform.Do);
+            // var (rookFrom, rookTo) = DoCastle(us, from, ref to, CastlePerform.Do);
 
             posKey ^= Zobrist.Psq(rookFrom, capturedPiece) ^ Zobrist.Psq(rookTo, capturedPiece);
 
@@ -787,7 +813,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         {
             posKey ^= Zobrist.Castleling(state.CastlelingRights);
             state.CastlelingRights &= ~(_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]);
-            posKey ^= Zobrist.Castleling(state.CastlelingRights);
+            //posKey ^= Zobrist.Castleling(state.CastlelingRights);
         }
 
         // Move the piece. The tricky Chess960 castle is handled earlier
@@ -1282,7 +1308,7 @@ public void TakeMove(Move m)
         }
 
         if (type == MoveTypes.Castling)
-            DoCastle(us, from, ref to, CastlePerform.Undo);
+            DoCastle(us, from, ref to, out var _, out var _, CastlePerform.Undo);
         else
         {
             // Note: The parameters are reversed, since we move the piece "back"
@@ -1299,7 +1325,7 @@ public void TakeMove(Move m)
                 {
                     captureSquare -= us.PawnPushDistance();
 
-                    Debug.Assert(GetPiece(to).Type() == PieceTypes.Pawn);
+                    // Debug.Assert(GetPiece(to).Type() == PieceTypes.Pawn);
                     Debug.Assert(to == State.Previous.EnPassantSquare);
                     Debug.Assert(to.RelativeRank(us) == Ranks.Rank6);
                     Debug.Assert(!IsOccupied(captureSquare));
@@ -1406,31 +1432,28 @@ private static CastleRights OrCastlingRight(Player c, bool isKingSide)
         => (CastleRights)((int)CastleRights.WhiteKing << ((!isKingSide).AsByte() + 2 * c.Side));
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    private (Square, Square) DoCastle(Player us, Square from, ref Square to, CastlePerform castlePerform)
+    private void DoCastle(
+        Player us,
+        Square from,
+        ref Square to,
+        out Square rookFrom,
+        out Square rookTo,
+        CastlePerform castlePerform)
     {
         var kingSide = to > from;
+        var doCastleling = castlePerform == CastlePerform.Do;
 
-        // Castling is encoded as "king captures friendly rook"
-        var rookFromTo = (rookFrom: to, rookTo: (kingSide ? Square.F1 : Square.D1).Relative(us));
-
+        rookFrom = to; // Castling is encoded as "king captures friendly rook"
+        rookTo = (kingSide ? Square.F1 : Square.D1).Relative(us);
         to = (kingSide ? Square.G1 : Square.C1).Relative(us);
 
-        // If we are performing castle move, just swap the squares
-        if (castlePerform == CastlePerform.Do)
-        {
-            (to, from) = (from, to);
-            (rookFromTo.rookFrom, rookFromTo.rookTo) = (rookFromTo.rookTo, rookFromTo.rookFrom);
-        }
-
         // Remove both pieces first since squares could overlap in Chess960
-        RemovePiece(to);
-        RemovePiece(rookFromTo.rookTo);
-        Board.ClearPiece(to);
-        Board.ClearPiece(rookFromTo.rookTo);
-        AddPiece(PieceTypes.King.MakePiece(us), from);
-        AddPiece(PieceTypes.Rook.MakePiece(us), rookFromTo.rookFrom);
-
-        return rookFromTo;
+        RemovePiece(doCastleling ? from : to);
+        RemovePiece(doCastleling ? rookFrom : rookTo);
+        Board.ClearPiece(doCastleling ? from : to);
+        Board.ClearPiece(doCastleling ? rookFrom : rookTo);
+        AddPiece(PieceTypes.King.MakePiece(us), doCastleling ? to : from);
+        AddPiece(PieceTypes.Rook.MakePiece(us), doCastleling ? rookTo : rookFrom);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
index 9f43cfc4..5ac40917 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
@@ -42,12 +42,12 @@ public class Uci : IUci
 
     private static readonly string[] OptionTypeStrings = Enum.GetNames(typeof(UciOptionType));
 
-    protected readonly ObjectPool<IMoveList> MoveListPool;
+    protected readonly ObjectPool<MoveList> MoveListPool;
 
     private readonly ObjectPool<StringBuilder> _pvPool;
     private readonly Dictionary<string, IOption> _options;
 
-    public Uci(ObjectPool<IMoveList> moveListPool)
+    public Uci(ObjectPool<MoveList> moveListPool)
     {
         var policy = new StringBuilderPooledObjectPolicy();
         _pvPool = new DefaultObjectPool<StringBuilder>(policy, 128);
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 1f9ed389..671b21fa 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -54,7 +54,8 @@
         <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
         <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
         <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
+        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.2" />
+        <PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
         <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0"/>
     </ItemGroup>
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
index aabaa738..6534aa91 100644
--- a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
+++ b/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
@@ -32,7 +32,7 @@ namespace Rudzoft.ChessLib.Tables.Perft;
 public struct PerftTableEntry : IPerftTableEntry
 {
     public HashKey Key { get; set; }
-    
+
     public UInt128 Count { get; set; }
 
     public int Depth { get; set; }
@@ -52,18 +52,16 @@ public sealed class PerftTable : HashTable<IPerftTableEntry>
 
     public PerftTable()
     {
-        Initialize(ElementSize, HashMemory, static key => new PerftTableEntry { Key = key, Count = ulong.MinValue, Depth = -1 });
+        Initialize(ElementSize, HashMemory, static key => new PerftTableEntry { Key = key, Count = UInt128.MinValue, Depth = -1 });
         _mask = Count - 1;
     }
 
-    public ref IPerftTableEntry TryGet(in IPosition pos, int depth, out bool found)
+    public ref IPerftTableEntry TryGet(in HashKey key, int depth, out bool found)
     {
-        var posKey = pos.State.PositionKey;
-        var entryKey = posKey & _mask ^ depth;
+        var entryKey = key.Key % (ulong)Count;
+
         ref var entry = ref this[entryKey];
-        found = entry.Key == entryKey && entry.Depth == depth;
-        // if (!found)
-        //     entry.Key = entryKey;
+        found = entry.Key == key && entry.Depth == depth;
         return ref entry;
     }
 
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 66291080..35b103f5 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -241,7 +241,6 @@ static BitBoards()
         }
 
         // mini local helpers
-
         Span<PieceTypes> validMagicPieces = stackalloc PieceTypes[] { PieceTypes.Bishop, PieceTypes.Rook };
 
         var bb = AllSquares;
@@ -279,7 +278,6 @@ static BitBoards()
             InitializeKingRing(s1, sq, file);
         }
 
-
         SlotFileBB =
         [
             FileEBB | FileFBB | FileGBB | FileHBB, // King
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index 9d4784d9..4986dbab 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -5,13 +5,13 @@
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Akka" Version="1.5.14" />
-      <PackageReference Include="Akka.DependencyInjection" Version="1.5.14" />
+      <PackageReference Include="Akka" Version="1.5.16" />
+      <PackageReference Include="Akka.DependencyInjection" Version="1.5.16" />
       <PackageReference Include="CommandLineParser" Version="2.9.1" />
       <PackageReference Include="Serilog" Version="3.1.1" />
-      <PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
+      <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
       <PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/>
-      <PackageReference Include="Serilog.Settings.Configuration" Version="7.0.1" />
+      <PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
       <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/>
       <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
       <PackageReference Include="Timestamp" Version="1.0.2"/>

From 0509f68b96bcd63db8b36b5501cb6f98da6190cb Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 20:24:27 +0100
Subject: [PATCH 080/119] Added first draft for PGN generate

---
 src/Rudzoft.ChessLib.PGN/PgnGenerator.cs | 83 ++++++++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 src/Rudzoft.ChessLib.PGN/PgnGenerator.cs

diff --git a/src/Rudzoft.ChessLib.PGN/PgnGenerator.cs b/src/Rudzoft.ChessLib.PGN/PgnGenerator.cs
new file mode 100644
index 00000000..2ac311f1
--- /dev/null
+++ b/src/Rudzoft.ChessLib.PGN/PgnGenerator.cs
@@ -0,0 +1,83 @@
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2024 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Extensions.ObjectPool;
+
+namespace Rudzoft.ChessLib.PGN;
+
+public sealed class PgnGenerator : IResettable
+{
+    private readonly StringBuilder _pgnBuilder = new();
+    private readonly List<string> _moves = [];
+
+    public PgnGenerator AddMetadata(
+        string eventStr,
+        string site,
+        string date,
+        string round,
+        string whitePlayer,
+        string blackPlayer,
+        string result)
+    {
+        _pgnBuilder.AppendLine($"[Event \"{eventStr}\"]");
+        _pgnBuilder.AppendLine($"[Site \"{site}\"]");
+        _pgnBuilder.AppendLine($"[Date \"{date}\"]");
+        _pgnBuilder.AppendLine($"[Round \"{round}\"]");
+        _pgnBuilder.AppendLine($"[White \"{whitePlayer}\"]");
+        _pgnBuilder.AppendLine($"[Black \"{blackPlayer}\"]");
+        _pgnBuilder.AppendLine($"[Result \"{result}\"]");
+        return this;
+    }
+
+    public PgnGenerator AddMove(string move)
+    {
+        _moves.Add(move);
+        return this;
+    }
+
+    public string Build()
+    {
+        var s = CollectionsMarshal.AsSpan(_moves);
+        for (var i = 0; i < s.Length; i += 2)
+        {
+            var moveNumber = ((i / 2) + 1);
+            _pgnBuilder.Append($"{moveNumber}. {s[i]} ");
+            if (i + 1 < s.Length)
+                _pgnBuilder.Append($"{s[i + 1]} ");
+        }
+
+        _pgnBuilder.AppendLine();
+        return _pgnBuilder.ToString();
+    }
+
+    public bool TryReset()
+    {
+        _pgnBuilder.Clear();
+        return true;
+    }
+}
\ No newline at end of file

From af47ed8344f34c0cf33df725caaf438390344177 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 20:24:42 +0100
Subject: [PATCH 081/119] testing out ValMove no longer uses properties

---
 src/Rudzoft.ChessLib/Types/ValMove.cs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Types/ValMove.cs b/src/Rudzoft.ChessLib/Types/ValMove.cs
index cb4d5bdb..7c37a97c 100644
--- a/src/Rudzoft.ChessLib/Types/ValMove.cs
+++ b/src/Rudzoft.ChessLib/Types/ValMove.cs
@@ -35,9 +35,9 @@ public struct ValMove : IEquatable<ValMove>, IComparable<ValMove>
 {
     public static readonly ValMove Empty = new();
 
-    public Move Move { get; set; }
+    public int Score;
 
-    public int Score { get; set; }
+    public Move Move;
 
     private ValMove(Move m, int s)
     {
@@ -60,8 +60,8 @@ private ValMove(Move m, int s)
     public bool Equals(ValMove other) => Move.Equals(other.Move);
 
     public readonly int CompareTo(ValMove other) => other.Score.CompareTo(Score);
-    
+
     public readonly override int GetHashCode() => Move.GetHashCode();
 
-    public readonly override string ToString()=> $"{Move}, {Score}";
+    public readonly override string ToString() => $"{Move}, {Score}";
 }
\ No newline at end of file

From 61afc9398cd32373dc2cb3b2c9f2723f97509557 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 20:25:08 +0100
Subject: [PATCH 082/119] Added explicit Vector2 operator for Score

---
 src/Rudzoft.ChessLib/Types/Score.cs | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index 764b3e52..6fef05b4 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -67,6 +67,9 @@ public struct Score : IEquatable<Score>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Score(Vector2 v) => new(v);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Vector2(Score v) => v._data;
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Score operator *(Score s, int v) => new(Vector2.Multiply(s._data, v));
 
@@ -92,16 +95,16 @@ public readonly void Deconstruct(out float mg, out float eg)
         mg = _data.X;
         eg = _data.Y;
     }
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void AddMg(int v) => _data.X += v;
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void AddEg(int v) => _data.Y += v;
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void SubtractMg(int v) => _data.X -= v;
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void SubtractEg(int v) => _data.Y -= v;
 

From c9eec949d6a12c6ef97175af5b64fd6802cea83a Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 20:26:41 +0100
Subject: [PATCH 083/119] Added test version for new Perft Table

---
 .../Hash/Tables/Transposition/PertT.cs        | 81 +++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs

diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs
new file mode 100644
index 00000000..1e90996c
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs
@@ -0,0 +1,81 @@
+using System.Runtime.CompilerServices;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
+
+public struct PerftEntry
+{
+    public HashKey Hash;
+    public UInt128 Nodes;
+    public byte Depth;
+
+    public HashKey GetHash() => Hash;
+    public byte GetAge() => 1; // Age doesn't matter when considering perft transposition table entries
+    public byte GetDepth() => Depth;
+}
+
+public sealed class PertT
+{
+    public const int DefaultTTSize = 64;
+    public const int NumTTBuckets = 2;
+    public const ulong SearchEntrySize = 16;
+    public const ulong PerftEntrySize = 24;
+    public const byte AlphaFlag = 1;
+    public const byte BetaFlag = 2;
+    public const byte ExactFlag = 3;
+    public const int Checkmate = 9000;
+
+    public PerftEntry[] Entries;
+    public ulong Size;
+
+    public PertT(ulong size)
+    {
+        Resize(in size, Unsafe.SizeOf<PerftEntry>());
+    }
+
+    public (UInt128, bool) Get(in HashKey hash, byte depth) {
+        ref var entry = ref Probe(hash);
+        if (entry.Hash == hash && entry.Depth == depth) {
+            return (entry.Nodes, true);
+        }
+        return (default, false);
+    }
+
+    public void Set(in HashKey hash, byte depth, in ulong nodes)
+    {
+        ref var entry = ref Probe(in hash);
+        entry.Hash = hash.Key;
+        entry.Depth = depth;
+        entry.Nodes = nodes;
+    }
+
+    public void Resize(in ulong sizeInMB, int entrySize)
+    {
+        Size = (sizeInMB * 1024 * 1024) / (ulong)entrySize;
+        Entries = new PerftEntry[Size];
+    }
+
+    public ref PerftEntry Probe(in HashKey hash)
+    {
+        var index = hash.Key % Size;
+        return ref Entries[index];
+    }
+
+    public ref PerftEntry Store(in ulong hash, byte depth, byte currAge)
+    {
+        var index = hash % Size;
+        return ref Entries[index];
+    }
+
+    public void Uninitialize()
+    {
+        Entries = null;
+        Size = 0;
+    }
+
+    public void Clear()
+    {
+        for (ulong idx = 0; idx < Size; idx++)
+            Entries[idx] = default;
+    }
+}
\ No newline at end of file

From db7ae5877fe0f791504e414ace6f83447ec3ef67 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 20:27:26 +0100
Subject: [PATCH 084/119] Updated MagicBB to use PEXT (from StockFish 16)

---
 src/Rudzoft.ChessLib/Types/MagicBB.cs | 210 ++++++++++++++------------
 1 file changed, 110 insertions(+), 100 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index 7a205e64..1e4b1a5a 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -1,31 +1,45 @@
 /*
- * Adapted from PortFish.
- * Minor modernizations by Rudy Alex Kohn
+ * Adapted to c# from StockFish.
  */
 
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics.X86;
 using Rudzoft.ChessLib.Hash;
 
 namespace Rudzoft.ChessLib.Types;
 
-public static class MagicBB
+public sealed class Magics
 {
-    private static readonly BitBoard[] RMasks = new BitBoard[64];
-    private static readonly BitBoard[] RMagics = new BitBoard[64];
-    private static readonly BitBoard[][] RAttacks = new BitBoard[64][];
-    private static readonly int[] RShifts = new int[64];
+    public BitBoard Mask { get; init; } = BitBoard.Empty;
+
+    public BitBoard Magic { get; set; } = BitBoard.Empty;
+
+    public BitBoard[] Attacks { get; set; } = new BitBoard[64];
 
-    private static readonly BitBoard[] BMasks = new BitBoard[64];
-    private static readonly BitBoard[] BMagics = new BitBoard[64];
-    private static readonly BitBoard[][] BAttacks = new BitBoard[64][];
-    private static readonly int[] BShifts = new int[64];
+    public uint Shift { get; set; }
 
-    private static readonly int[][] MagicBoosters =
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public uint Index(in BitBoard occupied)
+    {
+        return Bmi2.X64.IsSupported switch
+        {
+            true  => (uint)Bmi2.X64.ParallelBitExtract(occupied.Value, Mask.Value),
+            var _ => (uint)((occupied & Mask).Value * Magic >> (int)Shift).Value
+        };
+    }
+}
+
+public static class MagicBB
+{
+    private static readonly Magics[] RookMagics = new Magics[64];
+    private static readonly Magics[] BishopMagics = new Magics[64];
+
+    // Optimal PRNG seeds to pick the correct magics in the shortest time
+    private static readonly ulong[] Seeds =
     [
-        [3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996],
-        [1059, 3608, 605, 3234, 3326, 38, 2029, 3043]
+        728UL, 10316UL, 55013UL, 32803UL, 12281UL, 15100UL, 16645UL, 255UL
     ];
 
     private static readonly byte[] BitCount8Bit = new byte[256];
@@ -44,35 +58,36 @@ static MagicBB()
         Span<Direction> bishopDeltas = stackalloc Direction[]
             { Direction.NorthEast, Direction.SouthEast, Direction.SouthWest, Direction.NorthWest };
 
-        var occupancy = new BitBoard[4096];
-        var reference = new BitBoard[4096];
+        var rookTable = new BitBoard[0x19000]; // To store rook attacks
+        var bishopTable = new BitBoard[0x1480]; // To store bishop attacks
+
+        var epochs = new int[0x1000];
+        var occupancy = new BitBoard[0x1000];
+        var reference = new BitBoard[0x1000];
         var rk = new RKiss();
 
         ref var occupancyRef = ref MemoryMarshal.GetArrayDataReference(occupancy);
         ref var referenceRef = ref MemoryMarshal.GetArrayDataReference(reference);
+        ref var epochsRef = ref MemoryMarshal.GetArrayDataReference(epochs);
 
         InitMagics(
-            pt: PieceTypes.Rook,
-            attacks: RAttacks,
-            magics: RMagics,
-            masks: RMasks,
-            shifts: RShifts,
-            deltas: rookDeltas,
-            occupancyRef: ref occupancyRef,
-            referenceRef: ref referenceRef,
-            rk: rk
+            rookTable,
+            RookMagics,
+            rookDeltas,
+            ref occupancyRef,
+            ref referenceRef,
+            ref epochsRef,
+            rk
         );
 
         InitMagics(
-            pt: PieceTypes.Bishop,
-            attacks: BAttacks,
-            magics: BMagics,
-            masks: BMasks,
-            shifts: BShifts,
-            deltas: bishopDeltas,
-            occupancyRef: ref occupancyRef,
-            referenceRef: ref referenceRef,
-            rk: rk
+            bishopTable,
+            BishopMagics,
+            bishopDeltas,
+            ref occupancyRef,
+            ref referenceRef,
+            ref epochsRef,
+            rk
         );
 
         var end = Stopwatch.GetElapsedTime(start);
@@ -90,13 +105,13 @@ private static int PopCountMax15(ulong b)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard RookAttacks(this Square s, in BitBoard occ)
     {
-        return RAttacks[s.AsInt()][((occ & RMasks[s.AsInt()]).Value * RMagics[s.AsInt()].Value) >> RShifts[s.AsInt()]];
+        return RookMagics[s.AsInt()].Attacks[RookMagics[s.AsInt()].Index(in occ)];
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard BishopAttacks(this Square s, in BitBoard occ)
     {
-        return BAttacks[s.AsInt()][((occ & BMasks[s.AsInt()]).Value * BMagics[s.AsInt()].Value) >> BShifts[s.AsInt()]];
+        return BishopMagics[s.AsInt()].Attacks[BishopMagics[s.AsInt()].Index(in occ)];
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -106,126 +121,121 @@ public static BitBoard QueenAttacks(this Square s, in BitBoard occ)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static uint MagicIndex(PieceTypes pt, Square s, in BitBoard occ)
+    private static BitBoard SafeDistance(Square sq, Direction step)
     {
-        return pt == PieceTypes.Rook
-            ? (uint)(((occ & RMasks[s.AsInt()]).Value * RMagics[s.AsInt()].Value) >> RShifts[s.AsInt()])
-            : (uint)(((occ & BMasks[s.AsInt()]).Value * BMagics[s.AsInt()].Value) >> BShifts[s.AsInt()]);
+        var to = sq + step;
+        return to.IsOk && sq.Distance(to) <= 2 ? to.AsBb() : BitBoard.Empty;
     }
 
-    // init_magics() computes all rook and bishop attacks at startup. Magic
+    // InitMagics() computes all rook and bishop attacks at startup. Magic
     // bitboards are used to look up attacks of sliding pieces. As a reference see
     // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
     // use the so called "fancy" approach.
 
     private static void InitMagics(
-        PieceTypes pt,
-        BitBoard[][] attacks,
-        BitBoard[] magics,
-        BitBoard[] masks,
-        int[] shifts,
+        BitBoard[] table,
+        Magics[] magics,
         ReadOnlySpan<Direction> deltas,
         ref BitBoard occupancyRef,
         ref BitBoard referenceRef,
-        IRKiss rk)
+        ref int epochRef,
+        IRKiss rk
+    )
     {
+        var cnt = 0;
+        var size = 0;
         var rankMask = Rank.Rank1.RankBB() | Rank.Rank8.RankBB();
         var fileMask = File.FileA.FileBB() | File.FileH.FileBB();
+        ref var magicsRef = ref MemoryMarshal.GetArrayDataReference(magics);
 
-        for (var s = Squares.a1; s <= Squares.h8; s++)
+        for (var s = 0; s < 64; s++)
         {
             var sq = new Square(s);
-            // Board edges are not considered in the relevant occupancies
-            var edges = (rankMask & ~sq.Rank) | (fileMask & ~sq.File);
+            var rank = sq.Rank;
+            var edges = (rankMask & ~rank) | (fileMask & ~sq.File);
 
             // Given a square 's', the mask is the bitboard of sliding attacks from
             // 's' computed on an empty board. The index must be big enough to contain
             // all the attacks for each possible subset of the mask and so is 2 power
             // the number of 1s of the mask. Hence we deduce the size of the shift to
             // apply to the 64 or 32 bits word to get the index.
-            masks[s.AsInt()] = SlidingAttack(deltas, s, 0) & ~edges;
-            shifts[s.AsInt()] = 64 - PopCountMax15(masks[s.AsInt()].Value);
+            ref var m = ref Unsafe.Add(ref magicsRef, s);
+            m = new()
+            {
+                Mask = SlidingAttack(deltas, sq, 0) & ~edges,
+            };
+            m.Shift = (uint)(64 - m.Mask.Count);
+
+            // Set the offset for the attacks table of the square. We have individual
+            // table sizes for each square with "Fancy Magic Bitboards".
+            m.Attacks = sq == Square.A1 ? table : Unsafe.Add(ref magicsRef, s - 1).Attacks[size..];
+
 
             // Use Carry-Rippler trick to enumerate all subsets of masks[s] and
             // store the corresponding sliding attack bitboard in reference[].
             var b = BitBoard.Empty;
-            var size = 0;
+            size = 0;
             do
             {
                 Unsafe.Add(ref occupancyRef, size) = b;
-                Unsafe.Add(ref referenceRef, size) = SlidingAttack(deltas, sq, b);
+                ref var reference = ref Unsafe.Add(ref referenceRef, size);
+                reference = SlidingAttack(deltas, sq, in b);
+
+                if (Bmi2.X64.IsSupported)
+                    m.Attacks[Bmi2.X64.ParallelBitExtract(b.Value, m.Mask.Value)] = reference;
+
                 size++;
-                b = (b.Value - masks[s.AsInt()].Value) & masks[s.AsInt()];
+                b = (b.Value - m.Mask.Value) & m.Mask;
             } while (b.IsNotEmpty);
 
-            // Set the offset for the table of the next square. We have individual
-            // table sizes for each square with "Fancy Magic Bitboards".
-            var booster = MagicBoosters[1][sq.Rank.AsInt()];
+            if (Bmi2.X64.IsSupported)
+                continue;
 
-            attacks[s.AsInt()] = new BitBoard[size];
+            rk.Seed = Seeds[rank.AsInt()];
 
             // Find a magic for square 's' picking up an (almost) random number
             // until we find the one that passes the verification test.
-            int i;
-            do
+            for (var i = 0; i < size;)
             {
-                magics[s.AsInt()] = PickRandom(masks[s.AsInt()], rk, booster);
-                Array.Clear(attacks[s.AsInt()], 0, size);
+                for (m.Magic = BitBoard.Empty; (BitBoard.Create(m.Magic.Value * m.Mask.Value) >> 56).Value < 6;)
+                    m.Magic = rk.Sparse();
 
                 // A good magic must map every possible occupancy to an index that
                 // looks up the correct sliding attack in the attacks[s] database.
                 // Note that we build up the database for square 's' as a side
-                // effect of verifying the magic.
-                for (i = 0; i < size; i++)
+                // effect of verifying the magic. Keep track of the attempt count
+                // and save it in epoch[], little speed-up trick to avoid resetting
+                // m.attacks[] after every failed attempt.
+                for (++cnt, i = 0; i < size; ++i)
                 {
-                    var idx = MagicIndex(pt, s, in Unsafe.Add(ref occupancyRef, i));
-                    var attack = attacks[s.AsInt()][idx];
-                    ref var reference = ref Unsafe.Add(ref referenceRef, i);
+                    var idx = m.Index(Unsafe.Add(ref occupancyRef, i));
 
-                    if (attack.IsNotEmpty && attack != reference)
-                        break;
+                    ref var epoch = ref Unsafe.Add(ref epochRef, idx);
 
-                    attacks[s.AsInt()][idx] = reference;
+                    if (epoch < cnt)
+                    {
+                        epoch = cnt;
+                        m.Attacks[idx] = Unsafe.Add(ref referenceRef, i);
+                    }
+                    else if (m.Attacks[idx] != Unsafe.Add(ref referenceRef, i))
+                        break;
                 }
-            } while (i != size);
+            }
         }
     }
 
-    private static BitBoard SlidingAttack(ReadOnlySpan<Direction> deltas, Square sq, BitBoard occupied)
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    private static BitBoard SlidingAttack(ReadOnlySpan<Direction> deltas, Square sq, in BitBoard occupied)
     {
         var attack = BitBoard.Empty;
 
         foreach (var delta in deltas)
         {
-            for (var s = sq + delta; s.IsOk && s.Distance(s - delta) == 1; s += delta)
-            {
-                attack |= s;
-
-                if (occupied.Contains(s))
-                    break;
-            }
+            var s = sq;
+            while (SafeDistance(s, delta) && !occupied.Contains(s))
+                attack |= (s += delta).AsBb();
         }
 
         return attack;
     }
-
-    private static BitBoard PickRandom(BitBoard mask, IRKiss rk, int booster)
-    {
-        // Values s1 and s2 are used to rotate the candidate magic of a
-        // quantity known to be the optimal to quickly find the magics.
-        var s1 = booster & 63;
-        var s2 = (booster >> 6) & 63;
-
-        while (true)
-        {
-            ulong magic = rk.Rand();
-            magic = (magic >> s1) | (magic << (64 - s1));
-            magic &= rk.Rand();
-            magic = (magic >> s2) | (magic << (64 - s2));
-            magic &= rk.Rand();
-
-            if (BitCount8Bit[((mask.Value * magic) >> 56)] >= 6)
-                return magic;
-        }
-    }
 }
\ No newline at end of file

From 171acb2a7939bc4b1a2113c6113b355c5cab45a6 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 23:00:51 +0100
Subject: [PATCH 085/119] cleaned up MagicBB class

---
 src/Rudzoft.ChessLib/Types/MagicBB.cs | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index 1e4b1a5a..f10f8614 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -42,16 +42,11 @@ public static class MagicBB
         728UL, 10316UL, 55013UL, 32803UL, 12281UL, 15100UL, 16645UL, 255UL
     ];
 
-    private static readonly byte[] BitCount8Bit = new byte[256];
-
     [SkipLocalsInit]
     static MagicBB()
     {
         var start = Stopwatch.GetTimestamp();
 
-        for (ulong b = 0; b <= byte.MaxValue; b++)
-            BitCount8Bit[b] = (byte)PopCountMax15(b);
-
         Span<Direction> rookDeltas = stackalloc Direction[]
             { Direction.North, Direction.East, Direction.South, Direction.West };
 
@@ -60,7 +55,6 @@ static MagicBB()
 
         var rookTable = new BitBoard[0x19000]; // To store rook attacks
         var bishopTable = new BitBoard[0x1480]; // To store bishop attacks
-
         var epochs = new int[0x1000];
         var occupancy = new BitBoard[0x1000];
         var reference = new BitBoard[0x1000];
@@ -94,14 +88,6 @@ static MagicBB()
         Console.WriteLine($"MagicBB init took: {end}");
     }
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static int PopCountMax15(ulong b)
-    {
-        b -= (b >> 1) & 0x5555555555555555UL;
-        b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL);
-        return (int)((b * 0x1111111111111111UL) >> 60);
-    }
-
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard RookAttacks(this Square s, in BitBoard occ)
     {

From 6fb6fc8261d2dfc87f4c826821b6b72e72483824 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 23:01:21 +0100
Subject: [PATCH 086/119] Added implicit int operator for Piece + simplify
 usage of previous .AsInt()

---
 src/Rudzoft.ChessLib/Board.cs                 | 32 +++++++++----------
 .../Extensions/PieceExtensions.cs             |  6 ++--
 src/Rudzoft.ChessLib/Fen/Fen.cs               |  6 ++--
 src/Rudzoft.ChessLib/Hash/IZobrist.cs         |  4 +--
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          | 12 +++----
 src/Rudzoft.ChessLib/IBoard.cs                |  2 +-
 .../Polyglot/PolyglotBookZobrist.cs           |  4 +--
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  1 +
 src/Rudzoft.ChessLib/Types/Piece.cs           | 13 +++++---
 src/Rudzoft.ChessLib/Types/Score.cs           | 14 ++++++--
 10 files changed, 54 insertions(+), 40 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index cc3d35df..3e61f9f4 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -87,15 +87,15 @@ public void AddPiece(Piece pc, Square sq)
         _byType[PieceTypes.AllPieces.AsInt()] |= sq;
         _byType[pc.Type().AsInt()] |= sq;
         _bySide[pc.ColorOf().Side] |= sq;
-        _index[sq.AsInt()] = _pieceCount[pc.AsInt()]++;
-        _pieceList[pc.AsInt()][_index[sq.AsInt()]] = sq;
-        _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf()).AsInt()]++;
+        _index[sq.AsInt()] = _pieceCount[pc]++;
+        _pieceList[pc][_index[sq.AsInt()]] = sq;
+        _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf())]++;
     }
 
     public void RemovePiece(Square sq)
     {
         Debug.Assert(sq.IsOk);
-        
+
         // WARNING: This is not a reversible operation. If we remove a piece in MakeMove() and
         // then replace it in TakeMove() we will put it at the end of the list and not in its
         // original place, it means index[] and pieceList[] are not invariant to a MakeMove() +
@@ -105,11 +105,11 @@ public void RemovePiece(Square sq)
         _byType[pc.Type().AsInt()] ^= sq;
         _bySide[pc.ColorOf().Side] ^= sq;
         /* board[s] = NO_PIECE;  Not needed, overwritten by the capturing one */
-        var lastSquare = _pieceList[pc.AsInt()][--_pieceCount[pc.AsInt()]];
+        var lastSquare = _pieceList[pc][--_pieceCount[pc]];
         _index[lastSquare.AsInt()] = _index[sq.AsInt()];
-        _pieceList[pc.AsInt()][_index[lastSquare.AsInt()]] = lastSquare;
-        _pieceList[pc.AsInt()][_pieceCount[pc.AsInt()]] = Types.Square.None;
-        _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf()).AsInt()]--;
+        _pieceList[pc][_index[lastSquare.AsInt()]] = lastSquare;
+        _pieceList[pc][_pieceCount[pc]] = Types.Square.None;
+        _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf())]--;
     }
 
     public void ClearPiece(Square sq)
@@ -121,7 +121,7 @@ public void MovePiece(Square from, Square to)
         // accessed just by known occupied squares.
         var f = from.AsInt();
         var t = to.AsInt();
-        
+
         var pc = _pieces[f];
         var fromTo = from | to;
         _byType[PieceTypes.AllPieces.AsInt()] ^= fromTo;
@@ -130,7 +130,7 @@ public void MovePiece(Square from, Square to)
         _pieces[f] = Piece.EmptyPiece;
         _pieces[t] = pc;
         _index[t] = _index[f];
-        _pieceList[pc.AsInt()][_index[t]] = to;
+        _pieceList[pc][_index[t]] = to;
     }
 
     public Piece MovedPiece(Move m) => PieceAt(m.FromSquare());
@@ -150,13 +150,13 @@ public BitBoard Pieces(Player p, PieceTypes pt1, PieceTypes pt2)
 
     public Square Square(PieceTypes pt, Player p)
     {
-        Debug.Assert(_pieceCount[pt.MakePiece(p).AsInt()] == 1);
-        return _pieceList[pt.MakePiece(p).AsInt()][0];
+        Debug.Assert(_pieceCount[pt.MakePiece(p)] == 1);
+        return _pieceList[pt.MakePiece(p)][0];
     }
 
-    public ReadOnlySpan<Square> Squares(PieceTypes pt, Player c)
+    public ReadOnlySpan<Square> Squares(PieceTypes pt, Player p)
     {
-        var pcIndex = pt.MakePiece(c).AsInt();
+        var pcIndex = pt.MakePiece(p);
         var pcCount = _pieceCount[pcIndex];
 
         return pcCount == 0
@@ -164,12 +164,12 @@ public ReadOnlySpan<Square> Squares(PieceTypes pt, Player c)
             : _pieceList[pcIndex].AsSpan()[..pcCount];
     }
 
-    public int PieceCount(Piece pc) => _pieceCount[pc.AsInt()];
+    public int PieceCount(Piece pc) => _pieceCount[pc];
 
     public int PieceCount(PieceTypes pt, Player p) => PieceCount(pt.MakePiece(p));
 
     public int PieceCount(PieceTypes pt)
-        => _pieceCount[pt.MakePiece(Player.White).AsInt()] + _pieceCount[pt.MakePiece(Player.Black).AsInt()];
+        => _pieceCount[pt.MakePiece(Player.White)] + _pieceCount[pt.MakePiece(Player.Black)];
 
     public int PieceCount() => PieceCount(PieceTypes.AllPieces);
 
diff --git a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
index 5cb0ab3e..72a40a57 100644
--- a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
@@ -65,13 +65,13 @@ public static class PieceExtensions
     ];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetPieceChar(this Piece p) => PieceChars[p.AsInt()];
+    public static char GetPieceChar(this Piece p) => PieceChars[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static char GetPieceChar(this PieceTypes p) => PieceChars[(int)p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string GetPieceString(this Piece p) => PieceStrings[p.AsInt()];
+    public static string GetPieceString(this Piece p) => PieceStrings[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static string GetName(this Piece p) => PieceNames[p.Type().AsInt()];
@@ -83,7 +83,7 @@ public static class PieceExtensions
     public static char GetPgnChar(this Piece p) => PgnPieceChars[(int)p.Type()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetUnicodeChar(this Piece p) => PieceUnicodeChar[p.AsInt()];
+    public static char GetUnicodeChar(this Piece p) => PieceUnicodeChar[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static int AsInt(this Pieces piece) => (int)piece;
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index 2cfe71ba..282b5212 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -139,13 +139,13 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
             var pc = new Piece((Pieces)pieceIndex);
             var pt = pc.Type();
 
-            pieceCount[pc.AsInt()]++;
+            pieceCount[pc]++;
 
             var limit = limits[pt.AsInt()];
 
-            if (pieceCount[pc.AsInt()] > limit)
+            if (pieceCount[pc] > limit)
                 throw new InvalidFenException(
-                    $"Invalid fen (piece limit exceeded for {pc}. index={i},limit={limit},count={pieceCount[pc.AsInt()]}) {s.ToString()}");
+                    $"Invalid fen (piece limit exceeded for {pc}. index={i},limit={limit},count={pieceCount[pc]}) {s.ToString()}");
         }
 
         var whitePieces = pieceCount.Slice(1, 5);
diff --git a/src/Rudzoft.ChessLib/Hash/IZobrist.cs b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
index f20dbdeb..6926d796 100644
--- a/src/Rudzoft.ChessLib/Hash/IZobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
@@ -13,8 +13,8 @@ public interface IZobrist
     HashKey ComputeMaterialKey(IPosition pos);
     HashKey ComputePawnKey(IPosition pos);
     HashKey ComputePositionKey(IPosition pos);
-    ref HashKey Psq(Square square, Piece piece);
-    ref HashKey Psq(int pieceCount, Piece piece);
+    ref HashKey Psq(Square square, Piece pc);
+    ref HashKey Psq(int pieceCount, Piece pc);
     ref HashKey Castleling(CastleRights index);
     ref HashKey Castleling(CastleRight index);
     HashKey Side();
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index 9fe47c34..f78f2b06 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -72,17 +72,17 @@ public sealed class Zobrist : IZobrist
     public Zobrist(IRKiss rKiss)
     {
         _zobristPst = new HashKey[Square.Count][];
-        
+
         for (var sq = Squares.a1; sq <= Squares.h8; sq++)
         {
             var s = sq.AsInt();
             _zobristPst[s] = new HashKey[Piece.Count];
             foreach (var pc in Piece.All.AsSpan())
-                _zobristPst[s][pc.AsInt()] = rKiss.Rand();
+                _zobristPst[s][pc] = rKiss.Rand();
         }
 
         _zobristCastling = new HashKey[CastleRight.Count];
-        
+
         for (var cr = CastleRights.None; cr <= CastleRights.Any; cr++)
         {
             var v = cr.AsInt();
@@ -98,7 +98,7 @@ public Zobrist(IRKiss rKiss)
         _zobristEpFile = new HashKey[File.Count];
         for (var i = 0; i < _zobristEpFile.Length; i++)
             _zobristEpFile[i] = rKiss.Rand();
-        
+
         _zobristSide = rKiss.Rand();
         ZobristNoPawn = rKiss.Rand();
     }
@@ -151,10 +151,10 @@ public HashKey ComputePositionKey(IPosition pos)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey Psq(Square square, Piece piece) => ref _zobristPst[square.AsInt()][piece.AsInt()];
+    public ref HashKey Psq(Square square, Piece pc) => ref _zobristPst[square.AsInt()][pc];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey Psq(int pieceCount, Piece piece) => ref _zobristPst[pieceCount][piece.AsInt()];
+    public ref HashKey Psq(int pieceCount, Piece pc) => ref _zobristPst[pieceCount][pc];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public ref HashKey Castleling(CastleRights index) => ref _zobristCastling[index.AsInt()];
diff --git a/src/Rudzoft.ChessLib/IBoard.cs b/src/Rudzoft.ChessLib/IBoard.cs
index 90956d70..29288b12 100644
--- a/src/Rudzoft.ChessLib/IBoard.cs
+++ b/src/Rudzoft.ChessLib/IBoard.cs
@@ -45,7 +45,7 @@ public interface IBoard : IEnumerable<Piece>
     BitBoard Pieces(Player p, PieceTypes pt);
     BitBoard Pieces(Player p, PieceTypes pt1, PieceTypes pt2);
     Square Square(PieceTypes pt, Player p);
-    ReadOnlySpan<Square> Squares(PieceTypes pt, Player c);
+    ReadOnlySpan<Square> Squares(PieceTypes pt, Player p);
     int PieceCount(Piece pc);
     int PieceCount(PieceTypes pt, Player p);
     int PieceCount(PieceTypes pt);
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 01f02d54..21c8a893 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -350,9 +350,9 @@ internal static class PolyglotBookZobrist
 
     internal static ulong Psq(Piece pc, Square sq)
     {
-        return Psq(PieceMapping[pc.AsInt()], sq);
+        return Psq(PieceMapping[pc], sq);
     }
-    
+
     internal static ulong Psq(int piece, Square sq)
         => PsqKeys[piece, sq.AsInt()];
 
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 671b21fa..7edaf37d 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -1,5 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
+        <TargetFramework>net8.0</TargetFramework>
         <Platforms>AnyCPU</Platforms>
         <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index aefcde62..bc81b2fc 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -119,9 +119,9 @@ private Piece(Piece pc) : this(pc.Value) { }
     public static Piece MinValue => BlackKing;
 
     public static Range WhitePieces => new(0, 5);
-    
+
     public static Range BlackPieces => new(6, 11);
-    
+
     public static PieceTypes[] AllTypes { get; } =
     [
         PieceTypes.Pawn,
@@ -142,7 +142,7 @@ private Piece(Piece pc) : this(pc.Value) { }
     public static implicit operator Piece(Pieces pc) => new(pc);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece operator ~(Piece pc) => new(pc.AsInt() ^ 8);
+    public static Piece operator ~(Piece pc) => new(pc ^ 8);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Piece operator +(Piece left, Player right) => new(left.Value + (byte)(right << 3));
@@ -178,10 +178,13 @@ private Piece(Piece pc) : this(pc.Value) { }
     public static Piece operator --(Piece left) => new(left.Value - 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator true(Piece pc) => pc.Value.AsInt() != EmptyPiece.AsInt();
+    public static bool operator true(Piece pc) => pc != EmptyPiece;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator false(Piece pc) => pc == EmptyPiece;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool operator false(Piece pc) => pc.Value.AsInt() == EmptyPiece.AsInt();
+    public static implicit operator int(Piece p) => (int)p.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Player ColorOf() => new((int)Value >> 3);
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/Rudzoft.ChessLib/Types/Score.cs
index 6fef05b4..3365949b 100644
--- a/src/Rudzoft.ChessLib/Types/Score.cs
+++ b/src/Rudzoft.ChessLib/Types/Score.cs
@@ -80,13 +80,23 @@ public struct Score : IEquatable<Score>
     public static Score operator *(Score s, bool b) => b ? s : Zero;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Score operator +(Score s1, Score s2) => new(Vector2.Add(s1._data, s2._data));
+    public static Score operator +(Score s, Score s2) => new(Vector2.Add(s._data, s2._data));
 
-    public static Score operator -(Score s1, Score s2) => new(Vector2.Subtract(s1._data, s2._data));
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Score operator +(Score s, Vector2 v) => new(Vector2.Subtract(s._data, v));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Score operator +(Vector2 v, Score s) => new(Vector2.Subtract(v, s._data));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Score operator +(Score s, int v) => new(Vector2.Add(s._data, new(v, v)));
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Score operator -(Score s1, Score s2) => new(Vector2.Subtract(s1._data, s2._data));
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Score operator -(Score s1, Vector2 s2) => new(Vector2.Subtract(s1._data, s2));
+
     public static readonly Score Zero = new();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]

From 6023d3c826c7e965ad8420bd814ebda27edb1502 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 23:13:15 +0100
Subject: [PATCH 087/119] added int implicit operator to Square

---
 src/Rudzoft.ChessLib/Board.cs                 | 30 +++----
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |  4 +-
 .../Extensions/MathExtensions.cs              |  4 +-
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          |  4 +-
 .../Polyglot/PolyglotBookZobrist.cs           |  2 +-
 src/Rudzoft.ChessLib/Position.cs              | 10 +--
 .../Tables/History/HistoryHeuristic.cs        |  4 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 88 +++++++++----------
 src/Rudzoft.ChessLib/Types/MagicBB.cs         | 10 +--
 src/Rudzoft.ChessLib/Types/Move.cs            |  4 +-
 src/Rudzoft.ChessLib/Types/Piece.cs           |  2 +-
 src/Rudzoft.ChessLib/Types/Square.cs          |  5 +-
 12 files changed, 82 insertions(+), 85 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index 3e61f9f4..59d14e38 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -77,18 +77,18 @@ public void Clear()
         Array.Fill(_index, 0);
     }
 
-    public Piece PieceAt(Square sq) => _pieces[sq.AsInt()];
+    public Piece PieceAt(Square sq) => _pieces[sq];
 
-    public bool IsEmpty(Square sq) => _pieces[sq.AsInt()] == Piece.EmptyPiece;
+    public bool IsEmpty(Square sq) => _pieces[sq] == Piece.EmptyPiece;
 
     public void AddPiece(Piece pc, Square sq)
     {
-        _pieces[sq.AsInt()] = pc;
+        _pieces[sq] = pc;
         _byType[PieceTypes.AllPieces.AsInt()] |= sq;
         _byType[pc.Type().AsInt()] |= sq;
         _bySide[pc.ColorOf().Side] |= sq;
-        _index[sq.AsInt()] = _pieceCount[pc]++;
-        _pieceList[pc][_index[sq.AsInt()]] = sq;
+        _index[sq] = _pieceCount[pc]++;
+        _pieceList[pc][_index[sq]] = sq;
         _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf())]++;
     }
 
@@ -100,37 +100,35 @@ public void RemovePiece(Square sq)
         // then replace it in TakeMove() we will put it at the end of the list and not in its
         // original place, it means index[] and pieceList[] are not invariant to a MakeMove() +
         // TakeMove() sequence.
-        var pc = _pieces[sq.AsInt()];
+        var pc = _pieces[sq];
         _byType[PieceTypes.AllPieces.AsInt()] ^= sq;
         _byType[pc.Type().AsInt()] ^= sq;
         _bySide[pc.ColorOf().Side] ^= sq;
         /* board[s] = NO_PIECE;  Not needed, overwritten by the capturing one */
         var lastSquare = _pieceList[pc][--_pieceCount[pc]];
-        _index[lastSquare.AsInt()] = _index[sq.AsInt()];
-        _pieceList[pc][_index[lastSquare.AsInt()]] = lastSquare;
+        _index[lastSquare] = _index[sq];
+        _pieceList[pc][_index[lastSquare]] = lastSquare;
         _pieceList[pc][_pieceCount[pc]] = Types.Square.None;
         _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf())]--;
     }
 
     public void ClearPiece(Square sq)
-        => _pieces[sq.AsInt()] = Piece.EmptyPiece;
+        => _pieces[sq] = Piece.EmptyPiece;
 
     public void MovePiece(Square from, Square to)
     {
         // _index[from] is not updated and becomes stale. This works as long as _index[] is
         // accessed just by known occupied squares.
-        var f = from.AsInt();
-        var t = to.AsInt();
 
-        var pc = _pieces[f];
+        var pc = _pieces[from];
         var fromTo = from | to;
         _byType[PieceTypes.AllPieces.AsInt()] ^= fromTo;
         _byType[pc.Type().AsInt()] ^= fromTo;
         _bySide[pc.ColorOf().Side] ^= fromTo;
-        _pieces[f] = Piece.EmptyPiece;
-        _pieces[t] = pc;
-        _index[t] = _index[f];
-        _pieceList[pc][_index[t]] = to;
+        _pieces[from] = Piece.EmptyPiece;
+        _pieces[to] = pc;
+        _index[to] = _index[from];
+        _pieceList[pc][_index[to]] = to;
     }
 
     public Piece MovedPiece(Move m) => PieceAt(m.FromSquare());
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index 7e18abcd..87297ddc 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -99,8 +99,8 @@ public KpkBitBase()
     /// <returns></returns>
     private static int Index(Player stm, Square weakKingSq, Square strongKsq, Square strongPawnSq)
     {
-        return strongKsq.AsInt()
-               | (weakKingSq.AsInt() << 6)
+        return strongKsq
+               | (weakKingSq << 6)
                | (stm << 12)
                | (strongPawnSq.File.AsInt() << 13)
                | ((Ranks.Rank7.AsInt() - strongPawnSq.Rank.AsInt()) << 15);
diff --git a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
index 932c7761..8f983f1c 100644
--- a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
@@ -67,7 +67,7 @@ public static class MathExtensions
     public static int ModPow2(int input, int ceil) => input & (ceil - 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int Pow2(this int value) => 1 << BitBoards.Msb(value).AsInt();
+    public static int Pow2(this int value) => 1 << BitBoards.Msb(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool IsEven(this int value) => (value & 1) == 0;
@@ -77,7 +77,7 @@ public static class MathExtensions
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static long MidPoint(this long @this, long that) => (@this + that) >> 1;
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Value Min(this Value @this, Value other) => @this < other ? @this : other;
 }
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index f78f2b06..fa7c7734 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -90,7 +90,7 @@ public Zobrist(IRKiss rKiss)
             var bb = BitBoard.Create((uint)v);
             while (bb)
             {
-                var key = _zobristCastling[1UL << BitBoards.PopLsb(ref bb).AsInt()];
+                var key = _zobristCastling[1UL << BitBoards.PopLsb(ref bb)];
                 _zobristCastling[v] ^= !key.IsEmpty? key : rKiss.Rand();
             }
         }
@@ -151,7 +151,7 @@ public HashKey ComputePositionKey(IPosition pos)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey Psq(Square square, Piece pc) => ref _zobristPst[square.AsInt()][pc];
+    public ref HashKey Psq(Square square, Piece pc) => ref _zobristPst[square][pc];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public ref HashKey Psq(int pieceCount, Piece pc) => ref _zobristPst[pieceCount][pc];
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 21c8a893..8859c6c8 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -354,7 +354,7 @@ internal static ulong Psq(Piece pc, Square sq)
     }
 
     internal static ulong Psq(int piece, Square sq)
-        => PsqKeys[piece, sq.AsInt()];
+        => PsqKeys[piece, sq];
 
     internal static ulong Castle(CastleRight rights)
         => CastleKeys[rights.AsInt()];
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 4ce51a1a..90b2b6dc 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -391,7 +391,7 @@ public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
 
     public BitBoard GetAttacks(Square sq, PieceTypes pt) => GetAttacks(sq, pt, Pieces());
 
-    public CastleRight GetCastleRightsMask(Square sq) => _castlingRightsMask[sq.AsInt()];
+    public CastleRight GetCastleRightsMask(Square sq) => _castlingRightsMask[sq];
 
     public IEnumerator<Piece> GetEnumerator() => Board.GetEnumerator();
 
@@ -809,10 +809,10 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
 
         // Update castling rights if needed
         if (state.CastlelingRights != CastleRight.None &&
-            (_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]) != CastleRight.None)
+            (_castlingRightsMask[from] | _castlingRightsMask[to]) != CastleRight.None)
         {
             posKey ^= Zobrist.Castleling(state.CastlelingRights);
-            state.CastlelingRights &= ~(_castlingRightsMask[from.AsInt()] | _castlingRightsMask[to.AsInt()]);
+            state.CastlelingRights &= ~(_castlingRightsMask[from] | _castlingRightsMask[to]);
             //posKey ^= Zobrist.Castleling(state.CastlelingRights);
         }
 
@@ -1480,8 +1480,8 @@ private void SetCastlingRight(Player stm, Square rookFrom)
         var cr = OrCastlingRight(stm, isKingSide);
 
         State.CastlelingRights |= cr;
-        _castlingRightsMask[kingFrom.AsInt()] |= cr;
-        _castlingRightsMask[rookFrom.AsInt()] |= cr;
+        _castlingRightsMask[kingFrom] |= cr;
+        _castlingRightsMask[rookFrom] |= cr;
         _castlingRookSquare[cr.AsInt()] = rookFrom;
 
         var kingTo = (isKingSide ? Square.G1 : Square.C1).Relative(stm);
diff --git a/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs b/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
index 05b17c46..2a8071aa 100644
--- a/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
+++ b/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
@@ -47,10 +47,10 @@ public void Clear()
     }
 
     public void Set(Player p, Square from, Square to, int value)
-        => _table[p.Side][from.AsInt()][to.AsInt()] = value;
+        => _table[p.Side][from][to] = value;
 
     public int Retrieve(Player p, Square from, Square to)
-        => _table[p.Side][from.AsInt()][to.AsInt()];
+        => _table[p.Side][from][to];
 
     private void Initialize(Player p)
     {
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 35b103f5..3dfd5d82 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -213,17 +213,16 @@ static BitBoards()
             ForwardRanksBB[0][rank] = ~(ForwardRanksBB[1][rank + 1] = ForwardRanksBB[1][rank] | r.BitBoardRank());
         }
 
-        foreach (var player in Player.AllPlayers.AsSpan())
+        foreach (var p in Player.AllPlayers.AsSpan())
         {
-            foreach (var square in Square.All.AsSpan())
+            foreach (var sq in Square.All.AsSpan())
             {
-                var side = player.Side;
-                var s = square.AsInt();
-                var file = square.File;
-                var rank = square.Rank.AsInt();
-                ForwardFileBB[side][s] = ForwardRanksBB[side][rank] & file.BitBoardFile();
-                PawnAttackSpanBB[side][s] = ForwardRanksBB[side][rank] & AdjacentFilesBB[file.AsInt()];
-                PassedPawnMaskBB[side][s] = ForwardFileBB[side][s] | PawnAttackSpanBB[side][s];
+                var side = p.Side;
+                var file = sq.File;
+                var rank = sq.Rank.AsInt();
+                ForwardFileBB[side][sq] = ForwardRanksBB[side][rank] & file.BitBoardFile();
+                PawnAttackSpanBB[side][sq] = ForwardRanksBB[side][rank] & AdjacentFilesBB[file.AsInt()];
+                PassedPawnMaskBB[side][sq] = ForwardFileBB[side][sq] | PawnAttackSpanBB[side][sq];
             }
         }
 
@@ -247,12 +246,9 @@ static BitBoards()
         // Pseudo attacks for all pieces
         while (bb)
         {
-            var s1 = PopLsb(ref bb);
-            var sq = s1.AsInt();
+            var sq = PopLsb(ref bb);
 
-            var file = s1.File;
-
-            InitializePseudoAttacks(s1);
+            InitializePseudoAttacks(sq);
 
             // Compute lines and betweens
             foreach (var validMagicPiece in validMagicPieces)
@@ -267,15 +263,15 @@ static BitBoards()
 
                     var sq2 = s2.AsInt();
 
-                    LineBB[sq][sq2] = (GetAttacks(s1, validMagicPiece, EmptyBitBoard) &
-                                       GetAttacks(s2, validMagicPiece, EmptyBitBoard)) | s1 | s2;
-                    BetweenBB[sq][sq2] = GetAttacks(s1, validMagicPiece, BbSquares[sq2]) &
+                    LineBB[sq][sq2] = (GetAttacks(sq, validMagicPiece, EmptyBitBoard) &
+                                       GetAttacks(s2, validMagicPiece, EmptyBitBoard)) | sq | s2;
+                    BetweenBB[sq][sq2] = GetAttacks(sq, validMagicPiece, BbSquares[sq2]) &
                                          GetAttacks(s2, validMagicPiece, BbSquares[sq]);
                 }
             }
 
             // Compute KingRings
-            InitializeKingRing(s1, sq, file);
+            InitializeKingRing(sq);
         }
 
         SlotFileBB =
@@ -308,33 +304,33 @@ private static BitBoard ComputeKnightAttack(in BitBoard bb)
 
     private static void InitializePseudoAttacks(Square sq)
     {
-        var s = sq.AsInt();
         var b = sq.AsBb();
         var bishopAttacks = sq.BishopAttacks(EmptyBitBoard);
         var rookAttacks = sq.RookAttacks(EmptyBitBoard);
 
         // Pawns
-        PseudoAttacksBB[0][s] = b.NorthEastOne() | b.NorthWestOne();
-        PseudoAttacksBB[1][s] = b.SouthWestOne() | b.SouthEastOne();
-
-        PseudoAttacksBB[PieceTypes.Knight.AsInt()][s] = ComputeKnightAttack(in b);
-        PseudoAttacksBB[PieceTypes.Bishop.AsInt()][s] = bishopAttacks;
-        PseudoAttacksBB[PieceTypes.Rook.AsInt()][s] = rookAttacks;
-        PseudoAttacksBB[PieceTypes.Queen.AsInt()][s] = bishopAttacks | rookAttacks;
-        PseudoAttacksBB[PieceTypes.King.AsInt()][s] = b.NorthOne() | b.SouthOne() | b.EastOne()
+        PseudoAttacksBB[0][sq] = b.NorthEastOne() | b.NorthWestOne();
+        PseudoAttacksBB[1][sq] = b.SouthWestOne() | b.SouthEastOne();
+
+        PseudoAttacksBB[PieceTypes.Knight.AsInt()][sq] = ComputeKnightAttack(in b);
+        PseudoAttacksBB[PieceTypes.Bishop.AsInt()][sq] = bishopAttacks;
+        PseudoAttacksBB[PieceTypes.Rook.AsInt()][sq] = rookAttacks;
+        PseudoAttacksBB[PieceTypes.Queen.AsInt()][sq] = bishopAttacks | rookAttacks;
+        PseudoAttacksBB[PieceTypes.King.AsInt()][sq] = b.NorthOne() | b.SouthOne() | b.EastOne()
                                                       | b.WestOne() | b.NorthEastOne() | b.NorthWestOne()
                                                       | b.SouthEastOne() | b.SouthWestOne();
     }
 
-    private static void InitializeKingRing(Square s1, int sq, File file)
+    private static void InitializeKingRing(Square sq)
     {
         const int pt = (int)PieceTypes.King;
+        var file = sq.File;
 
         // TODO : Change to basic for-loop
         foreach (var player in Player.AllPlayers)
         {
             KingRingBB[player.Side][sq] = PseudoAttacksBB[pt][sq];
-            if (s1.RelativeRank(player) == Ranks.Rank1)
+            if (sq.RelativeRank(player) == Ranks.Rank1)
                 KingRingBB[player.Side][sq] |= KingRingBB[player.Side][sq].Shift(PawnPushDirections[player.Side]);
 
             if (file == Files.FileH)
@@ -380,13 +376,13 @@ public static BitBoard FileBB(File first, File last)
     public static BitBoard SeventhAndEightsRank(Player p) => Ranks7And8BB[p.Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PseudoAttacks(this PieceTypes pt, Square sq) => PseudoAttacksBB[pt.AsInt()][sq.AsInt()];
+    public static BitBoard PseudoAttacks(this PieceTypes pt, Square sq) => PseudoAttacksBB[pt.AsInt()][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KnightAttacks(this Square sq) => PseudoAttacksBB[PieceTypes.Knight.AsInt()][sq.AsInt()];
+    public static BitBoard KnightAttacks(this Square sq) => PseudoAttacksBB[PieceTypes.Knight.AsInt()][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KingAttacks(this Square sq) => PseudoAttacksBB[PieceTypes.King.AsInt()][sq.AsInt()];
+    public static BitBoard KingAttacks(this Square sq) => PseudoAttacksBB[PieceTypes.King.AsInt()][sq];
 
     /// <summary>
     /// Attack for pawn.
@@ -395,7 +391,7 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="p">The player side</param>
     /// <returns>ref to bitboard of attack</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnAttack(this Square sq, Player p) => PseudoAttacksBB[p.Side][sq.AsInt()];
+    public static BitBoard PawnAttack(this Square sq, Player p) => PseudoAttacksBB[p.Side][sq];
 
     /// <summary>
     /// Returns the bitboard representation of the rank of which the square is located.
@@ -436,7 +432,7 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="p">The side, white is north and black is south</param>
     /// <returns>The bitboard of all forward file squares</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ForwardFile(this Square sq, Player p) => ForwardFileBB[p.Side][sq.AsInt()];
+    public static BitBoard ForwardFile(this Square sq, Player p) => ForwardFileBB[p.Side][sq];
 
     /// <summary>
     /// Returns all squares in pawn attack pattern in front of the square.
@@ -445,7 +441,7 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="p">White = north, Black = south</param>
     /// <returns>The bitboard representation</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnAttackSpan(this Square sq, Player p) => PawnAttackSpanBB[p.Side][sq.AsInt()];
+    public static BitBoard PawnAttackSpan(this Square sq, Player p) => PawnAttackSpanBB[p.Side][sq];
 
     /// <summary>
     /// Returns all square of both file and pawn attack pattern in front of square. This is the
@@ -455,13 +451,13 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="p">White = north, Black = south</param>
     /// <returns>The bitboard representation</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PassedPawnFrontAttackSpan(this Square sq, Player p) => PassedPawnMaskBB[p.Side][sq.AsInt()];
+    public static BitBoard PassedPawnFrontAttackSpan(this Square sq, Player p) => PassedPawnMaskBB[p.Side][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ForwardRanks(this Square sq, Player p) => ForwardRanksBB[p.Side][sq.AsInt()];
+    public static BitBoard ForwardRanks(this Square sq, Player p) => ForwardRanksBB[p.Side][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard BitboardBetween(this Square sq1, Square sq2) => BetweenBB[sq1.AsInt()][sq2.AsInt()];
+    public static BitBoard BitboardBetween(this Square sq1, Square sq2) => BetweenBB[sq1][sq2];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Square Get(this in BitBoard bb, int pos) => (int)(bb.Value >> pos) & 0x1;
@@ -476,7 +472,7 @@ public static BitBoard FileBB(File first, File last)
     public static Square Last(this in BitBoard bb) => bb.Msb();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Line(this Square sq1, Square sq2) => LineBB[sq1.AsInt()][sq2.AsInt()];
+    public static BitBoard Line(this Square sq1, Square sq2) => LineBB[sq1][sq2];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard SlotFile(CastleSides cs) => SlotFileBB[cs.AsInt()];
@@ -489,16 +485,16 @@ public static BitBoard FileBB(File first, File last)
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard FrontSquares(this Player p, Square sq)
-        => ForwardRanksBB[p.Side][sq.AsInt()] & sq.BitBoardFile();
+        => ForwardRanksBB[p.Side][sq] & sq.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KingRing(this Square sq, Player p) => KingRingBB[p.Side][sq.AsInt()];
+    public static BitBoard KingRing(this Square sq, Player p) => KingRingBB[p.Side][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int Distance(this Square sq1, Square sq2) => SquareDistance[sq1.AsInt()][sq2.AsInt()];
+    public static int Distance(this Square sq1, Square sq2) => SquareDistance[sq1][sq2];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard DistanceRing(this Square sq, int length) => DistanceRingBB[sq.AsInt()][length];
+    public static BitBoard DistanceRing(this Square sq, int length) => DistanceRingBB[sq][length];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard PromotionRank(this Player p) => PromotionRanks[p.Side];
@@ -762,8 +758,8 @@ private static BitBoard GetAttacks(this in Square sq, PieceTypes pt, in BitBoard
     {
         return pt switch
         {
-            PieceTypes.Knight => PseudoAttacksBB[pt.AsInt()][sq.AsInt()],
-            PieceTypes.King => PseudoAttacksBB[pt.AsInt()][sq.AsInt()],
+            PieceTypes.Knight => PseudoAttacksBB[pt.AsInt()][sq],
+            PieceTypes.King => PseudoAttacksBB[pt.AsInt()][sq],
             PieceTypes.Bishop => sq.BishopAttacks(in occ),
             PieceTypes.Rook => sq.RookAttacks(in occ),
             PieceTypes.Queen => sq.QueenAttacks(in occ),
@@ -772,5 +768,5 @@ private static BitBoard GetAttacks(this in Square sq, PieceTypes pt, in BitBoard
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static BitBoard PseudoAttack(this in Square sq, PieceTypes pt) => PseudoAttacksBB[pt.AsInt()][sq.AsInt()];
+    private static BitBoard PseudoAttack(this in Square sq, PieceTypes pt) => PseudoAttacksBB[pt.AsInt()][sq];
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index f10f8614..4c3eedcd 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -91,13 +91,13 @@ static MagicBB()
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard RookAttacks(this Square s, in BitBoard occ)
     {
-        return RookMagics[s.AsInt()].Attacks[RookMagics[s.AsInt()].Index(in occ)];
+        return RookMagics[s].Attacks[RookMagics[s].Index(in occ)];
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard BishopAttacks(this Square s, in BitBoard occ)
     {
-        return BishopMagics[s.AsInt()].Attacks[BishopMagics[s.AsInt()].Index(in occ)];
+        return BishopMagics[s].Attacks[BishopMagics[s].Index(in occ)];
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -107,10 +107,10 @@ public static BitBoard QueenAttacks(this Square s, in BitBoard occ)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static BitBoard SafeDistance(Square sq, Direction step)
+    private static BitBoard SafeDistance(Square s, Direction step)
     {
-        var to = sq + step;
-        return to.IsOk && sq.Distance(to) <= 2 ? to.AsBb() : BitBoard.Empty;
+        var to = s + step;
+        return to.IsOk && s.Distance(to) <= 2 ? to.AsBb() : BitBoard.Empty;
     }
 
     // InitMagics() computes all rook and bishop attacks at startup. Magic
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index f2686b68..c54948e7 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -51,12 +51,12 @@ public readonly record struct Move(ushort Data) : ISpanFormattable
 {
     private const int MaxMoveStringSize = 5;
 
-    public Move(Square from, Square to) : this((ushort)(to | (from.AsInt() << 6)))
+    public Move(Square from, Square to) : this((ushort)(to | (from << 6)))
     {
     }
 
     public Move(Square from, Square to, MoveTypes moveType, PieceTypes promoPt = PieceTypes.Knight)
-        : this((ushort)(to | (from.AsInt() << 6) | moveType.AsInt() | ((promoPt - PieceTypes.Knight) << 12)))
+        : this((ushort)(to | (from << 6) | moveType.AsInt() | ((promoPt - PieceTypes.Knight) << 12)))
     {
     }
 
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index bc81b2fc..7cfb9518 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -184,7 +184,7 @@ private Piece(Piece pc) : this(pc.Value) { }
     public static bool operator false(Piece pc) => pc == EmptyPiece;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator int(Piece p) => (int)p.Value;
+    public static implicit operator int(Piece pc) => (int)pc.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Player ColorOf() => new((int)Value >> 3);
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index 259ed816..418a49d6 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -222,7 +222,7 @@ public void Deconstruct(out Rank r, out File f)
     public static bool operator !=(Square left, Squares right) => left.Value != right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square operator +(Square left, Square right) => new(left.Value + (byte)right.AsInt());
+    public static Square operator +(Square left, Square right) => new(left.Value + (byte)right);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Square operator +(Square left, int right) => new(left.Value + (byte)right);
@@ -290,6 +290,9 @@ public void Deconstruct(out Rank r, out File f)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool operator false(Square sq) => !sq.IsOk;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator int(Square sq) => (int)sq.Value;
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Square Relative(Player p) => (int)Value ^ (p.Side * 56);
 

From 7cbabb8547d7877579e209b5b1e24eccac5456b9 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 23:29:36 +0100
Subject: [PATCH 088/119] added implicit Player byte operator

---
 .../IterateBenchmark.cs                       |  2 +-
 src/Rudzoft.ChessLib/Board.cs                 | 12 ++--
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs | 36 ++++++------
 .../MoveGeneration/MoveGenerator.cs           |  2 +-
 src/Rudzoft.ChessLib/Position.cs              | 30 +++++-----
 .../Protocol/UCI/HiResTimer.cs                |  4 +-
 .../Protocol/UCI/MovesToGoModel.cs            |  2 +-
 .../Protocol/UCI/SearchParameters.cs          | 32 +++++------
 .../Tables/History/HistoryHeuristic.cs        | 14 ++---
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 57 +++++++++----------
 src/Rudzoft.ChessLib/Types/CastleRight.cs     |  2 +-
 src/Rudzoft.ChessLib/Types/Piece.cs           |  2 +-
 src/Rudzoft.ChessLib/Types/Player.cs          |  9 ++-
 src/Rudzoft.ChessLib/Types/Rank.cs            |  6 +-
 src/Rudzoft.ChessLib/Types/Square.cs          |  2 +-
 15 files changed, 107 insertions(+), 105 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
index 84cb795f..e9daf4ef 100644
--- a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
@@ -15,7 +15,7 @@ public void IterateOne()
         var res = 0;
         for (var i = 0; i < N; ++i)
             foreach (var player in players)
-                res += player.Side;
+                res += player;
 
         N = res;
     }
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index 59d14e38..70148009 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -86,7 +86,7 @@ public void AddPiece(Piece pc, Square sq)
         _pieces[sq] = pc;
         _byType[PieceTypes.AllPieces.AsInt()] |= sq;
         _byType[pc.Type().AsInt()] |= sq;
-        _bySide[pc.ColorOf().Side] |= sq;
+        _bySide[pc.ColorOf()] |= sq;
         _index[sq] = _pieceCount[pc]++;
         _pieceList[pc][_index[sq]] = sq;
         _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf())]++;
@@ -103,7 +103,7 @@ public void RemovePiece(Square sq)
         var pc = _pieces[sq];
         _byType[PieceTypes.AllPieces.AsInt()] ^= sq;
         _byType[pc.Type().AsInt()] ^= sq;
-        _bySide[pc.ColorOf().Side] ^= sq;
+        _bySide[pc.ColorOf()] ^= sq;
         /* board[s] = NO_PIECE;  Not needed, overwritten by the capturing one */
         var lastSquare = _pieceList[pc][--_pieceCount[pc]];
         _index[lastSquare] = _index[sq];
@@ -124,7 +124,7 @@ public void MovePiece(Square from, Square to)
         var fromTo = from | to;
         _byType[PieceTypes.AllPieces.AsInt()] ^= fromTo;
         _byType[pc.Type().AsInt()] ^= fromTo;
-        _bySide[pc.ColorOf().Side] ^= fromTo;
+        _bySide[pc.ColorOf()] ^= fromTo;
         _pieces[from] = Piece.EmptyPiece;
         _pieces[to] = pc;
         _index[to] = _index[from];
@@ -139,12 +139,12 @@ public void MovePiece(Square from, Square to)
 
     public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2) => _byType[pt1.AsInt()] | _byType[pt2.AsInt()];
 
-    public BitBoard Pieces(Player p) => _bySide[p.Side];
+    public BitBoard Pieces(Player p) => _bySide[p];
 
-    public BitBoard Pieces(Player p, PieceTypes pt) => _bySide[p.Side] & _byType[pt.AsInt()];
+    public BitBoard Pieces(Player p, PieceTypes pt) => _bySide[p] & _byType[pt.AsInt()];
 
     public BitBoard Pieces(Player p, PieceTypes pt1, PieceTypes pt2)
-        => _bySide[p.Side] & (_byType[pt1.AsInt()] | _byType[pt2.AsInt()]);
+        => _bySide[p] & (_byType[pt1.AsInt()] | _byType[pt2.AsInt()]);
 
     public Square Square(PieceTypes pt, Player p)
     {
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index 87297ddc..4c340378 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -125,27 +125,27 @@ public static KpkPosition Create(int idx)
             var psq = Square.Create(new(Ranks.Rank7.AsInt() - ((idx >> 15) & 0x7)), new((idx >> 13) & 0x3));
 
             // Invalid if two pieces are on the same square or if a king can be captured
-            if (ksq[Player.White.Side].Distance(ksq[Player.Black.Side]) <= 1
-                || ksq[Player.White.Side] == psq
-                || ksq[Player.Black.Side] == psq
-                || (stm.IsWhite && (psq.PawnAttack(Player.White) & ksq[Player.Black.Side]).IsNotEmpty))
+            if (ksq[Player.White].Distance(ksq[Player.Black]) <= 1
+                || ksq[Player.White] == psq
+                || ksq[Player.Black] == psq
+                || (stm.IsWhite && (psq.PawnAttack(Player.White) & ksq[Player.Black]).IsNotEmpty))
                 results = Results.None;
 
             // Win if the pawn can be promoted without getting captured
             else if (stm.IsWhite
                      && psq.Rank == Ranks.Rank7
-                     && ksq[Player.White.Side] != psq + Directions.North
-                     && (ksq[Player.Black.Side].Distance(psq + Directions.North) > 1
-                         || ksq[Player.White.Side].Distance(psq + Directions.North) == 1))
+                     && ksq[Player.White] != psq + Directions.North
+                     && (ksq[Player.Black].Distance(psq + Directions.North) > 1
+                         || ksq[Player.White].Distance(psq + Directions.North) == 1))
                 results = Results.Win;
 
             // Draw if it is stalemate or the black king can capture the pawn
             else if (stm.IsBlack
-                     && ((PieceTypes.King.PseudoAttacks(ksq[Player.Black.Side]) &
-                          ~(PieceTypes.King.PseudoAttacks(ksq[Player.White.Side]) | psq.PawnAttack(Player.White)))
+                     && ((PieceTypes.King.PseudoAttacks(ksq[Player.Black]) &
+                          ~(PieceTypes.King.PseudoAttacks(ksq[Player.White]) | psq.PawnAttack(Player.White)))
                          .IsEmpty
-                         || !(PieceTypes.King.PseudoAttacks(ksq[Player.Black.Side]) &
-                              ~PieceTypes.King.PseudoAttacks(ksq[Player.White.Side]) & psq).IsEmpty))
+                         || !(PieceTypes.King.PseudoAttacks(ksq[Player.Black]) &
+                              ~PieceTypes.King.PseudoAttacks(ksq[Player.White]) & psq).IsEmpty))
                 results = Results.Draw;
 
             // Position will be classified later in initialization
@@ -182,15 +182,15 @@ public Results Classify(ReadOnlySpan<KpkPosition> db)
                 // Single push
                 if (PawnSquare.Rank < Rank.Rank7)
                     r |= db[
-                        Index(Player.Black, KingSquares[Player.Black.Side], KingSquares[Player.White.Side],
+                        Index(Player.Black, KingSquares[Player.Black], KingSquares[Player.White],
                             PawnSquare + Directions.North)].Result;
 
                 // Double push
                 if (PawnSquare.Rank == Rank.Rank2
-                    && PawnSquare + Directions.North != KingSquares[Player.White.Side]
-                    && PawnSquare + Directions.North != KingSquares[Player.Black.Side])
+                    && PawnSquare + Directions.North != KingSquares[Player.White]
+                    && PawnSquare + Directions.North != KingSquares[Player.Black])
                     r |= db[
-                        Index(Player.Black, KingSquares[Player.Black.Side], KingSquares[Player.White.Side],
+                        Index(Player.Black, KingSquares[Player.Black], KingSquares[Player.White],
                             PawnSquare + Directions.NorthDouble)].Result;
             }
 
@@ -212,13 +212,13 @@ public Results Classify(ReadOnlySpan<KpkPosition> db)
         private Results GetInitialKingResults(ReadOnlySpan<KpkPosition> db)
         {
             var r = Results.None;
-            var b = PieceTypes.King.PseudoAttacks(KingSquares[Stm.Side]);
+            var b = PieceTypes.King.PseudoAttacks(KingSquares[Stm]);
 
             while (b)
             {
                 var (bkSq, wkSq) = Stm.IsWhite
-                    ? (KingSquares[Player.Black.Side], BitBoards.PopLsb(ref b))
-                    : (BitBoards.PopLsb(ref b), KingSquares[Player.White.Side]);
+                    ? (KingSquares[Player.Black], BitBoards.PopLsb(ref b))
+                    : (BitBoards.PopLsb(ref b), KingSquares[Player.White]);
                 r |= db[Index(~Stm, bkSq, wkSq, PawnSquare)].Result;
             }
 
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 0f611020..0b620b54 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -93,7 +93,7 @@ private static int GenerateAll(
         if (types == MoveGenerationTypes.Captures || !pos.CanCastle(pos.SideToMove))
             return index;
 
-        var (kingSide, queenSide) = CastleSideRights[us.Side];
+        var (kingSide, queenSide) = CastleSideRights[us];
 
         if (pos.CanCastle(kingSide) && !pos.CastlingImpeded(kingSide))
             moves[index++].Move = Move.Create(ksq, pos.CastlingRookSquare(kingSide), MoveTypes.Castling);
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 90b2b6dc..e93bac47 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -205,7 +205,7 @@ public BitBoard AttacksTo(Square sq, in BitBoard occ)
 
     public BitBoard AttacksTo(Square sq) => AttacksTo(sq, Board.Pieces());
 
-    public BitBoard KingBlockers(Player p) => State.BlockersForKing[p.Side];
+    public BitBoard KingBlockers(Player p) => State.BlockersForKing[p];
 
     public bool IsKingBlocker(Player p, Square sq) => KingBlockers(p).Contains(sq);
 
@@ -461,7 +461,7 @@ public bool GivesCheck(Move m)
         var them = ~us;
 
         // Is there a discovered check?
-        if ((State.BlockersForKing[them.Side] & from).IsNotEmpty
+        if ((State.BlockersForKing[them] & from).IsNotEmpty
             && !from.Aligned(to, GetKingSquare(them)))
             return true;
 
@@ -781,7 +781,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 state.PawnKey ^= Zobrist.Psq(captureSquare, capturedPiece);
             }
             else
-                _nonPawnMaterial[them.Side] -= Values.GetPieceValue(capturedPiece, Phases.Mg);
+                _nonPawnMaterial[them] -= Values.GetPieceValue(capturedPiece, Phases.Mg);
 
             // Update board and piece lists
             RemovePiece(captureSquare);
@@ -844,7 +844,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 posKey ^= Zobrist.Psq(to, pc) ^ Zobrist.Psq(to, promotionPiece);
                 state.PawnKey ^= Zobrist.Psq(to, pc);
 
-                _nonPawnMaterial[us.Side] += Values.GetPieceValue(promotionPiece, Phases.Mg);
+                _nonPawnMaterial[us] += Values.GetPieceValue(promotionPiece, Phases.Mg);
             }
 
             // Update pawn hash key
@@ -1011,7 +1011,7 @@ public bool BishopOpposed() =>
     public BitBoard PinnedPieces(Player p)
     {
         Debug.Assert(State != null);
-        return State.Pinners[p.Side];
+        return State.Pinners[p];
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -1060,7 +1060,7 @@ public bool SeeGe(Move m, Value threshold)
             // Don't allow pinned pieces to attack (except the king) as long as there are
             // pinners on their original square.
             if (PinnedPieces(~stm) & occupied)
-                stmAttackers &= ~State.BlockersForKing[stm.Side];
+                stmAttackers &= ~State.BlockersForKing[stm];
 
             if (stmAttackers.IsEmpty)
                 break;
@@ -1128,7 +1128,7 @@ public bool SeeGe(Move m, Value threshold)
         return res > 0;
     }
 
-    public Value NonPawnMaterial(Player p) => _nonPawnMaterial[p.Side];
+    public Value NonPawnMaterial(Player p) => _nonPawnMaterial[p];
 
     public Value NonPawnMaterial() => _nonPawnMaterial[0] - _nonPawnMaterial[1];
 
@@ -1272,7 +1272,7 @@ public IPosition Set(ReadOnlySpan<char> code, Player p, in State state)
         var kingPos = code.LastIndexOf('K');
         var sides = new[] { code[kingPos..].ToString(), code[..kingPos].ToString() };
 
-        sides[p.Side] = sides[p.Side].ToLower();
+        sides[p] = sides[p].ToLower();
 
         var fenStr = $"{sides[0]}{8 - sides[0].Length}/8/8/8/8/8/8/{sides[1]}{8 - sides[1].Length} w - - 0 10";
 
@@ -1304,7 +1304,7 @@ public void TakeMove(Move m)
             RemovePiece(to);
             var pc = PieceTypes.Pawn.MakePiece(us);
             AddPiece(pc, to);
-            _nonPawnMaterial[_sideToMove.Side] -= Values.GetPieceValue(pc, Phases.Mg);
+            _nonPawnMaterial[_sideToMove] -= Values.GetPieceValue(pc, Phases.Mg);
         }
 
         if (type == MoveTypes.Castling)
@@ -1336,7 +1336,7 @@ public void TakeMove(Move m)
                 if (State.CapturedPiece != PieceTypes.Pawn)
                 {
                     var them = ~_sideToMove;
-                    _nonPawnMaterial[them.Side] += Values.GetPieceValue(State.CapturedPiece, Phases.Mg);
+                    _nonPawnMaterial[them] += Values.GetPieceValue(State.CapturedPiece, Phases.Mg);
                 }
             }
         }
@@ -1429,7 +1429,7 @@ public PositionValidationResult Validate(PositionValidationTypes type = Position
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static CastleRights OrCastlingRight(Player c, bool isKingSide)
-        => (CastleRights)((int)CastleRights.WhiteKing << ((!isKingSide).AsByte() + 2 * c.Side));
+        => (CastleRights)((int)CastleRights.WhiteKing << ((!isKingSide).AsByte() + 2 * c));
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     private void DoCastle(
@@ -1494,9 +1494,9 @@ private void SetCastlingRight(Player stm, Square rookFrom)
 
     private void SetCheckInfo(in State state)
     {
-        (state.BlockersForKing[Player.White.Side], state.Pinners[Player.Black.Side]) =
+        (state.BlockersForKing[Player.White], state.Pinners[Player.Black]) =
             SliderBlockers(Board.Pieces(Player.Black), GetKingSquare(Player.White));
-        (state.BlockersForKing[Player.Black.Side], state.Pinners[Player.White.Side]) =
+        (state.BlockersForKing[Player.Black], state.Pinners[Player.White]) =
             SliderBlockers(Board.Pieces(Player.White), GetKingSquare(Player.Black));
 
         var ksq = GetKingSquare(~_sideToMove);
@@ -1519,7 +1519,7 @@ private void SetState(State state)
     {
         state.MaterialKey = HashKey.Empty;
 
-        _nonPawnMaterial[Player.White.Side] = _nonPawnMaterial[Player.Black.Side] = Value.ValueZero;
+        _nonPawnMaterial[Player.White] = _nonPawnMaterial[Player.Black] = Value.ValueZero;
         State.Checkers = AttacksTo(GetKingSquare(_sideToMove)) & Board.Pieces(~_sideToMove);
 
         SetCheckInfo(State);
@@ -1543,7 +1543,7 @@ private void SetState(State state)
             if (pc.Type() != PieceTypes.Pawn && pc.Type() != PieceTypes.King)
             {
                 var val = Values.GetPieceValue(pc.Type(), Phases.Mg).AsInt() * Board.PieceCount(pc);
-                _nonPawnMaterial[pc.ColorOf().Side] += val;
+                _nonPawnMaterial[pc.ColorOf()] += val;
             }
 
             for (var cnt = 0; cnt < Board.PieceCount(pc); ++cnt)
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
index b993f74b..168ccf82 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
@@ -97,13 +97,13 @@ public float Interval
 
     public static bool operator ==(HiResTimer left, int right) => left != null && left.Id == right;
 
-    public static bool operator ==(HiResTimer left, Player right) => left != null && left.Id == right.Side;
+    public static bool operator ==(HiResTimer left, Player right) => left != null && left.Id == right;
 
     public static bool operator !=(HiResTimer left, HiResTimer right) => !Equals(left, right);
 
     public static bool operator !=(HiResTimer left, int right) => left != null && left.Id != right;
 
-    public static bool operator !=(HiResTimer left, Player right) => left != null && left.Id != right.Side;
+    public static bool operator !=(HiResTimer left, Player right) => left != null && left.Id != right;
 
     public void Start()
     {
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
index 005b500a..549d0860 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
@@ -54,5 +54,5 @@ public ulong BlackTimeMilliseconds
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ulong Time(Player p) => _time[p.Side];
+    public ulong Time(Player p) => _time[p];
 }
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index 632635a1..332d8434 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -98,43 +98,43 @@ public ulong MovesToGo
 
     public ulong WhiteTimeMilliseconds
     {
-        get => _clock[Player.White.Side].Time;
-        set => _clock[Player.White.Side].Time = value;
+        get => _clock[Player.White].Time;
+        set => _clock[Player.White].Time = value;
     }
 
     public ulong BlackTimeMilliseconds
     {
-        get => _clock[Player.Black.Side].Time;
-        set => _clock[Player.Black.Side].Time = value;
+        get => _clock[Player.Black].Time;
+        set => _clock[Player.Black].Time = value;
     }
 
     public ulong WhiteIncrementTimeMilliseconds
     {
-        get => _clock[Player.White.Side].Inc;
-        set => _clock[Player.White.Side].Inc = value;
+        get => _clock[Player.White].Inc;
+        set => _clock[Player.White].Inc = value;
     }
 
     public ulong BlackIncrementTimeMilliseconds
     {
-        get => _clock[Player.Black.Side].Inc;
-        set => _clock[Player.Black.Side].Inc = value;
+        get => _clock[Player.Black].Inc;
+        set => _clock[Player.Black].Inc = value;
     }
 
     public ref Clock Clock(Player p)
     {
-        return ref _clock[p.Side];
+        return ref _clock[p];
     }
 
     public bool UseTimeManagement()
     {
-        return _clock[Player.White.Side].Time != 0 && _clock[Player.Black.Side].Time != 0;
+        return _clock[Player.White].Time != 0 && _clock[Player.Black].Time != 0;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ulong Time(Player p) => _clock[p.Side].Time;
+    public ulong Time(Player p) => _clock[p].Time;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ulong Inc(Player p) => _clock[p.Side].Inc;
+    public ulong Inc(Player p) => _clock[p].Inc;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Clear()
@@ -193,7 +193,7 @@ public bool TryFormat(
         destination[index++] = 'e';
         destination[index++] = ' ';
 
-        _clock[Player.White.Side].Time.TryFormat(destination[index..], out var written);
+        _clock[Player.White].Time.TryFormat(destination[index..], out var written);
         index += written;
 
         destination[index++] = ' ';
@@ -204,7 +204,7 @@ public bool TryFormat(
         destination[index++] = 'e';
         destination[index++] = ' ';
 
-        _clock[Player.Black.Side].Time.TryFormat(destination[index..], out written);
+        _clock[Player.Black].Time.TryFormat(destination[index..], out written);
         index += written;
 
         if (MoveTime > ulong.MinValue)
@@ -225,7 +225,7 @@ public bool TryFormat(
         destination[index++] = 'c';
         destination[index++] = ' ';
 
-        _clock[Player.White.Side].Inc.TryFormat(destination[index..], out written);
+        _clock[Player.White].Inc.TryFormat(destination[index..], out written);
         index += written;
 
         destination[index++] = ' ';
@@ -235,7 +235,7 @@ public bool TryFormat(
         destination[index++] = 'c';
         destination[index++] = ' ';
 
-        _clock[Player.Black.Side].Inc.TryFormat(destination[index..], out written);
+        _clock[Player.Black].Inc.TryFormat(destination[index..], out written);
         index += written;
 
         if (_movesToGo == ulong.MinValue)
diff --git a/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs b/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
index 2a8071aa..6639f8c6 100644
--- a/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
+++ b/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
@@ -47,21 +47,21 @@ public void Clear()
     }
 
     public void Set(Player p, Square from, Square to, int value)
-        => _table[p.Side][from][to] = value;
+        => _table[p][from][to] = value;
 
     public int Retrieve(Player p, Square from, Square to)
-        => _table[p.Side][from][to];
+        => _table[p][from][to];
 
     private void Initialize(Player p)
     {
-        _table[p.Side] = new int[Square.Count][];
-        for (var i = 0; i < _table[p.Side].Length; ++i)
-            _table[p.Side][i] = new int[Square.Count];
+        _table[p] = new int[Square.Count][];
+        for (var i = 0; i < _table[p].Length; ++i)
+            _table[p][i] = new int[Square.Count];
     }
 
     private void ClearTable(Player p)
     {
-        for (var i = 0; i < _table[p.Side].Length; i++)
-            _table[p.Side][i].Clear();
+        for (var i = 0; i < _table[p].Length; i++)
+            _table[p][i].Clear();
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 3dfd5d82..2ce82add 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -217,12 +217,11 @@ static BitBoards()
         {
             foreach (var sq in Square.All.AsSpan())
             {
-                var side = p.Side;
                 var file = sq.File;
                 var rank = sq.Rank.AsInt();
-                ForwardFileBB[side][sq] = ForwardRanksBB[side][rank] & file.BitBoardFile();
-                PawnAttackSpanBB[side][sq] = ForwardRanksBB[side][rank] & AdjacentFilesBB[file.AsInt()];
-                PassedPawnMaskBB[side][sq] = ForwardFileBB[side][sq] | PawnAttackSpanBB[side][sq];
+                ForwardFileBB[p][sq] = ForwardRanksBB[p][rank] & file.BitBoardFile();
+                PawnAttackSpanBB[p][sq] = ForwardRanksBB[p][rank] & AdjacentFilesBB[file.AsInt()];
+                PassedPawnMaskBB[p][sq] = ForwardFileBB[p][sq] | PawnAttackSpanBB[p][sq];
             }
         }
 
@@ -327,18 +326,18 @@ private static void InitializeKingRing(Square sq)
         var file = sq.File;
 
         // TODO : Change to basic for-loop
-        foreach (var player in Player.AllPlayers)
+        foreach (var p in Player.AllPlayers)
         {
-            KingRingBB[player.Side][sq] = PseudoAttacksBB[pt][sq];
-            if (sq.RelativeRank(player) == Ranks.Rank1)
-                KingRingBB[player.Side][sq] |= KingRingBB[player.Side][sq].Shift(PawnPushDirections[player.Side]);
+            KingRingBB[p][sq] = PseudoAttacksBB[pt][sq];
+            if (sq.RelativeRank(p) == Ranks.Rank1)
+                KingRingBB[p][sq] |= KingRingBB[p][sq].Shift(PawnPushDirections[p]);
 
             if (file == Files.FileH)
-                KingRingBB[player.Side][sq] |= KingRingBB[player.Side][sq].WestOne();
+                KingRingBB[p][sq] |= KingRingBB[p][sq].WestOne();
             else if (file == Files.FileA)
-                KingRingBB[player.Side][sq] |= KingRingBB[player.Side][sq].EastOne();
+                KingRingBB[p][sq] |= KingRingBB[p][sq].EastOne();
 
-            Debug.Assert(!KingRingBB[player.Side][sq].IsEmpty);
+            Debug.Assert(!KingRingBB[p.Side][sq].IsEmpty);
         }
     }
 
@@ -358,22 +357,22 @@ public static BitBoard FileBB(File first, File last)
     public static BitBoard RankBB(this Rank r) => RanksBB[r.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ColorBB(this Player p) => ColorsBB[p.Side];
+    public static BitBoard ColorBB(this Player p) => ColorsBB[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard FirstRank(Player p) => Ranks1[p.Side];
+    public static BitBoard FirstRank(Player p) => Ranks1[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ThirdRank(Player p) => Ranks3BB[p.Side];
+    public static BitBoard ThirdRank(Player p) => Ranks3BB[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SeventhRank(Player p) => Ranks7BB[p.Side];
+    public static BitBoard SeventhRank(Player p) => Ranks7BB[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SixthAndSeventhRank(Player p) => Ranks6And7BB[p.Side];
+    public static BitBoard SixthAndSeventhRank(Player p) => Ranks6And7BB[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SeventhAndEightsRank(Player p) => Ranks7And8BB[p.Side];
+    public static BitBoard SeventhAndEightsRank(Player p) => Ranks7And8BB[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard PseudoAttacks(this PieceTypes pt, Square sq) => PseudoAttacksBB[pt.AsInt()][sq];
@@ -391,7 +390,7 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="p">The player side</param>
     /// <returns>ref to bitboard of attack</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnAttack(this Square sq, Player p) => PseudoAttacksBB[p.Side][sq];
+    public static BitBoard PawnAttack(this Square sq, Player p) => PseudoAttacksBB[p][sq];
 
     /// <summary>
     /// Returns the bitboard representation of the rank of which the square is located.
@@ -432,7 +431,7 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="p">The side, white is north and black is south</param>
     /// <returns>The bitboard of all forward file squares</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ForwardFile(this Square sq, Player p) => ForwardFileBB[p.Side][sq];
+    public static BitBoard ForwardFile(this Square sq, Player p) => ForwardFileBB[p][sq];
 
     /// <summary>
     /// Returns all squares in pawn attack pattern in front of the square.
@@ -441,7 +440,7 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="p">White = north, Black = south</param>
     /// <returns>The bitboard representation</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnAttackSpan(this Square sq, Player p) => PawnAttackSpanBB[p.Side][sq];
+    public static BitBoard PawnAttackSpan(this Square sq, Player p) => PawnAttackSpanBB[p][sq];
 
     /// <summary>
     /// Returns all square of both file and pawn attack pattern in front of square. This is the
@@ -451,10 +450,10 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="p">White = north, Black = south</param>
     /// <returns>The bitboard representation</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PassedPawnFrontAttackSpan(this Square sq, Player p) => PassedPawnMaskBB[p.Side][sq];
+    public static BitBoard PassedPawnFrontAttackSpan(this Square sq, Player p) => PassedPawnMaskBB[p][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ForwardRanks(this Square sq, Player p) => ForwardRanksBB[p.Side][sq];
+    public static BitBoard ForwardRanks(this Square sq, Player p) => ForwardRanksBB[p][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard BitboardBetween(this Square sq1, Square sq2) => BetweenBB[sq1][sq2];
@@ -485,10 +484,10 @@ public static BitBoard FileBB(File first, File last)
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard FrontSquares(this Player p, Square sq)
-        => ForwardRanksBB[p.Side][sq] & sq.BitBoardFile();
+        => ForwardRanksBB[p][sq] & sq.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KingRing(this Square sq, Player p) => KingRingBB[p.Side][sq];
+    public static BitBoard KingRing(this Square sq, Player p) => KingRingBB[p][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static int Distance(this Square sq1, Square sq2) => SquareDistance[sq1][sq2];
@@ -497,7 +496,7 @@ public static BitBoard FrontSquares(this Player p, Square sq)
     public static BitBoard DistanceRing(this Square sq, int length) => DistanceRingBB[sq][length];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PromotionRank(this Player p) => PromotionRanks[p.Side];
+    public static BitBoard PromotionRank(this Player p) => PromotionRanks[p];
 
     public static string StringifyRaw(in ulong bb, string title = "") => Stringify(BitBoard.Create(bb), title);
 
@@ -620,7 +619,7 @@ public static BitBoard SouthFill(this BitBoard bb)
     /// <param name="p">The direction to fill in, white = north, black = south</param>
     /// <returns>Filled bitboard</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Fill(this in BitBoard bb, Player p) => FillFuncs[p.Side](bb);
+    public static BitBoard Fill(this in BitBoard bb, Player p) => FillFuncs[p](bb);
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     public static BitBoard Shift(this in BitBoard bb, Direction d)
@@ -713,10 +712,10 @@ public static int PopLsb(ref int v)
     public static int PopCount(in BitBoard bb) => BitOperations.PopCount(bb.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Rank7(this Player p) => Ranks7BB[p.Side];
+    public static BitBoard Rank7(this Player p) => Ranks7BB[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Rank3(this Player p) => Ranks3BB[p.Side];
+    public static BitBoard Rank3(this Player p) => Ranks3BB[p];
 
     /// <summary>
     /// Generate a bitboard based on a square.
@@ -733,7 +732,7 @@ public static int PopLsb(ref int v)
     /// <param name="sq2">The second square to generate bitboard from</param>
     /// <returns>The generated bitboard</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard MakeBitboard(Square sq, Square sq2) => sq.AsBb() | sq2.AsBb();
+    public static BitBoard MakeBitboard(Square sq, Square sq2) => sq.AsBb() | sq2;
 
     /// <summary>
     /// Generate a bitboard based on a variadic amount of squares.
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index 9d3a5074..83b8c147 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -123,7 +123,7 @@ private CastleRight(int cr) : this((CastleRights)cr) { }
     public static implicit operator CastleRight(Player p) => Create(p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight Create(Player p) => new((CastleRights)((int)CastleRights.White << (p.Side << 1)));
+    public static CastleRight Create(Player p) => new((CastleRights)((int)CastleRights.White << (p << 1)));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Equals(CastleRight other) => Rights == other.Rights;
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 7cfb9518..6741dd30 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -67,7 +67,7 @@ public static class PieceTypesExtensions
     public static int AsInt(this PieceTypes p) => (int)p;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece MakePiece(this PieceTypes @this, Player side) => (int)@this | (side.Side << 3);
+    public static Piece MakePiece(this PieceTypes @this, Player side) => (int)@this | (side << 3);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool IsSlider(this PieceTypes @this) => @this.InBetween(PieceTypes.Bishop, PieceTypes.Queen);
diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Player.cs
index 22161685..c9821cd0 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Player.cs
@@ -69,7 +69,7 @@ public Player(Players p)
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Deconstruct(out byte side) => side = Side;
-    
+
     public        bool     IsWhite    => Side == byte.MinValue;
     public        bool     IsBlack    => Side != byte.MinValue;
     public        int      Sign       => ScoreSign[Side];
@@ -101,7 +101,7 @@ public Player(Players p)
     public static Player Create(Players p) => new(p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Player operator ~(Player p) => new(p.Side ^ 1);
+    public static Player operator ~(Player p) => new(p ^ 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static int operator <<(Player left, int right) => left.Side << right;
@@ -110,7 +110,10 @@ public Player(Players p)
     public static int operator >>(Player left, int right) => left.Side >> right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Pieces operator +(PieceTypes pieceType, Player side) => (Pieces)pieceType + (byte)(side.Side << 3);
+    public static Pieces operator +(PieceTypes pieceType, Player side) => (Pieces)pieceType + (byte)(side << 3);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator byte(Player p) => p.Side;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Equals(Player other) => Side == other.Side;
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index d956777c..1af827a9 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -46,7 +46,7 @@ public enum Ranks
 public static class RanksExtensions
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank RelativeRank(this Ranks r, Player p) => new((Ranks)(r.AsInt() ^ (p.Side * 7)));
+    public static Rank RelativeRank(this Ranks r, Player p) => new((Ranks)(r.AsInt() ^ (p * 7)));
 
     public static int AsInt(this Ranks r) => (int)r;
 }
@@ -192,7 +192,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
     public override int GetHashCode() => AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank Relative(Player p) => new(AsInt() ^ (p.Side * 7));
+    public Rank Relative(Player p) => new(AsInt() ^ (p * 7));
 
     /// <summary>
     /// Fold rank [12345678] to rank [12344321]
@@ -203,7 +203,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Rank Clamp(Rank min, Rank max) => new(Math.Clamp(Value.AsInt(), min.AsInt(), max.AsInt()));
-    
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int Distance(Rank other) => Math.Abs(AsInt() - other.AsInt());
 }
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index 418a49d6..e7780347 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -52,7 +52,7 @@ public static class SquaresExtensions
     public static BitBoard BitBoardSquare(this Squares sq) => BitBoards.BbSquares[sq.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square RelativeSquare(this Squares sq, Player p) => sq.AsInt() ^ (p.Side * 56);
+    public static Square RelativeSquare(this Squares sq, Player p) => sq.AsInt() ^ (p * 56);
 
     public static int AsInt(this Squares sq) => (int)sq;
 }

From dddc95eec6159e1931d0f08ef219ba2e2cb77256 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 29 Feb 2024 23:42:02 +0100
Subject: [PATCH 089/119] added implicit Rank int operator

---
 src/Rudzoft.ChessLib/Blockage.cs                   |  4 ++--
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs      |  2 +-
 .../Notation/Notations/IccfNotation.cs             |  4 ++--
 src/Rudzoft.ChessLib/Position.cs                   |  7 +++++--
 src/Rudzoft.ChessLib/Types/BitBoards.cs            | 14 ++++++--------
 src/Rudzoft.ChessLib/Types/MagicBB.cs              |  2 +-
 src/Rudzoft.ChessLib/Types/Rank.cs                 |  5 ++++-
 src/Rudzoft.ChessLib/Types/Square.cs               |  4 ++--
 8 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index f646234b..95b642a5 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -322,7 +322,7 @@ private static bool FormsFence(Square sq, ref BitBoard processed, ref BitBoard f
     }
 
     private static Square NextFenceRankSquare(Span<Rank> fenceRank, File f, Player them)
-        => new Square(fenceRank[f.AsInt()].AsInt() * 8 + f.AsInt()) + them.PawnPushDistance();
+        => new Square(fenceRank[f.AsInt()] * 8 + f.AsInt()) + them.PawnPushDistance();
 
     private static bool FormFence(in BitBoard marked, ref BitBoard fence, ref BitBoard processed, Player us)
     {
@@ -364,7 +364,7 @@ private static BitBoard ComputeDynamicFencedPawns(
                     result |= sq;
             }
         }
-        
+
         return result;
     }
 
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index 4c340378..f618e675 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -103,7 +103,7 @@ private static int Index(Player stm, Square weakKingSq, Square strongKsq, Square
                | (weakKingSq << 6)
                | (stm << 12)
                | (strongPawnSq.File.AsInt() << 13)
-               | ((Ranks.Rank7.AsInt() - strongPawnSq.Rank.AsInt()) << 15);
+               | ((Rank.Rank7 - strongPawnSq.Rank) << 15);
     }
 
     [Flags]
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index 614c9bac..448f54a1 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -51,9 +51,9 @@ public override string Convert(IPosition pos, Move move)
         var        i  = 0;
 
         re[i++] = (char)('1' + from.File.AsInt());
-        re[i++] = (char)('1' + from.Rank.AsInt());
+        re[i++] = (char)('1' + from.Rank);
         re[i++] = (char)('1' + to.File.AsInt());
-        re[i++] = (char)('1' + to.Rank.AsInt());
+        re[i++] = (char)('1' + to.Rank);
 
         // ReSharper disable once InvertIf
         if (move.IsPromotionMove())
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index e93bac47..b792204f 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -38,6 +38,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Types;
 using Rudzoft.ChessLib.Validation;
+using File = Rudzoft.ChessLib.Types.File;
 
 namespace Rudzoft.ChessLib;
 
@@ -1179,7 +1180,9 @@ private void SetupEnPassant(ReadOnlySpan<char> fenChunk)
 
         if (enPassant)
         {
-            State.EnPassantSquare = new(fenChunk[1] - '1', fenChunk[0] - 'a');
+            Rank r = new(fenChunk[1] - '1');
+            File f = new(fenChunk[0] - 'a');
+            State.EnPassantSquare = new(r, f);
 
             var otherSide = ~_sideToMove;
 
@@ -1564,7 +1567,7 @@ private void SetupCastle(ReadOnlySpan<char> castle)
                 'K' => RookSquare(Square.H1.Relative(c), rook),
                 'Q' => RookSquare(Square.A1.Relative(c), rook),
                 var _ => char.IsBetween(token, 'A', 'H')
-                    ? new(Rank.Rank1.Relative(c), new(token - 'A'))
+                    ? new(Rank.Rank1.Relative(c).AsInt(), new File(token - 'A'))
                     : Square.None
             };
 
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 2ce82add..87fc3b8e 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -209,8 +209,7 @@ static BitBoards()
         // ForwardRanksBB population loop idea from sf
         for (var r = Rank.Rank1; r <= Rank.Rank8; r++)
         {
-            var rank = r.AsInt();
-            ForwardRanksBB[0][rank] = ~(ForwardRanksBB[1][rank + 1] = ForwardRanksBB[1][rank] | r.BitBoardRank());
+            ForwardRanksBB[0][r] = ~(ForwardRanksBB[1][r + 1] = ForwardRanksBB[1][r] | r.BitBoardRank());
         }
 
         foreach (var p in Player.AllPlayers.AsSpan())
@@ -218,9 +217,8 @@ static BitBoards()
             foreach (var sq in Square.All.AsSpan())
             {
                 var file = sq.File;
-                var rank = sq.Rank.AsInt();
-                ForwardFileBB[p][sq] = ForwardRanksBB[p][rank] & file.BitBoardFile();
-                PawnAttackSpanBB[p][sq] = ForwardRanksBB[p][rank] & AdjacentFilesBB[file.AsInt()];
+                ForwardFileBB[p][sq] = ForwardRanksBB[p][sq.Rank] & file.BitBoardFile();
+                PawnAttackSpanBB[p][sq] = ForwardRanksBB[p][sq.Rank] & AdjacentFilesBB[file.AsInt()];
                 PassedPawnMaskBB[p][sq] = ForwardFileBB[p][sq] | PawnAttackSpanBB[p][sq];
             }
         }
@@ -282,7 +280,7 @@ static BitBoards()
 
         return;
 
-        static int distanceRank(Square x, Square y) => distance(x.Rank.AsInt(), y.Rank.AsInt());
+        static int distanceRank(Square x, Square y) => distance(x.Rank, y.Rank);
         static int distanceFile(Square x, Square y) => distance(x.File.AsInt(), y.File.AsInt());
         // local helper functions to calculate distance
         static int distance(int x, int y) => Math.Abs(x - y);
@@ -354,7 +352,7 @@ public static BitBoard FileBB(File first, File last)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard RankBB(this Rank r) => RanksBB[r.AsInt()];
+    public static BitBoard RankBB(this Rank r) => RanksBB[r];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard ColorBB(this Player p) => ColorsBB[p];
@@ -406,7 +404,7 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="r">The rank</param>
     /// <returns>The bitboard of square rank</returns>
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard BitBoardRank(this Rank r) => Rank1BB << (8 * r.AsInt());
+    public static BitBoard BitBoardRank(this Rank r) => Rank1BB << (8 * r);
 
     /// <summary>
     /// Returns the bitboard representation of the file of which the square is located.
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index 4c3eedcd..04f6c9a9 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -177,7 +177,7 @@ IRKiss rk
             if (Bmi2.X64.IsSupported)
                 continue;
 
-            rk.Seed = Seeds[rank.AsInt()];
+            rk.Seed = Seeds[rank];
 
             // Find a magic for square 's' picking up an (almost) random number
             // until we find the one that passes the verification test.
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index 1af827a9..2ca2b76d 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -99,7 +99,7 @@ public Rank(Rank r) : this(r.Value) { }
     public static bool operator !=(Rank left, int right) => left.Value != (Ranks)right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank operator +(Rank left, Rank right) => new(left.AsInt() + right.AsInt());
+    public static Rank operator +(Rank left, Rank right) => new(left.Value + (int)right.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Rank operator +(Rank left, int right) => new(left.AsInt() + right);
@@ -167,6 +167,9 @@ public Rank(Rank r) : this(r.Value) { }
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool operator false(Rank r) => !r.IsOk;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator int(Rank r) => (int)r.Value;
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int AsInt() => (int)Value;
 
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index e7780347..ddcf2661 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -82,7 +82,7 @@ public Square(Square sq) : this(sq.Value) { }
     public Square(int rank, int file) : this((Squares)(rank << 3) + (byte)file) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square(Ranks r, Files f) : this((int)r, (int)f) { }
+    public Square(Ranks r, Files f) : this(new(r), new File(f)) { }
 
     public Square(Rank r, File f) : this(r.AsInt(), f.AsInt()) { }
 
@@ -360,5 +360,5 @@ public int CompareTo(Square other)
     /// <returns>Flipped square by Rank</returns>
     public Square FlipRank() => AsInt() ^ Squares.a8.AsInt();
 
-    public Player Color() => ((AsInt() + Rank.AsInt()) ^ 1) & 1;
+    public Player Color() => ((AsInt() + Rank) ^ 1) & 1;
 }
\ No newline at end of file

From ea9e9b428fc245f0d4b0e0844b63f29cd0f74901 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Fri, 1 Mar 2024 17:36:05 +0100
Subject: [PATCH 090/119] added more int implicit overloads + removed redundant
 perft stuff

---
 src/Rudzoft.ChessLib/Blockage.cs              | 10 ++---
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |  2 +-
 src/Rudzoft.ChessLib/Hash/IZobrist.cs         |  2 +-
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          |  2 +-
 .../Notation/Notations/IccfNotation.cs        |  4 +-
 .../Polyglot/PolyglotBookZobrist.cs           |  2 +-
 src/Rudzoft.ChessLib/Position.cs              |  2 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |  4 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       |  8 ++--
 src/Rudzoft.ChessLib/Types/File.cs            |  7 ++-
 src/Rudzoft.ChessLib/Types/Square.cs          |  2 +-
 src/Rudzoft.Perft/Models/IPerftResult.cs      | 43 -------------------
 src/Rudzoft.Perft/Models/PerftResult.cs       | 29 +++++++------
 src/Rudzoft.Perft/Services/PerftRunner.cs     | 33 +++++++-------
 14 files changed, 56 insertions(+), 94 deletions(-)
 delete mode 100644 src/Rudzoft.Perft/Models/IPerftResult.cs

diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 95b642a5..2efc7ffe 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -81,7 +81,7 @@ public bool IsBlocked(in IPosition pos)
 
         var ourKsq = pos.GetKingSquare(us);
 
-        if (ourKsq.RelativeRank(us) > fenceRank[ourKsq.File.AsInt()].Relative(us))
+        if (ourKsq.RelativeRank(us) > fenceRank[ourKsq.File].Relative(us))
             return false;
 
         var theirKsq = pos.GetKingSquare(them);
@@ -94,7 +94,7 @@ public bool IsBlocked(in IPosition pos)
             var (r, f) = sq;
             var rr = r.Relative(us);
 
-            if (r > fenceRank[f.AsInt()])
+            if (r > fenceRank[f])
             {
                 if ((theirPawns & sq.PassedPawnFrontAttackSpan(us)).IsEmpty &&
                     (theirKsq.File != f || theirKsq.Rank.Relative(us) < rr))
@@ -136,7 +136,7 @@ public bool IsBlocked(in IPosition pos)
                 if (BitBoards.MoreThanOne(ourPawns & f))
                     return false;
             }
-            else if (r < fenceRank[f.AsInt()])
+            else if (r < fenceRank[f])
             {
                 sq += up;
                 r = sq.Rank;
@@ -237,7 +237,7 @@ private static void ComputeFenceRanks(Span<Rank> fenceRank, in BitBoard fence)
         {
             var sq = BitBoards.PopLsb(ref covered);
             var (r, f) = sq;
-            fenceRank[f.AsInt()] = r;
+            fenceRank[f] = r;
         }
     }
 
@@ -322,7 +322,7 @@ private static bool FormsFence(Square sq, ref BitBoard processed, ref BitBoard f
     }
 
     private static Square NextFenceRankSquare(Span<Rank> fenceRank, File f, Player them)
-        => new Square(fenceRank[f.AsInt()] * 8 + f.AsInt()) + them.PawnPushDistance();
+        => new Square(fenceRank[f] * 8 + f.AsInt()) + them.PawnPushDistance();
 
     private static bool FormFence(in BitBoard marked, ref BitBoard fence, ref BitBoard processed, Player us)
     {
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index f618e675..527fe729 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -102,7 +102,7 @@ private static int Index(Player stm, Square weakKingSq, Square strongKsq, Square
         return strongKsq
                | (weakKingSq << 6)
                | (stm << 12)
-               | (strongPawnSq.File.AsInt() << 13)
+               | (strongPawnSq.File << 13)
                | ((Rank.Rank7 - strongPawnSq.Rank) << 15);
     }
 
diff --git a/src/Rudzoft.ChessLib/Hash/IZobrist.cs b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
index 6926d796..27e1f950 100644
--- a/src/Rudzoft.ChessLib/Hash/IZobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
@@ -18,6 +18,6 @@ public interface IZobrist
     ref HashKey Castleling(CastleRights index);
     ref HashKey Castleling(CastleRight index);
     HashKey Side();
-    ref HashKey EnPassant(File file);
+    ref HashKey EnPassant(File f);
     HashKey EnPassant(Square sq);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index fa7c7734..6d981cd2 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -166,7 +166,7 @@ public HashKey ComputePositionKey(IPosition pos)
     public HashKey Side() => _zobristSide;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey EnPassant(File file) => ref _zobristEpFile[file.AsInt()];
+    public ref HashKey EnPassant(File f) => ref _zobristEpFile[f];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public HashKey EnPassant(Square sq)
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index 448f54a1..9298434e 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -50,9 +50,9 @@ public override string Convert(IPosition pos, Move move)
         Span<char> re = stackalloc char[5];
         var        i  = 0;
 
-        re[i++] = (char)('1' + from.File.AsInt());
+        re[i++] = (char)('1' + from.File);
         re[i++] = (char)('1' + from.Rank);
-        re[i++] = (char)('1' + to.File.AsInt());
+        re[i++] = (char)('1' + to.File);
         re[i++] = (char)('1' + to.Rank);
 
         // ReSharper disable once InvertIf
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 8859c6c8..1b00449a 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -360,7 +360,7 @@ internal static ulong Castle(CastleRight rights)
         => CastleKeys[rights.AsInt()];
 
     internal static ulong EnPassant(File f)
-        => EnPassantKeys[f.AsInt()];
+        => EnPassantKeys[f];
 
     internal static ulong Turn()
         => TurnKey;
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index b792204f..f70b22f7 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1567,7 +1567,7 @@ private void SetupCastle(ReadOnlySpan<char> castle)
                 'K' => RookSquare(Square.H1.Relative(c), rook),
                 'Q' => RookSquare(Square.A1.Relative(c), rook),
                 var _ => char.IsBetween(token, 'A', 'H')
-                    ? new(Rank.Rank1.Relative(c).AsInt(), new File(token - 'A'))
+                    ? new(Rank.Rank1.Relative(c), new File(token - 'A'))
                     : Square.None
             };
 
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index 7c2039df..14a4a4b1 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -64,9 +64,9 @@ private BitBoard(int value) : this((ulong)value)
 
     public static BitBoard Empty => BitBoards.EmptyBitBoard;
 
-    public static BitBoard MaxValue => BitBoards.EmptyBitBoard;
+    public static BitBoard MaxValue => BitBoards.AllSquares;
 
-    public static BitBoard MinValue => BitBoards.AllSquares;
+    public static BitBoard MinValue => BitBoards.EmptyBitBoard;
 
     public string String => Convert.ToString((long)Value, 2).PadLeft(64, '0');
 
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 87fc3b8e..ef27b91c 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -218,7 +218,7 @@ static BitBoards()
             {
                 var file = sq.File;
                 ForwardFileBB[p][sq] = ForwardRanksBB[p][sq.Rank] & file.BitBoardFile();
-                PawnAttackSpanBB[p][sq] = ForwardRanksBB[p][sq.Rank] & AdjacentFilesBB[file.AsInt()];
+                PawnAttackSpanBB[p][sq] = ForwardRanksBB[p][sq.Rank] & AdjacentFilesBB[file];
                 PassedPawnMaskBB[p][sq] = ForwardFileBB[p][sq] | PawnAttackSpanBB[p][sq];
             }
         }
@@ -281,7 +281,7 @@ static BitBoards()
         return;
 
         static int distanceRank(Square x, Square y) => distance(x.Rank, y.Rank);
-        static int distanceFile(Square x, Square y) => distance(x.File.AsInt(), y.File.AsInt());
+        static int distanceFile(Square x, Square y) => distance(x.File, y.File);
         // local helper functions to calculate distance
         static int distance(int x, int y) => Math.Abs(x - y);
     }
@@ -420,7 +420,7 @@ public static BitBoard FileBB(File first, File last)
     /// <param name="f">The file</param>
     /// <returns>The bitboard of file</returns>
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
-    public static BitBoard BitBoardFile(this File f) => FileABB << f.AsInt();
+    public static BitBoard BitBoardFile(this File f) => FileABB << f;
 
     /// <summary>
     /// Returns all squares in front of the square in the same file as bitboard
@@ -475,7 +475,7 @@ public static BitBoard FileBB(File first, File last)
     public static BitBoard SlotFile(CastleSides cs) => SlotFileBB[cs.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard AdjacentFiles(File f) => AdjacentFilesBB[f.AsInt()];
+    public static BitBoard AdjacentFiles(File f) => AdjacentFilesBB[f];
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     public static bool Aligned(this Square sq1, Square sq2, Square sq3) => (Line(sq1, sq2) & sq3).IsNotEmpty;
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index 5d49a7bf..db1758c2 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -166,6 +166,9 @@ public File(File f) : this(f.Value)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool operator false(File f) => !f.IsOk;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator int(File f) => (int)f.Value;
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int AsInt() => (int)Value;
 
@@ -198,10 +201,10 @@ public bool TryFormat(
     /// </summary>
     /// <returns>The distance to the edge file</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int EdgeDistance() => Math.Min(AsInt() - FileA.AsInt(), FileH.AsInt() - AsInt());
+    public int EdgeDistance() => Math.Min(AsInt() - FileA, FileH - AsInt());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public File Clamp(File min, File max) => new(Math.Clamp(Value.AsInt(), min.AsInt(), max.AsInt()));
+    public File Clamp(File min, File max) => new(Math.Clamp(Value.AsInt(), min, max));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int Distance(File other) => Math.Abs(AsInt() - other.AsInt());
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index ddcf2661..3960ef14 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -345,7 +345,7 @@ public int CompareTo(Square other)
     {
         if (Value < other.Value)
             return -1;
-        return Value > other.Value ? 1 : 0;
+        return (Value > other.Value).AsByte();
     }
 
     /// <summary>
diff --git a/src/Rudzoft.Perft/Models/IPerftResult.cs b/src/Rudzoft.Perft/Models/IPerftResult.cs
deleted file mode 100644
index 623203f7..00000000
--- a/src/Rudzoft.Perft/Models/IPerftResult.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-Perft, a chess perft testing application
-
-MIT License
-
-Copyright (c) 2019-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-namespace Rudzoft.Perft.Models;
-
-public interface IPerftResult
-{
-    string   Id            { get; set; }
-    string   Fen           { get; set; }
-    int      Depth         { get; set; }
-    UInt128  Result        { get; set; }
-    UInt128  CorrectResult { get; set; }
-    TimeSpan Elapsed       { get; set; }
-    ulong    Nps           { get; set; }
-    ulong    TableHits     { get; set; }
-    bool     Passed        { get; set; }
-    int      Errors        { get; set; }
-
-    void Clear();
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Models/PerftResult.cs b/src/Rudzoft.Perft/Models/PerftResult.cs
index 22864070..83ba8ebe 100644
--- a/src/Rudzoft.Perft/Models/PerftResult.cs
+++ b/src/Rudzoft.Perft/Models/PerftResult.cs
@@ -24,27 +24,30 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using Microsoft.Extensions.ObjectPool;
+
 namespace Rudzoft.Perft.Models;
 
-public sealed class PerftResult : IPerftResult
+public sealed class PerftResult : IResettable
 {
-    public string   Id            { get; set; }
-    public string   Fen           { get; set; }
-    public int      Depth         { get; set; }
-    public UInt128  Result        { get; set; }
-    public UInt128  CorrectResult { get; set; }
-    public TimeSpan Elapsed       { get; set; }
-    public ulong    Nps           { get; set; }
-    public ulong    TableHits     { get; set; }
-    public bool     Passed        { get; set; }
-    public int      Errors        { get; set; }
-
-    public void Clear()
+    public string Id { get; set; }
+    public string Fen { get; set; }
+    public int Depth { get; set; }
+    public UInt128 Result { get; set; }
+    public UInt128 CorrectResult { get; set; }
+    public TimeSpan Elapsed { get; set; }
+    public ulong Nps { get; set; }
+    public ulong TableHits { get; set; }
+    public bool Passed { get; set; }
+    public int Errors { get; set; }
+
+    public bool TryReset()
     {
         Fen = string.Empty;
         Depth = Errors = 0;
         Result = CorrectResult = Nps = TableHits = ulong.MinValue;
         Elapsed = TimeSpan.Zero;
         Passed = false;
+        return true;
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Services/PerftRunner.cs b/src/Rudzoft.Perft/Services/PerftRunner.cs
index fbad84d6..fa51a8f2 100644
--- a/src/Rudzoft.Perft/Services/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Services/PerftRunner.cs
@@ -74,13 +74,13 @@ public PerftRunner(
         ObjectPool<PerftResult> resultPool,
         IUci uci)
     {
-        _epdParser                =   parser;
-        _perft                    =   perft;
+        _epdParser = parser;
+        _perft = perft;
         _perft.BoardPrintCallback ??= s => Log.Information("Board:\n{Board}", s);
-        _transpositionTable       =   transpositionTable;
-        _resultPool               =   resultPool;
-        _uci                      =   uci;
-        _runners                  =   [ParseEpd, ParseFen];
+        _transpositionTable = transpositionTable;
+        _resultPool = resultPool;
+        _uci = uci;
+        _runners = [ParseEpd, ParseFen];
 
         configuration.Bind("TranspositionTable", TranspositionTableOptions);
 
@@ -102,7 +102,7 @@ private async Task<int> InternalRun(CancellationToken cancellationToken = defaul
         if (TranspositionTableOptions is TTOptions { Use: true } ttOptions)
             _transpositionTable.SetSize(ttOptions.Size);
 
-        var errors      = 0;
+        var errors = 0;
         var runnerIndex = (Options is FenOptions).AsByte();
         _usingEpd = runnerIndex == 0;
         var positions = _runners[runnerIndex].Invoke(cancellationToken);
@@ -204,7 +204,6 @@ private static async IAsyncEnumerable<PerftPosition> ParseFen(
     private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken)
     {
         var result = _resultPool.Get();
-        result.Clear();
 
         var pp = _perft.Positions[^1];
         var baseFileName = SaveResults
@@ -228,7 +227,7 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
             var start = Stopwatch.GetTimestamp();
 
             var perftResult = await _perft.DoPerftAsync(depth).ConfigureAwait(false);
-            var elapsedMs   = Stopwatch.GetElapsedTime(start);
+            var elapsedMs = Stopwatch.GetElapsedTime(start);
 
             ComputeResults(in perftResult, depth, in expected, in elapsedMs, result);
 
@@ -248,7 +247,7 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
     }
 
     private static async Task WriteOutput(
-        IPerftResult result,
+        PerftResult result,
         string baseFileName,
         CancellationToken cancellationToken)
     {
@@ -262,20 +261,20 @@ private void ComputeResults(
         int depth,
         in ulong expected,
         in TimeSpan elapsedMs,
-        IPerftResult results)
+        PerftResult results)
     {
         // compute results
         results.Result = result;
-        results.Depth  = depth;
+        results.Depth = depth;
         // add 1 to avoid potential dbz
-        results.Elapsed       = elapsedMs.Add(TimeSpan.FromMicroseconds(1));
-        results.Nps           = _uci.Nps(in result, results.Elapsed);
+        results.Elapsed = elapsedMs.Add(TimeSpan.FromMicroseconds(1));
+        results.Nps = _uci.Nps(in result, results.Elapsed);
         results.CorrectResult = expected;
-        results.Passed        = expected == result;
-        results.TableHits     = _transpositionTable.Hits;
+        results.Passed = expected == result;
+        results.TableHits = _transpositionTable.Hits;
     }
 
-    private int LogResults(IPerftResult result)
+    private int LogResults(PerftResult result)
     {
         Log.Information("Time passed : {Elapsed}", result.Elapsed);
         Log.Information("Nps         : {Nps}", result.Nps);

From e4594790d086bb7e9d77013caa0de9eaec5c0fae Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 2 Mar 2024 11:57:58 +0100
Subject: [PATCH 091/119] minor updates

- move generator is now using unsafe references directly (slightly faster overall)
- move generator adjusted to no longer support slow enumeration interface (moved to concrete method)
- base perft functionality no longer async
---
 .../MoveGeneratorBenchmark.cs                 |  8 +-
 .../MoveGeneratorBenchmarkInfo.md             | 62 +++++++++++++
 src/Rudzoft.ChessLib.Benchmark/PerftBench.cs  |  4 +-
 .../IPerft.cs                                 |  2 +-
 src/Rudzoft.ChessLib.Perft/Perft.cs           | 13 +--
 .../NotationTests/SanTests.cs                 | 13 +--
 .../Services/MoveGeneratorService.cs          | 18 ++--
 .../MoveGeneration/MoveGenerator.cs           | 87 ++++++++++---------
 .../MoveGeneration/MoveList.cs                | 42 +++++----
 src/Rudzoft.ChessLib/Position.cs              |  2 +-
 src/Rudzoft.ChessLib/Types/Move.cs            |  9 ++
 src/Rudzoft.Perft/Actors/PerftActor.cs        |  6 +-
 src/Rudzoft.Perft/Services/PerftRunner.cs     |  2 +-
 src/Rudzoft.Perft/Services/PerftService.cs    | 10 +--
 14 files changed, 181 insertions(+), 97 deletions(-)
 create mode 100644 src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmarkInfo.md

diff --git a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
index b68e1b05..fcc18bd9 100644
--- a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
@@ -13,10 +13,10 @@ public class MoveGeneratorBenchmark
     [Params
         (
             Fen.Fen.StartPositionFen,
-            // "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
-            // "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
-            // "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
-            // "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
+            "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
+            "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
+            "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
+            "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
             "rnbqk2r/pppppppp/8/8/8/8/PPPPPPPP/RNBQK2R w KQkq - 5 25",
             "r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w KQkq - 0 1"
         )
diff --git a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmarkInfo.md b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmarkInfo.md
new file mode 100644
index 00000000..1c053514
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmarkInfo.md
@@ -0,0 +1,62 @@
+# Move generator benchmark overview
+
+
+## 2nd March 2024 Summary
+
+### Summary
+
+BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3155/23H2/2023Update/SunValley3)
+
+Intel Core i7-8086K CPU 4.00GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores
+
+.NET SDK 8.0.200
+
+[Host]     : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2
+
+DefaultJob : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2
+
+### Stats
+
+| Method                      | _fen                 | Mean       | Error    | StdDev   | Ratio | RatioSD | Gen0   | Gen1   | Allocated | Alloc Ratio |
+|---------------------------- |--------------------- |-----------:|---------:|---------:|------:|--------:|-------:|-------:|----------:|------------:|
+| GenerateMovesNoPool         | 8/2p5(...)- 0 1 [41] |   299.1 ns |  5.93 ns |  6.09 ns |  1.00 |    0.00 | 0.2866 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesUnsafeNoPool   | 8/2p5(...)- 0 1 [41] |   294.9 ns |  5.17 ns |  9.96 ns |  1.00 |    0.05 | 0.2866 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesWithPool       | 8/2p5(...)- 0 1 [41] |   218.4 ns |  1.59 ns |  1.33 ns |  0.73 |    0.01 |      - |      - |         - |        0.00 |
+| GenerateMovesUnsafeWithPool | 8/2p5(...)- 0 1 [41] |   208.2 ns |  2.12 ns |  1.98 ns |  0.70 |    0.01 |      - |      - |         - |        0.00 |
+| GenerateMovesEnumerated     | 8/2p5(...)- 0 1 [41] |   911.2 ns | 17.83 ns | 19.08 ns |  3.05 |    0.09 | 1.2341 | 0.0057 |    7744 B |        4.30 |
+|                             |                      |            |          |          |       |         |        |        |           |             |
+| GenerateMovesNoPool         | r3k2r(...)- 0 1 [68] |   460.7 ns |  8.48 ns |  7.52 ns |  1.00 |    0.00 | 0.2866 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesUnsafeNoPool   | r3k2r(...)- 0 1 [68] |   409.2 ns |  8.01 ns |  7.49 ns |  0.89 |    0.01 | 0.2866 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesWithPool       | r3k2r(...)- 0 1 [68] |   361.5 ns |  6.44 ns |  5.71 ns |  0.79 |    0.02 |      - |      - |         - |        0.00 |
+| GenerateMovesUnsafeWithPool | r3k2r(...)- 0 1 [68] |   316.7 ns |  2.92 ns |  2.44 ns |  0.69 |    0.01 |      - |      - |         - |        0.00 |
+| GenerateMovesEnumerated     | r3k2r(...)- 0 1 [68] | 1,635.0 ns | 29.18 ns | 27.29 ns |  3.54 |    0.07 | 1.3142 | 0.0095 |    8248 B |        4.58 |
+|                             |                      |            |          |          |       |         |        |        |           |             |
+| GenerateMovesNoPool         | r3k2r(...)- 0 1 [64] |   230.8 ns |  4.64 ns |  5.52 ns |  1.00 |    0.00 | 0.2868 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesUnsafeNoPool   | r3k2r(...)- 0 1 [64] |   231.3 ns |  4.14 ns |  6.68 ns |  1.01 |    0.04 | 0.2866 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesWithPool       | r3k2r(...)- 0 1 [64] |   146.8 ns |  1.26 ns |  1.05 ns |  0.63 |    0.02 |      - |      - |         - |        0.00 |
+| GenerateMovesUnsafeWithPool | r3k2r(...)- 0 1 [64] |   146.3 ns |  1.57 ns |  1.31 ns |  0.63 |    0.02 |      - |      - |         - |        0.00 |
+| GenerateMovesEnumerated     | r3k2r(...)- 0 1 [64] |   923.7 ns |  9.21 ns |  8.62 ns |  3.98 |    0.10 | 1.3180 | 0.0057 |    8272 B |        4.60 |
+|                             |                      |            |          |          |       |         |        |        |           |             |
+| GenerateMovesNoPool         | r3k2r(...)- 0 1 [50] |   357.3 ns |  4.38 ns |  3.66 ns |  1.00 |    0.00 | 0.2866 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesUnsafeNoPool   | r3k2r(...)- 0 1 [50] |   357.1 ns |  6.86 ns |  7.90 ns |  1.00 |    0.03 | 0.2866 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesWithPool       | r3k2r(...)- 0 1 [50] |   268.2 ns |  3.69 ns |  3.27 ns |  0.75 |    0.01 |      - |      - |         - |        0.00 |
+| GenerateMovesUnsafeWithPool | r3k2r(...)- 0 1 [50] |   253.6 ns |  4.83 ns |  4.28 ns |  0.71 |    0.01 |      - |      - |         - |        0.00 |
+| GenerateMovesEnumerated     | r3k2r(...)- 0 1 [50] | 1,065.2 ns | 20.78 ns | 34.72 ns |  3.01 |    0.15 | 1.2436 | 0.0076 |    7808 B |        4.34 |
+|                             |                      |            |          |          |       |         |        |        |           |             |
+| GenerateMovesNoPool         | r4rk1(...) 0 10 [72] |   556.1 ns |  4.96 ns |  4.40 ns |  1.00 |    0.00 | 0.2861 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesUnsafeNoPool   | r4rk1(...) 0 10 [72] |   483.9 ns |  3.33 ns |  2.78 ns |  0.87 |    0.01 | 0.2861 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesWithPool       | r4rk1(...) 0 10 [72] |   406.8 ns |  3.37 ns |  2.99 ns |  0.73 |    0.01 |      - |      - |         - |        0.00 |
+| GenerateMovesUnsafeWithPool | r4rk1(...) 0 10 [72] |   387.9 ns |  3.80 ns |  2.97 ns |  0.70 |    0.01 |      - |      - |         - |        0.00 |
+| GenerateMovesEnumerated     | r4rk1(...) 0 10 [72] | 1,755.4 ns | 34.98 ns | 53.42 ns |  3.12 |    0.07 | 1.3142 | 0.0095 |    8248 B |        4.58 |
+|                             |                      |            |          |          |       |         |        |        |           |             |
+| GenerateMovesNoPool         | rnbqk(...) 5 25 [55] |   278.8 ns |  1.77 ns |  1.57 ns |  1.00 |    0.00 | 0.2866 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesUnsafeNoPool   | rnbqk(...) 5 25 [55] |   272.8 ns |  5.14 ns |  4.81 ns |  0.98 |    0.02 | 0.2866 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesWithPool       | rnbqk(...) 5 25 [55] |   210.9 ns |  1.07 ns |  0.90 ns |  0.76 |    0.00 |      - |      - |         - |        0.00 |
+| GenerateMovesUnsafeWithPool | rnbqk(...) 5 25 [55] |   196.9 ns |  1.41 ns |  1.18 ns |  0.71 |    0.01 |      - |      - |         - |        0.00 |
+| GenerateMovesEnumerated     | rnbqk(...) 5 25 [55] | 1,077.0 ns | 14.41 ns | 12.77 ns |  3.86 |    0.06 | 1.2798 | 0.0076 |    8040 B |        4.47 |
+|                             |                      |            |          |          |       |         |        |        |           |             |
+| GenerateMovesNoPool         | rnbqk(...)- 0 1 [56] |   227.3 ns |  3.20 ns |  3.00 ns |  1.00 |    0.00 | 0.2868 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesUnsafeNoPool   | rnbqk(...)- 0 1 [56] |   224.4 ns |  4.49 ns |  5.17 ns |  0.99 |    0.02 | 0.2868 | 0.0019 |    1800 B |        1.00 |
+| GenerateMovesWithPool       | rnbqk(...)- 0 1 [56] |   151.5 ns |  0.75 ns |  0.63 ns |  0.67 |    0.01 |      - |      - |         - |        0.00 |
+| GenerateMovesUnsafeWithPool | rnbqk(...)- 0 1 [56] |   151.8 ns |  2.89 ns |  3.55 ns |  0.67 |    0.02 |      - |      - |         - |        0.00 |
+| GenerateMovesEnumerated     | rnbqk(...)- 0 1 [56] | 1,056.0 ns |  6.40 ns |  5.35 ns |  4.65 |    0.06 | 1.2951 | 0.0057 |    8128 B |        4.52 |
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index 32a666b4..90367053 100644
--- a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -99,11 +99,11 @@ public async Task<UInt128> PerftIAsync()
     }
 
     [Benchmark]
-    public async Task<UInt128> Perft()
+    public UInt128 Perft()
     {
         var total = UInt128.MinValue;
         for (var i = 0; i < N; ++i)
-            total += await _perft.DoPerftAsync(N);
+            total += _perft.DoPerftSimple(N);
 
         return total;
     }
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
index c216f294..6a4825b8 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
@@ -40,7 +40,7 @@ public interface IPerft
 
     IAsyncEnumerable<UInt128> DoPerft(int depth);
 
-    Task<UInt128> DoPerftAsync(int depth);
+    UInt128 DoPerftSimple(int depth);
 
     void ClearPositions();
 
diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/Rudzoft.ChessLib.Perft/Perft.cs
index 9f68bfde..838686b1 100644
--- a/src/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/Rudzoft.ChessLib.Perft/Perft.cs
@@ -76,14 +76,17 @@ public async IAsyncEnumerable<UInt128> DoPerft(int depth)
         foreach (var fd in Positions.Select(static p => new FenData(p.Fen)))
         {
             Game.Pos.Set(in fd, ChessMode.Normal, in state);
-            var result = Game.Perft(depth);
+            var baseKey = game.Pos.State.PositionKey;
+            var result = Game.Perft(in baseKey, depth);
             yield return result;
         }
     }
 
-    public Task<UInt128> DoPerftAsync(int depth)
-        => Task.Run(()
-            => Game.Perft(depth));
+    public UInt128 DoPerftSimple(int depth)
+    {
+        var baseKey = game.Pos.State.PositionKey;
+        return Game.Perft(in baseKey, depth);
+    }
 
     public string GetBoard()
         => Game.ToString();
@@ -92,7 +95,7 @@ public void SetGamePosition(PerftPosition pp)
     {
         var fp = new FenData(pp.Fen);
         var state = new State();
-        Game.Pos.Set(in fp, ChessMode.Normal, in state);
+        game.Pos.Set(in fp, ChessMode.Normal, in state);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index ea67a41f..bbe35fbb 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -149,12 +149,13 @@ public void RookSanAmbiguity()
         var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
         var notation     = moveNotation.ToNotation(moveNotations);
 
-        var sanMoves = pos
-                       .GenerateMoves()
-                       .Select(static em => em.Move)
-                       .Where(m => pos.GetPiece(m.FromSquare()) == targetPiece)
-                       .Select(m => notation.Convert(pos, m))
-                       .ToArray();
+        var ml = pos.GenerateMoves();
+        ml.Generate(in pos);
+
+        var sanMoves = ml
+            .GetMoves(move => pos.GetPiece(move.FromSquare()) == targetPiece)
+            .Select(m => notation.Convert(pos, m))
+            .ToArray();
 
         foreach (var notationResult in expectedNotations)
             Assert.Contains(sanMoves, s => s == notationResult);
diff --git a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
index ba4d0baa..3d88c105 100644
--- a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
+++ b/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
@@ -3,23 +3,19 @@
 
 namespace Rudzoft.ChessLib.WebApi.Services;
 
-public sealed class MoveGeneratorService : IMoveGeneratorService
+public sealed class MoveGeneratorService(ILogger<MoveGeneratorService> logger, IPosition pos) : IMoveGeneratorService
 {
-    private readonly ILogger<IMoveGeneratorService> _logger;
-    private readonly IPosition _position;
-
-    public MoveGeneratorService(ILogger<MoveGeneratorService> logger, IPosition pos)
-    {
-        _logger = logger;
-        _position = pos;
-    }
+    private readonly ILogger<IMoveGeneratorService> _logger = logger;
 
     public IEnumerable<string> GenerateMoves(MoveQuery parameters)
     {
         _logger.LogInformation("Generating moves. fen={Fen},type={Type},mode={Mode}", parameters.Fen, parameters.Types, parameters.Mode);
 
         var state = new State();
-        _position.Set(parameters.Fen, parameters.Mode, state);
-        return _position.GenerateMoves(parameters.Types).Select(static em => em.Move.ToString());
+        pos.Set(parameters.Fen, parameters.Mode, state);
+
+        var ml = pos.GenerateMoves();
+        ml.Generate(in pos);
+        return ml.GetMoves().Select(static m => m.ToString());
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 0b620b54..638c09d2 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -42,26 +42,26 @@ private static readonly (CastleRight, CastleRight)[] CastleSideRights =
 
     public static int Generate(
         in IPosition pos,
-        Span<ValMove> moves,
+        ref ValMove moves,
         int index,
         Player us,
         MoveGenerationTypes types)
     {
         if (types == MoveGenerationTypes.Legal)
-            return GenerateLegal(index, in pos, moves, us);
+            return GenerateLegal(index, in pos, ref moves, us);
 
         const MoveGenerationTypes capturesQuietsNonEvasions =
             MoveGenerationTypes.Captures | MoveGenerationTypes.Quiets | MoveGenerationTypes.NonEvasions;
 
         if (capturesQuietsNonEvasions.HasFlagFast(types))
-            return GenerateCapturesQuietsNonEvasions(in pos, moves, index, us, types);
+            return GenerateCapturesQuietsNonEvasions(in pos, ref moves, index, us, types);
 
         switch (types)
         {
             case MoveGenerationTypes.Evasions:
-                return GenerateEvasions(index, in pos, moves, us);
+                return GenerateEvasions(index, in pos, ref moves, us);
             case MoveGenerationTypes.QuietChecks:
-                return GenerateQuietChecks(index, in pos, moves, us);
+                return GenerateQuietChecks(index, in pos, ref moves, us);
             default:
                 Debug.Assert(false);
                 throw new ArgumentOutOfRangeException(nameof(types), types, null);
@@ -70,25 +70,25 @@ public static int Generate(
 
     private static int GenerateAll(
         in IPosition pos,
-        Span<ValMove> moves,
+        ref ValMove moves,
         int index,
         in BitBoard target,
         Player us,
         MoveGenerationTypes types)
     {
-        index = GeneratePawnMoves(index, in pos, moves, in target, us, types);
+        index = GeneratePawnMoves(index, in pos, ref moves, in target, us, types);
 
         var checks = types == MoveGenerationTypes.QuietChecks;
 
         for (var pt = PieceTypes.Knight; pt <= PieceTypes.Queen; ++pt)
-            index = GenerateMoves(index, in pos, moves, us, in target, pt, checks);
+            index = GenerateMoves(index, in pos, ref moves, us, in target, pt, checks);
 
         if (checks || types == MoveGenerationTypes.Evasions)
             return index;
 
         var ksq = pos.GetKingSquare(us);
         var b = pos.GetAttacks(ksq, PieceTypes.King) & target;
-        index = Move.Create(moves, index, ksq, ref b);
+        index = Move.Create(ref moves, index, ksq, ref b);
 
         if (types == MoveGenerationTypes.Captures || !pos.CanCastle(pos.SideToMove))
             return index;
@@ -96,10 +96,10 @@ private static int GenerateAll(
         var (kingSide, queenSide) = CastleSideRights[us];
 
         if (pos.CanCastle(kingSide) && !pos.CastlingImpeded(kingSide))
-            moves[index++].Move = Move.Create(ksq, pos.CastlingRookSquare(kingSide), MoveTypes.Castling);
+            Unsafe.Add(ref moves, index++).Move = Move.Create(ksq, pos.CastlingRookSquare(kingSide), MoveTypes.Castling);
 
         if (pos.CanCastle(queenSide) && !pos.CastlingImpeded(queenSide))
-            moves[index++].Move = Move.Create(ksq, pos.CastlingRookSquare(queenSide), MoveTypes.Castling);
+            Unsafe.Add(ref moves, index++).Move = Move.Create(ksq, pos.CastlingRookSquare(queenSide), MoveTypes.Castling);
 
         return index;
     }
@@ -115,7 +115,7 @@ private static int GenerateAll(
     /// <returns></returns>
     private static int GenerateCapturesQuietsNonEvasions(
         in IPosition pos,
-        Span<ValMove> moves,
+        ref ValMove moves,
         int index,
         Player us,
         MoveGenerationTypes types)
@@ -131,7 +131,7 @@ private static int GenerateCapturesQuietsNonEvasions(
             var _ => BitBoard.Empty
         };
 
-        return GenerateAll(in pos, moves, index, in target, us, types);
+        return GenerateAll(in pos, ref moves, index, in target, us, types);
     }
 
     /// <summary>
@@ -146,7 +146,7 @@ private static int GenerateCapturesQuietsNonEvasions(
     private static int GenerateEvasions(
         int index,
         in IPosition pos,
-        Span<ValMove> moves,
+        ref ValMove moves,
         Player us)
     {
         Debug.Assert(pos.InCheck);
@@ -169,7 +169,7 @@ private static int GenerateEvasions(
 
         // Generate evasions for king, capture and non capture moves
         var b = pos.GetAttacks(ksq, PieceTypes.King) & ~pos.Pieces(us) & ~sliderAttacks;
-        index = Move.Create(moves, index, ksq, ref b);
+        index = Move.Create(ref moves, index, ksq, ref b);
 
         if (pos.Checkers.MoreThanOne())
             return index; // Double check, only a king move can save the day
@@ -178,7 +178,7 @@ private static int GenerateEvasions(
         checkSquare = pos.Checkers.Lsb();
         var target = checkSquare.BitboardBetween(ksq) | checkSquare;
 
-        return GenerateAll(in pos, moves, index, in target, us, MoveGenerationTypes.Evasions);
+        return GenerateAll(in pos, ref moves, index, in target, us, MoveGenerationTypes.Evasions);
     }
 
     /// <summary>
@@ -192,12 +192,12 @@ private static int GenerateEvasions(
     private static int GenerateLegal(
         int index,
         in IPosition pos,
-        Span<ValMove> moves,
+        ref ValMove moves,
         Player us)
     {
         var end = pos.InCheck
-            ? GenerateEvasions(index, in pos, moves, us)
-            : Generate(in pos, moves, index, us, MoveGenerationTypes.NonEvasions);
+            ? GenerateEvasions(index, in pos, ref moves, us)
+            : Generate(in pos, ref moves, index, us, MoveGenerationTypes.NonEvasions);
 
         var pinned = pos.KingBlockers(us) & pos.Pieces(us);
         var ksq = pos.GetKingSquare(us);
@@ -206,11 +206,14 @@ private static int GenerateLegal(
         // it's an en-passant move the move is checked, otherwise we assume the move is legal.
         var pinnedIsEmpty = pinned.IsEmpty;
         while (index != end)
-            if ((!pinnedIsEmpty || moves[index].Move.FromSquare() == ksq || moves[index].Move.IsEnPassantMove())
-                && !pos.IsLegal(moves[index].Move))
-                moves[index].Move = moves[--end].Move;
+        {
+            ref var current = ref Unsafe.Add(ref moves, index);
+            if ((!pinnedIsEmpty || current.Move.FromSquare() == ksq || current.Move.IsEnPassantMove())
+                && !pos.IsLegal(current.Move))
+                current.Move = Unsafe.Add(ref moves, --end).Move;
             else
                 ++index;
+        }
 
         return end;
     }
@@ -229,7 +232,7 @@ private static int GenerateLegal(
     private static int GenerateMoves(
         int index,
         in IPosition pos,
-        Span<ValMove> moves,
+        ref ValMove moves,
         Player us,
         in BitBoard target,
         PieceTypes pt,
@@ -253,7 +256,7 @@ private static int GenerateMoves(
             if (checks)
                 b &= pos.CheckedSquares(pt);
 
-            index = Move.Create(moves, index, from, ref b);
+            index = Move.Create(ref moves, index, from, ref b);
         }
 
         return index;
@@ -272,7 +275,7 @@ private static int GenerateMoves(
     private static int GeneratePawnMoves(
         int index,
         in IPosition pos,
-        Span<ValMove> moves,
+        ref ValMove moves,
         in BitBoard target,
         Player us,
         MoveGenerationTypes types)
@@ -345,13 +348,13 @@ private static int GeneratePawnMoves(
             while (pawnOne)
             {
                 var to = BitBoards.PopLsb(ref pawnOne);
-                moves[index++].Move = Move.Create(to - up, to);
+                Unsafe.Add(ref moves, index++).Move = Move.Create(to - up, to);
             }
 
             while (pawnTwo)
             {
                 var to = BitBoards.PopLsb(ref pawnTwo);
-                moves[index++].Move = Move.Create(to - up - up, to);
+                Unsafe.Add(ref moves, index++).Move = Move.Create(to - up - up, to);
             }
         }
 
@@ -377,13 +380,13 @@ private static int GeneratePawnMoves(
             var pawnPromoUp = pawnsOn7.Shift(up) & emptySquares;
 
             while (pawnPromoRight)
-                index = MakePromotions(index, moves, BitBoards.PopLsb(ref pawnPromoRight), ksq, right, types);
+                index = MakePromotions(index, ref moves, BitBoards.PopLsb(ref pawnPromoRight), ksq, right, types);
 
             while (pawnPromoLeft)
-                index = MakePromotions(index, moves, BitBoards.PopLsb(ref pawnPromoLeft), ksq, left, types);
+                index = MakePromotions(index, ref moves, BitBoards.PopLsb(ref pawnPromoLeft), ksq, left, types);
 
             while (pawnPromoUp)
-                index = MakePromotions(index, moves, BitBoards.PopLsb(ref pawnPromoUp), ksq, up, types);
+                index = MakePromotions(index, ref moves, BitBoards.PopLsb(ref pawnPromoUp), ksq, up, types);
         }
 
         const MoveGenerationTypes capturesEnPassant = MoveGenerationTypes.Captures | MoveGenerationTypes.Evasions |
@@ -399,13 +402,13 @@ private static int GeneratePawnMoves(
         while (pawnOne)
         {
             var to = BitBoards.PopLsb(ref pawnOne);
-            moves[index++].Move = Move.Create(to - right, to);
+            Unsafe.Add(ref moves, index++).Move = Move.Create(to - right, to);
         }
 
         while (pawnTwo)
         {
             var to = BitBoards.PopLsb(ref pawnTwo);
-            moves[index++].Move = Move.Create(to - left, to);
+            Unsafe.Add(ref moves, index++).Move = Move.Create(to - left, to);
         }
 
         if (pos.EnPassantSquare == Square.None)
@@ -422,7 +425,7 @@ private static int GeneratePawnMoves(
         pawnOne = pawnsNotOn7 & pos.EnPassantSquare.PawnAttack(them);
         Debug.Assert(!pawnOne.IsEmpty);
         while (pawnOne)
-            moves[index++].Move = Move.Create(BitBoards.PopLsb(ref pawnOne), pos.EnPassantSquare, MoveTypes.Enpassant);
+            Unsafe.Add(ref moves, index++).Move = Move.Create(BitBoards.PopLsb(ref pawnOne), pos.EnPassantSquare, MoveTypes.Enpassant);
 
         return index;
     }
@@ -439,7 +442,7 @@ private static int GeneratePawnMoves(
     private static int GenerateQuietChecks(
         int index,
         in IPosition pos,
-        Span<ValMove> moves,
+        ref ValMove moves,
         Player us)
     {
         Debug.Assert(!pos.InCheck);
@@ -461,10 +464,10 @@ private static int GenerateQuietChecks(
             if (pt == PieceTypes.King)
                 b &= ~PieceTypes.Queen.PseudoAttacks(pos.GetKingSquare(~us));
 
-            index = Move.Create(moves, index, from, ref b);
+            index = Move.Create(ref moves, index, from, ref b);
         }
 
-        return GenerateAll(in pos, moves, index, in emptySquares, us, MoveGenerationTypes.QuietChecks);
+        return GenerateAll(in pos, ref moves, index, in emptySquares, us, MoveGenerationTypes.QuietChecks);
     }
 
     /// <summary>
@@ -479,7 +482,7 @@ private static int GenerateQuietChecks(
     /// <returns></returns>
     private static int MakePromotions(
         int index,
-        Span<ValMove> moves,
+        ref ValMove moves,
         Square to,
         Square ksq,
         Direction direction,
@@ -493,9 +496,9 @@ private static int MakePromotions(
 
         if (types.HasFlagFast(queenPromotion))
         {
-            moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Queen);
+            Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Queen);
             if (PieceTypes.Knight.PseudoAttacks(to) & ksq)
-                moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion);
+                Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion);
         }
 
         const MoveGenerationTypes nonQueenPromotion = MoveGenerationTypes.Quiets
@@ -505,11 +508,11 @@ private static int MakePromotions(
         if (!types.HasFlagFast(nonQueenPromotion))
             return index;
 
-        moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Rook);
-        moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Bishop);
+        Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Rook);
+        Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Bishop);
 
         if (!(PieceTypes.Knight.PseudoAttacks(to) & ksq))
-            moves[index++].Move = Move.Create(from, to, MoveTypes.Promotion);
+            Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion);
 
         return index;
     }
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
index 128087c4..8338baf8 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Collections;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Microsoft.Extensions.ObjectPool;
@@ -38,13 +37,11 @@ namespace Rudzoft.ChessLib.MoveGeneration;
  * It works through an index pointer,
  * which means that re-use is never actually clearing any data, just resetting the index.
  */
-public sealed class MoveList : IReadOnlyCollection<ValMove>, IResettable
+public sealed class MoveList : IResettable
 {
     private readonly ValMove[] _moves = new ValMove[218];
     private int _cur;
 
-    int IReadOnlyCollection<ValMove>.Count => Length;
-
     public int Length { get; private set; }
 
     public ref ValMove CurrentMove => ref _moves[_cur];
@@ -66,7 +63,10 @@ public ValMove this[int index]
     public void Add(in ValMove item) => _moves[Length++] = item;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void Add(Move item) => _moves[Length++].Move = item;
+    public void Add(Move item)
+    {
+        _moves[Length++].Move = item;
+    }
 
     /// <inheritdoc />
     /// <summary>
@@ -110,11 +110,30 @@ public bool Contains(Predicate<Move> predicate)
         return false;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public IEnumerable<Move> GetMoves(Predicate<Move> predicate)
+    {
+        for (var i = 0; i < Length; i++)
+        {
+            var m = this[i];
+            if (predicate(m.Move))
+                yield return m.Move;
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public IEnumerable<Move> GetMoves()
+    {
+        for (var i = 0; i < Length; i++)
+            yield return this[i].Move;
+    }
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Generate(in IPosition pos, MoveGenerationTypes types = MoveGenerationTypes.Legal)
     {
         _cur = 0;
-        Length = MoveGenerator.Generate(in pos, _moves.AsSpan(), 0, pos.SideToMove, types);
+        ref var movesSpace = ref MemoryMarshal.GetReference(_moves.AsSpan());
+        Length = MoveGenerator.Generate(in pos, ref movesSpace, 0, pos.SideToMove, types);
         _moves[Length] = ValMove.Empty;
     }
 
@@ -122,7 +141,8 @@ public void Generate(in IPosition pos, MoveGenerationTypes types = MoveGeneratio
     public static int GenerateMoveCount(in IPosition pos, MoveGenerationTypes types = MoveGenerationTypes.Legal)
     {
         Span<ValMove> moves = stackalloc ValMove[218];
-        return MoveGenerator.Generate(in pos, moves, 0, pos.SideToMove, types);
+        ref var movesSpace = ref MemoryMarshal.GetReference(moves);
+        return MoveGenerator.Generate(in pos, ref movesSpace, 0, pos.SideToMove, types);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -131,14 +151,6 @@ public ReadOnlySpan<ValMove> Get() =>
             ? ReadOnlySpan<ValMove>.Empty
             : _moves.AsSpan()[..Length];
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public IEnumerator<ValMove> GetEnumerator()
-        => _moves.Take(Length).GetEnumerator();
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    IEnumerator IEnumerable.GetEnumerator()
-        => GetEnumerator();
-
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool TryReset()
     {
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index f70b22f7..7f793349 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -825,7 +825,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         if (pc.Type() == PieceTypes.Pawn)
         {
             // Set en-passant square, only if moved pawn can be captured
-            if ((to.Value.AsInt() ^ from.Value.AsInt()) == 16
+            if ((to.AsInt() ^ from.AsInt()) == 16
                 && CanEnPassant(them, from + us.PawnPushDistance()))
             {
                 state.EnPassantSquare = from + us.PawnPushDistance();
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index c54948e7..8d82db78 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -98,6 +98,15 @@ public static int Create(Span<ValMove> moves, int index, Square from, ref BitBoa
         return index;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static int Create(ref ValMove moves, int index, Square from, ref BitBoard to)
+    {
+        while (to)
+            Unsafe.Add(ref moves, index++).Move = Create(from, BitBoards.PopLsb(ref to));
+        return index;
+    }
+
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Move Create(Square from, Square to, MoveTypes moveType, PieceTypes promoPt = PieceTypes.Knight)
         => new(from, to, moveType, promoPt);
diff --git a/src/Rudzoft.Perft/Actors/PerftActor.cs b/src/Rudzoft.Perft/Actors/PerftActor.cs
index 719967dc..7e1e5bed 100644
--- a/src/Rudzoft.Perft/Actors/PerftActor.cs
+++ b/src/Rudzoft.Perft/Actors/PerftActor.cs
@@ -9,15 +9,15 @@ namespace Rudzoft.Perft.Actors;
 public sealed class PerftActor : ReceiveActor
 {
     public sealed record StartPerft;
-    
+
     private readonly IServiceProvider _sp;
-    
+
     public PerftActor(IServiceProvider sp)
     {
         _sp = sp;
         var optionsFactory = _sp.GetRequiredService<IOptionsFactory>();
         var options = optionsFactory.Parse().ToImmutableArray();
-        
+
         Receive<StartPerft>(_ =>
         {
             var runner = Context.ActorOf(PerftRunnerActor.Prop(_sp), "runner-actor");
diff --git a/src/Rudzoft.Perft/Services/PerftRunner.cs b/src/Rudzoft.Perft/Services/PerftRunner.cs
index fa51a8f2..80e3c189 100644
--- a/src/Rudzoft.Perft/Services/PerftRunner.cs
+++ b/src/Rudzoft.Perft/Services/PerftRunner.cs
@@ -226,7 +226,7 @@ private async Task<PerftResult> ComputePerft(CancellationToken cancellationToken
 
             var start = Stopwatch.GetTimestamp();
 
-            var perftResult = await _perft.DoPerftAsync(depth).ConfigureAwait(false);
+            var perftResult = _perft.DoPerftSimple(depth);
             var elapsedMs = Stopwatch.GetElapsedTime(start);
 
             ComputeResults(in perftResult, depth, in expected, in elapsedMs, result);
diff --git a/src/Rudzoft.Perft/Services/PerftService.cs b/src/Rudzoft.Perft/Services/PerftService.cs
index 0ff20049..61fc2953 100644
--- a/src/Rudzoft.Perft/Services/PerftService.cs
+++ b/src/Rudzoft.Perft/Services/PerftService.cs
@@ -16,7 +16,7 @@ public sealed class PerftService : IHostedService
     private readonly IServiceProvider _serviceProvider;
     private readonly IHostApplicationLifetime _applicationLifetime;
     private readonly IBuildTimeStamp _buildTimeStamp;
-    
+
     private ActorSystem _actorSystem;
     private IActorRef _perftActor;
 
@@ -47,13 +47,11 @@ public Task StartAsync(CancellationToken cancellationToken)
         // start ActorSystem
         _actorSystem = ActorSystem.Create("perft", actorSystemSetup);
         _perftActor = _actorSystem.ActorOf(PerftActor.Prop(_serviceProvider));
-        
+
         _perftActor.Tell(new PerftActor.StartPerft());
-        
+
         // add a continuation task that will guarantee shutdown of application if ActorSystem terminates
-        _actorSystem.WhenTerminated.ContinueWith(_ => {
-            _applicationLifetime.StopApplication();
-        }, cancellationToken);
+        _actorSystem.WhenTerminated.ContinueWith(_ => { _applicationLifetime.StopApplication(); }, cancellationToken);
         return Task.CompletedTask;
     }
 

From c653dc9be33e00e8a84cb47ae737d9a8d5c41325 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 2 Mar 2024 17:54:03 +0100
Subject: [PATCH 092/119] simplify move generator a bit

---
 .../Enums/MoveGenerationTypes.cs              | 11 +++-
 .../MoveGeneration/MoveGenerator.cs           | 63 +++++++------------
 2 files changed, 32 insertions(+), 42 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs b/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
index 88625698..8d44ecdd 100644
--- a/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
+++ b/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
@@ -35,7 +35,7 @@ namespace Rudzoft.ChessLib.Enums;
 public enum MoveGenerationTypes
 {
     None = 0,
-    
+
     /// <summary>
     /// Generate all legal moves
     /// </summary>
@@ -65,8 +65,13 @@ public enum MoveGenerationTypes
     /// Generate only moves which are not captures and gives check
     /// </summary>
     QuietChecks = 32,
-    
-    All = Legal | Captures | Quiets | NonEvasions | Evasions | QuietChecks
+
+    All = Legal | Captures | Quiets | NonEvasions | Evasions | QuietChecks,
+    CapturesQuietsNonEvasions = Captures | Quiets | NonEvasions,
+    AllQuiets = Quiets | QuietChecks,
+    CapturesEnPassant = Captures | Evasions | NonEvasions,
+    NonQueenPromotion = Quiets | Evasions | NonEvasions,
+    QueenPromotion = Captures | Evasions | NonEvasions
 }
 
 public static class MoveGenerationTypesExtensions
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 638c09d2..2e7fe600 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -47,25 +47,20 @@ public static int Generate(
         Player us,
         MoveGenerationTypes types)
     {
-        if (types == MoveGenerationTypes.Legal)
+        if (types.HasFlagFast(MoveGenerationTypes.Legal))
             return GenerateLegal(index, in pos, ref moves, us);
 
-        const MoveGenerationTypes capturesQuietsNonEvasions =
-            MoveGenerationTypes.Captures | MoveGenerationTypes.Quiets | MoveGenerationTypes.NonEvasions;
-
-        if (capturesQuietsNonEvasions.HasFlagFast(types))
+        if (MoveGenerationTypes.CapturesQuietsNonEvasions.HasFlagFast(types))
             return GenerateCapturesQuietsNonEvasions(in pos, ref moves, index, us, types);
 
-        switch (types)
-        {
-            case MoveGenerationTypes.Evasions:
-                return GenerateEvasions(index, in pos, ref moves, us);
-            case MoveGenerationTypes.QuietChecks:
-                return GenerateQuietChecks(index, in pos, ref moves, us);
-            default:
-                Debug.Assert(false);
-                throw new ArgumentOutOfRangeException(nameof(types), types, null);
-        }
+        if (types.HasFlagFast(MoveGenerationTypes.Evasions))
+            return GenerateEvasions(index, in pos, ref moves, us);
+
+        if (types.HasFlagFast(MoveGenerationTypes.QuietChecks))
+            return GenerateQuietChecks(index, in pos, ref moves, us);
+
+        Debug.Assert(false);
+        throw new ArgumentOutOfRangeException(nameof(types), types, null);
     }
 
     private static int GenerateAll(
@@ -96,10 +91,12 @@ private static int GenerateAll(
         var (kingSide, queenSide) = CastleSideRights[us];
 
         if (pos.CanCastle(kingSide) && !pos.CastlingImpeded(kingSide))
-            Unsafe.Add(ref moves, index++).Move = Move.Create(ksq, pos.CastlingRookSquare(kingSide), MoveTypes.Castling);
+            Unsafe.Add(ref moves, index++).Move
+                = Move.Create(ksq, pos.CastlingRookSquare(kingSide), MoveTypes.Castling);
 
         if (pos.CanCastle(queenSide) && !pos.CastlingImpeded(queenSide))
-            Unsafe.Add(ref moves, index++).Move = Move.Create(ksq, pos.CastlingRookSquare(queenSide), MoveTypes.Castling);
+            Unsafe.Add(ref moves, index++).Move
+                = Move.Create(ksq, pos.CastlingRookSquare(queenSide), MoveTypes.Castling);
 
         return index;
     }
@@ -125,10 +122,10 @@ private static int GenerateCapturesQuietsNonEvasions(
         Debug.Assert(!pos.InCheck);
         var target = types switch
         {
-            MoveGenerationTypes.Captures => pos.Pieces(~us),
-            MoveGenerationTypes.Quiets => ~pos.Pieces(),
+            MoveGenerationTypes.Captures    => pos.Pieces(~us),
+            MoveGenerationTypes.Quiets      => ~pos.Pieces(),
             MoveGenerationTypes.NonEvasions => ~pos.Pieces(us),
-            var _ => BitBoard.Empty
+            var _                           => BitBoard.Empty
         };
 
         return GenerateAll(in pos, ref moves, index, in target, us, types);
@@ -291,7 +288,7 @@ private static int GeneratePawnMoves(
         {
             MoveGenerationTypes.Evasions => pos.Pieces(them) & target,
             MoveGenerationTypes.Captures => target,
-            var _ => pos.Pieces(them)
+            var _                        => pos.Pieces(them)
         };
 
         var ksq = pos.GetKingSquare(them);
@@ -304,9 +301,7 @@ private static int GeneratePawnMoves(
         // Single and double pawn pushes, no promotions
         if (types != MoveGenerationTypes.Captures)
         {
-            const MoveGenerationTypes quiets = MoveGenerationTypes.Quiets | MoveGenerationTypes.QuietChecks;
-
-            emptySquares = quiets.HasFlagFast(types)
+            emptySquares = types.HasFlagFast(MoveGenerationTypes.AllQuiets)
                 ? target
                 : ~pos.Pieces();
 
@@ -389,11 +384,8 @@ private static int GeneratePawnMoves(
                 index = MakePromotions(index, ref moves, BitBoards.PopLsb(ref pawnPromoUp), ksq, up, types);
         }
 
-        const MoveGenerationTypes capturesEnPassant = MoveGenerationTypes.Captures | MoveGenerationTypes.Evasions |
-                                                      MoveGenerationTypes.NonEvasions;
-
         // Standard and en-passant captures
-        if (!types.HasFlagFast(capturesEnPassant))
+        if (!types.HasFlagFast(MoveGenerationTypes.CapturesEnPassant))
             return index;
 
         pawnOne = pawnsNotOn7.Shift(right) & enemies;
@@ -425,7 +417,8 @@ private static int GeneratePawnMoves(
         pawnOne = pawnsNotOn7 & pos.EnPassantSquare.PawnAttack(them);
         Debug.Assert(!pawnOne.IsEmpty);
         while (pawnOne)
-            Unsafe.Add(ref moves, index++).Move = Move.Create(BitBoards.PopLsb(ref pawnOne), pos.EnPassantSquare, MoveTypes.Enpassant);
+            Unsafe.Add(ref moves, index++).Move
+                = Move.Create(BitBoards.PopLsb(ref pawnOne), pos.EnPassantSquare, MoveTypes.Enpassant);
 
         return index;
     }
@@ -490,22 +483,14 @@ private static int MakePromotions(
     {
         var from = to - direction;
 
-        const MoveGenerationTypes queenPromotion = MoveGenerationTypes.Captures
-                                                   | MoveGenerationTypes.Evasions
-                                                   | MoveGenerationTypes.NonEvasions;
-
-        if (types.HasFlagFast(queenPromotion))
+        if (types.HasFlagFast(MoveGenerationTypes.QueenPromotion))
         {
             Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Queen);
             if (PieceTypes.Knight.PseudoAttacks(to) & ksq)
                 Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion);
         }
 
-        const MoveGenerationTypes nonQueenPromotion = MoveGenerationTypes.Quiets
-                                                       | MoveGenerationTypes.Evasions
-                                                       | MoveGenerationTypes.NonEvasions;
-
-        if (!types.HasFlagFast(nonQueenPromotion))
+        if (!types.HasFlagFast(MoveGenerationTypes.NonQueenPromotion))
             return index;
 
         Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Rook);

From c58caab254ca7b0940e3089c16e4beff5afc4594 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 2 Mar 2024 19:34:19 +0100
Subject: [PATCH 093/119] simplify en-passant setup when parsing fen

---
 src/Rudzoft.ChessLib/Position.cs | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 7f793349..0e5d68fb 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1180,20 +1180,19 @@ private void SetupEnPassant(ReadOnlySpan<char> fenChunk)
 
         if (enPassant)
         {
-            Rank r = new(fenChunk[1] - '1');
-            File f = new(fenChunk[0] - 'a');
-            State.EnPassantSquare = new(r, f);
+            var r = new Rank(fenChunk[1] - '1');
+            var f = new File(fenChunk[0] - 'a');
+            var epSquare = new Square(r, f);
 
-            var otherSide = ~_sideToMove;
+            var us = _sideToMove;
+            var them = ~us;
 
-            enPassant = !(State.EnPassantSquare.PawnAttack(otherSide) & Pieces(PieceTypes.Pawn, _sideToMove)).IsEmpty
-                        && !(Pieces(PieceTypes.Pawn, otherSide) &
-                             (State.EnPassantSquare + otherSide.PawnPushDistance())).IsEmpty
-                        && (Pieces() &
-                            (State.EnPassantSquare | (State.EnPassantSquare + _sideToMove.PawnPushDistance()))).IsEmpty;
+            if (!(epSquare.PawnAttack(them) & Pieces(PieceTypes.Pawn, us)).IsEmpty
+                && !(Pieces(PieceTypes.Pawn, them) & (epSquare + them.PawnPushDistance())).IsEmpty
+                && (Pieces() & (epSquare | (epSquare + us.PawnPushDistance()))).IsEmpty)
+                State.EnPassantSquare = epSquare;
         }
-
-        if (!enPassant)
+        else
             State.EnPassantSquare = Square.None;
     }
 

From 7c97a7ed2f9065e56bf5a2909e2d67a8ca936068 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sun, 3 Mar 2024 09:29:36 +0100
Subject: [PATCH 094/119] simplify board structure

---
 src/Rudzoft.ChessLib/Board.cs                 | 88 ++++++-------------
 src/Rudzoft.ChessLib/IBoard.cs                |  1 -
 src/Rudzoft.ChessLib/IPosition.cs             | 14 ++-
 .../MoveGeneration/MoveGenerator.cs           |  8 +-
 src/Rudzoft.ChessLib/Position.cs              | 68 +++++++-------
 5 files changed, 69 insertions(+), 110 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index 70148009..0ae6c454 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -33,7 +33,7 @@ namespace Rudzoft.ChessLib;
 
 public sealed class Board : IBoard
 {
-    private readonly Piece[] _pieces;
+    private readonly Piece[] _board;
 
     private readonly BitBoard[] _bySide;
 
@@ -41,94 +41,68 @@ public sealed class Board : IBoard
 
     private readonly int[] _pieceCount;
 
-    private readonly Square[][] _pieceList;
-
-    private readonly int[] _index;
-
     public Board()
     {
-        _pieces = new Piece[Types.Square.Count];
+        _board = new Piece[Types.Square.Count];
         _bySide = new BitBoard[Player.Count];
         _byType = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
         _pieceCount = new int[Piece.Count];
-        _pieceList = new Square[Types.Square.Count][];
-        for (var i = 0; i < _pieceList.Length; i++)
-        {
-            _pieceList[i] =
-            [
-                Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None,
-                Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None,
-                Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None,
-                Types.Square.None, Types.Square.None, Types.Square.None, Types.Square.None
-            ];
-        }
-
-        _index = new int[Types.Square.Count];
     }
 
     public void Clear()
     {
-        Array.Fill(_pieces, Piece.EmptyPiece);
+        Array.Fill(_board, Piece.EmptyPiece);
         _bySide[0] = _bySide[1] = BitBoard.Empty;
-        _byType.Fill(in _bySide[0]);
+        Array.Fill(_byType, BitBoard.Empty);
         Array.Fill(_pieceCount, 0);
-        foreach (var s in _pieceList)
-            Array.Fill(s, Types.Square.None);
-        Array.Fill(_index, 0);
     }
 
-    public Piece PieceAt(Square sq) => _pieces[sq];
+    public Piece PieceAt(Square sq) => _board[sq];
 
-    public bool IsEmpty(Square sq) => _pieces[sq] == Piece.EmptyPiece;
+    public bool IsEmpty(Square sq) => _board[sq] == Piece.EmptyPiece;
 
     public void AddPiece(Piece pc, Square sq)
     {
-        _pieces[sq] = pc;
+        Debug.Assert(sq.IsOk);
+
+        var color = pc.ColorOf();
+        var type = pc.Type();
+
+        _board[sq] = pc;
         _byType[PieceTypes.AllPieces.AsInt()] |= sq;
-        _byType[pc.Type().AsInt()] |= sq;
-        _bySide[pc.ColorOf()] |= sq;
-        _index[sq] = _pieceCount[pc]++;
-        _pieceList[pc][_index[sq]] = sq;
-        _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf())]++;
+        _byType[type.AsInt()] |= sq;
+        _bySide[color] |= sq;
+        _pieceCount[pc]++;
+        _pieceCount[PieceTypes.AllPieces.MakePiece(color)]++;
     }
 
     public void RemovePiece(Square sq)
     {
         Debug.Assert(sq.IsOk);
 
-        // WARNING: This is not a reversible operation. If we remove a piece in MakeMove() and
-        // then replace it in TakeMove() we will put it at the end of the list and not in its
-        // original place, it means index[] and pieceList[] are not invariant to a MakeMove() +
-        // TakeMove() sequence.
-        var pc = _pieces[sq];
+        var pc = _board[sq];
         _byType[PieceTypes.AllPieces.AsInt()] ^= sq;
         _byType[pc.Type().AsInt()] ^= sq;
         _bySide[pc.ColorOf()] ^= sq;
-        /* board[s] = NO_PIECE;  Not needed, overwritten by the capturing one */
-        var lastSquare = _pieceList[pc][--_pieceCount[pc]];
-        _index[lastSquare] = _index[sq];
-        _pieceList[pc][_index[lastSquare]] = lastSquare;
-        _pieceList[pc][_pieceCount[pc]] = Types.Square.None;
+        _board[sq] = Piece.EmptyPiece;
+        _pieceCount[pc]--;
         _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf())]--;
     }
 
     public void ClearPiece(Square sq)
-        => _pieces[sq] = Piece.EmptyPiece;
+        => _board[sq] = Piece.EmptyPiece;
 
     public void MovePiece(Square from, Square to)
     {
-        // _index[from] is not updated and becomes stale. This works as long as _index[] is
-        // accessed just by known occupied squares.
+        Debug.Assert(from.IsOk && to.IsOk);
 
-        var pc = _pieces[from];
+        var pc = _board[from];
         var fromTo = from | to;
         _byType[PieceTypes.AllPieces.AsInt()] ^= fromTo;
         _byType[pc.Type().AsInt()] ^= fromTo;
         _bySide[pc.ColorOf()] ^= fromTo;
-        _pieces[from] = Piece.EmptyPiece;
-        _pieces[to] = pc;
-        _index[to] = _index[from];
-        _pieceList[pc][_index[to]] = to;
+        _board[from] = Piece.EmptyPiece;
+        _board[to] = pc;
     }
 
     public Piece MovedPiece(Move m) => PieceAt(m.FromSquare());
@@ -149,17 +123,7 @@ public BitBoard Pieces(Player p, PieceTypes pt1, PieceTypes pt2)
     public Square Square(PieceTypes pt, Player p)
     {
         Debug.Assert(_pieceCount[pt.MakePiece(p)] == 1);
-        return _pieceList[pt.MakePiece(p)][0];
-    }
-
-    public ReadOnlySpan<Square> Squares(PieceTypes pt, Player p)
-    {
-        var pcIndex = pt.MakePiece(p);
-        var pcCount = _pieceCount[pcIndex];
-
-        return pcCount == 0
-            ? ReadOnlySpan<Square>.Empty
-            : _pieceList[pcIndex].AsSpan()[..pcCount];
+        return Pieces(p, pt).Lsb();
     }
 
     public int PieceCount(Piece pc) => _pieceCount[pc];
@@ -172,7 +136,7 @@ public int PieceCount(PieceTypes pt)
     public int PieceCount() => PieceCount(PieceTypes.AllPieces);
 
     public IEnumerator<Piece> GetEnumerator()
-        => _pieces.Where(static pc => pc != Piece.EmptyPiece).GetEnumerator();
+        => _board.Where(static pc => pc != Piece.EmptyPiece).GetEnumerator();
 
     IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/IBoard.cs b/src/Rudzoft.ChessLib/IBoard.cs
index 29288b12..ad5b728b 100644
--- a/src/Rudzoft.ChessLib/IBoard.cs
+++ b/src/Rudzoft.ChessLib/IBoard.cs
@@ -45,7 +45,6 @@ public interface IBoard : IEnumerable<Piece>
     BitBoard Pieces(Player p, PieceTypes pt);
     BitBoard Pieces(Player p, PieceTypes pt1, PieceTypes pt2);
     Square Square(PieceTypes pt, Player p);
-    ReadOnlySpan<Square> Squares(PieceTypes pt, Player p);
     int PieceCount(Piece pc);
     int PieceCount(PieceTypes pt, Player p);
     int PieceCount(PieceTypes pt);
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index c5e68f6c..fd7df181 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -50,7 +50,7 @@ public interface IPosition : IEnumerable<Piece>
     IBoard Board { get; }
 
     IZobrist Zobrist { get; }
-    
+
     IValues Values { get; }
 
     BitBoard Checkers { get; }
@@ -118,7 +118,7 @@ public interface IPosition : IEnumerable<Piece>
     BitBoard Pieces(PieceTypes pt, Player p);
 
     BitBoard Pieces(PieceTypes pt1, PieceTypes pt2, Player p);
-    
+
     int PieceCount();
 
     int PieceCount(Piece pc);
@@ -135,8 +135,6 @@ public interface IPosition : IEnumerable<Piece>
 
     bool BishopOpposed();
 
-    ReadOnlySpan<Square> Squares(PieceTypes pt, Player p);
-
     Square GetPieceSquare(PieceTypes pt, Player p);
 
     Square GetKingSquare(Player p);
@@ -172,7 +170,7 @@ public interface IPosition : IEnumerable<Piece>
     bool IsPawnPassedAt(Player p, Square sq);
 
     BitBoard PawnPassSpan(Player p, Square sq);
-    
+
     bool CanCastle(CastleRight cr);
 
     bool CanCastle(Player p);
@@ -196,7 +194,7 @@ public interface IPosition : IEnumerable<Piece>
     IPosition Set(string fen, ChessMode chessMode, in State state, bool validate = false, int searcher = 0);
 
     IPosition Set(ReadOnlySpan<char> code, Player p, in State state);
-    
+
     HashKey GetKey();
 
     HashKey GetPawnKey();
@@ -216,10 +214,10 @@ public interface IPosition : IEnumerable<Piece>
     bool SeeGe(Move m, Value threshold);
 
     Value NonPawnMaterial(Player p);
-    
+
     Value NonPawnMaterial();
 
     HashKey MovePositionKey(Move m);
-    
+
     PositionValidationResult Validate(PositionValidationTypes type = PositionValidationTypes.Basic);
 }
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 2e7fe600..88955491 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 using Rudzoft.ChessLib.Enums;
 using Rudzoft.ChessLib.Types;
 
@@ -237,12 +236,11 @@ private static int GenerateMoves(
     {
         Debug.Assert(pt != PieceTypes.King && pt != PieceTypes.Pawn);
 
-        var squares = pos.Squares(pt, us);
+        var pieces = pos.Pieces(pt, us);
 
-        ref var squaresSpace = ref MemoryMarshal.GetReference(squares);
-        for (var i = 0; i < squares.Length; ++i)
+        while (pieces.IsNotEmpty)
         {
-            var from = Unsafe.Add(ref squaresSpace, i);
+            var from = BitBoards.PopLsb(ref pieces);
             if (checks
                 && ((pos.KingBlockers(~us) & from).IsNotEmpty ||
                     (pt.PseudoAttacks(from) & target & pos.CheckedSquares(pt)).IsNotEmpty))
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 0e5d68fb..c45b1108 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -73,16 +73,16 @@ public Position(
         _castlingRightsMask = new CastleRight[Square.Count];
         _castlingRightsMask.Fill(CastleRight.None);
         _castlingRookSquare = new Square[CastleRight.Count];
-        _nonPawnMaterial    = [Value.ValueZero, Value.ValueZero];
-        _outputObjectPool   = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
-        _moveListPool       = moveListPool;
-        _positionValidator  = positionValidator;
-        Board               = board;
-        IsProbing           = true;
-        Values              = values;
-        Zobrist             = zobrist;
-        _cuckoo             = cuckoo;
-        State               = new();
+        _nonPawnMaterial = [Value.ValueZero, Value.ValueZero];
+        _outputObjectPool = new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
+        _moveListPool = moveListPool;
+        _positionValidator = positionValidator;
+        Board = board;
+        IsProbing = true;
+        Values = values;
+        Zobrist = zobrist;
+        _cuckoo = cuckoo;
+        State = new();
         Clear();
     }
 
@@ -182,7 +182,7 @@ public bool IsCaptureOrPromotion(Move m)
         return mt switch
         {
             MoveTypes.Normal => Board.Pieces(~SideToMove).Contains(m.ToSquare()),
-            var _ => mt != MoveTypes.Castling
+            var _            => mt != MoveTypes.Castling
         };
     }
 
@@ -364,8 +364,8 @@ public FenData GenerateFen()
 
         State.ClockPly.TryFormat(fen[length..], out var written, format);
 
-        length        += written;
-        fen[length++] =  space;
+        length += written;
+        fen[length++] = space;
 
         var ply = 1 + (Ply - _sideToMove.IsBlack.AsByte() / 2);
         ply.TryFormat(fen[length..], out written, format);
@@ -382,11 +382,11 @@ public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
         return pt switch
         {
             PieceTypes.Knight => pt.PseudoAttacks(sq),
-            PieceTypes.King => pt.PseudoAttacks(sq),
+            PieceTypes.King   => pt.PseudoAttacks(sq),
             PieceTypes.Bishop => sq.BishopAttacks(in occ),
-            PieceTypes.Rook => sq.RookAttacks(in occ),
-            PieceTypes.Queen => sq.QueenAttacks(in occ),
-            var _ => BitBoard.Empty
+            PieceTypes.Rook   => sq.RookAttacks(in occ),
+            PieceTypes.Queen  => sq.QueenAttacks(in occ),
+            var _             => BitBoard.Empty
         };
     }
 
@@ -535,7 +535,7 @@ public bool IsDraw(int ply)
         => State.ClockPly switch
         {
             > 99 when State.Checkers.IsEmpty || HasMoves() => true,
-            var _ => State.Repetition > 0 && State.Repetition < ply
+            var _                                          => State.Repetition > 0 && State.Repetition < ply
         };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -683,9 +683,9 @@ public bool IsPseudoLegal(Move m)
             if (to.Rank == Rank.Rank8.Relative(us))
                 return false;
 
-            if ((from.PawnAttack(us) & Pieces(~us) & to).IsEmpty // Not a capture
+            if ((from.PawnAttack(us) & Pieces(~us) & to).IsEmpty            // Not a capture
                 && !(from + us.PawnPushDistance() == to && !IsOccupied(to)) // Not a single push
-                && !(from + us.PawnDoublePushDistance() == to // Not a double push
+                && !(from + us.PawnDoublePushDistance() == to               // Not a double push
                      && from.Rank == Rank.Rank2.Relative(us)
                      && !IsOccupied(to)
                      && !IsOccupied(to - us.PawnPushDistance())))
@@ -1007,7 +1007,7 @@ public bool BishopOpposed() =>
         Board.PieceCount(Piece.WhiteBishop) == 1
         && Board.PieceCount(Piece.BlackBishop) == 1
         && Board.Square(PieceTypes.Bishop, Player.White)
-            .IsOppositeColor(Board.Square(PieceTypes.Bishop, Player.Black));
+                .IsOppositeColor(Board.Square(PieceTypes.Bishop, Player.Black));
 
     public BitBoard PinnedPieces(Player p)
     {
@@ -1230,7 +1230,8 @@ private void SetupMoveNumber(ReadOnlySpan<char> fen, in Range halfMove, in Range
     /// <param name="state">State reference to use. Allows to keep track of states if pre-created (i.e. in a stack) before engine search start</param>
     /// <param name="validate">If true, the fen should be validated, otherwise not</param>
     /// <param name="searcher">Searcher index, to help point to a specific search index in thread-based search array</param>
-    public IPosition Set(in FenData fenData, ChessMode chessMode, in State state, bool validate = false, int searcher = 0)
+    public IPosition Set(
+        in FenData fenData, ChessMode chessMode, in State state, bool validate = false, int searcher = 0)
     {
         if (validate)
             Fen.Fen.Validate(fenData.Fen.ToString());
@@ -1244,9 +1245,9 @@ public IPosition Set(in FenData fenData, ChessMode chessMode, in State state, bo
 
         const StringSplitOptions splitOptions = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries;
 
-        var         fen        = fenData.Fen.Span;
-        Span<Range> ranges     = stackalloc Range[7];
-        var         splitCount = fen.Split(ranges, ' ', splitOptions);
+        var fen = fenData.Fen.Span;
+        Span<Range> ranges = stackalloc Range[7];
+        var splitCount = fen.Split(ranges, ' ', splitOptions);
 
         if (splitCount < 4)
             throw new InvalidFenException("Invalid FEN string");
@@ -1281,9 +1282,6 @@ public IPosition Set(ReadOnlySpan<char> code, Player p, in State state)
         return Set(fenStr, ChessMode.Normal, in state);
     }
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ReadOnlySpan<Square> Squares(PieceTypes pt, Player p) => Board.Squares(pt, p);
-
     public void TakeMove(Move m)
     {
         Debug.Assert(m.IsValidMove());
@@ -1389,7 +1387,7 @@ public override string ToString()
 
             for (var file = Files.FileA; file <= Files.FileH; file++)
             {
-                var piece      = GetPiece(new(rank, file));
+                var piece = GetPiece(new(rank, file));
                 row[rowIndex++] = splitter;
                 row[rowIndex++] = space;
                 row[rowIndex++] = piece.GetPieceChar();
@@ -1527,7 +1525,7 @@ private void SetState(State state)
         SetCheckInfo(State);
 
         state.PositionKey = GetKey();
-        state.PawnKey     = GetPawnKey();
+        state.PawnKey = GetPawnKey();
 
         var b = Pieces(PieceTypes.Pawn);
         while (b.IsNotEmpty)
@@ -1647,9 +1645,10 @@ private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
         Debug.Assert(epSquare.IsOk);
         Debug.Assert(epSquare.RelativeRank(us) != Rank.Rank3);
 
-        var them       = ~us;
+        var them = ~us;
 
-        if (moved && !(Pieces(PieceTypes.Pawn, ~us).Contains(epSquare + them.PawnPushDistance()) && Board.IsEmpty(epSquare) && Board.IsEmpty(epSquare + us.PawnPushDistance())))
+        if (moved && !(Pieces(PieceTypes.Pawn, ~us).Contains(epSquare + them.PawnPushDistance()) &&
+                       Board.IsEmpty(epSquare) && Board.IsEmpty(epSquare + us.PawnPushDistance())))
             return false;
 
         // En-passant attackers
@@ -1669,9 +1668,10 @@ private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
 
         var mocc = (Pieces() ^ cap) | epSquare;
 
-        while (attackers) {
+        while (attackers)
+        {
             var sq = BitBoards.PopLsb(ref attackers);
-            var amocc =  mocc ^ sq;
+            var amocc = mocc ^ sq;
             // Check en-passant is legal for the position
             if (
                 (bq.IsEmpty || (bq & GetAttacks(ksq, PieceTypes.Bishop, in amocc)).IsEmpty) &&

From 1fe557192a568f4c8f4b00db63f4143ebe1f8d80 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 4 Mar 2024 23:05:34 +0100
Subject: [PATCH 095/119] updates

- added PieceType struct
- fixed a validation bug which would allow a fen to be detected as faulty
- renamed some "Castleling" stuff to "Castle"
- removed some redundant stuff for project files
- removed build timestamp from perft app
- updated appsettings.json for perft app
---
 .../Rudzoft.ChessLib.Benchmark.csproj         |   5 -
 .../Rudzoft.ChessLib.Perft.Interfaces.csproj  |   4 +
 .../Rudzoft.ChessLib.Perft.csproj             |   1 -
 .../BoardTests/BoardTests.cs                  | 127 +++----
 .../NotationTests/FanTests.cs                 |   6 +-
 .../NotationTests/RanTests.cs                 |   3 +-
 .../NotationTests/SanTests.cs                 |   8 +-
 .../PerftTests/PerftVerify.cs                 |   2 +-
 .../PiecesTests/SliderMobilityFixture.cs      |   6 +-
 .../PositionTests/ValidationTests.cs          |   4 +-
 .../Rudzoft.ChessLib.Test.csproj              |   8 -
 .../ScoreTests/ScoreTests.cs                  |   1 -
 .../TablesTests/KillerMovesTests.cs           |   4 +-
 .../ZobristTests/ZobristHashTests.cs          |   2 +-
 src/Rudzoft.ChessLib/Blockage.cs              |  14 +-
 src/Rudzoft.ChessLib/Board.cs                 |  51 ++-
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |  10 +-
 .../Extensions/PieceExtensions.cs             |   8 +-
 src/Rudzoft.ChessLib/Fen/Fen.cs               |   4 +-
 src/Rudzoft.ChessLib/Game.cs                  |  19 +-
 src/Rudzoft.ChessLib/Hash/IZobrist.cs         |   4 +-
 .../Hash/Tables/Transposition/PertT.cs        |   4 +-
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          |   6 +-
 src/Rudzoft.ChessLib/IBoard.cs                |  14 +-
 src/Rudzoft.ChessLib/IGame.cs                 |   2 +-
 src/Rudzoft.ChessLib/IPosition.cs             |  30 +-
 src/Rudzoft.ChessLib/IValues.cs               |   4 +-
 .../MoveGeneration/MoveGenerator.cs           |  20 +-
 .../Notation/Notations/IccfNotation.cs        |  26 +-
 .../Notation/Notations/LanNotation.cs         |   2 +-
 .../Notation/Notations/Notation.cs            |  12 +-
 .../Polyglot/IPolyglotBook.cs                 |   2 +-
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |  31 +-
 .../Polyglot/PolyglotBookEntry.cs             |  16 +-
 .../Polyglot/PolyglotBookZobrist.cs           |  10 +-
 .../ReverseEndianBinaryStreamReader.cs        |   6 +-
 src/Rudzoft.ChessLib/Position.cs              | 338 ++++++++++--------
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  25 +-
 .../Rudzoft.ChessLib.csproj.DotSettings       |   3 -
 src/Rudzoft.ChessLib/State.cs                 |  16 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |  12 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       |  60 ++--
 src/Rudzoft.ChessLib/Types/MagicBB.cs         |   2 +-
 src/Rudzoft.ChessLib/Types/Move.cs            |   2 +-
 src/Rudzoft.ChessLib/Types/Piece.cs           |  35 +-
 src/Rudzoft.ChessLib/Types/PieceType.cs       |  96 +++++
 src/Rudzoft.ChessLib/Types/Square.cs          |   4 +
 src/Rudzoft.ChessLib/Types/Values.cs          |  65 ++--
 .../Validation/PositionValidator.cs           |  13 +-
 src/Rudzoft.Perft/Options/OptionsFactory.cs   |   2 +-
 src/Rudzoft.Perft/Program.cs                  |   1 -
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |  10 +-
 src/Rudzoft.Perft/Services/BuildTimeStamp.cs  |  51 ---
 src/Rudzoft.Perft/Services/IBuildTimeStamp.cs |  32 --
 src/Rudzoft.Perft/Services/PerftService.cs    |   7 +-
 src/Rudzoft.Perft/appsettings.json            |  32 +-
 56 files changed, 638 insertions(+), 644 deletions(-)
 delete mode 100644 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj.DotSettings
 create mode 100644 src/Rudzoft.ChessLib/Types/PieceType.cs
 delete mode 100644 src/Rudzoft.Perft/Services/BuildTimeStamp.cs
 delete mode 100644 src/Rudzoft.Perft/Services/IBuildTimeStamp.cs

diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index fa67f879..00926add 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -4,14 +4,9 @@
         <OutputType>Exe</OutputType>
         <Platforms>AnyCPU</Platforms>
         <LangVersion>default</LangVersion>
-        <Configurations>Debug;Release</Configurations>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     </PropertyGroup>
 
-    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-        <PlatformTarget>x64</PlatformTarget>
-    </PropertyGroup>
-
     <ItemGroup>
         <PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
         <PackageReference Include="System.Runtime" Version="4.3.1"/>
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
index 38eefd3f..edbce89a 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
@@ -1,5 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
+    <PropertyGroup>
+        <IsPackable>false</IsPackable>
+    </PropertyGroup>
+
     <ItemGroup>
         <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
     </ItemGroup>
diff --git a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
index f1dc57f3..45be559d 100644
--- a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
+++ b/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
@@ -2,7 +2,6 @@
 
     <PropertyGroup>
         <Nullable>enable</Nullable>
-        <PublishAot>true</PublishAot>
         <ServerGarbageCollection>true</ServerGarbageCollection>
         <GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
     </PropertyGroup>
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 9bd262a2..cbac868e 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -38,6 +38,39 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Test.BoardTests;
 
+public sealed class BoardTestsTheoryData : TheoryData<string, PieceType, Player, int>
+{
+    public BoardTestsTheoryData(string[] fens, PieceType[] pts, Player[] players, int[] expectedCounts)
+    {
+        Debug.Assert(fens != null);
+        Debug.Assert(pts != null);
+        Debug.Assert(players != null);
+        Debug.Assert(expectedCounts != null);
+        Debug.Assert(fens.Length == pts.Length);
+        Debug.Assert(fens.Length == players.Length);
+        Debug.Assert(fens.Length == expectedCounts.Length);
+
+        var fensSpan = fens.AsSpan();
+        var ptsSpan = pts.AsSpan();
+        var playersSpan = players.AsSpan();
+        var expectedCountSpan = expectedCounts.AsSpan();
+
+        ref var fenSpace = ref MemoryMarshal.GetReference(fensSpan);
+        ref var ptsSpace = ref MemoryMarshal.GetReference(ptsSpan);
+        ref var playersSpace = ref MemoryMarshal.GetReference(playersSpan);
+        ref var expectedCountSpace = ref MemoryMarshal.GetReference(expectedCountSpan);
+
+        for (var i = 0; i < fens.Length; ++i)
+        {
+            var fen = Unsafe.Add(ref fenSpace, i);
+            var pt = Unsafe.Add(ref ptsSpace, i);
+            var player = Unsafe.Add(ref playersSpace, i);
+            var expectedCount = Unsafe.Add(ref expectedCountSpace, i);
+            Add(fen, pt, player, expectedCount);
+        }
+    }
+}
+
 public sealed class BoardTests
 {
     private readonly IServiceProvider _serviceProvider;
@@ -45,54 +78,21 @@ public sealed class BoardTests
     public BoardTests()
     {
         _serviceProvider = new ServiceCollection()
-            .AddTransient<IBoard, Board>()
-            .AddSingleton<IValues, Values>()
-            .AddSingleton<IRKiss, RKiss>()
-            .AddSingleton<IZobrist, Zobrist>()
-            .AddSingleton<ICuckoo, Cuckoo>()
-            .AddSingleton<IPositionValidator, PositionValidator>()
-            .AddTransient<IPosition, Position>()
-            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
-            .AddSingleton(static serviceProvider =>
-            {
-                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new DefaultPooledObjectPolicy<MoveList>();
-                return provider.Create(policy);
-            })
-            .BuildServiceProvider();
-    }
-
-    public sealed class BoardTestsTheoryData : TheoryData<string, PieceTypes, Player, int>
-    {
-        public BoardTestsTheoryData(string[] fens, PieceTypes[] pts, Player[] players, int[] expectedCounts)
-        {
-            Debug.Assert(fens != null);
-            Debug.Assert(pts != null);
-            Debug.Assert(players != null);
-            Debug.Assert(expectedCounts != null);
-            Debug.Assert(fens.Length == pts.Length);
-            Debug.Assert(fens.Length == players.Length);
-            Debug.Assert(fens.Length == expectedCounts.Length);
-
-            var fensSpan = fens.AsSpan();
-            var ptsSpan = pts.AsSpan();
-            var playersSpan = players.AsSpan();
-            var expectedCountSpan = expectedCounts.AsSpan();
-
-            ref var fenSpace = ref MemoryMarshal.GetReference(fensSpan);
-            ref var ptsSpace = ref MemoryMarshal.GetReference(ptsSpan);
-            ref var playersSpace = ref MemoryMarshal.GetReference(playersSpan);
-            ref var expectedCountSpace = ref MemoryMarshal.GetReference(expectedCountSpan);
-
-            for (var i = 0; i < fens.Length; ++i)
-            {
-                var fen = Unsafe.Add(ref fenSpace, i);
-                var pt = Unsafe.Add(ref ptsSpace, i);
-                var player = Unsafe.Add(ref playersSpace, i);
-                var expectedCount = Unsafe.Add(ref expectedCountSpace, i);
-                Add(fen, pt, player, expectedCount);
-            }
-        }
+                           .AddTransient<IBoard, Board>()
+                           .AddSingleton<IValues, Values>()
+                           .AddSingleton<IRKiss, RKiss>()
+                           .AddSingleton<IZobrist, Zobrist>()
+                           .AddSingleton<ICuckoo, Cuckoo>()
+                           .AddSingleton<IPositionValidator, PositionValidator>()
+                           .AddTransient<IPosition, Position>()
+                           .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+                           .AddSingleton(static serviceProvider =>
+                           {
+                               var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                               var policy = new DefaultPooledObjectPolicy<MoveList>();
+                               return provider.Create(policy);
+                           })
+                           .BuildServiceProvider();
     }
 
     private static readonly string[] Fens =
@@ -123,20 +123,17 @@ public BoardTestsTheoryData(string[] fens, PieceTypes[] pts, Player[] players, i
         "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53"
     ];
 
-    private static readonly PieceTypes[] PieceType =
-    [
-        PieceTypes.Pawn, PieceTypes.Pawn, PieceTypes.Knight, PieceTypes.Knight, PieceTypes.Bishop, PieceTypes.Bishop,
-        PieceTypes.Rook, PieceTypes.Rook, PieceTypes.Queen, PieceTypes.Queen, PieceTypes.King, PieceTypes.King,
-        PieceTypes.Pawn, PieceTypes.Pawn, PieceTypes.Knight, PieceTypes.Knight, PieceTypes.Bishop, PieceTypes.Bishop,
-        PieceTypes.Rook, PieceTypes.Rook, PieceTypes.Queen, PieceTypes.Queen, PieceTypes.King, PieceTypes.King
-    ];
 
     private static readonly Player[] Player =
     [
-        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
-        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
-        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black,
-        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black
+        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White,
+        Types.Player.Black,
+        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White,
+        Types.Player.Black,
+        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White,
+        Types.Player.Black,
+        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White,
+        Types.Player.Black
     ];
 
     private static readonly int[] ExpectedCount =
@@ -145,11 +142,19 @@ public BoardTestsTheoryData(string[] fens, PieceTypes[] pts, Player[] players, i
         4, 3, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1
     ];
 
-    public static readonly BoardTestsTheoryData TheoryData = new(Fens, PieceType, Player, ExpectedCount);
+    private static readonly PieceType[] PieceTypes =
+    [
+        PieceType.Pawn, PieceType.Pawn, PieceType.Knight, PieceType.Knight, PieceType.Bishop, PieceType.Bishop,
+        PieceType.Rook, PieceType.Rook, PieceType.Queen, PieceType.Queen, PieceType.King, PieceType.King,
+        PieceType.Pawn, PieceType.Pawn, PieceType.Knight, PieceType.Knight, PieceType.Bishop, PieceType.Bishop,
+        PieceType.Rook, PieceType.Rook, PieceType.Queen, PieceType.Queen, PieceType.King, PieceType.King
+    ];
+
+    public static readonly BoardTestsTheoryData TheoryData = new(Fens, PieceTypes, Player, ExpectedCount);
 
     [Theory]
     [MemberData(nameof(TheoryData))]
-    public void BoardPieceCount(string fen, PieceTypes pt, Player p, int expected)
+    public void BoardPieceCount(string fen, PieceType pt, Player p, int expected)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index f052988a..ae0fea53 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -58,7 +58,8 @@ public void FanRankAmbiguities(
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var pc = movingPt.MakePiece(pos.SideToMove);
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
@@ -104,7 +105,8 @@ public void FanFileAmbiguities(
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var pc = movingPt.MakePiece(pos.SideToMove);
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index 007e22e7..86cab0e3 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -64,7 +64,8 @@ public void RanLanAmbiguities(
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var pc = movingPt.MakePiece(pos.SideToMove);
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index bbe35fbb..4345f224 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -55,7 +55,8 @@ public void SanRankAmbiguities(
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var pc = movingPt.MakePiece(pos.SideToMove);
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
@@ -98,7 +99,8 @@ public void SanFileAmbiguities(
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var pc = movingPt.MakePiece(pos.SideToMove);
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
 
         var fromOne = new Square(fromSqOne);
         var fromTwo = new Square(fromSqTwo);
@@ -144,7 +146,7 @@ public void RookSanAmbiguity()
         pos.Set(in fenData, ChessMode.Normal, state);
 
         var sideToMove  = pos.SideToMove;
-        var targetPiece = PieceTypes.Rook.MakePiece(sideToMove);
+        var targetPiece = PieceType.Rook.MakePiece(sideToMove);
 
         var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
         var notation     = moveNotation.ToNotation(moveNotations);
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
index 5d1c9d60..8b4db3ce 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
@@ -50,7 +50,7 @@ protected void AssertPerft(string fen, int depth, in ulong expected)
 
         var initialZobrist = g.Pos.State.PositionKey;
 
-        var actual = g.Perft(depth);
+        var actual = g.Perft(in initialZobrist, depth);
 
         var afterZobrist = g.Pos.State.PositionKey;
 
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
index faca0fc2..000e91b6 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
@@ -35,7 +35,7 @@ public sealed class SliderMobilityFixture
 
     public int[] RookExpected { get; } = [14, 14, 14, 14];
 
-    public BitBoard SliderAttacks(PieceTypes pt, Square sq, in BitBoard occ)
+    public BitBoard SliderAttacks(PieceType pt, Square sq, in BitBoard occ)
     {
         var index = SliderIndex(pt);
         return index switch
@@ -47,6 +47,6 @@ public BitBoard SliderAttacks(PieceTypes pt, Square sq, in BitBoard occ)
         };
     }
 
-    private static int SliderIndex(PieceTypes pt)
-        => pt.AsInt() - 3;
+    private static int SliderIndex(PieceType pt)
+        => pt - 3;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index 7c6e3d76..b76e909c 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -72,7 +72,7 @@ public void ValidationKingsNegative()
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var pc = PieceTypes.King.MakePiece(Player.White);
+        var pc = PieceType.King.MakePiece(Player.White);
 
         pos.AddPiece(pc, Square.E4);
 
@@ -85,7 +85,7 @@ public void ValidationKingsNegative()
     }
 
     [Fact]
-    public void ValidateCastleling()
+    public void ValidateCastle()
     {
         // position only has pawns, rooks and kings
         const string fen = "r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w KQkq - 0 1";
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 886a6b3a..b23f78c9 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -4,14 +4,6 @@
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     </PropertyGroup>
 
-    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-        <PlatformTarget>x64</PlatformTarget>
-    </PropertyGroup>
-
-    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
-        <PlatformTarget>x64</PlatformTarget>
-    </PropertyGroup>
-
     <ItemGroup>
         <None Remove="BookTests\gm2600.bin"/>
     </ItemGroup>
diff --git a/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs b/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
index 438facf5..701574d2 100644
--- a/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Numerics;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Test.ScoreTests;
diff --git a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
index de8578eb..6fda1fa1 100644
--- a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
+++ b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
@@ -37,7 +37,7 @@ public void BaseAddMove()
         var km = KillerMoves.Create(128);
         const int depth = 1;
         var move = Move.Create(Square.A2, Square.A3);
-        var pc = PieceTypes.Pawn.MakePiece(Player.White);
+        var pc = PieceType.Pawn.MakePiece(Player.White);
 
         km.UpdateValue(depth, move, pc);
 
@@ -52,7 +52,7 @@ public void GetValueWithWrongDepthYieldsZero()
         var km = KillerMoves.Create(128);
         const int depth = 1;
         var move = Move.Create(Square.A2, Square.A3);
-        var pc = PieceTypes.Pawn.MakePiece(Player.White);
+        var pc = PieceType.Pawn.MakePiece(Player.White);
 
         km.UpdateValue(depth, move, pc);
 
diff --git a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
index 69d2c290..46745697 100644
--- a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
@@ -167,7 +167,7 @@ public void OnlyUniqueZobristHashKeys()
 
         foreach (var cr in castleRights)
         {
-            added = set.Add(zobrist.Castleling(cr));
+            added = set.Add(zobrist.Castle(cr));
             Assert.True(added);
         }
 
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 2efc7ffe..7a23e061 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -51,15 +51,15 @@ public bool IsBlocked(in IPosition pos)
     {
         // Quick check if there is only pawns and kings on the board
         // It might be possible to have a minor piece and exchange it into a passing pawn
-        if (pos.PieceCount(PieceTypes.AllPieces) > pos.PieceCount(PieceTypes.Pawn) + 2)
+        if (pos.PieceCount(PieceType.AllPieces) > pos.PieceCount(PieceType.Pawn) + 2)
             return false;
 
         var us = pos.SideToMove;
         var them = ~us;
 
-        var ourPawns = pos.Pieces(PieceTypes.Pawn, us);
-        var theirPawns = pos.Pieces(PieceTypes.Pawn, them);
-        var ourPawn = PieceTypes.Pawn.MakePiece(us);
+        var ourPawns = pos.Pieces(PieceType.Pawn, us);
+        var theirPawns = pos.Pieces(PieceType.Pawn, them);
+        var ourPawn = PieceType.Pawn.MakePiece(us);
 
         var up = us.PawnPushDistance();
 
@@ -253,8 +253,8 @@ private static MarkedPawns MarkPawns(in IPosition pos, in BitBoard theirPawns)
         var us = pos.SideToMove;
         var them = ~us;
         var up = us.PawnPushDistance();
-        var ourPawns = pos.Pieces(PieceTypes.Pawn, us);
-        var theirPawn = PieceTypes.Pawn.MakePiece(them);
+        var ourPawns = pos.Pieces(PieceType.Pawn, us);
+        var theirPawn = PieceType.Pawn.MakePiece(them);
 
         while (ourPawns)
         {
@@ -350,7 +350,7 @@ private static BitBoard ComputeDynamicFencedPawns(
         var down = us.PawnPushDistance();
 
         var them = ~us;
-        var ourPawn = PieceTypes.Pawn.MakePiece(us);
+        var ourPawn = PieceType.Pawn.MakePiece(us);
         var result = BitBoard.Empty;
 
         foreach (var f in File.AllFiles.AsSpan())
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index 0ae6c454..404ff14d 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -26,28 +26,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System.Collections;
 using System.Diagnostics;
-using Rudzoft.ChessLib.Extensions;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib;
 
 public sealed class Board : IBoard
 {
-    private readonly Piece[] _board;
+    private readonly Piece[] _board = new Piece[Types.Square.Count];
 
-    private readonly BitBoard[] _bySide;
+    private readonly BitBoard[] _bySide = new BitBoard[Player.Count];
 
-    private readonly BitBoard[] _byType;
+    private readonly BitBoard[] _byType = new BitBoard[PieceType.Count];
 
-    private readonly int[] _pieceCount;
-
-    public Board()
-    {
-        _board = new Piece[Types.Square.Count];
-        _bySide = new BitBoard[Player.Count];
-        _byType = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
-        _pieceCount = new int[Piece.Count];
-    }
+    private readonly int[] _pieceCount = new int[Piece.Count];
 
     public void Clear()
     {
@@ -69,11 +60,11 @@ public void AddPiece(Piece pc, Square sq)
         var type = pc.Type();
 
         _board[sq] = pc;
-        _byType[PieceTypes.AllPieces.AsInt()] |= sq;
-        _byType[type.AsInt()] |= sq;
+        _byType[PieceType.AllPieces] |= sq;
+        _byType[type] |= sq;
         _bySide[color] |= sq;
         _pieceCount[pc]++;
-        _pieceCount[PieceTypes.AllPieces.MakePiece(color)]++;
+        _pieceCount[PieceType.AllPieces.MakePiece(color)]++;
     }
 
     public void RemovePiece(Square sq)
@@ -81,12 +72,12 @@ public void RemovePiece(Square sq)
         Debug.Assert(sq.IsOk);
 
         var pc = _board[sq];
-        _byType[PieceTypes.AllPieces.AsInt()] ^= sq;
-        _byType[pc.Type().AsInt()] ^= sq;
+        _byType[PieceType.AllPieces] ^= sq;
+        _byType[pc.Type()] ^= sq;
         _bySide[pc.ColorOf()] ^= sq;
         _board[sq] = Piece.EmptyPiece;
         _pieceCount[pc]--;
-        _pieceCount[PieceTypes.AllPieces.MakePiece(pc.ColorOf())]--;
+        _pieceCount[PieceType.AllPieces.MakePiece(pc.ColorOf())]--;
     }
 
     public void ClearPiece(Square sq)
@@ -98,8 +89,8 @@ public void MovePiece(Square from, Square to)
 
         var pc = _board[from];
         var fromTo = from | to;
-        _byType[PieceTypes.AllPieces.AsInt()] ^= fromTo;
-        _byType[pc.Type().AsInt()] ^= fromTo;
+        _byType[PieceType.AllPieces] ^= fromTo;
+        _byType[pc.Type()] ^= fromTo;
         _bySide[pc.ColorOf()] ^= fromTo;
         _board[from] = Piece.EmptyPiece;
         _board[to] = pc;
@@ -107,20 +98,20 @@ public void MovePiece(Square from, Square to)
 
     public Piece MovedPiece(Move m) => PieceAt(m.FromSquare());
 
-    public BitBoard Pieces() => _byType[PieceTypes.AllPieces.AsInt()];
+    public BitBoard Pieces() => _byType[PieceType.AllPieces];
 
-    public BitBoard Pieces(PieceTypes pt) => _byType[pt.AsInt()];
+    public BitBoard Pieces(PieceType pt) => _byType[pt];
 
-    public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2) => _byType[pt1.AsInt()] | _byType[pt2.AsInt()];
+    public BitBoard Pieces(PieceType pt1, PieceType pt2) => _byType[pt1] | _byType[pt2];
 
     public BitBoard Pieces(Player p) => _bySide[p];
 
-    public BitBoard Pieces(Player p, PieceTypes pt) => _bySide[p] & _byType[pt.AsInt()];
+    public BitBoard Pieces(Player p, PieceType pt) => _bySide[p] & _byType[pt];
 
-    public BitBoard Pieces(Player p, PieceTypes pt1, PieceTypes pt2)
-        => _bySide[p] & (_byType[pt1.AsInt()] | _byType[pt2.AsInt()]);
+    public BitBoard Pieces(Player p, PieceType pt1, PieceType pt2)
+        => _bySide[p] & (_byType[pt1] | _byType[pt2]);
 
-    public Square Square(PieceTypes pt, Player p)
+    public Square Square(PieceType pt, Player p)
     {
         Debug.Assert(_pieceCount[pt.MakePiece(p)] == 1);
         return Pieces(p, pt).Lsb();
@@ -128,9 +119,9 @@ public Square Square(PieceTypes pt, Player p)
 
     public int PieceCount(Piece pc) => _pieceCount[pc];
 
-    public int PieceCount(PieceTypes pt, Player p) => PieceCount(pt.MakePiece(p));
+    public int PieceCount(PieceType pt, Player p) => PieceCount(pt.MakePiece(p));
 
-    public int PieceCount(PieceTypes pt)
+    public int PieceCount(PieceType pt)
         => _pieceCount[pt.MakePiece(Player.White)] + _pieceCount[pt.MakePiece(Player.Black)];
 
     public int PieceCount() => PieceCount(PieceTypes.AllPieces);
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index 527fe729..c20c9581 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -141,11 +141,11 @@ public static KpkPosition Create(int idx)
 
             // Draw if it is stalemate or the black king can capture the pawn
             else if (stm.IsBlack
-                     && ((PieceTypes.King.PseudoAttacks(ksq[Player.Black]) &
-                          ~(PieceTypes.King.PseudoAttacks(ksq[Player.White]) | psq.PawnAttack(Player.White)))
+                     && ((PieceType.King.PseudoAttacks(ksq[Player.Black]) &
+                          ~(PieceType.King.PseudoAttacks(ksq[Player.White]) | psq.PawnAttack(Player.White)))
                          .IsEmpty
-                         || !(PieceTypes.King.PseudoAttacks(ksq[Player.Black]) &
-                              ~PieceTypes.King.PseudoAttacks(ksq[Player.White]) & psq).IsEmpty))
+                         || !(PieceType.King.PseudoAttacks(ksq[Player.Black]) &
+                              ~PieceType.King.PseudoAttacks(ksq[Player.White]) & psq).IsEmpty))
                 results = Results.Draw;
 
             // Position will be classified later in initialization
@@ -212,7 +212,7 @@ public Results Classify(ReadOnlySpan<KpkPosition> db)
         private Results GetInitialKingResults(ReadOnlySpan<KpkPosition> db)
         {
             var r = Results.None;
-            var b = PieceTypes.King.PseudoAttacks(KingSquares[Stm]);
+            var b = PieceType.King.PseudoAttacks(KingSquares[Stm]);
 
             while (b)
             {
diff --git a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
index 72a40a57..3192827f 100644
--- a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
@@ -68,19 +68,19 @@ public static class PieceExtensions
     public static char GetPieceChar(this Piece p) => PieceChars[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetPieceChar(this PieceTypes p) => PieceChars[(int)p];
+    public static char GetPieceChar(this PieceType p) => PieceChars[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static string GetPieceString(this Piece p) => PieceStrings[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string GetName(this Piece p) => PieceNames[p.Type().AsInt()];
+    public static string GetName(this Piece p) => PieceNames[p.Type()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetPromotionChar(this PieceTypes p) => PromotionPieceNotation[p.AsInt()];
+    public static char GetPromotionChar(this PieceType p) => PromotionPieceNotation[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetPgnChar(this Piece p) => PgnPieceChars[(int)p.Type()];
+    public static char GetPgnChar(this Piece p) => PgnPieceChars[p.Type()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static char GetUnicodeChar(this Piece p) => PieceUnicodeChar[p];
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index 282b5212..cb4106e9 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -41,7 +41,7 @@ public static class Fen
 
     private const string FenRankRegexSnippet = "[1-8KkQqRrBbNnPp]{1,8}";
 
-    private const string ValidChars = "012345678pPnNbBrRqQkK/ w-abcdefgh";
+    private const string ValidChars = "0123456789pPnNbBrRqQkK/ w-abcdefgh";
 
     private const char Space = ' ';
 
@@ -141,7 +141,7 @@ private static bool CountPieceValidity(ReadOnlySpan<char> s)
 
             pieceCount[pc]++;
 
-            var limit = limits[pt.AsInt()];
+            var limit = limits[pt];
 
             if (pieceCount[pc] > limit)
                 throw new InvalidFenException(
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index f4c3ad1a..3f8a9e94 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -47,7 +47,7 @@ public sealed class Game(
     ObjectPool<MoveList> moveListPool)
     : IGame
 {
-    private readonly PertT            _perftTable   = new(8);
+    private readonly PertT _perftTable = new(8);
 
     public Action<IPieceSquare> PieceUpdated => pos.PieceUpdated;
 
@@ -116,27 +116,26 @@ public override string ToString()
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Player CurrentPlayer() => pos.SideToMove;
 
-    public UInt128 Perft(int depth, bool root = true)
+    public UInt128 Perft(in HashKey baseKey, int depth, bool root = true)
     {
         var tot = UInt128.MinValue;
-        var ml  = moveListPool.Get();
+        var ml = moveListPool.Get();
         ml.Generate(in pos);
-        var state = new State();
-
         var moves = ml.Get();
-        ref var movesSpace = ref MemoryMarshal.GetReference(moves);
 
         if (root && depth <= 1)
         {
-            tot = (ulong)ml.Length;
+            tot = (ulong)moves.Length;
             moveListPool.Return(ml);
             return tot;
         }
 
+        ref var movesSpace = ref MemoryMarshal.GetReference(moves);
+
         for (var i = 0; i < moves.Length; ++i)
         {
-            var valMove = Unsafe.Add(ref movesSpace, i);
-            var m = valMove.Move;
+            var m = Unsafe.Add(ref movesSpace, i).Move;
+            var state = new State();
 
             pos.MakeMove(m, in state);
 
@@ -149,7 +148,7 @@ public UInt128 Perft(int depth, bool root = true)
             }
             else
             {
-                var next = Perft(depth - 1, false);
+                var next = Perft(in baseKey, depth - 1, false);
                 tot += next;
             }
 
diff --git a/src/Rudzoft.ChessLib/Hash/IZobrist.cs b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
index 27e1f950..78d6367c 100644
--- a/src/Rudzoft.ChessLib/Hash/IZobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/IZobrist.cs
@@ -15,8 +15,8 @@ public interface IZobrist
     HashKey ComputePositionKey(IPosition pos);
     ref HashKey Psq(Square square, Piece pc);
     ref HashKey Psq(int pieceCount, Piece pc);
-    ref HashKey Castleling(CastleRights index);
-    ref HashKey Castleling(CastleRight index);
+    ref HashKey Castle(CastleRights index);
+    ref HashKey Castle(CastleRight index);
     HashKey Side();
     ref HashKey EnPassant(File f);
     HashKey EnPassant(Square sq);
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs
index 1e90996c..fa5f5a93 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs
@@ -33,7 +33,7 @@ public PertT(ulong size)
         Resize(in size, Unsafe.SizeOf<PerftEntry>());
     }
 
-    public (UInt128, bool) Get(in HashKey hash, byte depth) {
+    public (UInt128, bool) Get(in HashKey hash, int depth) {
         ref var entry = ref Probe(hash);
         if (entry.Hash == hash && entry.Depth == depth) {
             return (entry.Nodes, true);
@@ -41,7 +41,7 @@ public PertT(ulong size)
         return (default, false);
     }
 
-    public void Set(in HashKey hash, byte depth, in ulong nodes)
+    public void Set(in HashKey hash, byte depth, in UInt128 nodes)
     {
         ref var entry = ref Probe(in hash);
         entry.Hash = hash.Key;
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index 6d981cd2..23fb24de 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -144,7 +144,7 @@ public HashKey ComputePositionKey(IPosition pos)
         if (pos.SideToMove.IsWhite)
             key ^= _zobristSide;
 
-        key ^= Castleling(pos.State.CastlelingRights.Rights);
+        key ^= Castle(pos.State.CastleRights.Rights);
         key ^= EnPassant(pos.EnPassantSquare.File);
 
         return key;
@@ -157,10 +157,10 @@ public HashKey ComputePositionKey(IPosition pos)
     public ref HashKey Psq(int pieceCount, Piece pc) => ref _zobristPst[pieceCount][pc];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey Castleling(CastleRights index) => ref _zobristCastling[index.AsInt()];
+    public ref HashKey Castle(CastleRights index) => ref _zobristCastling[index.AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ref HashKey Castleling(CastleRight index) => ref Castleling(index.Rights);
+    public ref HashKey Castle(CastleRight index) => ref Castle(index.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public HashKey Side() => _zobristSide;
diff --git a/src/Rudzoft.ChessLib/IBoard.cs b/src/Rudzoft.ChessLib/IBoard.cs
index ad5b728b..09347f94 100644
--- a/src/Rudzoft.ChessLib/IBoard.cs
+++ b/src/Rudzoft.ChessLib/IBoard.cs
@@ -39,14 +39,14 @@ public interface IBoard : IEnumerable<Piece>
     void MovePiece(Square from, Square to);
     Piece MovedPiece(Move m);
     BitBoard Pieces();
-    BitBoard Pieces(PieceTypes pt);
-    BitBoard Pieces(PieceTypes pt1, PieceTypes pt2);
+    BitBoard Pieces(PieceType pt);
+    BitBoard Pieces(PieceType pt1, PieceType pt2);
     BitBoard Pieces(Player p);
-    BitBoard Pieces(Player p, PieceTypes pt);
-    BitBoard Pieces(Player p, PieceTypes pt1, PieceTypes pt2);
-    Square Square(PieceTypes pt, Player p);
+    BitBoard Pieces(Player p, PieceType pt);
+    BitBoard Pieces(Player p, PieceType pt1, PieceType pt2);
+    Square Square(PieceType pt, Player p);
     int PieceCount(Piece pc);
-    int PieceCount(PieceTypes pt, Player p);
-    int PieceCount(PieceTypes pt);
+    int PieceCount(PieceType pt, Player p);
+    int PieceCount(PieceType pt);
     int PieceCount();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/IGame.cs b/src/Rudzoft.ChessLib/IGame.cs
index 28cc3f90..ba72670e 100644
--- a/src/Rudzoft.ChessLib/IGame.cs
+++ b/src/Rudzoft.ChessLib/IGame.cs
@@ -61,5 +61,5 @@ public interface IGame : IEnumerable<Piece>
 
     Player CurrentPlayer();
 
-    UInt128 Perft(int depth, bool root = true);
+    UInt128 Perft(in HashKey baseKey, int depth, bool root = true);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index fd7df181..9d6ffeae 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -85,11 +85,11 @@ public interface IPosition : IEnumerable<Piece>
 
     Piece GetPiece(Square sq);
 
-    PieceTypes GetPieceType(Square sq);
+    PieceType GetPieceType(Square sq);
 
-    bool IsPieceTypeOnSquare(Square sq, PieceTypes pt);
+    bool IsPieceTypeOnSquare(Square sq, PieceType pt);
 
-    BitBoard CheckedSquares(PieceTypes pt);
+    BitBoard CheckedSquares(PieceType pt);
 
     BitBoard PinnedPieces(Player p);
 
@@ -111,21 +111,21 @@ public interface IPosition : IEnumerable<Piece>
 
     BitBoard Pieces(Piece pc);
 
-    BitBoard Pieces(PieceTypes pt);
+    BitBoard Pieces(PieceType pt);
 
-    BitBoard Pieces(PieceTypes pt1, PieceTypes pt2);
+    BitBoard Pieces(PieceType pt1, PieceType pt2);
 
-    BitBoard Pieces(PieceTypes pt, Player p);
+    BitBoard Pieces(PieceType pt, Player p);
 
-    BitBoard Pieces(PieceTypes pt1, PieceTypes pt2, Player p);
+    BitBoard Pieces(PieceType pt1, PieceType pt2, Player p);
 
     int PieceCount();
 
     int PieceCount(Piece pc);
 
-    int PieceCount(PieceTypes pt);
+    int PieceCount(PieceType pt);
 
-    int PieceCount(PieceTypes pt, Player p);
+    int PieceCount(PieceType pt, Player p);
 
     BitBoard PawnsOnColor(Player p, Square sq);
 
@@ -135,13 +135,13 @@ public interface IPosition : IEnumerable<Piece>
 
     bool BishopOpposed();
 
-    Square GetPieceSquare(PieceTypes pt, Player p);
+    Square GetPieceSquare(PieceType pt, Player p);
 
     Square GetKingSquare(Player p);
 
     Piece MovedPiece(Move m);
 
-    bool PieceOnFile(Square sq, Player p, PieceTypes pt);
+    bool PieceOnFile(Square sq, Player p, PieceType pt);
 
     bool PawnIsolated(Square sq, Player p);
 
@@ -161,7 +161,7 @@ public interface IPosition : IEnumerable<Piece>
 
     bool AttackedByKing(Square sq, Player p);
 
-    BitBoard AttacksBy(PieceTypes pt, Player p);
+    BitBoard AttacksBy(PieceType pt, Player p);
 
     bool IsCapture(Move m);
 
@@ -195,13 +195,13 @@ public interface IPosition : IEnumerable<Piece>
 
     IPosition Set(ReadOnlySpan<char> code, Player p, in State state);
 
-    HashKey GetKey();
+    HashKey GetKey(State state);
 
     HashKey GetPawnKey();
 
-    BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ);
+    BitBoard GetAttacks(Square sq, PieceType pt, in BitBoard occ);
 
-    BitBoard GetAttacks(Square sq, PieceTypes pt);
+    BitBoard GetAttacks(Square sq, PieceType pt);
 
     void MoveToString(Move m, in StringBuilder output);
 
diff --git a/src/Rudzoft.ChessLib/IValues.cs b/src/Rudzoft.ChessLib/IValues.cs
index 9922d8fa..a0808d3e 100644
--- a/src/Rudzoft.ChessLib/IValues.cs
+++ b/src/Rudzoft.ChessLib/IValues.cs
@@ -75,6 +75,6 @@ public interface IValues
     void SetPieceValues(DefaultPieceValues[] values, Phases phase);
 
     DefaultPieceValues GetPieceValue(Piece pc, Phases phase);
-    
-    DefaultPieceValues GetPieceValue(PieceTypes pt, Phases phase);
+
+    DefaultPieceValues GetPieceValue(PieceType pt, Phases phase);
 }
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index 88955491..a29d2202 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -231,14 +231,14 @@ private static int GenerateMoves(
         ref ValMove moves,
         Player us,
         in BitBoard target,
-        PieceTypes pt,
+        PieceType pt,
         bool checks)
     {
         Debug.Assert(pt != PieceTypes.King && pt != PieceTypes.Pawn);
 
         var pieces = pos.Pieces(pt, us);
 
-        while (pieces.IsNotEmpty)
+        while (pieces)
         {
             var from = BitBoards.PopLsb(ref pieces);
             if (checks
@@ -447,13 +447,13 @@ private static int GenerateQuietChecks(
 
             // No need to generate pawn quiet checks,
             // as these will be generated together with direct checks
-            if (pt == PieceTypes.Pawn)
+            if (pt == PieceType.Pawn)
                 continue;
 
             var b = pos.GetAttacks(from, pt) & emptySquares;
 
-            if (pt == PieceTypes.King)
-                b &= ~PieceTypes.Queen.PseudoAttacks(pos.GetKingSquare(~us));
+            if (pt == PieceType.King)
+                b &= ~PieceType.Queen.PseudoAttacks(pos.GetKingSquare(~us));
 
             index = Move.Create(ref moves, index, from, ref b);
         }
@@ -483,18 +483,18 @@ private static int MakePromotions(
 
         if (types.HasFlagFast(MoveGenerationTypes.QueenPromotion))
         {
-            Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Queen);
-            if (PieceTypes.Knight.PseudoAttacks(to) & ksq)
+            Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceType.Queen.Value);
+            if (PieceType.Knight.PseudoAttacks(to) & ksq)
                 Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion);
         }
 
         if (!types.HasFlagFast(MoveGenerationTypes.NonQueenPromotion))
             return index;
 
-        Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Rook);
-        Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceTypes.Bishop);
+        Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceType.Rook.Value);
+        Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion, PieceType.Bishop.Value);
 
-        if (!(PieceTypes.Knight.PseudoAttacks(to) & ksq))
+        if (!(PieceType.Knight.PseudoAttacks(to) & ksq))
             Unsafe.Add(ref moves, index++).Move = Move.Create(from, to, MoveTypes.Promotion);
 
         return index;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index 9298434e..7baa5c11 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -48,7 +48,7 @@ public override string Convert(IPosition pos, Move move)
         var (from, to) = move;
 
         Span<char> re = stackalloc char[5];
-        var        i  = 0;
+        var i = 0;
 
         re[i++] = (char)('1' + from.File);
         re[i++] = (char)('1' + from.Rank);
@@ -58,19 +58,23 @@ public override string Convert(IPosition pos, Move move)
         // ReSharper disable once InvertIf
         if (move.IsPromotionMove())
         {
-            // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
-            var c = move.PromotedPieceType() switch
-            {
-                PieceTypes.Queen  => 1,
-                PieceTypes.Rook   => 2,
-                PieceTypes.Bishop => 3,
-                PieceTypes.Knight => 4,
-                var _             => throw new NotImplementedException()
-            };
-
+            var c = PieceTypeToValue(move.PromotedPieceType());
             re[i++] = (char)('0' + c);
         }
 
         return new(re[..i]);
     }
+
+    private static int PieceTypeToValue(PieceType pt)
+    {
+        if (pt == PieceType.Queen)
+            return 1;
+        if (pt == PieceType.Rook)
+            return 2;
+        if (pt == PieceType.Bishop)
+            return 3;
+        if (pt == PieceType.Knight)
+            return 4;
+        throw new NotImplementedException();
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
index 3bf63563..1cb49705 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
@@ -56,7 +56,7 @@ public override string Convert(IPosition pos, Move move)
 
         var pt = pos.GetPieceType(from);
 
-        if (pt != PieceTypes.Pawn)
+        if (pt != PieceType.Pawn)
             re[i++] = pt.GetPieceChar();
 
         re[i++] = from.FileChar;
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
index d2448a31..59b19c98 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
@@ -62,8 +62,8 @@ protected char GetCheckChar(IPosition pos)
     private static MoveAmbiguities Ambiguity(IPosition pos, Square from, BitBoard similarTypeAttacks)
     {
         var ambiguity = MoveAmbiguities.None;
-        var c         = pos.SideToMove;
-        var pinned    = pos.PinnedPieces(c);
+        var c = pos.SideToMove;
+        var pinned = pos.PinnedPieces(c);
 
         while (similarTypeAttacks)
         {
@@ -128,10 +128,8 @@ private static BitBoard GetSimilarAttacks(IPosition pos, Move move)
         var from = move.FromSquare();
         var pt = pos.GetPieceType(from);
 
-        return pt switch
-        {
-            PieceTypes.Pawn or PieceTypes.King => BitBoard.Empty,
-            var _ => pos.GetAttacks(move.ToSquare(), pt, pos.Pieces()) ^ from
-        };
+        return pt == PieceTypes.Pawn || pt == PieceTypes.King
+            ? BitBoard.Empty
+            : pos.GetAttacks(move.ToSquare(), pt, pos.Pieces()) ^ from;
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
index b1199610..8efba318 100644
--- a/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
@@ -28,7 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Polyglot;
 
-public interface IPolyglotBook : IDisposable
+public interface IPolyglotBook : IAsyncDisposable
 {
     string BookFile { get; init; }
     Move Probe(IPosition pos, bool pickBest = true);
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index c7f946b9..7458150f 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -128,12 +128,6 @@ public Move Probe(IPosition pos, bool pickBest = true)
             : ConvertMove(pos, polyMove);
     }
 
-    public void Dispose()
-    {
-        _fileStream?.Dispose();
-        _binaryReader?.Dispose();
-    }
-
     private Move ConvertMove(IPosition pos, ushort polyMove)
     {
         // A PolyGlot book move is encoded as follows:
@@ -150,10 +144,7 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
         var polyPt = (polyMove >> 12) & 7;
 
         if (polyPt > 0)
-        {
-            static PieceTypes PolyToPt(int pt) => (PieceTypes)(3 - pt);
             move = Move.Create(from, to, MoveTypes.Promotion, PolyToPt(polyPt));
-        }
 
         var ml = _moveListPool.Get();
         ml.Generate(in pos);
@@ -164,15 +155,22 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
         _moveListPool.Return(ml);
 
         return mm;
+
+        static PieceTypes PolyToPt(int pt) => (PieceTypes)(3 - pt);
     }
 
     private static Move SelectMove(
-        in IPosition pos, Square polyFrom, Square polyTo, MoveTypes polyType, ReadOnlySpan<ValMove> moves)
+        in IPosition pos,
+        Square polyFrom,
+        Square polyTo,
+        MoveTypes polyType,
+        ReadOnlySpan<ValMove> moves)
     {
+        ref var valMoveRef = ref MemoryMarshal.GetReference(moves);
         // Iterate all known moves for current position to find a match.
-        foreach (var valMove in moves)
+        for (var i = 0; i < moves.Length; i++)
         {
-            var m = valMove.Move;
+            var m = Unsafe.Add(ref valMoveRef, i).Move;
 
             if (polyFrom != m.FromSquare() || polyTo != m.ToSquare())
                 continue;
@@ -219,7 +217,7 @@ public HashKey ComputePolyglotKey(in IPosition pos)
         for (var i = 0; i < CastleRights.Length; ++i)
         {
             var cr = Unsafe.Add(ref crSpace, i);
-            if (pos.State.CastlelingRights.Has(cr))
+            if (pos.State.CastleRights.Has(cr))
                 k ^= PolyglotBookZobrist.Castle(cr);
         }
 
@@ -258,4 +256,11 @@ private long FindFirst(in HashKey key)
 
         return low;
     }
+
+    public async ValueTask DisposeAsync()
+    {
+        if (_fileStream is not null)
+            await _fileStream.DisposeAsync();
+        _binaryReader?.Dispose();
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs
index dfc01003..5eb2e3ce 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs
@@ -26,18 +26,4 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Polyglot;
 
-public struct PolyglotBookEntry
-{
-    public readonly ulong Key;
-    public readonly ushort Move;
-    public readonly ushort Count;
-    public uint Learn;
-
-    public PolyglotBookEntry(ulong key, ushort move, ushort count, uint learn)
-    {
-        Key = key;
-        Move = move;
-        Count = count;
-        Learn = learn;
-    }
-}
+internal record struct PolyglotBookEntry(ulong Key, ushort Move, ushort Count, uint Learn);
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
index 1b00449a..2f9994a1 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 using File = Rudzoft.ChessLib.Types.File;
 
@@ -348,20 +349,23 @@ internal static class PolyglotBookZobrist
     // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
     private static readonly int[] PieceMapping = [-1, 1, 3, 5, 7, 9, 11, -1, -1, 0, 2, 4, 6, 8, 10];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal static ulong Psq(Piece pc, Square sq)
-    {
-        return Psq(PieceMapping[pc], sq);
-    }
+        => Psq(PieceMapping[pc], sq);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal static ulong Psq(int piece, Square sq)
         => PsqKeys[piece, sq];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal static ulong Castle(CastleRight rights)
         => CastleKeys[rights.AsInt()];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal static ulong EnPassant(File f)
         => EnPassantKeys[f];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal static ulong Turn()
         => TurnKey;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs b/src/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs
index 01eeee37..86110186 100644
--- a/src/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs
@@ -26,12 +26,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Polyglot;
 
-internal sealed class ReverseEndianBinaryStreamReader : BinaryReader
+internal sealed class ReverseEndianBinaryStreamReader(Stream stream) : BinaryReader(stream)
 {
-    public ReverseEndianBinaryStreamReader(Stream stream) : base(stream)
-    {
-    }
-
     public override short ReadInt16()
     {
         var data = ReadBytes(2);
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index c45b1108..c3749b58 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -133,34 +133,34 @@ public void AddPiece(Piece pc, Square sq)
     }
 
     public bool AttackedByKing(Square sq, Player p)
-        => (GetAttacks(sq, PieceTypes.King) & GetKingSquare(p)).IsNotEmpty;
+        => (GetAttacks(sq, PieceType.King) & GetKingSquare(p)).IsNotEmpty;
 
     public bool AttackedByKnight(Square sq, Player p)
-        => (Board.Pieces(p, PieceTypes.Knight) & GetAttacks(sq, PieceTypes.Knight)).IsNotEmpty;
+        => (Board.Pieces(p, PieceType.Knight) & GetAttacks(sq, PieceType.Knight)).IsNotEmpty;
 
     public bool AttackedByPawn(Square sq, Player p) =>
-        (Board.Pieces(p, PieceTypes.Pawn) & sq.PawnAttack(~p)).IsNotEmpty;
+        (Board.Pieces(p, PieceType.Pawn) & sq.PawnAttack(~p)).IsNotEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool AttackedBySlider(Square sq, Player p)
     {
         var occupied = Board.Pieces();
         var rookAttacks = sq.RookAttacks(in occupied);
-        if (Board.Pieces(p, PieceTypes.Rook) & rookAttacks)
+        if (Board.Pieces(p, PieceType.Rook) & rookAttacks)
             return true;
 
         var bishopAttacks = sq.BishopAttacks(in occupied);
-        if (Board.Pieces(p, PieceTypes.Bishop) & bishopAttacks)
+        if (Board.Pieces(p, PieceType.Bishop) & bishopAttacks)
             return true;
 
-        return (Board.Pieces(p, PieceTypes.Queen) & (bishopAttacks | rookAttacks)).IsNotEmpty;
+        return (Board.Pieces(p, PieceType.Queen) & (bishopAttacks | rookAttacks)).IsNotEmpty;
     }
 
-    public BitBoard AttacksBy(PieceTypes pt, Player p)
+    public BitBoard AttacksBy(PieceType pt, Player p)
     {
         var attackers = Pieces(pt, p);
 
-        if (pt == PieceTypes.Pawn)
+        if (pt == PieceType.Pawn)
             return attackers.PawnAttacks(p);
 
         var threats = BitBoard.Empty;
@@ -187,7 +187,7 @@ public bool IsCaptureOrPromotion(Move m)
     }
 
     public bool IsPawnPassedAt(Player p, Square sq)
-        => (Board.Pieces(~p, PieceTypes.Pawn) & sq.PassedPawnFrontAttackSpan(p)).IsEmpty;
+        => (Board.Pieces(~p, PieceType.Pawn) & sq.PassedPawnFrontAttackSpan(p)).IsEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard PawnPassSpan(Player p, Square sq) => p.FrontSquares(sq) | sq.PawnAttackSpan(p);
@@ -196,12 +196,12 @@ public BitBoard AttacksTo(Square sq, in BitBoard occ)
     {
         Debug.Assert(sq.IsOk);
 
-        return (sq.PawnAttack(Player.White) & Board.Pieces(Player.Black, PieceTypes.Pawn))
-               | (sq.PawnAttack(Player.Black) & Board.Pieces(Player.White, PieceTypes.Pawn))
-               | (GetAttacks(sq, PieceTypes.Knight) & Board.Pieces(PieceTypes.Knight))
-               | (GetAttacks(sq, PieceTypes.Rook, in occ) & Board.Pieces(PieceTypes.Rook, PieceTypes.Queen))
-               | (GetAttacks(sq, PieceTypes.Bishop, in occ) & Board.Pieces(PieceTypes.Bishop, PieceTypes.Queen))
-               | (GetAttacks(sq, PieceTypes.King) & Board.Pieces(PieceTypes.King));
+        return (sq.PawnAttack(Player.White) & Board.Pieces(Player.Black, PieceType.Pawn))
+               | (sq.PawnAttack(Player.Black) & Board.Pieces(Player.White, PieceType.Pawn))
+               | (GetAttacks(sq, PieceType.Knight) & Board.Pieces(PieceType.Knight))
+               | (GetAttacks(sq, PieceType.Rook, in occ) & Board.Pieces(PieceType.Rook, PieceType.Queen))
+               | (GetAttacks(sq, PieceType.Bishop, in occ) & Board.Pieces(PieceType.Bishop, PieceType.Queen))
+               | (GetAttacks(sq, PieceType.King) & Board.Pieces(PieceType.King));
     }
 
     public BitBoard AttacksTo(Square sq) => AttacksTo(sq, Board.Pieces());
@@ -242,9 +242,9 @@ public BitBoard SliderBlockerOn(Square sq, BitBoard attackers, ref BitBoard pinn
         return blockers;
     }
 
-    public bool CanCastle(CastleRight cr) => State.CastlelingRights.Has(cr);
+    public bool CanCastle(CastleRight cr) => State.CastleRights.Has(cr);
 
-    public bool CanCastle(Player p) => State.CastlelingRights.Has(p);
+    public bool CanCastle(Player p) => State.CastleRights.Has(p);
 
     public ref BitBoard CastleKingPath(CastleRight cr) => ref _castleKingPath[cr.AsInt()];
 
@@ -262,7 +262,7 @@ public Square CastlingRookSquare(CastleRight cr)
         return _castlingRookSquare[cr.Rights.AsInt()];
     }
 
-    public BitBoard CheckedSquares(PieceTypes pt) => State.CheckedSquares[pt.AsInt()];
+    public BitBoard CheckedSquares(PieceType pt) => State.CheckedSquares[pt];
 
     public void Clear()
     {
@@ -313,7 +313,7 @@ public FenData GenerateFen()
         fen[length++] = _sideToMove.Fen;
         fen[length++] = space;
 
-        if (State.CastlelingRights == CastleRight.None)
+        if (State.CastleRights == CastleRight.None)
             fen[length++] = dash;
         else
         {
@@ -375,22 +375,26 @@ public FenData GenerateFen()
         return new(new string(fen[..length]));
     }
 
-    public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
+    public BitBoard GetAttacks(Square sq, PieceType pt, in BitBoard occ)
     {
         Debug.Assert(pt != PieceTypes.Pawn, "Pawns need player");
 
-        return pt switch
-        {
-            PieceTypes.Knight => pt.PseudoAttacks(sq),
-            PieceTypes.King   => pt.PseudoAttacks(sq),
-            PieceTypes.Bishop => sq.BishopAttacks(in occ),
-            PieceTypes.Rook   => sq.RookAttacks(in occ),
-            PieceTypes.Queen  => sq.QueenAttacks(in occ),
-            var _             => BitBoard.Empty
-        };
+        if (pt == PieceType.Knight || pt == PieceType.King)
+            return pt.PseudoAttacks(sq);
+
+        if (pt == PieceType.Bishop)
+            return sq.BishopAttacks(in occ);
+
+        if (pt == PieceType.Rook)
+            return sq.RookAttacks(in occ);
+
+        if (pt == PieceType.Queen)
+            return sq.QueenAttacks(in occ);
+
+        return BitBoards.EmptyBitBoard;
     }
 
-    public BitBoard GetAttacks(Square sq, PieceTypes pt) => GetAttacks(sq, pt, Pieces());
+    public BitBoard GetAttacks(Square sq, PieceType pt) => GetAttacks(sq, pt, Pieces());
 
     public CastleRight GetCastleRightsMask(Square sq) => _castlingRightsMask[sq];
 
@@ -398,13 +402,13 @@ public BitBoard GetAttacks(Square sq, PieceTypes pt, in BitBoard occ)
 
     IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
-    public Square GetKingSquare(Player p) => Board.Square(PieceTypes.King, p);
+    public Square GetKingSquare(Player p) => Board.Square(PieceType.King, p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public HashKey GetPawnKey()
     {
         var result = Zobrist.ZobristNoPawn;
-        var pieces = Board.Pieces(PieceTypes.Pawn);
+        var pieces = Board.Pieces(PieceType.Pawn);
         while (pieces)
         {
             var sq = BitBoards.PopLsb(ref pieces);
@@ -419,7 +423,7 @@ public HashKey GetPawnKey()
     public Piece GetPiece(Square sq) => Board.PieceAt(sq);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public HashKey GetKey()
+    public HashKey GetKey(State state)
     {
         var result = HashKey.Empty;
         var pieces = Board.Pieces();
@@ -430,8 +434,8 @@ public HashKey GetKey()
             result ^= Zobrist.Psq(sq, pc);
         }
 
-        if (State.EnPassantSquare != Square.None)
-            result ^= Zobrist.EnPassant(State.EnPassantSquare);
+        if (state.EnPassantSquare != Square.None)
+            result ^= Zobrist.EnPassant(state.EnPassantSquare);
 
         if (_sideToMove == Player.Black)
             result ^= Zobrist.Side();
@@ -439,10 +443,10 @@ public HashKey GetKey()
         return result;
     }
 
-    public Square GetPieceSquare(PieceTypes pt, Player p) => Board.Square(pt, p);
+    public Square GetPieceSquare(PieceType pt, Player p) => Board.Square(pt, p);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public PieceTypes GetPieceType(Square sq) => Board.PieceAt(sq).Type();
+    public PieceType GetPieceType(Square sq) => Board.PieceAt(sq).Type();
 
     public bool GivesCheck(Move m)
     {
@@ -455,7 +459,7 @@ public bool GivesCheck(Move m)
         var pt = pc.Type();
 
         // Is there a direct check?
-        if ((State.CheckedSquares[pt.AsInt()] & to).IsNotEmpty)
+        if ((State.CheckedSquares[pt] & to).IsNotEmpty)
             return true;
 
         var us = _sideToMove;
@@ -483,10 +487,10 @@ public bool GivesCheck(Move m)
                 var b = (Board.Pieces() ^ from ^ captureSquare) | to;
                 var ksq = GetKingSquare(them);
 
-                var attacks = (GetAttacks(ksq, PieceTypes.Rook, in b) &
-                               Board.Pieces(us, PieceTypes.Rook, PieceTypes.Queen))
-                              | (GetAttacks(ksq, PieceTypes.Bishop, in b) &
-                                 Board.Pieces(us, PieceTypes.Bishop, PieceTypes.Queen));
+                var attacks = (GetAttacks(ksq, PieceType.Rook, in b) &
+                               Board.Pieces(us, PieceType.Rook, PieceType.Queen))
+                              | (GetAttacks(ksq, PieceType.Bishop, in b) &
+                                 Board.Pieces(us, PieceType.Bishop, PieceType.Queen));
                 return attacks.IsNotEmpty;
             }
             case MoveTypes.Castling:
@@ -499,8 +503,8 @@ public bool GivesCheck(Move m)
                 var rookTo = (rookFrom > kingFrom ? Square.F1 : Square.D1).Relative(us);
                 var ksq = GetKingSquare(them);
 
-                return (PieceTypes.Rook.PseudoAttacks(rookTo) & ksq).IsNotEmpty
-                       && (GetAttacks(rookTo, PieceTypes.Rook,
+                return (PieceType.Rook.PseudoAttacks(rookTo) & ksq).IsNotEmpty
+                       && (GetAttacks(rookTo, PieceType.Rook,
                            (Board.Pieces() ^ kingFrom ^ rookFrom) | rookTo | kingTo) & ksq).IsNotEmpty;
             }
             default:
@@ -550,24 +554,24 @@ public bool IsLegal(Move m)
         var (from, to, type) = m;
         var ksq = GetKingSquare(us);
 
-        Debug.Assert(GetPiece(GetKingSquare(us)) == PieceTypes.King.MakePiece(us));
+        Debug.Assert(GetPiece(GetKingSquare(us)) == PieceType.King.MakePiece(us));
         Debug.Assert(to != from);
 
         // En passant captures are a tricky special case. Because they are rather uncommon, we
         // do it simply by testing whether the king is attacked after the move is made.
         if (type == MoveTypes.Enpassant)
         {
-            Debug.Assert(MovedPiece(m) == PieceTypes.Pawn.MakePiece(us));
+            Debug.Assert(MovedPiece(m) == PieceType.Pawn.MakePiece(us));
             return IsEnPassantMoveLegal(to, us, from, ksq);
         }
 
         // Check for legal castleling move
         if (type == MoveTypes.Castling)
-            return IsCastlelingMoveLegal(m, to, from, us);
+            return IsCastleMoveLegal(m, to, from, us);
 
         // If the moving piece is a king, check whether the destination square is attacked by
         // the opponent.
-        if (MovedPiece(m).Type() == PieceTypes.King)
+        if (MovedPiece(m).Type() == PieceType.King)
             return (AttacksTo(to) & Board.Pieces(~us)).IsEmpty;
 
         // A non-king move is legal if and only if it is not pinned or it is moving along the
@@ -575,7 +579,7 @@ public bool IsLegal(Move m)
         return (KingBlockers(us) & from).IsEmpty || from.Aligned(to, ksq);
     }
 
-    private bool IsCastlelingMoveLegal(Move m, Square to, Square from, Player us)
+    private bool IsCastleMoveLegal(Move m, Square to, Square from, Player us)
     {
         // After castling, the rook and king final positions are the same in Chess960 as
         // they would be in standard chess.
@@ -587,12 +591,12 @@ private bool IsCastlelingMoveLegal(Move m, Square to, Square from, Player us)
         if (isKingSide)
         {
             to = Square.G1.Relative(us);
-            step = Directions.West;
+            step = Direction.West;
         }
         else
         {
             to = Square.C1.Relative(us);
-            step = Directions.East;
+            step = Direction.East;
         }
 
         for (var s = to; s != from; s += step)
@@ -607,11 +611,11 @@ private bool IsCastlelingMoveLegal(Move m, Square to, Square from, Player us)
 
         var attacks = GetAttacks(
             sq: to,
-            pt: PieceTypes.Rook,
+            pt: PieceType.Rook,
             occ: Board.Pieces() ^ m.ToSquare()
         );
 
-        var occupied = Board.Pieces(them, PieceTypes.Rook, PieceTypes.Queen);
+        var occupied = Board.Pieces(them, PieceType.Rook, PieceType.Queen);
 
         return (attacks & occupied).IsEmpty;
 
@@ -629,20 +633,20 @@ private bool IsEnPassantMoveLegal(Square to, Player us, Square from, Square ksq)
         var occupied = (Board.Pieces() ^ from ^ captureSquare) | to;
 
         Debug.Assert(to == EnPassantSquare);
-        Debug.Assert(GetPiece(captureSquare) == PieceTypes.Pawn.MakePiece(~us));
+        Debug.Assert(GetPiece(captureSquare) == PieceType.Pawn.MakePiece(~us));
         Debug.Assert(GetPiece(to) == Piece.EmptyPiece);
 
-        return (GetAttacks(ksq, PieceTypes.Rook, in occupied) &
-                Board.Pieces(~us, PieceTypes.Rook, PieceTypes.Queen)).IsEmpty
-               && (GetAttacks(ksq, PieceTypes.Bishop, in occupied) &
-                   Board.Pieces(~us, PieceTypes.Bishop, PieceTypes.Queen)).IsEmpty;
+        return (GetAttacks(ksq, PieceType.Rook, in occupied) &
+                Board.Pieces(~us, PieceType.Rook, PieceType.Queen)).IsEmpty
+               && (GetAttacks(ksq, PieceType.Bishop, in occupied) &
+                   Board.Pieces(~us, PieceType.Bishop, PieceType.Queen)).IsEmpty;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool IsOccupied(Square sq) => !Board.IsEmpty(sq);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsPieceTypeOnSquare(Square sq, PieceTypes pt) => Board.PieceAt(sq).Type() == pt;
+    public bool IsPieceTypeOnSquare(Square sq, PieceType pt) => Board.PieceAt(sq).Type() == pt;
 
     /// <summary>
     /// <para>
@@ -660,7 +664,7 @@ public bool IsPseudoLegal(Move m)
             return ContainsMove(m);
 
         // Is not a promotion, so promotion piece must be empty
-        if (m.PromotedPieceType() - 2 != PieceTypes.NoPieceType)
+        if (m.PromotedPieceType() - 2 != PieceType.NoPieceType)
             return false;
 
         var us = _sideToMove;
@@ -677,7 +681,7 @@ public bool IsPseudoLegal(Move m)
             return false;
 
         // Handle the special case of a pawn move
-        if (pc.Type() == PieceTypes.Pawn)
+        if (pc.Type() == PieceType.Pawn)
         {
             // We have already handled promotion moves, so destination cannot be on the 8th/1st rank.
             if (to.Rank == Rank.Rank8.Relative(us))
@@ -702,7 +706,7 @@ public bool IsPseudoLegal(Move m)
         // relies on this. We therefore have to take care that the same kind of moves are
         // filtered out here.
 
-        if (pc.Type() != PieceTypes.King)
+        if (pc.Type() != PieceType.King)
         {
             // Double check? In this case a king move is required
             if (Checkers.MoreThanOne())
@@ -740,7 +744,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         var (from, to, type) = m;
         var pc = GetPiece(from);
         var capturedPiece = m.IsEnPassantMove()
-            ? PieceTypes.Pawn.MakePiece(them)
+            ? PieceType.Pawn.MakePiece(them)
             : GetPiece(to);
 
         Debug.Assert(pc.ColorOf() == us);
@@ -750,8 +754,8 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
 
         if (type == MoveTypes.Castling)
         {
-            Debug.Assert(pc == PieceTypes.King.MakePiece(us));
-            Debug.Assert(capturedPiece == PieceTypes.Rook.MakePiece(us));
+            Debug.Assert(pc == PieceType.King.MakePiece(us));
+            Debug.Assert(capturedPiece == PieceType.Rook.MakePiece(us));
 
             DoCastle(us, from, ref to, out var rookFrom, out var rookTo, CastlePerform.Do);
             // var (rookFrom, rookTo) = DoCastle(us, from, ref to, CastlePerform.Do);
@@ -766,7 +770,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         {
             var captureSquare = to;
 
-            if (capturedPiece.Type() == PieceTypes.Pawn)
+            if (capturedPiece.Type() == PieceType.Pawn)
             {
                 if (type == MoveTypes.Enpassant)
                 {
@@ -809,12 +813,12 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         }
 
         // Update castling rights if needed
-        if (state.CastlelingRights != CastleRight.None &&
+        if (state.CastleRights != CastleRight.None &&
             (_castlingRightsMask[from] | _castlingRightsMask[to]) != CastleRight.None)
         {
-            posKey ^= Zobrist.Castleling(state.CastlelingRights);
-            state.CastlelingRights &= ~(_castlingRightsMask[from] | _castlingRightsMask[to]);
-            //posKey ^= Zobrist.Castleling(state.CastlelingRights);
+            posKey ^= Zobrist.Castle(state.CastleRights);
+            state.CastleRights &= ~(_castlingRightsMask[from] | _castlingRightsMask[to]);
+            posKey ^= Zobrist.Castle(state.CastleRights);
         }
 
         // Move the piece. The tricky Chess960 castle is handled earlier
@@ -822,7 +826,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
             MovePiece(from, to);
 
         // If the moving piece is a pawn do some special extra work
-        if (pc.Type() == PieceTypes.Pawn)
+        if (pc.Type() == PieceType.Pawn)
         {
             // Set en-passant square, only if moved pawn can be captured
             if ((to.AsInt() ^ from.AsInt()) == 16
@@ -951,12 +955,12 @@ public bool PassedPawn(Square sq)
     {
         var pc = Board.PieceAt(sq);
 
-        if (pc.Type() != PieceTypes.Pawn)
+        if (pc.Type() != PieceType.Pawn)
             return false;
 
         var c = pc.ColorOf();
 
-        return (sq.PassedPawnFrontAttackSpan(c) & Board.Pieces(c, PieceTypes.Pawn)).IsEmpty;
+        return (sq.PassedPawnFrontAttackSpan(c) & Board.Pieces(c, PieceType.Pawn)).IsEmpty;
     }
 
     /// <summary>
@@ -967,48 +971,65 @@ public bool PassedPawn(Square sq)
     /// <returns></returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool PawnIsolated(Square sq, Player p)
-        => ((sq.PawnAttackSpan(p) | sq.PawnAttackSpan(~p)) & Board.Pieces(p, PieceTypes.Pawn)).IsEmpty;
+        => ((sq.PawnAttackSpan(p) | sq.PawnAttackSpan(~p)) & Board.Pieces(p, PieceType.Pawn)).IsEmpty;
 
-    public bool PieceOnFile(Square sq, Player p, PieceTypes pt) => (Board.Pieces(p, pt) & sq).IsNotEmpty;
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool PieceOnFile(Square sq, Player p, PieceType pt) => (Board.Pieces(p, pt) & sq).IsNotEmpty;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces() => Board.Pieces();
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces(Player p) => Board.Pieces(p);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces(Piece pc) => Board.Pieces(pc.ColorOf(), pc.Type());
 
-    public BitBoard Pieces(PieceTypes pt) => Board.Pieces(pt);
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public BitBoard Pieces(PieceType pt) => Board.Pieces(pt);
 
-    public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2) => Board.Pieces(pt1, pt2);
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public BitBoard Pieces(PieceType pt1, PieceType pt2) => Board.Pieces(pt1, pt2);
 
-    public BitBoard Pieces(PieceTypes pt, Player p) => Board.Pieces(p, pt);
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public BitBoard Pieces(PieceType pt, Player p) => Board.Pieces(p, pt);
 
-    public BitBoard Pieces(PieceTypes pt1, PieceTypes pt2, Player p) => Board.Pieces(p, pt1, pt2);
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public BitBoard Pieces(PieceType pt1, PieceType pt2, Player p) => Board.Pieces(p, pt1, pt2);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int PieceCount() => Board.PieceCount();
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int PieceCount(Piece pc) => Board.PieceCount(pc.Type(), pc.ColorOf());
 
-    public int PieceCount(PieceTypes pt) => Board.PieceCount(pt);
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public int PieceCount(PieceType pt) => Board.PieceCount(pt);
 
-    public int PieceCount(PieceTypes pt, Player p) => Board.PieceCount(pt, p);
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public int PieceCount(PieceType pt, Player p) => Board.PieceCount(pt, p);
 
-    public BitBoard PawnsOnColor(Player p, Square sq) => Pieces(PieceTypes.Pawn, p) & sq.Color().ColorBB();
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public BitBoard PawnsOnColor(Player p, Square sq) => Pieces(PieceType.Pawn, p) & sq.Color().ColorBB();
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool SemiOpenFileOn(Player p, Square sq)
-        => (Board.Pieces(p, PieceTypes.Pawn) & sq.File.BitBoardFile()).IsEmpty;
+        => (Board.Pieces(p, PieceType.Pawn) & sq.File.BitBoardFile()).IsEmpty;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool BishopPaired(Player p) =>
-        Board.PieceCount(PieceTypes.Bishop, p) >= 2
-        && (Board.Pieces(p, PieceTypes.Bishop) & Player.White.ColorBB()).IsNotEmpty
-        && (Board.Pieces(p, PieceTypes.Bishop) & Player.Black.ColorBB()).IsNotEmpty;
+        Board.PieceCount(PieceType.Bishop, p) >= 2
+        && (Board.Pieces(p, PieceType.Bishop) & Player.White.ColorBB()).IsNotEmpty
+        && (Board.Pieces(p, PieceType.Bishop) & Player.Black.ColorBB()).IsNotEmpty;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool BishopOpposed() =>
         Board.PieceCount(Piece.WhiteBishop) == 1
         && Board.PieceCount(Piece.BlackBishop) == 1
-        && Board.Square(PieceTypes.Bishop, Player.White)
-                .IsOppositeColor(Board.Square(PieceTypes.Bishop, Player.Black));
+        && Board.Square(PieceType.Bishop, Player.White)
+                .IsOppositeColor(Board.Square(PieceType.Bishop, Player.Black));
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard PinnedPieces(Player p)
     {
         Debug.Assert(State != null);
@@ -1070,51 +1091,51 @@ public bool SeeGe(Move m, Value threshold)
 
             // Locate and remove the next least valuable attacker, and add to the bitboard
             // 'attackers' any X-ray attackers behind it.
-            var bb = stmAttackers & Board.Pieces(PieceTypes.Pawn);
+            var bb = stmAttackers & Board.Pieces(PieceType.Pawn);
             if (bb.IsNotEmpty)
             {
                 if ((swap = Values.PawnValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
-                attackers |= GetAttacks(to, PieceTypes.Bishop, in occupied) &
-                             Board.Pieces(PieceTypes.Bishop, PieceTypes.Queen);
+                attackers |= GetAttacks(to, PieceType.Bishop, in occupied) &
+                             Board.Pieces(PieceType.Bishop, PieceType.Queen);
             }
-            else if ((bb = stmAttackers & Board.Pieces(PieceTypes.Knight)).IsNotEmpty)
+            else if ((bb = stmAttackers & Board.Pieces(PieceType.Knight)).IsNotEmpty)
             {
                 if ((swap = Values.KnightValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
             }
-            else if ((bb = stmAttackers & Board.Pieces(PieceTypes.Bishop)).IsNotEmpty)
+            else if ((bb = stmAttackers & Board.Pieces(PieceType.Bishop)).IsNotEmpty)
             {
                 if ((swap = Values.BishopValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
-                attackers |= GetAttacks(to, PieceTypes.Bishop, in occupied) &
-                             Board.Pieces(PieceTypes.Bishop, PieceTypes.Queen);
+                attackers |= GetAttacks(to, PieceType.Bishop, in occupied) &
+                             Board.Pieces(PieceType.Bishop, PieceType.Queen);
             }
-            else if ((bb = stmAttackers & Board.Pieces(PieceTypes.Rook)).IsNotEmpty)
+            else if ((bb = stmAttackers & Board.Pieces(PieceType.Rook)).IsNotEmpty)
             {
                 if ((swap = Values.RookValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
-                attackers |= GetAttacks(to, PieceTypes.Rook, in occupied) &
-                             Board.Pieces(PieceTypes.Rook, PieceTypes.Queen);
+                attackers |= GetAttacks(to, PieceType.Rook, in occupied) &
+                             Board.Pieces(PieceType.Rook, PieceType.Queen);
             }
-            else if ((bb = stmAttackers & Board.Pieces(PieceTypes.Queen)).IsNotEmpty)
+            else if ((bb = stmAttackers & Board.Pieces(PieceType.Queen)).IsNotEmpty)
             {
                 if ((swap = Values.QueenValueMg - swap) < res)
                     break;
 
                 occupied ^= bb.Lsb();
-                attackers |= (GetAttacks(to, PieceTypes.Bishop, in occupied) &
-                              Board.Pieces(PieceTypes.Bishop, PieceTypes.Queen))
-                             | (GetAttacks(to, PieceTypes.Rook, in occupied) &
-                                Board.Pieces(PieceTypes.Rook, PieceTypes.Queen));
+                attackers |= (GetAttacks(to, PieceType.Bishop, in occupied) &
+                              Board.Pieces(PieceType.Bishop, PieceType.Queen))
+                             | (GetAttacks(to, PieceType.Rook, in occupied) &
+                                Board.Pieces(PieceType.Rook, PieceType.Queen));
             }
             else // KING
                 // If we "capture" with the king but opponent still has attackers, reverse the result.
@@ -1129,8 +1150,10 @@ public bool SeeGe(Move m, Value threshold)
         return res > 0;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Value NonPawnMaterial(Player p) => _nonPawnMaterial[p];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Value NonPawnMaterial() => _nonPawnMaterial[0] - _nonPawnMaterial[1];
 
     private void SetupPieces(ReadOnlySpan<char> fenChunk)
@@ -1160,7 +1183,8 @@ private void SetupPieces(ReadOnlySpan<char> fenChunk)
 
                 var square = new Square(r - 1, f - 1);
 
-                var pc = ((PieceTypes)pieceIndex).MakePiece(p);
+                var pt = new PieceType(pieceIndex);
+                var pc = pt.MakePiece(p);
                 AddPiece(pc, square);
 
                 f++;
@@ -1187,8 +1211,8 @@ private void SetupEnPassant(ReadOnlySpan<char> fenChunk)
             var us = _sideToMove;
             var them = ~us;
 
-            if (!(epSquare.PawnAttack(them) & Pieces(PieceTypes.Pawn, us)).IsEmpty
-                && !(Pieces(PieceTypes.Pawn, them) & (epSquare + them.PawnPushDistance())).IsEmpty
+            if (!(epSquare.PawnAttack(them) & Pieces(PieceType.Pawn, us)).IsEmpty
+                && !(Pieces(PieceType.Pawn, them) & (epSquare + them.PawnPushDistance())).IsEmpty
                 && (Pieces() & (epSquare | (epSquare + us.PawnPushDistance()))).IsEmpty)
                 State.EnPassantSquare = epSquare;
         }
@@ -1263,6 +1287,7 @@ public IPosition Set(
         return this;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public IPosition Set(string fen, ChessMode chessMode, in State state, bool validate = false, int searcher = 0)
         => Set(new FenData(fen), chessMode, state, validate, searcher);
 
@@ -1302,7 +1327,7 @@ public void TakeMove(Move m)
             Debug.Assert(m.PromotedPieceType() >= PieceTypes.Knight && m.PromotedPieceType() <= PieceTypes.Queen);
 
             RemovePiece(to);
-            var pc = PieceTypes.Pawn.MakePiece(us);
+            var pc = PieceType.Pawn.MakePiece(us);
             AddPiece(pc, to);
             _nonPawnMaterial[_sideToMove] -= Values.GetPieceValue(pc, Phases.Mg);
         }
@@ -1407,6 +1432,7 @@ public override string ToString()
         return result;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public HashKey MovePositionKey(Move m)
     {
         Debug.Assert(m.IsValidMove());
@@ -1441,19 +1467,39 @@ private void DoCastle(
         CastlePerform castlePerform)
     {
         var kingSide = to > from;
-        var doCastleling = castlePerform == CastlePerform.Do;
+        var doCastle = castlePerform == CastlePerform.Do;
 
         rookFrom = to; // Castling is encoded as "king captures friendly rook"
-        rookTo = (kingSide ? Square.F1 : Square.D1).Relative(us);
-        to = (kingSide ? Square.G1 : Square.C1).Relative(us);
+        if (kingSide)
+        {
+            rookTo = Square.F1.Relative(us);
+            to = Square.G1.Relative(us);
+        }
+        else
+        {
+            rookTo = Square.D1.Relative(us);
+            to = Square.C1.Relative(us);
+        }
 
         // Remove both pieces first since squares could overlap in Chess960
-        RemovePiece(doCastleling ? from : to);
-        RemovePiece(doCastleling ? rookFrom : rookTo);
-        Board.ClearPiece(doCastleling ? from : to);
-        Board.ClearPiece(doCastleling ? rookFrom : rookTo);
-        AddPiece(PieceTypes.King.MakePiece(us), doCastleling ? to : from);
-        AddPiece(PieceTypes.Rook.MakePiece(us), doCastleling ? rookTo : rookFrom);
+        if (doCastle)
+        {
+            RemovePiece(from);
+            RemovePiece(rookFrom);
+            Board.ClearPiece(from);
+            Board.ClearPiece(rookFrom);
+            AddPiece(PieceType.King.MakePiece(us), to);
+            AddPiece(PieceType.Rook.MakePiece(us), rookTo);
+        }
+        else
+        {
+            RemovePiece(to);
+            RemovePiece(rookTo);
+            Board.ClearPiece(to);
+            Board.ClearPiece(rookTo);
+            AddPiece(PieceType.King.MakePiece(us), from);
+            AddPiece(PieceType.Rook.MakePiece(us), rookFrom);
+        }
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -1479,7 +1525,7 @@ private void SetCastlingRight(Player stm, Square rookFrom)
         var isKingSide = kingFrom < rookFrom;
         var cr = OrCastlingRight(stm, isKingSide);
 
-        State.CastlelingRights |= cr;
+        State.CastleRights |= cr;
         _castlingRightsMask[kingFrom] |= cr;
         _castlingRightsMask[rookFrom] |= cr;
         _castlingRookSquare[cr.AsInt()] = rookFrom;
@@ -1501,19 +1547,18 @@ private void SetCheckInfo(in State state)
 
         var ksq = GetKingSquare(~_sideToMove);
 
-        state.CheckedSquares[PieceTypes.Pawn.AsInt()] = ksq.PawnAttack(~_sideToMove);
-        state.CheckedSquares[PieceTypes.Knight.AsInt()] = GetAttacks(ksq, PieceTypes.Knight);
-        state.CheckedSquares[PieceTypes.Bishop.AsInt()] = GetAttacks(ksq, PieceTypes.Bishop);
-        state.CheckedSquares[PieceTypes.Rook.AsInt()] = GetAttacks(ksq, PieceTypes.Rook);
-        state.CheckedSquares[PieceTypes.Queen.AsInt()] = state.CheckedSquares[PieceTypes.Bishop.AsInt()] |
-                                                         state.CheckedSquares[PieceTypes.Rook.AsInt()];
-        state.CheckedSquares[PieceTypes.King.AsInt()] = BitBoard.Empty;
+        state.CheckedSquares[PieceType.Pawn] = ksq.PawnAttack(~_sideToMove);
+        state.CheckedSquares[PieceType.Knight] = GetAttacks(ksq, PieceType.Knight);
+        state.CheckedSquares[PieceType.Bishop] = GetAttacks(ksq, PieceType.Bishop);
+        state.CheckedSquares[PieceType.Rook] = GetAttacks(ksq, PieceType.Rook);
+        state.CheckedSquares[PieceType.Queen] = state.CheckedSquares[PieceType.Bishop] |
+                                                state.CheckedSquares[PieceType.Rook];
+        state.CheckedSquares[PieceType.King] = BitBoard.Empty;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private void CopyState(in State newState)
-    {
-        State = State.CopyTo(newState);
-    }
+        => State = State.CopyTo(newState);
 
     private void SetState(State state)
     {
@@ -1522,17 +1567,16 @@ private void SetState(State state)
         _nonPawnMaterial[Player.White] = _nonPawnMaterial[Player.Black] = Value.ValueZero;
         State.Checkers = AttacksTo(GetKingSquare(_sideToMove)) & Board.Pieces(~_sideToMove);
 
-        SetCheckInfo(State);
+        SetCheckInfo(state);
 
-        state.PositionKey = GetKey();
+        state.PositionKey = GetKey(state);
         state.PawnKey = GetPawnKey();
 
-        var b = Pieces(PieceTypes.Pawn);
-        while (b.IsNotEmpty)
+        var b = Pieces(PieceType.Pawn);
+        while (b)
         {
             var sq = BitBoards.PopLsb(ref b);
-            var pc = Board.PieceAt(sq);
-            state.MaterialKey ^= Zobrist.Psq(sq, pc);
+            state.MaterialKey ^= Zobrist.Psq(sq, Board.PieceAt(sq));
         }
 
         ref var pieces = ref MemoryMarshal.GetArrayDataReference(Piece.All);
@@ -1540,9 +1584,10 @@ private void SetState(State state)
         for (var i = 0; i < Piece.All.Length; i++)
         {
             ref var pc = ref Unsafe.Add(ref pieces, i);
-            if (pc.Type() != PieceTypes.Pawn && pc.Type() != PieceTypes.King)
+            var pt = pc.Type();
+            if (pt != PieceType.Pawn && pt != PieceType.King)
             {
-                var val = Values.GetPieceValue(pc.Type(), Phases.Mg).AsInt() * Board.PieceCount(pc);
+                var val = Values.GetPieceValue(pt, Phases.Mg).AsInt() * Board.PieceCount(pc);
                 _nonPawnMaterial[pc.ColorOf()] += val;
             }
 
@@ -1556,7 +1601,7 @@ private void SetupCastle(ReadOnlySpan<char> castle)
         foreach (var ca in castle)
         {
             Player c = char.IsLower(ca);
-            var rook = PieceTypes.Rook.MakePiece(c);
+            var rook = PieceType.Rook.MakePiece(c);
             var token = char.ToUpper(ca);
 
             var rsq = token switch
@@ -1573,6 +1618,7 @@ private void SetupCastle(ReadOnlySpan<char> castle)
         }
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private Square RookSquare(Square startSq, Piece rook)
     {
         var targetSq = startSq;
@@ -1586,8 +1632,8 @@ private Square RookSquare(Square startSq, Piece rook)
         var result = (blockers: BitBoard.Empty, pinners: BitBoard.Empty);
 
         // Snipers are sliders that attack 's' when a piece and other snipers are removed
-        var snipers = (PieceTypes.Rook.PseudoAttacks(s) & Board.Pieces(PieceTypes.Queen, PieceTypes.Rook)
-                       | (PieceTypes.Bishop.PseudoAttacks(s) & Board.Pieces(PieceTypes.Queen, PieceTypes.Bishop)))
+        var snipers = (PieceType.Rook.PseudoAttacks(s) & Board.Pieces(PieceType.Queen, PieceType.Rook)
+                       | (PieceType.Bishop.PseudoAttacks(s) & Board.Pieces(PieceType.Queen, PieceType.Bishop)))
                       & sliders;
         var occupancy = Board.Pieces() ^ snipers;
 
@@ -1647,12 +1693,12 @@ private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
 
         var them = ~us;
 
-        if (moved && !(Pieces(PieceTypes.Pawn, ~us).Contains(epSquare + them.PawnPushDistance()) &&
+        if (moved && !(Pieces(PieceType.Pawn, ~us).Contains(epSquare + them.PawnPushDistance()) &&
                        Board.IsEmpty(epSquare) && Board.IsEmpty(epSquare + us.PawnPushDistance())))
             return false;
 
         // En-passant attackers
-        var attackers = Pieces(PieceTypes.Pawn, us) & epSquare.PawnAttack(them);
+        var attackers = Pieces(PieceType.Pawn, us) & epSquare.PawnAttack(them);
 
         Debug.Assert(attackers.Count <= 2);
 
@@ -1660,11 +1706,11 @@ private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
             return false;
 
         var cap = moved ? epSquare - us.PawnPushDistance() : epSquare + us.PawnPushDistance();
-        Debug.Assert(Board.PieceAt(cap) == PieceTypes.Pawn.MakePiece(them));
+        Debug.Assert(Board.PieceAt(cap) == PieceType.Pawn.MakePiece(them));
 
-        var ksq = Board.Square(PieceTypes.King, us);
-        var bq = Pieces(PieceTypes.Bishop, PieceTypes.Queen, them) & GetAttacks(ksq, PieceTypes.Bishop);
-        var rq = Pieces(PieceTypes.Rook, PieceTypes.Queen, them) & GetAttacks(ksq, PieceTypes.Rook);
+        var ksq = Board.Square(PieceType.King, us);
+        var bq = Pieces(PieceType.Bishop, PieceType.Queen, them) & GetAttacks(ksq, PieceType.Bishop);
+        var rq = Pieces(PieceType.Rook, PieceType.Queen, them) & GetAttacks(ksq, PieceType.Rook);
 
         var mocc = (Pieces() ^ cap) | epSquare;
 
@@ -1674,8 +1720,8 @@ private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
             var amocc = mocc ^ sq;
             // Check en-passant is legal for the position
             if (
-                (bq.IsEmpty || (bq & GetAttacks(ksq, PieceTypes.Bishop, in amocc)).IsEmpty) &&
-                (rq.IsEmpty || (rq & GetAttacks(ksq, PieceTypes.Rook, in amocc)).IsEmpty))
+                (bq.IsEmpty || (bq & GetAttacks(ksq, PieceType.Bishop, in amocc)).IsEmpty) &&
+                (rq.IsEmpty || (rq & GetAttacks(ksq, PieceType.Rook, in amocc)).IsEmpty))
                 return true;
         }
 
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 7edaf37d..07d2e9b1 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -4,15 +4,12 @@
         <Platforms>AnyCPU</Platforms>
         <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
-        <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
-        <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
         <PackageVersion>0.0.4</PackageVersion>
         <Authors>Rudy Alex Kohn</Authors>
         <Company>None</Company>
         <Version>0.0.4</Version>
         <Description>Chess library with data structures and move generation.</Description>
-        <Copyright>(C) 2017-2023 Rudy Alex Kohn</Copyright>
+        <Copyright>(C) 2017-2024 Rudy Alex Kohn</Copyright>
         <AssemblyVersion>0.0.4</AssemblyVersion>
         <FileVersion>0.0.4</FileVersion>
         <PackageProjectUrl>https://github.com/rudzen/ChessLib</PackageProjectUrl>
@@ -27,29 +24,9 @@
         <RepositoryUrl>https://github.com/rudzen/ChessLib</RepositoryUrl>
         <RepositoryType>git</RepositoryType>
         <Product>Rudzoft.ChessLib</Product>
-        <Configurations>Debug;Release</Configurations>
         <IsPackable>true</IsPackable>
     </PropertyGroup>
 
-    <PropertyGroup Condition="'$(IsWindows)'=='true'">
-        <DefineConstants>Windows</DefineConstants>
-    </PropertyGroup>
-    <PropertyGroup Condition="'$(IsOSX)'=='true'">
-        <DefineConstants>OSX</DefineConstants>
-    </PropertyGroup>
-    <PropertyGroup Condition="'$(IsLinux)'=='true'">
-        <DefineConstants>Linux</DefineConstants>
-    </PropertyGroup>
-    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-        <PlatformTarget>x64</PlatformTarget>
-    </PropertyGroup>
-
-    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
-        <PlatformTarget>AnyCPU</PlatformTarget>
-    </PropertyGroup>
-    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-        <PlatformTarget>AnyCPU</PlatformTarget>
-    </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
         <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj.DotSettings b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj.DotSettings
deleted file mode 100644
index 472ae7f3..00000000
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj.DotSettings
+++ /dev/null
@@ -1,3 +0,0 @@
-<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
-	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=chess/@EntryIndexedValue">True</s:Boolean>
-	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=interfaces/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index a8392dfd..d368ab74 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -45,7 +45,7 @@ public sealed class State : IEquatable<State>
 
     public int NullPly { get; set; }
 
-    public CastleRight CastlelingRights { get; set; } = CastleRight.None;
+    public CastleRight CastleRights { get; set; } = CastleRight.None;
 
     [Description("En-passant -> 'in-passing' square")]
     public Square EnPassantSquare { get; set; } = Square.None;
@@ -66,9 +66,9 @@ public sealed class State : IEquatable<State>
 
     public BitBoard[] Pinners { get; } = [BitBoard.Empty, BitBoard.Empty];
 
-    public BitBoard[] CheckedSquares { get; private set; } = new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
+    public BitBoard[] CheckedSquares { get; private set; } = new BitBoard[PieceType.Count];
 
-    public PieceTypes CapturedPiece { get; set; } = PieceTypes.NoPieceType;
+    public PieceType CapturedPiece { get; set; } = PieceType.NoPieceType;
 
     public Move LastMove { get; set; } = Move.EmptyMove;
 
@@ -85,12 +85,12 @@ public State CopyTo(State other)
         other.PawnKey = PawnKey;
         other.ClockPly = ClockPly;
         other.NullPly = NullPly;
-        other.CastlelingRights = CastlelingRights;
+        other.CastleRights = CastleRights;
         other.EnPassantSquare = EnPassantSquare;
         other.Previous = this;
 
         // initialize the rest of the values
-        other.CheckedSquares ??= new BitBoard[PieceTypes.PieceTypeNb.AsInt()];
+        other.CheckedSquares ??= new BitBoard[PieceType.NoPieceType];
 
         return other;
     }
@@ -101,7 +101,7 @@ public void Clear()
         PawnKey = PositionKey = MaterialKey = HashKey.Empty;
         NullPly = 0;
         Repetition = 0;
-        CastlelingRights = CastleRight.None;
+        CastleRights = CastleRight.None;
         EnPassantSquare = Square.None;
         CheckedSquares.Fill(BitBoard.Empty);
         Pinners[0] = Pinners[1] = BitBoard.Empty;
@@ -140,7 +140,7 @@ public bool Equals(State other)
                && PositionKey.Equals(other.PositionKey)
                && PawnKey.Equals(other.PawnKey)
                && EnPassantSquare.Equals(other.EnPassantSquare)
-               && CastlelingRights == other.CastlelingRights
+               && CastleRights == other.CastleRights
                && NullPly == other.NullPly
                && ClockPly == other.ClockPly
                && Pinners.Equals(other.Pinners)
@@ -160,7 +160,7 @@ public override int GetHashCode()
         hashCode.Add(NullPly);
         hashCode.Add(ClockPly);
         hashCode.Add(PositionKey);
-        hashCode.Add(CastlelingRights.Rights.AsInt());
+        hashCode.Add(CastleRights.Rights.AsInt());
         hashCode.Add(EnPassantSquare);
         hashCode.Add(Checkers);
         hashCode.Add(Previous);
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index 14a4a4b1..f24708e7 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -42,19 +42,13 @@ namespace Rudzoft.ChessLib.Types;
 public readonly record struct BitBoard(ulong Value) : IEnumerable<Square>, IMinMaxValue<BitBoard>
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard(BitBoard value) : this(value.Value)
-    {
-    }
+    public BitBoard(BitBoard value) : this(value.Value) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard(Square sq) : this(sq.AsBb())
-    {
-    }
+    public BitBoard(Square sq) : this(sq.AsBb()) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private BitBoard(int value) : this((ulong)value)
-    {
-    }
+    private BitBoard(int value) : this((ulong)value) { }
 
     public int Count => BitBoards.PopCount(in this);
 
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index ef27b91c..1caf5401 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -148,7 +148,7 @@ public static class BitBoards
     /// are a special case, as index range 0,sq are for White and 1,sq are for Black. This is
     /// possible because index 0 is NoPiece type.
     /// </summary>
-    private static readonly BitBoard[][] PseudoAttacksBB = new BitBoard[PieceTypes.PieceTypeNb.AsInt()][];
+    private static readonly BitBoard[][] PseudoAttacksBB = new BitBoard[PieceType.Count][];
 
     private static readonly BitBoard[][] PawnAttackSpanBB = new BitBoard[Player.Count][];
 
@@ -237,7 +237,7 @@ static BitBoards()
         }
 
         // mini local helpers
-        Span<PieceTypes> validMagicPieces = stackalloc PieceTypes[] { PieceTypes.Bishop, PieceTypes.Rook };
+        Span<PieceType> validMagicPieces = stackalloc PieceType[] { PieceType.Bishop, PieceType.Rook };
 
         var bb = AllSquares;
         // Pseudo attacks for all pieces
@@ -250,7 +250,7 @@ static BitBoards()
             // Compute lines and betweens
             foreach (var validMagicPiece in validMagicPieces)
             {
-                var pt = validMagicPiece.AsInt();
+                var pt = validMagicPiece;
                 var bb3 = AllSquares;
                 while (bb3)
                 {
@@ -281,7 +281,9 @@ static BitBoards()
         return;
 
         static int distanceRank(Square x, Square y) => distance(x.Rank, y.Rank);
+
         static int distanceFile(Square x, Square y) => distance(x.File, y.File);
+
         // local helper functions to calculate distance
         static int distance(int x, int y) => Math.Abs(x - y);
     }
@@ -309,13 +311,13 @@ private static void InitializePseudoAttacks(Square sq)
         PseudoAttacksBB[0][sq] = b.NorthEastOne() | b.NorthWestOne();
         PseudoAttacksBB[1][sq] = b.SouthWestOne() | b.SouthEastOne();
 
-        PseudoAttacksBB[PieceTypes.Knight.AsInt()][sq] = ComputeKnightAttack(in b);
-        PseudoAttacksBB[PieceTypes.Bishop.AsInt()][sq] = bishopAttacks;
-        PseudoAttacksBB[PieceTypes.Rook.AsInt()][sq] = rookAttacks;
-        PseudoAttacksBB[PieceTypes.Queen.AsInt()][sq] = bishopAttacks | rookAttacks;
-        PseudoAttacksBB[PieceTypes.King.AsInt()][sq] = b.NorthOne() | b.SouthOne() | b.EastOne()
-                                                      | b.WestOne() | b.NorthEastOne() | b.NorthWestOne()
-                                                      | b.SouthEastOne() | b.SouthWestOne();
+        PseudoAttacksBB[PieceType.Knight][sq] = ComputeKnightAttack(in b);
+        PseudoAttacksBB[PieceType.Bishop][sq] = bishopAttacks;
+        PseudoAttacksBB[PieceType.Rook][sq] = rookAttacks;
+        PseudoAttacksBB[PieceType.Queen][sq] = bishopAttacks | rookAttacks;
+        PseudoAttacksBB[PieceType.King][sq] = b.NorthOne() | b.SouthOne() | b.EastOne()
+                                              | b.WestOne() | b.NorthEastOne() | b.NorthWestOne()
+                                              | b.SouthEastOne() | b.SouthWestOne();
     }
 
     private static void InitializeKingRing(Square sq)
@@ -373,13 +375,13 @@ public static BitBoard FileBB(File first, File last)
     public static BitBoard SeventhAndEightsRank(Player p) => Ranks7And8BB[p];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PseudoAttacks(this PieceTypes pt, Square sq) => PseudoAttacksBB[pt.AsInt()][sq];
+    public static BitBoard PseudoAttacks(this PieceType pt, Square sq) => PseudoAttacksBB[pt][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KnightAttacks(this Square sq) => PseudoAttacksBB[PieceTypes.Knight.AsInt()][sq];
+    public static BitBoard KnightAttacks(this Square sq) => PseudoAttacksBB[PieceType.Knight][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KingAttacks(this Square sq) => PseudoAttacksBB[PieceTypes.King.AsInt()][sq];
+    public static BitBoard KingAttacks(this Square sq) => PseudoAttacksBB[PieceType.King][sq];
 
     /// <summary>
     /// Attack for pawn.
@@ -501,7 +503,7 @@ public static BitBoard FrontSquares(this Player p, Square sq)
     [SkipLocalsInit]
     public static string Stringify(in BitBoard bb, string title = "")
     {
-        const string line   = "+---+---+---+---+---+---+---+---+---+";
+        const string line = "+---+---+---+---+---+---+---+---+---+";
         const string bottom = "|   | A | B | C | D | E | F | G | H |";
 
         Span<char> span = stackalloc char[768];
@@ -516,12 +518,12 @@ public static string Stringify(in BitBoard bb, string title = "")
         Span<char> rank = stackalloc char[4] { '|', ' ', ' ', ' ' };
         for (var r = Ranks.Rank8; r >= Ranks.Rank1; --r)
         {
-            rank[2]     = (char)('0' + (int)r + 1);
+            rank[2] = (char)('0' + (int)r + 1);
             rank.CopyTo(span[idx..]);
             idx += rank.Length;
             for (var f = Files.FileA; f <= Files.FileH; ++f)
             {
-                rank[2]     = (bb & new Square(r, f)).IsEmpty ? ' ' : 'X';
+                rank[2] = (bb & new Square(r, f)).IsEmpty ? ' ' : 'X';
                 rank.CopyTo(span[idx..]);
                 idx += rank.Length;
             }
@@ -751,19 +753,23 @@ public static BitBoard MakeBitboard(params Square[] sqs)
 
     private static Func<BitBoard, BitBoard>[] MakeFillFuncs() => [NorthFill, SouthFill];
 
-    private static BitBoard GetAttacks(this in Square sq, PieceTypes pt, in BitBoard occ = default)
+    private static BitBoard GetAttacks(this in Square sq, PieceType pt, in BitBoard occ = default)
     {
-        return pt switch
-        {
-            PieceTypes.Knight => PseudoAttacksBB[pt.AsInt()][sq],
-            PieceTypes.King => PseudoAttacksBB[pt.AsInt()][sq],
-            PieceTypes.Bishop => sq.BishopAttacks(in occ),
-            PieceTypes.Rook => sq.RookAttacks(in occ),
-            PieceTypes.Queen => sq.QueenAttacks(in occ),
-            var _ => EmptyBitBoard
-        };
+        if (pt == PieceType.Knight || pt == PieceType.King)
+            return sq.PseudoAttack(pt);
+
+        if (pt == PieceType.Bishop)
+            return sq.BishopAttacks(in occ);
+
+        if (pt == PieceType.Rook)
+            return sq.RookAttacks(in occ);
+
+        if (pt == PieceType.Queen)
+            return sq.QueenAttacks(in occ);
+
+        return EmptyBitBoard;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static BitBoard PseudoAttack(this in Square sq, PieceTypes pt) => PseudoAttacksBB[pt.AsInt()][sq];
+    private static BitBoard PseudoAttack(this in Square sq, PieceType pt) => PseudoAttacksBB[pt][sq];
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index 04f6c9a9..bdf24a08 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -172,7 +172,7 @@ IRKiss rk
 
                 size++;
                 b = (b.Value - m.Mask.Value) & m.Mask;
-            } while (b.IsNotEmpty);
+            } while (b);
 
             if (Bmi2.X64.IsSupported)
                 continue;
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index 8d82db78..391bf22d 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -118,7 +118,7 @@ public static Move Create(Square from, Square to, MoveTypes moveType, PieceTypes
     public Square ToSquare() => new(Data & 0x3F);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public PieceTypes PromotedPieceType() => (PieceTypes)(((Data >> 12) & 3) + 2);
+    public PieceType PromotedPieceType() => (PieceTypes)(((Data >> 12) & 3) + 2);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool IsQueenPromotion() => PromotedPieceType() == PieceTypes.Queen;
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 6741dd30..66eb99ef 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -48,35 +48,6 @@ public enum Pieces : byte
     PieceNb = 15
 }
 
-public enum PieceTypes
-{
-    NoPieceType = 0,
-    Pawn = 1,
-    Knight = 2,
-    Bishop = 3,
-    Rook = 4,
-    Queen = 5,
-    King = 6,
-    PieceTypeNb = 7,
-    AllPieces = NoPieceType
-}
-
-public static class PieceTypesExtensions
-{
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int AsInt(this PieceTypes p) => (int)p;
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece MakePiece(this PieceTypes @this, Player side) => (int)@this | (side << 3);
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool IsSlider(this PieceTypes @this) => @this.InBetween(PieceTypes.Bishop, PieceTypes.Queen);
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool InBetween(this PieceTypes v, PieceTypes min, PieceTypes max) =>
-        (uint)v - (uint)min <= (uint)max - (uint)min;
-}
-
 /// <summary>
 /// Piece. Contains the piece type which indicate what type and color the piece is
 /// </summary>
@@ -205,11 +176,11 @@ public static Piece GetPiece(char c)
             return EmptyPiece;
 
         Player p = new(char.IsLower(PieceExtensions.PieceChars[pcIndex]));
-        return ((PieceTypes)pcIndex).MakePiece(p);
+        return new PieceType(pcIndex).MakePiece(p);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void Deconstruct(out PieceTypes pt, out Player p)
+    public void Deconstruct(out PieceType pt, out Player p)
     {
         pt = Type();
         p = ColorOf();
@@ -219,5 +190,5 @@ public void Deconstruct(out PieceTypes pt, out Player p)
     public int AsInt() => (int)Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public PieceTypes Type() => (PieceTypes)(AsInt() & 0x7);
+    public PieceType Type() => (PieceTypes)(AsInt() & 0x7);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/PieceType.cs b/src/Rudzoft.ChessLib/Types/PieceType.cs
new file mode 100644
index 00000000..16f962c1
--- /dev/null
+++ b/src/Rudzoft.ChessLib/Types/PieceType.cs
@@ -0,0 +1,96 @@
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace Rudzoft.ChessLib.Types;
+
+public enum PieceTypes : byte
+{
+    NoPieceType = 0,
+    Pawn = 1,
+    Knight = 2,
+    Bishop = 3,
+    Rook = 4,
+    Queen = 5,
+    King = 6,
+    PieceTypeNb = 7,
+    AllPieces = NoPieceType
+}
+
+public readonly record struct PieceType(PieceTypes Value) : IMinMaxValue<PieceType>
+{
+    public PieceType(int pt) : this((PieceTypes)pt) { }
+
+    private PieceType(PieceType pt) : this(pt.Value) { }
+
+    public static int Count => (int)PieceTypes.PieceTypeNb + 1;
+
+    public static PieceType NoPieceType => new(PieceTypes.NoPieceType);
+    public static PieceType Pawn => new(PieceTypes.Pawn);
+    public static PieceType Knight => new(PieceTypes.Knight);
+    public static PieceType Bishop => new(PieceTypes.Bishop);
+    public static PieceType Rook => new(PieceTypes.Rook);
+    public static PieceType Queen => new(PieceTypes.Queen);
+    public static PieceType King => new(PieceTypes.King);
+    public static PieceType AllPieces => new(PieceTypes.AllPieces);
+
+    public static PieceType[] AllPieceTypes =>
+    [
+        Pawn, Knight, Bishop, Rook, Queen, King
+    ];
+
+    public static PieceType MaxValue => King;
+
+    public static PieceType MinValue => Pawn;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator PieceType(int value) => new(value);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator PieceType(PieceTypes pt) => new(pt);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator ==(PieceType left, PieceTypes right) => left.Value == right;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator !=(PieceType left, PieceTypes right) => left.Value != right;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator <=(PieceType left, PieceTypes right) => left.Value <= right;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator >=(PieceType left, PieceTypes right) => left.Value >= right;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator <(PieceType left, PieceTypes right) => left.Value < right;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator >(PieceType left, PieceTypes right) => left.Value > right;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static PieceType operator ++(PieceType left) => new(left.Value + 1);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static PieceType operator --(PieceType left) => new(left.Value - 1);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator true(PieceType pc) => pc != NoPieceType;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator false(PieceType pc) => pc == NoPieceType;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator int(PieceType pc) => (int)pc.Value;
+
+    // [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    // public int AsInt() => (int)Value;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public Piece MakePiece(Player side) => (int)Value | (side << 3);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool IsSlider() => InBetween(PieceTypes.Bishop, PieceTypes.Queen);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool InBetween(PieceTypes min, PieceTypes max) =>
+        (uint)Value - (uint)min <= (uint)max - (uint)min;
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index 3960ef14..2ee34f35 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -204,8 +204,12 @@ public void Deconstruct(out Rank r, out File f)
 
     public static readonly Range BlackSide = new(32, 64);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Square Create(Rank r, File f) => new(r, f);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Square Create(byte b) => new((Squares)b);
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Square(int value) => new(value);
 
diff --git a/src/Rudzoft.ChessLib/Types/Values.cs b/src/Rudzoft.ChessLib/Types/Values.cs
index cd75bef3..2599fbb8 100644
--- a/src/Rudzoft.ChessLib/Types/Values.cs
+++ b/src/Rudzoft.ChessLib/Types/Values.cs
@@ -80,9 +80,15 @@ public Values()
         for (var index = 0; index < _defaults.Length; index++)
             _defaults[index] = new DefaultPieceValues[6];
 
-        _defaults[0] = [DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueMg, DefaultPieceValues.KnightValueMg, DefaultPieceValues.BishopValueMg, DefaultPieceValues.RookValueMg, DefaultPieceValues.QueenValueMg
+        _defaults[0] =
+        [
+            DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueMg, DefaultPieceValues.KnightValueMg,
+            DefaultPieceValues.BishopValueMg, DefaultPieceValues.RookValueMg, DefaultPieceValues.QueenValueMg
         ];
-        _defaults[1] = [DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueEg, DefaultPieceValues.KnightValueEg, DefaultPieceValues.BishopValueEg, DefaultPieceValues.RookValueEg, DefaultPieceValues.QueenValueEg
+        _defaults[1] =
+        [
+            DefaultPieceValues.ValueZero, DefaultPieceValues.PawnValueEg, DefaultPieceValues.KnightValueEg,
+            DefaultPieceValues.BishopValueEg, DefaultPieceValues.RookValueEg, DefaultPieceValues.QueenValueEg
         ];
 
         SetDefaults();
@@ -158,49 +164,46 @@ public void SetDefaults()
         var sum = Value.ValueZero;
         var sumNoPawns = Value.ValueZero;
 
-        Span<PieceTypes> pieceTypes = stackalloc PieceTypes[] { PieceTypes.Pawn, PieceTypes.Knight, PieceTypes.Bishop, PieceTypes.Rook, PieceTypes.Queen };
+        Span<PieceType> pieceTypes = stackalloc PieceType[]
+            { PieceType.Pawn, PieceType.Knight, PieceType.Bishop, PieceType.Rook, PieceType.Queen };
 
         foreach (var pt in pieceTypes)
         {
-            var value = new Value(PieceValues[0][pt.AsInt()]);
-            switch (pt)
-            {
-                case PieceTypes.Pawn:
-                    value *= 8;
-                    break;
-
-                case PieceTypes.Knight:
-                case PieceTypes.Bishop:
-                case PieceTypes.Rook:
-                    value *= 2;
-                    break;
-            }
-            if (pt != PieceTypes.Pawn)
+            var value = new Value(PieceValues[0][pt]);
+
+            if (pt == PieceType.Pawn)
+                value *= 8;
+
+            if (pt == PieceType.Knight || pt == PieceType.Bishop || pt == PieceType.Rook)
+                value *= 2;
+
+            if (pt != PieceType.Pawn)
                 sumNoPawns += value;
+
             sum += value;
         }
 
         MaxValueWithoutPawns = sumNoPawns * 2;
         MaxValue = sum * 2;
 
-        PawnValueMg = GetPieceValue(PieceTypes.Pawn, Phases.Mg);
-        PawnValueEg = GetPieceValue(PieceTypes.Pawn, Phases.Eg);
-        KnightValueMg = GetPieceValue(PieceTypes.Knight, Phases.Mg);
-        KnightValueEg = GetPieceValue(PieceTypes.Knight, Phases.Eg);
-        BishopValueMg = GetPieceValue(PieceTypes.Bishop, Phases.Mg);
-        BishopValueEg = GetPieceValue(PieceTypes.Bishop, Phases.Eg);
-        RookValueMg = GetPieceValue(PieceTypes.Rook, Phases.Mg);
-        RookValueEg = GetPieceValue(PieceTypes.Rook, Phases.Eg);
-        QueenValueMg = GetPieceValue(PieceTypes.Queen, Phases.Mg);
-        QueenValueEg = GetPieceValue(PieceTypes.Queen, Phases.Eg);
+        PawnValueMg = GetPieceValue(PieceType.Pawn, Phases.Mg);
+        PawnValueEg = GetPieceValue(PieceType.Pawn, Phases.Eg);
+        KnightValueMg = GetPieceValue(PieceType.Knight, Phases.Mg);
+        KnightValueEg = GetPieceValue(PieceType.Knight, Phases.Eg);
+        BishopValueMg = GetPieceValue(PieceType.Bishop, Phases.Mg);
+        BishopValueEg = GetPieceValue(PieceType.Bishop, Phases.Eg);
+        RookValueMg = GetPieceValue(PieceType.Rook, Phases.Mg);
+        RookValueEg = GetPieceValue(PieceType.Rook, Phases.Eg);
+        QueenValueMg = GetPieceValue(PieceType.Queen, Phases.Mg);
+        QueenValueEg = GetPieceValue(PieceType.Queen, Phases.Eg);
     }
 
     public void SetPieceValues(DefaultPieceValues[] values, Phases phase)
         => Array.Copy(values, PieceValues[phase.AsInt()], values.Length);
 
     public DefaultPieceValues GetPieceValue(Piece pc, Phases phase)
-        => PieceValues[(int)phase][pc.Type().AsInt()];
+        => PieceValues[(int)phase][pc.Type()];
 
-    public DefaultPieceValues GetPieceValue(PieceTypes pt, Phases phase)
-        => PieceValues[phase.AsInt()][pt.AsInt()];
-}
+    public DefaultPieceValues GetPieceValue(PieceType pt, Phases phase)
+        => PieceValues[phase.AsInt()][pt];
+}
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index d3219162..8f4dc290 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Runtime.Intrinsics;
 using Rudzoft.ChessLib.Hash;
 using Rudzoft.ChessLib.Types;
 
@@ -67,7 +68,7 @@ public PositionValidationResult Validate(in IPosition pos, PositionValidationTyp
             errors.AddRange(ValidateBasic(pos));
 
         if (type.HasFlagFast(PositionValidationTypes.Castle))
-            errors.AddRange(ValidateCastleling(pos));
+            errors.AddRange(ValidateCastle(pos));
 
         if (type.HasFlagFast(PositionValidationTypes.Kings))
             errors.AddRange(ValidateKings(pos));
@@ -106,7 +107,7 @@ private static IEnumerable<string> ValidateBasic(IPosition pos)
             yield return $"{nameof(pos.EnPassantSquare)} square is not on relative rank 6";
     }
 
-    private static IEnumerable<string> ValidateCastleling(IPosition pos)
+    private static IEnumerable<string> ValidateCastle(IPosition pos)
     {
         var crs = new[] { CastleRight.None, CastleRight.None };
 
@@ -115,7 +116,7 @@ private static IEnumerable<string> ValidateCastleling(IPosition pos)
             crs[0] = CastleRights.King.MakeCastleRights(p);
             crs[1] = CastleRights.Queen.MakeCastleRights(p);
 
-            var ourRook = PieceTypes.Rook.MakePiece(p);
+            var ourRook = PieceType.Rook.MakePiece(p);
             foreach (var cr in crs)
             {
                 if (!pos.CanCastle(cr))
@@ -127,10 +128,10 @@ private static IEnumerable<string> ValidateCastleling(IPosition pos)
                     yield return $"rook does not appear on its position for {p}";
 
                 if (pos.GetCastleRightsMask(rookSq) != cr)
-                    yield return $"castleling rights mask at {rookSq} does not match for player {p}";
+                    yield return $"castle rights mask at {rookSq} does not match for player {p}";
 
                 if ((pos.GetCastleRightsMask(pos.GetKingSquare(p)) & cr) != cr)
-                    yield return $"castleling rights mask at {pos.GetKingSquare(p)} does not match for player {p}";
+                    yield return $"castle rights mask at {pos.GetKingSquare(p)} does not match for player {p}";
             }
         }
     }
@@ -189,7 +190,7 @@ private IEnumerable<string> ValidateState(IPosition pos)
     {
         var state = pos.State;
 
-        if (state.PositionKey.Key == 0 && pos.PieceCount() != 0)
+        if (state.PositionKey == HashKey.Empty && pos.PieceCount() != 0)
             yield return "state key is invalid";
 
         if (pos.PieceCount(PieceTypes.Pawn) == 0 && state.PawnKey != _zobrist.ZobristNoPawn)
diff --git a/src/Rudzoft.Perft/Options/OptionsFactory.cs b/src/Rudzoft.Perft/Options/OptionsFactory.cs
index 3eb45f3f..fae9691b 100644
--- a/src/Rudzoft.Perft/Options/OptionsFactory.cs
+++ b/src/Rudzoft.Perft/Options/OptionsFactory.cs
@@ -37,7 +37,7 @@ public OptionsFactory(CommandLineArgs args)
     {
         _args = args.Args;
     }
-    
+
     public IEnumerable<PerftOption> Parse()
     {
         var optionsUsed = OptionType.None;
diff --git a/src/Rudzoft.Perft/Program.cs b/src/Rudzoft.Perft/Program.cs
index 69800aa4..fc0ef6d6 100644
--- a/src/Rudzoft.Perft/Program.cs
+++ b/src/Rudzoft.Perft/Program.cs
@@ -23,7 +23,6 @@
 
         services.AddSingleton(ConfigureLogger(configuration));
 
-        services.AddSingleton<IBuildTimeStamp, BuildTimeStamp>();
         services.AddTransient<IPerft, Perft>();
         services.AddTransient<IPerftRunner, PerftRunner>();
         services.AddSingleton<IOptionsFactory, OptionsFactory>();
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index 4986dbab..10255bcb 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -2,11 +2,12 @@
 
     <PropertyGroup>
         <OutputType>Exe</OutputType>
+        <IsPackable>false</IsPackable>
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Akka" Version="1.5.16" />
-      <PackageReference Include="Akka.DependencyInjection" Version="1.5.16" />
+      <PackageReference Include="Akka" Version="1.5.17.1" />
+      <PackageReference Include="Akka.DependencyInjection" Version="1.5.17.1" />
       <PackageReference Include="CommandLineParser" Version="2.9.1" />
       <PackageReference Include="Serilog" Version="3.1.1" />
       <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
@@ -14,7 +15,6 @@
       <PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
       <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/>
       <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
-      <PackageReference Include="Timestamp" Version="1.0.2"/>
     </ItemGroup>
 
     <ItemGroup>
@@ -32,5 +32,9 @@
         </Content>
     </ItemGroup>
 
+    <ItemGroup>
+      <Folder Include="Actors\Perft\" />
+    </ItemGroup>
+
 
 </Project>
diff --git a/src/Rudzoft.Perft/Services/BuildTimeStamp.cs b/src/Rudzoft.Perft/Services/BuildTimeStamp.cs
deleted file mode 100644
index c6e8f0bb..00000000
--- a/src/Rudzoft.Perft/Services/BuildTimeStamp.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
-Perft, a chess perft testing application
-
-MIT License
-
-Copyright (c) 2019-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using System.Reflection;
-
-namespace Rudzoft.Perft.Services;
-
-internal sealed class BuildTimeStamp : IBuildTimeStamp
-{
-    private const string AttributeName = "TimestampAttribute";
-
-    private static readonly Lazy<string> LazyTimeStamp = new(GetTimestamp);
-
-    public string TimeStamp => LazyTimeStamp.Value;
-
-    private static string GetTimestamp()
-    {
-        var attribute = Assembly.GetExecutingAssembly()
-            .GetCustomAttributesData()
-            .First(static x => x.AttributeType.Name == AttributeName);
-
-        var first = attribute.ConstructorArguments.FirstOrDefault();
-
-        var value = first.Value as string;
-
-        return string.IsNullOrWhiteSpace(value) ? string.Empty : value;
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Services/IBuildTimeStamp.cs b/src/Rudzoft.Perft/Services/IBuildTimeStamp.cs
deleted file mode 100644
index c7a5609e..00000000
--- a/src/Rudzoft.Perft/Services/IBuildTimeStamp.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-Perft, a chess perft testing application
-
-MIT License
-
-Copyright (c) 2019-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-namespace Rudzoft.Perft.Services;
-
-public interface IBuildTimeStamp
-{
-    string TimeStamp { get; }
-}
diff --git a/src/Rudzoft.Perft/Services/PerftService.cs b/src/Rudzoft.Perft/Services/PerftService.cs
index 61fc2953..695bbeda 100644
--- a/src/Rudzoft.Perft/Services/PerftService.cs
+++ b/src/Rudzoft.Perft/Services/PerftService.cs
@@ -15,24 +15,21 @@ public sealed class PerftService : IHostedService
 
     private readonly IServiceProvider _serviceProvider;
     private readonly IHostApplicationLifetime _applicationLifetime;
-    private readonly IBuildTimeStamp _buildTimeStamp;
 
     private ActorSystem _actorSystem;
     private IActorRef _perftActor;
 
     public PerftService(
         IServiceProvider serviceProvider,
-        IHostApplicationLifetime applicationLifetime,
-        IBuildTimeStamp buildTimeStamp)
+        IHostApplicationLifetime applicationLifetime)
     {
         _serviceProvider = serviceProvider;
         _applicationLifetime = applicationLifetime;
-        _buildTimeStamp = buildTimeStamp;
     }
 
     public Task StartAsync(CancellationToken cancellationToken)
     {
-        Log.Information("ChessLib Perft test program {Version} ({Time})", Version, _buildTimeStamp.TimeStamp);
+        Log.Information("ChessLib Perft test program {Version}", Version);
         Log.Information("High timer resolution : {HighRes}", Stopwatch.IsHighResolution);
         Log.Information("Initializing..");
 
diff --git a/src/Rudzoft.Perft/appsettings.json b/src/Rudzoft.Perft/appsettings.json
index f34de0fc..c4a020f6 100644
--- a/src/Rudzoft.Perft/appsettings.json
+++ b/src/Rudzoft.Perft/appsettings.json
@@ -1,13 +1,21 @@
 {
-  "Connections": {
-    "ConnectionString": "server=DEVRUDYKOHN\\SQLEXPRESS; database=Perft;integrated security=true; user=; password=; multipleactiveresultsets=true;app=Perft"
+  "Epd": {
+    "Files": [
+      "D:\\perft-random.epd"
+    ]
   },
-  "TranspositionTable": {
-    "Use": true,
-    "Size": 1024
+  "Fen": {
+    "Entry": {
+      "Fen": "",
+      "Depth": 0,
+      "ExpectedMove": 0
+    }
   },
   "Serilog": {
-    "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
+    "Using": [
+      "Serilog.Sinks.Console",
+      "Serilog.Sinks.File"
+    ],
     "MinimumLevel": "Debug",
     "WriteTo": [
       {
@@ -20,19 +28,11 @@
       {
         "Name": "File",
         "Args": {
-          "path": "Logs\\Perft.log.txt", // log file path
-          "rollingInterval": "Day", // Rolling Interval
+          "path": "Logs\\Perft.log.txt",
+          "rollingInterval": "Day",
           "outputTemplate": "{Timestamp:dd-MMM-yyyy HH:mm:ss.fff zzz} {ThreadId} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
         }
       }
-      //{
-      //  "Name": "EventLog",
-      //  "Args": {
-      //    "MinimumLevel": "Error",
-      //    "outputTemplate": "{Timestamp:dd-MMM-yyyy HH:mm:ss.fff zzz} {ThreadId} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
-      //  }
-      //}
-
     ],
     "Properties": {
       "Application": "Perft"

From e4583c3896f7edf34bf0deaa37929778ed0f5acc Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Mon, 4 Mar 2024 23:05:50 +0100
Subject: [PATCH 096/119] added Peter Jones' perft tests

---
 .../PerftTests/PerftTest.cs                   | 52 +++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
index 4ea8acd0..613f88b9 100644
--- a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
+++ b/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
@@ -205,6 +205,58 @@ public sealed class WeirdErrorFenTest : PerftVerify
 
     public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
 
+    [Theory]
+    [MemberData(nameof(PerftTheoryData))]
+    public void Perft(string fen, int depth, ulong expected)
+        => AssertPerft(fen, depth, in expected);
+}
+
+/// <summary>
+/// https://gist.github.com/peterellisjones/8c46c28141c162d1d8a0f0badbc9cff9
+/// </summary>
+public sealed class PeterJonesPerftTests : PerftVerify
+{
+    private static readonly string[] Fens =
+    [
+        "r6r/1b2k1bq/8/8/7B/8/8/R3K2R b KQ - 3 2",
+        "8/8/8/2k5/2pP4/8/B7/4K3 b - d3 0 3",
+        "r1bqkbnr/pppppppp/n7/8/8/P7/1PPPPPPP/RNBQKBNR w KQkq - 2 2",
+        "r3k2r/p1pp1pb1/bn2Qnp1/2qPN3/1p2P3/2N5/PPPBBPPP/R3K2R b KQkq - 3 2",
+        "2kr3r/p1ppqpb1/bn2Qnp1/3PN3/1p2P3/2N5/PPPBBPPP/R3K2R b KQ - 3 2",
+        "rnb2k1r/pp1Pbppp/2p5/q7/2B5/8/PPPQNnPP/RNB1K2R w KQ - 3 9",
+        "2r5/3pk3/8/2P5/8/2K5/8/8 w - - 5 4",
+        "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8",
+        "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
+        "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1",
+        "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1",
+        "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1",
+        "5k2/8/8/8/8/8/8/4K2R w K - 0 1",
+        "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1",
+        "r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1",
+        "r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1",
+        "2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1",
+        "8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1",
+        "4k3/1P6/8/8/8/8/K7/8 w - - 0 1",
+        "8/P1k5/K7/8/8/8/8/8 w - - 0 1",
+        "K1k5/8/P7/8/8/8/8/8 w - - 0 1",
+        "8/k1P5/8/1K6/8/8/8/8 w - - 0 1",
+        "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1"
+    ];
+
+    private static readonly int[] Depths =
+    [
+        1, 1, 1, 1, 1, 1, 1, 3, 3, 6, 6, 6, 6, 6, 4, 4, 6,
+        5, 6, 6, 6, 7, 4
+    ];
+
+    private static readonly ulong[] Results =
+    [
+        8, 8, 19, 5, 44, 39, 9, 62379, 89890, 1134888, 1015133, 1440467, 661072, 803711, 1274206, 1720476, 3821001,
+        1004658, 217342, 92683, 2217, 567584, 23527
+    ];
+
+    public static readonly PerftTheoryData PerftTheoryData = new(Fens, Depths, Results);
+
     [Theory]
     [MemberData(nameof(PerftTheoryData))]
     public void Perft(string fen, int depth, ulong expected)

From abaea7d4402d9f27925f343d1165a5b77cd9f398 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Wed, 26 Jun 2024 13:17:29 +0200
Subject: [PATCH 097/119] Semi-major update:

- Player -> Color (to eliminate confusing semantics)
- Added CPM
- Update PGN parsing
- Add PieceType type
- Add CastleSide type
- Fix a couple of variable namings
- Added a zobrist hash test
- Expanded HashKey a bit to be more flexible
---
 Directory.Packages.props                      |  57 ++++++
 Rudzoft.ChessLib.sln                          |   3 +-
 src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs  |   2 +-
 .../IterateBenchmark.cs                       |   4 +-
 .../KnightMoveGenerateBenchmark.cs            |  26 +++
 .../Rudzoft.ChessLib.Benchmark.csproj         |   4 +-
 .../SquareIterateBenchmark.cs                 |   2 +-
 .../StringBenchmarks.cs                       |   2 +-
 .../Rudzoft.ChessLib.PGN.Test.csproj          |  19 +-
 src/Rudzoft.ChessLib.PGN/IPgnParser.cs        |   1 +
 src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs |  33 +++-
 src/Rudzoft.ChessLib.PGN/PgnGenerator.cs      |   2 +-
 src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs    |  20 +-
 .../Rudzoft.ChessLib.PGN.csproj               |   4 +-
 .../Rudzoft.ChessLib.Perft.Interfaces.csproj  |   4 -
 src/Rudzoft.ChessLib.Perft/Perft.cs           |   2 +-
 .../BitboardTests/BitboardTests.cs            |   4 +-
 .../BoardTests/BoardTests.cs                  |  28 +--
 .../DataTests/DataTests.cs                    |   2 +-
 .../EvaluationTests/KpkBitBaseTests.cs        |  20 +-
 .../MoveTests/MoveGen_49.cs                   |   6 +-
 .../MoveTests/MoveTests.cs                    |   4 +-
 .../NotationTests/IccfTests.cs                |   2 +-
 .../NotationTests/SanTests.cs                 |  10 +-
 .../PiecesTests/PawnDoubleAttackTests.cs      |   4 +-
 .../PiecesTests/PieceAttacksKingTests.cs      |  11 +-
 .../PiecesTests/PieceAttacksPawnTests.cs      |  20 +-
 .../PiecesTests/SliderMobilityTests.cs        |  13 +-
 .../PositionTests/PositionTests.cs            |   4 +-
 .../PositionTests/ValidationTests.cs          |   4 +-
 .../Rudzoft.ChessLib.Test.csproj              |  20 +-
 .../SquareTests/SquareRangeTests.cs           |   2 +-
 .../SquareTests/SquareTests.cs                |   4 +-
 .../TablesTests/KillerMovesTests.cs           |   4 +-
 .../ZobristTests/ZobristHashTests.cs          |  45 ++++-
 .../Rudzoft.ChessLib.WebApi.csproj            |  18 +-
 src/Rudzoft.ChessLib/Blockage.cs              |  14 +-
 src/Rudzoft.ChessLib/Board.cs                 |  22 +--
 .../Evaluation/IKpkBitBase.cs                 |  12 +-
 src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |  60 +++---
 .../Extensions/PieceExtensions.cs             |  16 +-
 src/Rudzoft.ChessLib/Fen/Fen.cs               |   7 +-
 src/Rudzoft.ChessLib/Game.cs                  |   4 +-
 .../Transposition/TranspositionTableEntry.cs  |   4 +-
 src/Rudzoft.ChessLib/Hash/Zobrist.cs          |  10 +-
 src/Rudzoft.ChessLib/IBoard.cs                |  10 +-
 src/Rudzoft.ChessLib/IGame.cs                 |   4 +-
 src/Rudzoft.ChessLib/IPosition.cs             |  52 +++---
 .../MoveGeneration/MoveGenerator.cs           |  29 ++-
 .../Notation/Notations/FanNotation.cs         |   4 +-
 .../Notation/Notations/IccfNotation.cs        |  25 +--
 .../Notation/Notations/LanNotation.cs         |   2 +-
 .../Notation/Notations/Notation.cs            |   2 +-
 .../Notation/Notations/RanNotation.cs         |   4 +-
 .../Notation/Notations/SanNotation.cs         |   4 +-
 src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |   2 +-
 src/Rudzoft.ChessLib/Position.cs              | 159 ++++++++--------
 .../Protocol/UCI/HiResTimer.cs                |   4 +-
 .../Protocol/UCI/IMovesToGoModel.cs           |   2 +-
 .../Protocol/UCI/ISearchParameters.cs         |   8 +-
 .../Protocol/UCI/MovesToGoModel.cs            |   2 +-
 .../Protocol/UCI/SearchParameters.cs          |  36 ++--
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  15 +-
 src/Rudzoft.ChessLib/State.cs                 |  38 ++--
 .../Tables/History/HistoryHeuristic.cs        |  32 ++--
 .../Tables/History/IHistoryHeuristic.cs       |   4 +-
 src/Rudzoft.ChessLib/Types/BitBoard.cs        |   3 +-
 src/Rudzoft.ChessLib/Types/BitBoards.cs       | 176 ++++++++----------
 src/Rudzoft.ChessLib/Types/CastleRight.cs     |  83 +++++----
 .../Types/{Player.cs => Color.cs}             |  56 +++---
 src/Rudzoft.ChessLib/Types/File.cs            |   2 +-
 src/Rudzoft.ChessLib/Types/HashKey.cs         |   8 +-
 src/Rudzoft.ChessLib/Types/MagicBB.cs         |   3 +-
 src/Rudzoft.ChessLib/Types/Move.cs            |  14 +-
 src/Rudzoft.ChessLib/Types/Piece.cs           |  32 ++--
 src/Rudzoft.ChessLib/Types/PieceType.cs       |   6 +-
 src/Rudzoft.ChessLib/Types/Rank.cs            |   4 +-
 src/Rudzoft.ChessLib/Types/Square.cs          |  16 +-
 src/Rudzoft.ChessLib/Types/Value.cs           |   2 +-
 .../Validation/PositionValidator.cs           |  53 +++---
 src/Rudzoft.Perft/Rudzoft.Perft.csproj        |  24 +--
 81 files changed, 801 insertions(+), 678 deletions(-)
 create mode 100644 Directory.Packages.props
 create mode 100644 src/Rudzoft.ChessLib.Benchmark/KnightMoveGenerateBenchmark.cs
 rename src/Rudzoft.ChessLib/Types/{Player.cs => Color.cs} (75%)

diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 00000000..2d3e5714
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,57 @@
+<Project>
+  <PropertyGroup>
+    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
+    <PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="8.0.6" />
+    <PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
+    <PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
+  </ItemGroup>
+  <ItemGroup Label="Benchmark">
+    <PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
+  </ItemGroup>
+  <ItemGroup Label="Test">
+    <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
+    <PackageVersion Include="SimdLinq" Version="1.3.2" />
+    <PackageVersion Include="coverlet.collector" Version="6.0.2" />
+    <PackageVersion Include="xunit" Version="2.8.1" />
+    <PackageVersion Include="xunit.analyzers" Version="1.14.0">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageVersion>
+    <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.1">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageVersion>
+  </ItemGroup>
+  <ItemGroup Label="Perft">
+    <PackageVersion Include="Akka" Version="1.5.25" />
+    <PackageVersion Include="Akka.DependencyInjection" Version="1.5.25" />
+    <PackageVersion Include="CommandLineParser" Version="2.9.1" />
+    <PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
+    <PackageVersion Include="Serilog" Version="4.0.0" />
+    <PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" />
+    <PackageVersion Include="Serilog.Settings.Configuration" Version="8.0.1" />
+    <PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
+    <PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
+  </ItemGroup>
+  <ItemGroup Label="WebApi Example">
+    <PackageVersion Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.6">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageVersion>
+    <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.6" />
+    <PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
+    <PackageVersion Include="Microsoft.OpenApi" Version="1.6.15" />
+    <PackageVersion Include="Swashbuckle.AspNetCore" Version="6.6.2" />
+    <PackageVersion Include="Swashbuckle.AspNetCore.Swagger" Version="6.6.2" />
+    <PackageVersion Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.6.2" />
+    <PackageVersion Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.6.2" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/Rudzoft.ChessLib.sln b/Rudzoft.ChessLib.sln
index efa0c88b..73bfd7ad 100644
--- a/Rudzoft.ChessLib.sln
+++ b/Rudzoft.ChessLib.sln
@@ -25,12 +25,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN", "src
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN.Test", "src\Rudzoft.ChessLib.PGN.Test\Rudzoft.ChessLib.PGN.Test.csproj", "{556ADC0D-074D-4B8F-9E45-1275342FCB74}"
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solutionitems", "solutionitems", "{5B4D0B75-CE2C-45E2-92D3-987704C20001}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5B4D0B75-CE2C-45E2-92D3-987704C20001}"
 	ProjectSection(SolutionItems) = preProject
 		README.md = README.md
 		Directory.Build.props = Directory.Build.props
 		global.json = global.json
 		.editorconfig = .editorconfig
+		Directory.Packages.props = Directory.Packages.props
 	EndProjectSection
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.Perft", "src\Rudzoft.Perft\Rudzoft.Perft.csproj", "{5E02C821-DB31-4D63-9E0D-681B08BD22E1}"
diff --git a/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs b/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
index a69d50de..2aae0d7c 100644
--- a/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
@@ -95,7 +95,7 @@ public static IEnumerable<object> BitboardParams()
         yield return BitBoards.Center;
         yield return BitBoards.BlackArea;
         yield return BitBoards.CenterFiles;
-        yield return Player.Black.ColorBB();
+        yield return Color.Black.ColorBB();
         yield return BitBoards.CornerA1;
         yield return BitBoards.CornerA8;
         yield return BitBoards.CornerH1;
diff --git a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
index e9daf4ef..7a0eb045 100644
--- a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
@@ -10,7 +10,7 @@ public class IterateBenchmark
     [Benchmark(Description = "Stackalloc")]
     public void IterateOne()
     {
-        Span<Player> players = stackalloc Player[] { Player.White, Player.Black };
+        Span<Color> players = stackalloc Color[] { Color.White, Color.Black };
 
         var res = 0;
         for (var i = 0; i < N; ++i)
@@ -26,7 +26,7 @@ public void IterateTwo()
         var res = 0;
         for (var i = 0; i < N; ++i)
         {
-            for (var player = Players.White; player < Players.PlayerNb; ++player)
+            for (var player = Colors.White; player < Colors.PlayerNb; ++player)
                 res += (int)player;
         }
 
diff --git a/src/Rudzoft.ChessLib.Benchmark/KnightMoveGenerateBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/KnightMoveGenerateBenchmark.cs
new file mode 100644
index 00000000..ee81c68f
--- /dev/null
+++ b/src/Rudzoft.ChessLib.Benchmark/KnightMoveGenerateBenchmark.cs
@@ -0,0 +1,26 @@
+// using Rudzoft.ChessLib.Types;
+//
+// [MemoryDiagnoser]
+// public class ComputeKnightAttackBenchmark
+// {
+//     private BitBoard bitBoard;
+//
+//     [GlobalSetup]
+//     public void Setup()
+//     {
+//         // Initialize the BitBoard with some data
+//         bitBoard = new BitBoard(0x5555555555555555);
+//     }
+//
+//     [Benchmark]
+//     public void OldComputeKnightAttack()
+//     {
+//         BitBoards.ComputeKnightAttack(bitBoard);
+//     }
+//
+//     [Benchmark]
+//     public void NewComputeKnightAttackSimd()
+//     {
+//         BitBoards.ComputeKnightAttackSimd(bitBoard);
+//     }
+// }
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 00926add..47411d0d 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -3,13 +3,11 @@
     <PropertyGroup>
         <OutputType>Exe</OutputType>
         <Platforms>AnyCPU</Platforms>
-        <LangVersion>default</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
-        <PackageReference Include="System.Runtime" Version="4.3.1"/>
+        <PackageReference Include="BenchmarkDotNet"/>
     </ItemGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
index f5bd38a3..4e5e3842 100644
--- a/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
@@ -54,7 +54,7 @@ public int ForLoop_AsSpan_Marshal()
             sum += Unsafe.Add(ref space, i).AsInt();
         return sum;
     }
-    
+
     [Benchmark]
     public int BitBoard_WhileLoop()
     {
diff --git a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
index f71e432d..d7f35f09 100644
--- a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
@@ -18,7 +18,7 @@ public string NameOf(Square sq)
     {
         return GetNameOf(sq.Value);
     }
-    
+
     public static IEnumerable<object> Squares()
     {
         var sqs = BitBoards.MakeBitboard(Square.A1, Square.H8);
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index 2d71c538..43f7b0c1 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -1,23 +1,16 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
     <PropertyGroup>
-        <Nullable>enable</Nullable>
         <IsTestProject>true</IsTestProject>
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
-        <PackageReference Include="xunit" Version="2.7.0" />
-        <PackageReference Include="xunit.analyzers" Version="1.11.0" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
-            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-            <PrivateAssets>all</PrivateAssets>
-        </PackageReference>
-        <PackageReference Include="coverlet.collector" Version="6.0.1">
-            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-            <PrivateAssets>all</PrivateAssets>
-        </PackageReference>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk"/>
+        <PackageReference Include="xunit"/>
+        <PackageReference Include="xunit.analyzers"/>
+        <PackageReference Include="xunit.runner.visualstudio"/>
+        <PackageReference Include="coverlet.collector"/>
     </ItemGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib.PGN/IPgnParser.cs b/src/Rudzoft.ChessLib.PGN/IPgnParser.cs
index 90a99330..cfe9f49d 100644
--- a/src/Rudzoft.ChessLib.PGN/IPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/IPgnParser.cs
@@ -29,4 +29,5 @@ namespace Rudzoft.ChessLib.PGN;
 public interface IPgnParser
 {
     IAsyncEnumerable<PgnGame> ParseFile(string pgnFile, CancellationToken cancellationToken = default);
+    IAsyncEnumerable<PgnGame> ParseStream(Stream stream, CancellationToken cancellationToken = default);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
index c02c8381..2dc9b130 100644
--- a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
@@ -36,8 +36,27 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
         string pgnFile,
         [EnumeratorCancellation] CancellationToken cancellationToken = default)
     {
+        if (string.IsNullOrWhiteSpace(pgnFile))
+            yield break;
+
+        var fileInfo = new FileInfo(pgnFile);
+
+        if (!fileInfo.Exists)
+            yield break;
+
         await using var fileStream = new FileStream(pgnFile, FileMode.Open, FileAccess.Read);
-        using var streamReader = new StreamReader(fileStream);
+
+        await foreach (var game in ParseStream(fileStream, cancellationToken))
+            yield return game;
+    }
+
+    public async IAsyncEnumerable<PgnGame> ParseStream(
+        Stream stream,
+        [EnumeratorCancellation] CancellationToken cancellationToken = default)
+    {
+        ArgumentNullException.ThrowIfNull(stream);
+
+        using var streamReader = new StreamReader(stream);
 
         var currentGameTags = new Dictionary<string, string>();
         var currentGameMoves = new List<PgnMove>();
@@ -46,9 +65,7 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
         while (await streamReader.ReadLineAsync(cancellationToken) is { } line)
         {
             if (string.IsNullOrWhiteSpace(line))
-            {
                 inMoveSection = currentGameTags.Count > 0;
-            }
 
             if (inMoveSection)
             {
@@ -77,9 +94,9 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
                              word.Contains('*'))
                     {
                         yield return new(currentGameTags, currentGameMoves);
-                        currentGameTags  = new();
+                        currentGameTags = new();
                         currentGameMoves = [];
-                        inMoveSection    = false;
+                        inMoveSection = false;
                     }
                 }
             }
@@ -90,7 +107,7 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
 
                 var firstSpaceIndex = line.IndexOf(' ');
                 var firstQuoteIndex = line.IndexOf('"');
-                var lastQuoteIndex  = line.LastIndexOf('"');
+                var lastQuoteIndex = line.LastIndexOf('"');
 
                 if (firstSpaceIndex <= 0 || firstQuoteIndex <= firstSpaceIndex
                                          || lastQuoteIndex <= firstQuoteIndex)
@@ -98,8 +115,8 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
 
                 var tagName = line.Substring(1, firstSpaceIndex - 1).Trim();
                 var tagValue = line
-                    .Substring(firstQuoteIndex + 1, lastQuoteIndex - firstQuoteIndex - 1)
-                    .Trim();
+                               .Substring(firstQuoteIndex + 1, lastQuoteIndex - firstQuoteIndex - 1)
+                               .Trim();
 
                 currentGameTags[tagName] = tagValue;
             }
diff --git a/src/Rudzoft.ChessLib.PGN/PgnGenerator.cs b/src/Rudzoft.ChessLib.PGN/PgnGenerator.cs
index 2ac311f1..ce28d773 100644
--- a/src/Rudzoft.ChessLib.PGN/PgnGenerator.cs
+++ b/src/Rudzoft.ChessLib.PGN/PgnGenerator.cs
@@ -65,7 +65,7 @@ public string Build()
         var s = CollectionsMarshal.AsSpan(_moves);
         for (var i = 0; i < s.Length; i += 2)
         {
-            var moveNumber = ((i / 2) + 1);
+            var moveNumber = (i / 2) + 1;
             _pgnBuilder.Append($"{moveNumber}. {s[i]} ");
             if (i + 1 < s.Length)
                 _pgnBuilder.Append($"{s[i + 1]} ");
diff --git a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
index dc67bd35..cad613d7 100644
--- a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
+++ b/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
@@ -49,8 +49,24 @@ public async IAsyncEnumerable<PgnGame> ParseFile(
         if (string.IsNullOrWhiteSpace(pgnFile))
             yield break;
 
-        await using var fileStream = new FileStream(pgnFile, FileMode.Open, FileAccess.Read);
-        using var streamReader = new StreamReader(fileStream);
+        var fileInfo = new FileInfo(pgnFile);
+
+        if (!fileInfo.Exists)
+            yield break;
+
+        await using var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
+
+        await foreach (var game in ParseStream(fileStream, cancellationToken))
+            yield return game;
+    }
+
+    public async IAsyncEnumerable<PgnGame> ParseStream(
+        Stream stream,
+        [EnumeratorCancellation] CancellationToken cancellationToken = default)
+    {
+        ArgumentNullException.ThrowIfNull(stream);
+
+        using var streamReader = new StreamReader(stream);
         var currentGameTags = new Dictionary<string, string>(DefaultTagCapacity);
         var currentGameMoves = new List<PgnMove>(DefaultMoveListCapacity);
         var inMoveSection = false;
diff --git a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
index 468ff3fd..45ecd2d2 100644
--- a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
+++ b/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
@@ -5,8 +5,8 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.2" />
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions"/>
+        <PackageReference Include="Microsoft.Extensions.ObjectPool"/>
     </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
index edbce89a..38eefd3f 100644
--- a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
+++ b/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
@@ -1,9 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
-    <PropertyGroup>
-        <IsPackable>false</IsPackable>
-    </PropertyGroup>
-
     <ItemGroup>
         <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
     </ItemGroup>
diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/Rudzoft.ChessLib.Perft/Perft.cs
index 838686b1..6d5a46d1 100644
--- a/src/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/Rudzoft.ChessLib.Perft/Perft.cs
@@ -73,7 +73,7 @@ public async IAsyncEnumerable<UInt128> DoPerft(int depth)
 
         var state = new State();
 
-        foreach (var fd in Positions.Select(static p => new FenData(p.Fen)))
+        foreach (var fd in Positions.Select(static perftPosition => new FenData(perftPosition.Fen)))
         {
             Game.Pos.Set(in fd, ChessMode.Normal, in state);
             var baseKey = game.Pos.State.PositionKey;
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
index b15cc0e2..bffdc826 100644
--- a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
@@ -81,7 +81,7 @@ public void BlackAreaCount()
     public void LightSquaresCount()
     {
         const int expected = 32;
-        var actual = Player.White.ColorBB().Count;
+        var actual = Color.White.ColorBB().Count;
         Assert.Equal(expected, actual);
     }
 
@@ -89,7 +89,7 @@ public void LightSquaresCount()
     public void DarkSquaresCount()
     {
         const int expected = 32;
-        var actual = Player.Black.ColorBB().Count;
+        var actual = Color.Black.ColorBB().Count;
         Assert.Equal(expected, actual);
     }
 
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index cbac868e..0497d9da 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -38,9 +38,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Test.BoardTests;
 
-public sealed class BoardTestsTheoryData : TheoryData<string, PieceType, Player, int>
+public sealed class BoardTestsTheoryData : TheoryData<string, PieceType, Color, int>
 {
-    public BoardTestsTheoryData(string[] fens, PieceType[] pts, Player[] players, int[] expectedCounts)
+    public BoardTestsTheoryData(string[] fens, PieceType[] pts, Color[] players, int[] expectedCounts)
     {
         Debug.Assert(fens != null);
         Debug.Assert(pts != null);
@@ -124,16 +124,16 @@ public BoardTests()
     ];
 
 
-    private static readonly Player[] Player =
+    private static readonly Color[] Player =
     [
-        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White,
-        Types.Player.Black,
-        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White,
-        Types.Player.Black,
-        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White,
-        Types.Player.Black,
-        Types.Player.White, Types.Player.Black, Types.Player.White, Types.Player.Black, Types.Player.White,
-        Types.Player.Black
+        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
+        Types.Color.Black,
+        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
+        Types.Color.Black,
+        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
+        Types.Color.Black,
+        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
+        Types.Color.Black
     ];
 
     private static readonly int[] ExpectedCount =
@@ -154,7 +154,7 @@ public BoardTests()
 
     [Theory]
     [MemberData(nameof(TheoryData))]
-    public void BoardPieceCount(string fen, PieceType pt, Player p, int expected)
+    public void BoardPieceCount(string fen, PieceType pt, Color c, int expected)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
@@ -165,8 +165,8 @@ public void BoardPieceCount(string fen, PieceType pt, Player p, int expected)
 
         var board = pos.Board;
 
-        var posCount = pos.Pieces(pt, p).Count;
-        var boardCount = board.PieceCount(pt, p);
+        var posCount = pos.Pieces(pt, c).Count;
+        var boardCount = board.PieceCount(pt, c);
 
         Assert.Equal(posCount, boardCount);
         Assert.Equal(expected, boardCount);
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs b/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
index 84480f3c..2fd62511 100644
--- a/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
+++ b/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
@@ -42,7 +42,7 @@ public void SquareChars()
             var sq = BitBoards.PopLsb(ref all);
             c[0] = sq.FileChar;
             c[1] = sq.RankChar;
-            Assert.Equal(sq.ToString(), new string(c));
+            Assert.Equal(sq.ToString(), new(c));
         }
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
index 6167f7f0..44b8801c 100644
--- a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
+++ b/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
@@ -64,27 +64,27 @@ public KpkBitBaseTests()
     // theory data layout:
     // fen, player, expected win by strong side
     [Theory]
-    [InlineData("2k5/8/8/8/1PK5/8/8/8 w - - 0 1", Players.White, true)]
-    [InlineData("5k2/8/6KP/8/8/8/8/8 b - - 0 1", Players.White, true)]
-    [InlineData("8/8/8/K7/P2k4/8/8/8 w - - 0 1", Players.White, true)]
-    [InlineData("8/8/8/K7/P2k4/8/8/8 b - - 0 1", Players.White, true)]
-    public void KpkWin(string fen, Players rawPlayer, bool expected)
+    [InlineData("2k5/8/8/8/1PK5/8/8/8 w - - 0 1", Colors.White, true)]
+    [InlineData("5k2/8/6KP/8/8/8/8/8 b - - 0 1", Colors.White, true)]
+    [InlineData("8/8/8/K7/P2k4/8/8/8 w - - 0 1", Colors.White, true)]
+    [InlineData("8/8/8/K7/P2k4/8/8/8 b - - 0 1", Colors.White, true)]
+    public void KpkWin(string fen, Colors rawColor, bool expected)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
         var fenData = new FenData(fen);
         var state = new State();
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        Player strongSide = rawPlayer;
+        Color strongSide = rawColor;
         var weakSide = ~strongSide;
 
         var kpkBitBase = _serviceProvider.GetRequiredService<IKpkBitBase>();
 
-        var strongKing = kpkBitBase.Normalize(pos, strongSide,  pos.GetPieceSquare(PieceTypes.King, strongSide));
-        var strongPawn = kpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceTypes.Pawn, strongSide));
-        var weakKing = kpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceTypes.King, weakSide));
+        var strongKing = kpkBitBase.Normalize(pos, strongSide,  pos.GetPieceSquare(PieceType.King, strongSide));
+        var strongPawn = kpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceType.Pawn, strongSide));
+        var weakKing = kpkBitBase.Normalize(pos, strongSide, pos.GetPieceSquare(PieceType.King, weakSide));
 
-        var us = strongSide == pos.SideToMove ? Player.White : Player.Black;
+        var us = strongSide == pos.SideToMove ? Color.White : Color.Black;
 
         var won = !kpkBitBase.Probe(strongKing, strongPawn, weakKing, us);
 
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
index b75ffb17..695a7616 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
@@ -74,8 +74,10 @@ public void MoveListContainsMismatchedElement()
         var ml = pos.GenerateMoves();
 
         const bool expected = false;
-        var actual = ml.Contains(new Move(new Square(Ranks.Rank1, Files.FileE), new Square(Ranks.Rank1, Files.FileG),
-            MoveTypes.Castling));
+        var from = new Square(Ranks.Rank1, Files.FileE);
+        var to = new Square(Ranks.Rank1, Files.FileG);
+        var move = Move.Create(from, to, MoveTypes.Castling);
+        var actual = ml.Contains(move);
 
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index 57ec8d7b..fcab9184 100644
--- a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -175,8 +175,8 @@ public void MoveListToStringTest()
         // generate 256-ish random moves
         for (var i = 0; i < 256; i++)
         {
-            Square rndSquareFrom = (Squares)rngeezuz.Next((int)Squares.a1, (int)Squares.h8);
-            Square rndSquareTo = (Squares)rngeezuz.Next((int)Squares.a1, (int)Squares.h8);
+            Square rndSquareFrom = rngeezuz.Next(Square.A1, Square.H8);
+            Square rndSquareTo = rngeezuz.Next(Square.A1, Square.H8);
 
             // Skip same squares to and from
             if (rndSquareFrom == rndSquareTo)
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index 73531d2a..e312c7b0 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -54,7 +54,7 @@ public void IccfRegularMove()
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var w1 = Move.Create(Squares.d2, Squares.f3);
+        var w1 = Move.Create(Square.D2, Square.F3);
 
         var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
         var notation     = moveNotation.ToNotation(moveNotations);
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index 4345f224..5a8473c3 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -45,8 +45,12 @@ public sealed class SanTests
     [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.San, PieceTypes.Knight, Squares.d2, Squares.f2,
         Squares.e4)]
     public void SanRankAmbiguities(
-        string fen, MoveNotations moveNotations, PieceTypes movingPt, Squares fromSqOne,
-        Squares fromSqTwo, Squares toSq)
+        string fen,
+        MoveNotations moveNotations,
+        PieceTypes movingPt,
+        Squares fromSqOne,
+        Squares fromSqTwo,
+        Squares toSq)
     {
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
@@ -179,7 +183,7 @@ public void SanCaptureWithCheck(string fen, string expected)
         var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
         var notation     = moveNotation.ToNotation(MoveNotations.San);
 
-        var move    = Move.Create(Squares.d1, Squares.d8, MoveTypes.Normal);
+        var move    = Move.Create(Square.D1, Square.D8, MoveTypes.Normal);
         var sanMove = notation.Convert(pos, move);
 
         // Capturing a piece with check
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
index 2b552150..96435f0e 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
@@ -69,9 +69,9 @@ public void WhitePlayerPawnDoubleAttacksAttackSpan()
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var whitePawns = pos.Pieces(PieceTypes.Pawn, Player.White);
+        var whitePawns = pos.Pieces(PieceType.Pawn, Color.White);
 
-        var attackSpan = whitePawns.PawnDoubleAttacks(Player.White);
+        var attackSpan = whitePawns.PawnDoubleAttacks(Color.White);
 
         const int expected = 6;
         var actual = attackSpan.Count;
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
index 5185111d..eafe0259 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Rudzoft.ChessLib.Types;
+using SimdLinq;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
 
@@ -65,9 +66,9 @@ public void BetaPattern()
         const int attackIndex = 2;
         var bands = Bands[index];
         var expected = _fixture.KingExpected[index] * bands.Count;
-        var actuals = bands.Select(x => _fixture.RegAttacks[attackIndex](x).Count);
+        var actuals = bands.Select(x => _fixture.RegAttacks[attackIndex](x).Count).ToArray();
         var actual = actuals.Sum();
-        
+
         Assert.Equal(expected, actual);
     }
 
@@ -78,7 +79,7 @@ public void GammaPattern()
         const int attackIndex = 2;
         var band = Bands[index];
         var expected = _fixture.KingExpected[index] * band.Count;
-        var actuals = band.Select(x => _fixture.RegAttacks[attackIndex](x).Count);
+        var actuals = band.Select(x => _fixture.RegAttacks[attackIndex](x).Count).ToArray();
         var actual = actuals.Sum();
 
         Assert.Equal(expected, actual);
@@ -91,9 +92,9 @@ public void DeltaPattern()
         const int attackIndex = 2;
         var band = Bands[index];
         var expected = _fixture.KingExpected[index] * band.Count;
-        var actuals = band.Select(x => _fixture.RegAttacks[attackIndex](x).Count);
+        var actuals = band.Select(x => _fixture.RegAttacks[attackIndex](x).Count).ToArray();
         var actual = actuals.Sum();
-        
+
         Assert.Equal(expected, actual);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs
index cf3bb69f..b5fb36fe 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs
@@ -65,18 +65,18 @@ public sealed class PieceAttacksPawnTests : PieceAttacks
      */
 
     [Theory]
-    [InlineData(PawnAlpha, Players.White, 1)]
-    [InlineData(PawnAlpha, Players.Black, 1)]
-    [InlineData(PawnBeta, Players.White, 2)]
-    [InlineData(PawnBeta, Players.Black, 2)]
-    [InlineData(PawnGamma, Players.White, 2)]
-    [InlineData(PawnGamma, Players.Black, 2)]
-    [InlineData(PawnDelta, Players.White, 2)]
-    [InlineData(PawnDelta, Players.Black, 2)]
-    public void PawnAttacks(ulong squares, Players side, int expected)
+    [InlineData(PawnAlpha, Colors.White, 1)]
+    [InlineData(PawnAlpha, Colors.Black, 1)]
+    [InlineData(PawnBeta, Colors.White, 2)]
+    [InlineData(PawnBeta, Colors.Black, 2)]
+    [InlineData(PawnGamma, Colors.White, 2)]
+    [InlineData(PawnGamma, Colors.Black, 2)]
+    [InlineData(PawnDelta, Colors.White, 2)]
+    [InlineData(PawnDelta, Colors.Black, 2)]
+    public void PawnAttacks(ulong squares, Colors c, int expected)
     {
         var bb = BitBoard.Create(squares);
-        var us = Player.Create(side);
+        var us = Color.Create(c);
 
         while (bb)
         {
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
index 0eaa1561..77409bad 100644
--- a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
@@ -25,18 +25,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
 
 using Rudzoft.ChessLib.Types;
+using SimdLinq;
 
 namespace Rudzoft.ChessLib.Test.PiecesTests;
 
-public sealed class SliderMobilityTests : PieceAttacks, IClassFixture<SliderMobilityFixture>
+public sealed class SliderMobilityTests(SliderMobilityFixture fixture) : PieceAttacks,
+    IClassFixture<SliderMobilityFixture>
 {
-    private readonly SliderMobilityFixture _fixture;
-
-    public SliderMobilityTests(SliderMobilityFixture fixture)
-    {
-        _fixture = fixture;
-    }
-
     [Theory]
     [InlineData(Alpha, PieceTypes.Bishop, 7)]
     [InlineData(Beta, PieceTypes.Bishop, 9)]
@@ -56,7 +51,7 @@ public void BishopMobility(ulong pattern, PieceTypes pt, int expectedMobility)
         var bb = new BitBoard(pattern);
 
         var expected = bb.Count * expectedMobility;
-        var actual = bb.Select(x => _fixture.SliderAttacks(pt, x, in empty).Count).Sum();
+        var actual = bb.Select(x => fixture.SliderAttacks(pt, x, in empty).Count).ToArray().Sum();
 
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
index c568255e..5e8d2fcb 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
@@ -97,7 +97,7 @@ public void KingBlocker()
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var b = pos.KingBlockers(Player.Black);
+        var b = pos.KingBlockers(Color.Black);
 
         // b must contain one square at this point
         var pinnedCount = b.Count;
@@ -125,7 +125,7 @@ public void SetByCodeCreatesSameMaterialKey(string code, string fen)
 
         var materialKey = pos.State.MaterialKey;
 
-        var posCode = pos.Set(code, Player.White, in state);
+        var posCode = pos.Set(code, Color.White, in state);
         var codeMaterialKey = posCode.State.MaterialKey;
 
         Assert.Equal(materialKey, codeMaterialKey);
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
index b76e909c..67d5cbd4 100644
--- a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
+++ b/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
@@ -63,7 +63,7 @@ public ValidationTests()
     public void ValidationKingsNegative()
     {
         const PositionValidationTypes type = PositionValidationTypes.Kings;
-        var expectedErrorMsg = $"king count for player {Player.White} was 2";
+        var expectedErrorMsg = $"king count for player {Color.White} was 2";
 
         var pos = _serviceProvider.GetRequiredService<IPosition>();
 
@@ -72,7 +72,7 @@ public void ValidationKingsNegative()
 
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        var pc = PieceType.King.MakePiece(Player.White);
+        var pc = PieceType.King.MakePiece(Color.White);
 
         pos.AddPiece(pc, Square.E4);
 
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index b23f78c9..5164cc6e 100644
--- a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -4,6 +4,15 @@
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     </PropertyGroup>
 
+    <ItemGroup>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk"/>
+        <PackageReference Include="SimdLinq" />
+        <PackageReference Include="xunit"/>
+        <PackageReference Include="xunit.analyzers"/>
+        <PackageReference Include="xunit.runner.visualstudio"/>
+    </ItemGroup>
+
     <ItemGroup>
         <None Remove="BookTests\gm2600.bin"/>
     </ItemGroup>
@@ -14,17 +23,6 @@
         </Content>
     </ItemGroup>
 
-    <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
-        <PackageReference Include="xunit" Version="2.7.0" />
-        <PackageReference Include="xunit.analyzers" Version="1.11.0" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
-            <PrivateAssets>all</PrivateAssets>
-            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-        </PackageReference>
-    </ItemGroup>
-
     <ItemGroup>
         <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
     </ItemGroup>
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs b/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
index b3a5e9b8..4bb5f5e8 100644
--- a/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
@@ -78,7 +78,7 @@ public void InWhiteSquaresRange(Squares sq, bool expectedInWhiteRange, bool expe
 
         var actualInWhite = whiteSquares.Contains(s);
         var actualInBlack = blackSquares.Contains(s);
-        
+
         Assert.Equal(expectedInWhiteRange, actualInWhite);
         Assert.Equal(expectedInBlackRange, actualInBlack);
     }
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs b/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
index 118ed358..2571cc2d 100644
--- a/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
@@ -64,7 +64,7 @@ public void RelativeSquareBlack()
 
         var s = new Square(Rank.Rank1, File.FileH);
 
-        var actual = s.Relative(Player.Black);
+        var actual = s.Relative(Color.Black);
 
         Assert.Equal(expected, actual);
     }
@@ -76,7 +76,7 @@ public void RelativeSquareWhite()
 
         var s = new Square(Rank.Rank3,  File.FileC);
 
-        var actual = s.Relative(Player.White);
+        var actual = s.Relative(Color.White);
 
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
index 6fda1fa1..2f6c1ff0 100644
--- a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
+++ b/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
@@ -37,7 +37,7 @@ public void BaseAddMove()
         var km = KillerMoves.Create(128);
         const int depth = 1;
         var move = Move.Create(Square.A2, Square.A3);
-        var pc = PieceType.Pawn.MakePiece(Player.White);
+        var pc = PieceType.Pawn.MakePiece(Color.White);
 
         km.UpdateValue(depth, move, pc);
 
@@ -52,7 +52,7 @@ public void GetValueWithWrongDepthYieldsZero()
         var km = KillerMoves.Create(128);
         const int depth = 1;
         var move = Move.Create(Square.A2, Square.A3);
-        var pc = PieceType.Pawn.MakePiece(Player.White);
+        var pc = PieceType.Pawn.MakePiece(Color.White);
 
         km.UpdateValue(depth, move, pc);
 
diff --git a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
index 46745697..52f14739 100644
--- a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
+++ b/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
@@ -74,7 +74,7 @@ public void BackAndForthBasicMoves(string fen, Squares from, Squares to)
 
         var startKey = pos.State.PositionKey;
 
-        var move = new Move(from, to);
+        var move = Move.Create(from, to);
 
         stateIndex++;
         states.Add(new());
@@ -108,7 +108,7 @@ public void AdvancedMoves(string fen, Squares from, Squares to, MoveTypes moveTy
 
         var startKey = pos.State.PositionKey;
 
-        var move = new Move(from, to, moveType);
+        var move = Move.Create(from, to, moveType);
 
         stateIndex++;
         states.Add(new());
@@ -174,4 +174,45 @@ public void OnlyUniqueZobristHashKeys()
         added = set.Add(zobrist.Side());
         Assert.True(added);
     }
+
+    [Theory]
+    [InlineData("rnbqkb1r/pppp1ppp/7n/3Pp3/8/8/PPP1PPPP/RNBQKBNR w KQkq e6 0 1", Squares.d5, Squares.e6, MoveTypes.Enpassant)]
+    [InlineData("r3kb1r/p1pp1p1p/bpn2qpn/3Pp3/1P6/2P1BNP1/P3PPBP/RN1QK2R w KQkq - 0 1", Squares.e1, Squares.h1, MoveTypes.Castling)]
+    [InlineData("r3kb1r/p1pp1p1p/bpn2qpn/3Pp3/1P6/2P1BNP1/P3PPBP/RN1QK2R b KQkq - 0 1", Squares.e8, Squares.a8, MoveTypes.Castling)]
+    public void GetKey(string fen, Squares from, Squares to, MoveTypes moveType)
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var stateIndex = 0;
+        var states = new List<State> { new() };
+
+        pos.Set(fen, ChessMode.Normal, states[stateIndex]);
+
+        var startKey = pos.State.PositionKey;
+        var getKey = pos.GetKey(pos.State);
+
+        Assert.Equal(startKey, getKey);
+
+        var move = Move.Create(from, to, moveType);
+
+        stateIndex++;
+        states.Add(new());
+
+        pos.MakeMove(move, states[stateIndex]);
+
+        var moveKey = pos.State.PositionKey;
+
+        pos.TakeMove(move);
+
+        var finalState = pos.State;
+        var finalKey = finalState.PositionKey;
+        var finalGetKey = pos.GetKey(finalState);
+
+        Assert.Equal(finalKey, finalGetKey);
+        Assert.NotEqual(startKey, moveKey);
+        Assert.Equal(startKey, states[0].PositionKey);
+        Assert.NotEqual(startKey, states[1].PositionKey);
+        Assert.Equal(startKey, finalKey);
+    }
+
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index e0fa21b2..d48a0997 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -6,18 +6,18 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.2">
+        <PackageReference Include="Microsoft.Extensions.ApiDescription.Server">
             <PrivateAssets>all</PrivateAssets>
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
-        <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.2" />
-        <PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
-        <PackageReference Include="Microsoft.OpenApi" Version="1.6.13" />
-        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
-        <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0"/>
-        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0"/>
-        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0"/>
+        <PackageReference Include="Microsoft.Extensions.Caching.Memory"/>
+        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks"/>
+        <PackageReference Include="Microsoft.Extensions.Options"/>
+        <PackageReference Include="Microsoft.OpenApi"/>
+        <PackageReference Include="Swashbuckle.AspNetCore"/>
+        <PackageReference Include="Swashbuckle.AspNetCore.Swagger"/>
+        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen"/>
+        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI"/>
     </ItemGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/Rudzoft.ChessLib/Blockage.cs
index 7a23e061..db14054e 100644
--- a/src/Rudzoft.ChessLib/Blockage.cs
+++ b/src/Rudzoft.ChessLib/Blockage.cs
@@ -168,7 +168,7 @@ public bool IsBlocked(in IPosition pos)
 
                     if (f != File.FileA
                         && !LowerRankNotFileABelowFenceRankNotBlocked(
-                            in pos, sq, ourPawn, in ourPawns, f, in fixedPawns, r, in fence))
+                            pos.Board, sq, ourPawn, in ourPawns, f, in fixedPawns, r, in fence))
                         return false;
 
                     if (f != File.FileH)
@@ -196,7 +196,7 @@ public bool IsBlocked(in IPosition pos)
     }
 
     private static bool LowerRankNotFileABelowFenceRankNotBlocked(
-        in IPosition pos,
+        in IBoard pos,
         Square sq,
         Piece ourPawn,
         in BitBoard ourPawns,
@@ -205,7 +205,7 @@ private static bool LowerRankNotFileABelowFenceRankNotBlocked(
         Rank r,
         in BitBoard fence)
     {
-        return pos.GetPiece(sq + Direction.West) == ourPawn
+        return pos.PieceAt(sq + Direction.West) == ourPawn
                && BitBoards.PopCount(ourPawns & (f - 1)) <= 1
                && (fixedPawns & Square.Create(r, PreviousFile(f))).IsNotEmpty
                && (fence & Square.Create(r, PreviousFile(f))).IsNotEmpty;
@@ -290,7 +290,7 @@ private static MarkedPawns MarkPawns(in IPosition pos, in BitBoard theirPawns)
     /// <param name="marked"></param>
     /// <param name="us"></param>
     /// <returns>true if the square is in the fence</returns>
-    private static bool FormsFence(Square sq, ref BitBoard processed, ref BitBoard fence, in BitBoard marked, Player us)
+    private static bool FormsFence(Square sq, ref BitBoard processed, ref BitBoard fence, in BitBoard marked, Color us)
     {
         processed |= sq;
 
@@ -321,10 +321,10 @@ private static bool FormsFence(Square sq, ref BitBoard processed, ref BitBoard f
         return false;
     }
 
-    private static Square NextFenceRankSquare(Span<Rank> fenceRank, File f, Player them)
+    private static Square NextFenceRankSquare(Span<Rank> fenceRank, File f, Color them)
         => new Square(fenceRank[f] * 8 + f.AsInt()) + them.PawnPushDistance();
 
-    private static bool FormFence(in BitBoard marked, ref BitBoard fence, ref BitBoard processed, Player us)
+    private static bool FormFence(in BitBoard marked, ref BitBoard fence, ref BitBoard processed, Color us)
     {
         var bb = PawnFileASquares;
         while (bb)
@@ -344,7 +344,7 @@ private static BitBoard ComputeDynamicFencedPawns(
         in IPosition pos,
         Span<Rank> fenceRank,
         in BitBoard theirPawns,
-        Player us)
+        Color us)
     {
         // reverse order of Down
         var down = us.PawnPushDistance();
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index 404ff14d..c043e255 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -34,7 +34,7 @@ public sealed class Board : IBoard
 {
     private readonly Piece[] _board = new Piece[Types.Square.Count];
 
-    private readonly BitBoard[] _bySide = new BitBoard[Player.Count];
+    private readonly BitBoard[] _bySide = new BitBoard[Color.Count];
 
     private readonly BitBoard[] _byType = new BitBoard[PieceType.Count];
 
@@ -104,27 +104,27 @@ public void MovePiece(Square from, Square to)
 
     public BitBoard Pieces(PieceType pt1, PieceType pt2) => _byType[pt1] | _byType[pt2];
 
-    public BitBoard Pieces(Player p) => _bySide[p];
+    public BitBoard Pieces(Color c) => _bySide[c];
 
-    public BitBoard Pieces(Player p, PieceType pt) => _bySide[p] & _byType[pt];
+    public BitBoard Pieces(Color c, PieceType pt) => _bySide[c] & _byType[pt];
 
-    public BitBoard Pieces(Player p, PieceType pt1, PieceType pt2)
-        => _bySide[p] & (_byType[pt1] | _byType[pt2]);
+    public BitBoard Pieces(Color c, PieceType pt1, PieceType pt2)
+        => _bySide[c] & (_byType[pt1] | _byType[pt2]);
 
-    public Square Square(PieceType pt, Player p)
+    public Square Square(PieceType pt, Color c)
     {
-        Debug.Assert(_pieceCount[pt.MakePiece(p)] == 1);
-        return Pieces(p, pt).Lsb();
+        Debug.Assert(_pieceCount[pt.MakePiece(c)] == 1);
+        return Pieces(c, pt).Lsb();
     }
 
     public int PieceCount(Piece pc) => _pieceCount[pc];
 
-    public int PieceCount(PieceType pt, Player p) => PieceCount(pt.MakePiece(p));
+    public int PieceCount(PieceType pt, Color c) => PieceCount(pt.MakePiece(c));
 
     public int PieceCount(PieceType pt)
-        => _pieceCount[pt.MakePiece(Player.White)] + _pieceCount[pt.MakePiece(Player.Black)];
+        => _pieceCount[pt.MakePiece(Color.White)] + _pieceCount[pt.MakePiece(Color.Black)];
 
-    public int PieceCount() => PieceCount(PieceTypes.AllPieces);
+    public int PieceCount() => PieceCount(PieceType.AllPieces);
 
     public IEnumerator<Piece> GetEnumerator()
         => _board.Where(static pc => pc != Piece.EmptyPiece).GetEnumerator();
diff --git a/src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs
index 482f1b5e..3e9838d1 100644
--- a/src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs
@@ -38,26 +38,26 @@ public interface IKpkBitBase
     /// <param name="strongSide">The strong side (the one with the pawn)</param>
     /// <param name="sq">The square that needs to be normalized</param>
     /// <returns>Normalized square to be used with probing</returns>
-    Square Normalize(IPosition pos, Player strongSide, Square sq);
-    
+    Square Normalize(IPosition pos, Color strongSide, Square sq);
+
     /// <summary>
     /// Probe with normalized squares and strong player
     /// </summary>
     /// <param name="strongKsq">"Strong" side king square</param>
     /// <param name="strongPawnSq">"Strong" side pawn square</param>
     /// <param name="weakKsq">"Weak" side king square</param>
-    /// <param name="stm">strong side. fx strongSide == pos.SideToMove ? Player.White : Player.Black</param>
+    /// <param name="stm">strong side. fx strongSide == pos.SideToMove ? Color.White : Color.Black</param>
     /// <returns>true if strong side "won"</returns>
-    bool Probe(Square strongKsq, Square strongPawnSq, Square weakKsq, Player stm);
+    bool Probe(Square strongKsq, Square strongPawnSq, Square weakKsq, Color stm);
 
     /// <summary>
     /// </summary>
-    /// <param name="stngActive"></param>
+    /// <param name="strongActive"></param>
     /// <param name="skSq">White King</param>
     /// <param name="wkSq">Black King</param>
     /// <param name="spSq">White Pawn</param>
     /// <returns></returns>
-    bool Probe(bool stngActive, Square skSq, Square wkSq, Square spSq);
+    bool Probe(bool strongActive, Square skSq, Square wkSq, Square spSq);
 
     bool IsDraw(IPosition pos);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
index c20c9581..00b89f5b 100644
--- a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
+++ b/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
@@ -72,7 +72,7 @@ public KpkBitBase()
             }
         } while (repeat != 0);
 
-        // Fill the bitbase with the decisive results
+        // Fill the bit base with the decisive results
         for (var i = 0; i < db.Length; ++i)
         {
             ref var kpkPosition = ref Unsafe.Add(ref dbSpace, i);
@@ -97,7 +97,7 @@ public KpkBitBase()
     /// <param name="strongKsq"></param>
     /// <param name="strongPawnSq"></param>
     /// <returns></returns>
-    private static int Index(Player stm, Square weakKingSq, Square strongKsq, Square strongPawnSq)
+    private static int Index(Color stm, Square weakKingSq, Square strongKsq, Square strongPawnSq)
     {
         return strongKsq
                | (weakKingSq << 6)
@@ -115,37 +115,37 @@ private enum Results
         Win = 4
     }
 
-    private readonly record struct KpkPosition(Results Result, Player Stm, Square[] KingSquares, Square PawnSquare)
+    private readonly record struct KpkPosition(Results Result, Color Stm, Square[] KingSquares, Square PawnSquare)
     {
         public static KpkPosition Create(int idx)
         {
             Results results;
-            var stm = new Player((idx >> 12) & 0x01);
+            var stm = new Color((idx >> 12) & 0x01);
             var ksq = new Square[] { (idx >> 0) & 0x3F, (idx >> 6) & 0x3F };
             var psq = Square.Create(new(Ranks.Rank7.AsInt() - ((idx >> 15) & 0x7)), new((idx >> 13) & 0x3));
 
             // Invalid if two pieces are on the same square or if a king can be captured
-            if (ksq[Player.White].Distance(ksq[Player.Black]) <= 1
-                || ksq[Player.White] == psq
-                || ksq[Player.Black] == psq
-                || (stm.IsWhite && (psq.PawnAttack(Player.White) & ksq[Player.Black]).IsNotEmpty))
+            if (ksq[Color.White].Distance(ksq[Color.Black]) <= 1
+                || ksq[Color.White] == psq
+                || ksq[Color.Black] == psq
+                || (stm.IsWhite && (psq.PawnAttack(Color.White) & ksq[Color.Black]).IsNotEmpty))
                 results = Results.None;
 
             // Win if the pawn can be promoted without getting captured
             else if (stm.IsWhite
                      && psq.Rank == Ranks.Rank7
-                     && ksq[Player.White] != psq + Directions.North
-                     && (ksq[Player.Black].Distance(psq + Directions.North) > 1
-                         || ksq[Player.White].Distance(psq + Directions.North) == 1))
+                     && ksq[Color.White] != psq + Directions.North
+                     && (ksq[Color.Black].Distance(psq + Directions.North) > 1
+                         || ksq[Color.White].Distance(psq + Directions.North) == 1))
                 results = Results.Win;
 
             // Draw if it is stalemate or the black king can capture the pawn
             else if (stm.IsBlack
-                     && ((PieceType.King.PseudoAttacks(ksq[Player.Black]) &
-                          ~(PieceType.King.PseudoAttacks(ksq[Player.White]) | psq.PawnAttack(Player.White)))
+                     && ((PieceType.King.PseudoAttacks(ksq[Color.Black]) &
+                          ~(PieceType.King.PseudoAttacks(ksq[Color.White]) | psq.PawnAttack(Color.White)))
                          .IsEmpty
-                         || !(PieceType.King.PseudoAttacks(ksq[Player.Black]) &
-                              ~PieceType.King.PseudoAttacks(ksq[Player.White]) & psq).IsEmpty))
+                         || !(PieceType.King.PseudoAttacks(ksq[Color.Black]) &
+                              ~PieceType.King.PseudoAttacks(ksq[Color.White]) & psq).IsEmpty))
                 results = Results.Draw;
 
             // Position will be classified later in initialization
@@ -182,15 +182,15 @@ public Results Classify(ReadOnlySpan<KpkPosition> db)
                 // Single push
                 if (PawnSquare.Rank < Rank.Rank7)
                     r |= db[
-                        Index(Player.Black, KingSquares[Player.Black], KingSquares[Player.White],
+                        Index(Color.Black, KingSquares[Color.Black], KingSquares[Color.White],
                             PawnSquare + Directions.North)].Result;
 
                 // Double push
                 if (PawnSquare.Rank == Rank.Rank2
-                    && PawnSquare + Directions.North != KingSquares[Player.White]
-                    && PawnSquare + Directions.North != KingSquares[Player.Black])
+                    && PawnSquare + Directions.North != KingSquares[Color.White]
+                    && PawnSquare + Directions.North != KingSquares[Color.Black])
                     r |= db[
-                        Index(Player.Black, KingSquares[Player.Black], KingSquares[Player.White],
+                        Index(Color.Black, KingSquares[Color.Black], KingSquares[Color.White],
                             PawnSquare + Directions.NorthDouble)].Result;
             }
 
@@ -217,8 +217,8 @@ private Results GetInitialKingResults(ReadOnlySpan<KpkPosition> db)
             while (b)
             {
                 var (bkSq, wkSq) = Stm.IsWhite
-                    ? (KingSquares[Player.Black], BitBoards.PopLsb(ref b))
-                    : (BitBoards.PopLsb(ref b), KingSquares[Player.White]);
+                    ? (KingSquares[Color.Black], BitBoards.PopLsb(ref b))
+                    : (BitBoards.PopLsb(ref b), KingSquares[Color.White]);
                 r |= db[Index(~Stm, bkSq, wkSq, PawnSquare)].Result;
             }
 
@@ -227,35 +227,35 @@ private Results GetInitialKingResults(ReadOnlySpan<KpkPosition> db)
     }
 
     /// <inheritdoc />
-    public Square Normalize(IPosition pos, Player strongSide, Square sq)
+    public Square Normalize(IPosition pos, Color strongSide, Square sq)
     {
-        Debug.Assert(pos.Board.PieceCount(PieceTypes.Pawn, strongSide) == 1);
+        Debug.Assert(pos.Board.PieceCount(PieceType.Pawn, strongSide) == 1);
 
-        if (pos.GetPieceSquare(PieceTypes.Pawn, strongSide).File >= File.FileE)
+        if (pos.GetPieceSquare(PieceType.Pawn, strongSide).File >= File.FileE)
             sq = sq.FlipFile();
 
         return strongSide.IsWhite ? sq : sq.FlipRank();
     }
 
     /// <inheritdoc />
-    public bool Probe(Square strongKsq, Square strongPawnSq, Square weakKsq, Player stm)
+    public bool Probe(Square strongKsq, Square strongPawnSq, Square weakKsq, Color stm)
     {
         Debug.Assert(strongPawnSq.File <= File.FileD);
         return _kpKbb[Index(stm, weakKsq, strongKsq, strongPawnSq)];
     }
 
     /// <inheritdoc />
-    public bool Probe(bool stngActive, Square skSq, Square wkSq, Square spSq)
+    public bool Probe(bool strongActive, Square skSq, Square wkSq, Square spSq)
     {
-        return _kpKbb[Index(stngActive, skSq, wkSq, spSq)];
+        return _kpKbb[Index(strongActive, skSq, wkSq, spSq)];
     }
 
     public bool IsDraw(IPosition pos)
     {
         return !Probe(
-            strongKsq: pos.GetPieceSquare(PieceTypes.King, Player.White),
-            strongPawnSq: pos.Pieces(PieceTypes.Pawn).Lsb(),
-            weakKsq: pos.GetPieceSquare(PieceTypes.King, Player.Black),
+            strongKsq: pos.GetPieceSquare(PieceType.King, Color.White),
+            strongPawnSq: pos.Pieces(PieceType.Pawn).Lsb(),
+            weakKsq: pos.GetPieceSquare(PieceType.King, Color.Black),
             stm: pos.SideToMove);
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
index 3192827f..85955616 100644
--- a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
+++ b/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
@@ -65,26 +65,26 @@ public static class PieceExtensions
     ];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetPieceChar(this Piece p) => PieceChars[p];
+    public static char GetPieceChar(this Piece pc) => PieceChars[pc];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetPieceChar(this PieceType p) => PieceChars[p];
+    public static char GetPieceChar(this PieceType pt) => PieceChars[pt];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string GetPieceString(this Piece p) => PieceStrings[p];
+    public static string GetPieceString(this Piece pc) => PieceStrings[pc];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string GetName(this Piece p) => PieceNames[p.Type()];
+    public static string GetName(this Piece pc) => PieceNames[pc.Type()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetPromotionChar(this PieceType p) => PromotionPieceNotation[p];
+    public static char GetPromotionChar(this PieceType pt) => PromotionPieceNotation[pt];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetPgnChar(this Piece p) => PgnPieceChars[p.Type()];
+    public static char GetPgnChar(this Piece pc) => PgnPieceChars[pc.Type()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static char GetUnicodeChar(this Piece p) => PieceUnicodeChar[p];
+    public static char GetUnicodeChar(this Piece pc) => PieceUnicodeChar[pc];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int AsInt(this Pieces piece) => (int)piece;
+    public static int AsInt(this Pieces pc) => (int)pc;
 }
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/Rudzoft.ChessLib/Fen/Fen.cs
index cb4106e9..661637d3 100644
--- a/src/Rudzoft.ChessLib/Fen/Fen.cs
+++ b/src/Rudzoft.ChessLib/Fen/Fen.cs
@@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+using System.Buffers;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text.RegularExpressions;
@@ -41,8 +42,6 @@ public static class Fen
 
     private const string FenRankRegexSnippet = "[1-8KkQqRrBbNnPp]{1,8}";
 
-    private const string ValidChars = "0123456789pPnNbBrRqQkK/ w-abcdefgh";
-
     private const char Space = ' ';
 
     private const int SpaceCount = 3;
@@ -51,6 +50,8 @@ public static class Fen
 
     private const int SeparatorCount = 7;
 
+    private static readonly SearchValues<char> ValidChars = SearchValues.Create("0123456789pPnNbBrRqQkK/ w-abcdefgh");
+
     private static readonly Regex ValidFenRegex = new(
         string.Format(
             """^ \s* {0}/{0}/{0}/{0}/{0}/{0}/{0}/{0} \s+ (?:w|b) \s+ (?:[KkQq]+|\-) \s+ (?:[a-h][1-8]|\-) \s+ \d+ \s+ \d+ \s* $""",
@@ -75,7 +76,7 @@ public static void Validate(string fen)
         if (f.IsEmpty)
             throw new InvalidFenException("Fen is empty.");
 
-        var invalidCharIndex = f.IndexOfAnyExcept(ValidChars.AsSpan());
+        var invalidCharIndex = f.IndexOfAnyExcept(ValidChars);
 
         if (invalidCharIndex > -1)
             throw new InvalidFenException($"Invalid char detected in fen. fen={f}, pos={invalidCharIndex}");
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/Rudzoft.ChessLib/Game.cs
index 3f8a9e94..26983cd2 100644
--- a/src/Rudzoft.ChessLib/Game.cs
+++ b/src/Rudzoft.ChessLib/Game.cs
@@ -111,10 +111,10 @@ public override string ToString()
     public IEnumerator<Piece> GetEnumerator() => pos.GetEnumerator();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard OccupiedBySide(Player p) => pos.Pieces(p);
+    public BitBoard OccupiedBySide(Color c) => pos.Pieces(c);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Player CurrentPlayer() => pos.SideToMove;
+    public Color CurrentPlayer() => pos.SideToMove;
 
     public UInt128 Perft(in HashKey baseKey, int depth, bool root = true)
     {
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
index 21bad571..ebad5408 100644
--- a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
+++ b/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
@@ -73,10 +73,10 @@ internal void save(uint k, Value v, Bound t, Depth d, Move m, int g, Value statV
     internal void set_generation(int g) { generation8 = (byte)g; }
 
     //internal UInt32 key() { return key32; }
-    internal Depth depth() { return depth16; }
+    public Depth depth() { return depth16; }
     internal Move move() { return move16; }
     internal Value value() { return value16; }
-    internal Bound type() { return bound; }
+    public Bound type() { return bound; }
     internal int generation() { return generation8; }
     internal Value static_value() { return staticValue; }
     internal Value static_value_margin() { return staticMargin; }
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
index 23fb24de..e0012a0b 100644
--- a/src/Rudzoft.ChessLib/Hash/Zobrist.cs
+++ b/src/Rudzoft.ChessLib/Hash/Zobrist.cs
@@ -73,12 +73,11 @@ public Zobrist(IRKiss rKiss)
     {
         _zobristPst = new HashKey[Square.Count][];
 
-        for (var sq = Squares.a1; sq <= Squares.h8; sq++)
+        foreach (var sq in Square.All)
         {
-            var s = sq.AsInt();
-            _zobristPst[s] = new HashKey[Piece.Count];
+            _zobristPst[sq] = new HashKey[Piece.Count];
             foreach (var pc in Piece.All.AsSpan())
-                _zobristPst[s][pc] = rKiss.Rand();
+                _zobristPst[sq][pc] = rKiss.Rand();
         }
 
         _zobristCastling = new HashKey[CastleRight.Count];
@@ -91,7 +90,8 @@ public Zobrist(IRKiss rKiss)
             while (bb)
             {
                 var key = _zobristCastling[1UL << BitBoards.PopLsb(ref bb)];
-                _zobristCastling[v] ^= !key.IsEmpty? key : rKiss.Rand();
+                key = !key.IsEmpty ? key : rKiss.Rand();
+                _zobristCastling[v] ^= key.Key;
             }
         }
 
diff --git a/src/Rudzoft.ChessLib/IBoard.cs b/src/Rudzoft.ChessLib/IBoard.cs
index 09347f94..f4004ac3 100644
--- a/src/Rudzoft.ChessLib/IBoard.cs
+++ b/src/Rudzoft.ChessLib/IBoard.cs
@@ -41,12 +41,12 @@ public interface IBoard : IEnumerable<Piece>
     BitBoard Pieces();
     BitBoard Pieces(PieceType pt);
     BitBoard Pieces(PieceType pt1, PieceType pt2);
-    BitBoard Pieces(Player p);
-    BitBoard Pieces(Player p, PieceType pt);
-    BitBoard Pieces(Player p, PieceType pt1, PieceType pt2);
-    Square Square(PieceType pt, Player p);
+    BitBoard Pieces(Color c);
+    BitBoard Pieces(Color c, PieceType pt);
+    BitBoard Pieces(Color c, PieceType pt1, PieceType pt2);
+    Square Square(PieceType pt, Color c);
     int PieceCount(Piece pc);
-    int PieceCount(PieceType pt, Player p);
+    int PieceCount(PieceType pt, Color c);
     int PieceCount(PieceType pt);
     int PieceCount();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/IGame.cs b/src/Rudzoft.ChessLib/IGame.cs
index ba72670e..0f683166 100644
--- a/src/Rudzoft.ChessLib/IGame.cs
+++ b/src/Rudzoft.ChessLib/IGame.cs
@@ -57,9 +57,9 @@ public interface IGame : IEnumerable<Piece>
 
     string ToString();
 
-    BitBoard OccupiedBySide(Player p);
+    BitBoard OccupiedBySide(Color c);
 
-    Player CurrentPlayer();
+    Color CurrentPlayer();
 
     UInt128 Perft(in HashKey baseKey, int depth, bool root = true);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/Rudzoft.ChessLib/IPosition.cs
index 9d6ffeae..a1763c74 100644
--- a/src/Rudzoft.ChessLib/IPosition.cs
+++ b/src/Rudzoft.ChessLib/IPosition.cs
@@ -41,7 +41,7 @@ public interface IPosition : IEnumerable<Piece>
 
     ChessMode ChessMode { get; set; }
 
-    Player SideToMove { get; }
+    Color SideToMove { get; }
 
     Square EnPassantSquare { get; }
 
@@ -91,23 +91,23 @@ public interface IPosition : IEnumerable<Piece>
 
     BitBoard CheckedSquares(PieceType pt);
 
-    BitBoard PinnedPieces(Player p);
+    BitBoard PinnedPieces(Color c);
 
-    BitBoard KingBlockers(Player p);
+    BitBoard KingBlockers(Color c);
 
-    bool IsKingBlocker(Player p, Square sq);
+    bool IsKingBlocker(Color c, Square sq);
 
     BitBoard SliderBlockerOn(Square sq, BitBoard attackers, ref BitBoard pinners, ref BitBoard hidders);
 
     bool IsOccupied(Square sq);
 
-    bool IsAttacked(Square sq, Player p);
+    bool IsAttacked(Square sq, Color c);
 
     bool GivesCheck(Move m);
 
     BitBoard Pieces();
 
-    BitBoard Pieces(Player p);
+    BitBoard Pieces(Color c);
 
     BitBoard Pieces(Piece pc);
 
@@ -115,9 +115,9 @@ public interface IPosition : IEnumerable<Piece>
 
     BitBoard Pieces(PieceType pt1, PieceType pt2);
 
-    BitBoard Pieces(PieceType pt, Player p);
+    BitBoard Pieces(PieceType pt, Color c);
 
-    BitBoard Pieces(PieceType pt1, PieceType pt2, Player p);
+    BitBoard Pieces(PieceType pt1, PieceType pt2, Color c);
 
     int PieceCount();
 
@@ -125,25 +125,25 @@ public interface IPosition : IEnumerable<Piece>
 
     int PieceCount(PieceType pt);
 
-    int PieceCount(PieceType pt, Player p);
+    int PieceCount(PieceType pt, Color c);
 
-    BitBoard PawnsOnColor(Player p, Square sq);
+    BitBoard PawnsOnColor(Color c, Square sq);
 
-    bool SemiOpenFileOn(Player p, Square sq);
+    bool SemiOpenFileOn(Color c, Square sq);
 
-    bool BishopPaired(Player p);
+    bool BishopPaired(Color c);
 
     bool BishopOpposed();
 
-    Square GetPieceSquare(PieceType pt, Player p);
+    Square GetPieceSquare(PieceType pt, Color c);
 
-    Square GetKingSquare(Player p);
+    Square GetKingSquare(Color c);
 
     Piece MovedPiece(Move m);
 
-    bool PieceOnFile(Square sq, Player p, PieceType pt);
+    bool PieceOnFile(Square sq, Color c, PieceType pt);
 
-    bool PawnIsolated(Square sq, Player p);
+    bool PawnIsolated(Square sq, Color c);
 
     bool PassedPawn(Square sq);
 
@@ -153,27 +153,27 @@ public interface IPosition : IEnumerable<Piece>
 
     BitBoard AttacksTo(Square sq);
 
-    bool AttackedBySlider(Square sq, Player p);
+    bool AttackedBySlider(Square sq, Color c);
 
-    bool AttackedByKnight(Square sq, Player p);
+    bool AttackedByKnight(Square sq, Color c);
 
-    bool AttackedByPawn(Square sq, Player p);
+    bool AttackedByPawn(Square sq, Color c);
 
-    bool AttackedByKing(Square sq, Player p);
+    bool AttackedByKing(Square sq, Color c);
 
-    BitBoard AttacksBy(PieceType pt, Player p);
+    BitBoard AttacksBy(PieceType pt, Color c);
 
     bool IsCapture(Move m);
 
     bool IsCaptureOrPromotion(Move m);
 
-    bool IsPawnPassedAt(Player p, Square sq);
+    bool IsPawnPassedAt(Color c, Square sq);
 
-    BitBoard PawnPassSpan(Player p, Square sq);
+    BitBoard PawnPassSpan(Color c, Square sq);
 
     bool CanCastle(CastleRight cr);
 
-    bool CanCastle(Player p);
+    bool CanCastle(Color c);
 
     public ref BitBoard CastleKingPath(CastleRight cr);
 
@@ -193,7 +193,7 @@ public interface IPosition : IEnumerable<Piece>
 
     IPosition Set(string fen, ChessMode chessMode, in State state, bool validate = false, int searcher = 0);
 
-    IPosition Set(ReadOnlySpan<char> code, Player p, in State state);
+    IPosition Set(ReadOnlySpan<char> code, Color c, in State state);
 
     HashKey GetKey(State state);
 
@@ -213,7 +213,7 @@ public interface IPosition : IEnumerable<Piece>
 
     bool SeeGe(Move m, Value threshold);
 
-    Value NonPawnMaterial(Player p);
+    Value NonPawnMaterial(Color c);
 
     Value NonPawnMaterial();
 
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
index a29d2202..04b51909 100644
--- a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
+++ b/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
@@ -43,7 +43,7 @@ public static int Generate(
         in IPosition pos,
         ref ValMove moves,
         int index,
-        Player us,
+        Color us,
         MoveGenerationTypes types)
     {
         if (types.HasFlagFast(MoveGenerationTypes.Legal))
@@ -67,7 +67,7 @@ private static int GenerateAll(
         ref ValMove moves,
         int index,
         in BitBoard target,
-        Player us,
+        Color us,
         MoveGenerationTypes types)
     {
         index = GeneratePawnMoves(index, in pos, ref moves, in target, us, types);
@@ -81,7 +81,7 @@ private static int GenerateAll(
             return index;
 
         var ksq = pos.GetKingSquare(us);
-        var b = pos.GetAttacks(ksq, PieceTypes.King) & target;
+        var b = pos.GetAttacks(ksq, PieceType.King) & target;
         index = Move.Create(ref moves, index, ksq, ref b);
 
         if (types == MoveGenerationTypes.Captures || !pos.CanCastle(pos.SideToMove))
@@ -113,7 +113,7 @@ private static int GenerateCapturesQuietsNonEvasions(
         in IPosition pos,
         ref ValMove moves,
         int index,
-        Player us,
+        Color us,
         MoveGenerationTypes types)
     {
         Debug.Assert(types is MoveGenerationTypes.Captures or MoveGenerationTypes.Quiets
@@ -143,12 +143,12 @@ private static int GenerateEvasions(
         int index,
         in IPosition pos,
         ref ValMove moves,
-        Player us)
+        Color us)
     {
         Debug.Assert(pos.InCheck);
         var ksq = pos.GetKingSquare(us);
         var sliderAttacks = BitBoard.Empty;
-        var sliders = pos.Checkers & ~pos.Pieces(PieceTypes.Pawn, PieceTypes.Knight);
+        var sliders = pos.Checkers & ~pos.Pieces(PieceType.Pawn, PieceType.Knight);
         Square checkSquare;
 
         // Find all the squares attacked by slider checkers. We will remove them from the king
@@ -164,7 +164,7 @@ private static int GenerateEvasions(
         }
 
         // Generate evasions for king, capture and non capture moves
-        var b = pos.GetAttacks(ksq, PieceTypes.King) & ~pos.Pieces(us) & ~sliderAttacks;
+        var b = pos.GetAttacks(ksq, PieceType.King) & ~pos.Pieces(us) & ~sliderAttacks;
         index = Move.Create(ref moves, index, ksq, ref b);
 
         if (pos.Checkers.MoreThanOne())
@@ -189,7 +189,7 @@ private static int GenerateLegal(
         int index,
         in IPosition pos,
         ref ValMove moves,
-        Player us)
+        Color us)
     {
         var end = pos.InCheck
             ? GenerateEvasions(index, in pos, ref moves, us)
@@ -229,12 +229,12 @@ private static int GenerateMoves(
         int index,
         in IPosition pos,
         ref ValMove moves,
-        Player us,
+        Color us,
         in BitBoard target,
         PieceType pt,
         bool checks)
     {
-        Debug.Assert(pt != PieceTypes.King && pt != PieceTypes.Pawn);
+        Debug.Assert(pt != PieceType.King && pt != PieceType.Pawn);
 
         var pieces = pos.Pieces(pt, us);
 
@@ -272,7 +272,7 @@ private static int GeneratePawnMoves(
         in IPosition pos,
         ref ValMove moves,
         in BitBoard target,
-        Player us,
+        Color us,
         MoveGenerationTypes types)
     {
         // Compute our parametrized parameters named according to the point of view of white side.
@@ -280,7 +280,7 @@ private static int GeneratePawnMoves(
         var them = ~us;
         var rank7Bb = us.Rank7();
 
-        var pawns = pos.Pieces(PieceTypes.Pawn, us);
+        var pawns = pos.Pieces(PieceType.Pawn, us);
 
         var enemies = types switch
         {
@@ -320,12 +320,11 @@ private static int GeneratePawnMoves(
                     pawnOne &= ksq.PawnAttack(them);
                     pawnTwo &= ksq.PawnAttack(them);
 
-                    var dcCandidates = pawnsNotOn7 & pos.KingBlockers(them);
-
                     // Add pawn pushes which give discovered check. This is possible only if
                     // the pawn is not on the same file as the enemy king, because we don't
                     // generate captures. Note that a possible discovery check promotion has
                     // been already generated among captures.
+                    var dcCandidates = pawnsNotOn7 & pos.KingBlockers(them);
                     if (dcCandidates)
                     {
                         var dc1 = dcCandidates.Shift(up) & emptySquares & ~ksq;
@@ -434,7 +433,7 @@ private static int GenerateQuietChecks(
         int index,
         in IPosition pos,
         ref ValMove moves,
-        Player us)
+        Color us)
     {
         Debug.Assert(!pos.InCheck);
         var dc = pos.KingBlockers(~us) & pos.Pieces(us);
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
index 8aede2f9..c8ba4607 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
@@ -56,7 +56,7 @@ public override string Convert(IPosition pos, Move move)
         Span<char> re = stackalloc char[6];
         var i = 0;
 
-        if (pt != PieceTypes.Pawn)
+        if (pt != PieceType.Pawn)
         {
             re[i++] = pc.GetUnicodeChar();
             foreach (var c in Disambiguation(pos, move, from))
@@ -73,7 +73,7 @@ public override string Convert(IPosition pos, Move move)
         {
             if (pos.GetPiece(to) != Piece.EmptyPiece)
             {
-                if (pt == PieceTypes.Pawn)
+                if (pt == PieceType.Pawn)
                     re[i++] = from.FileChar;
                 re[i++] = 'x';
             }
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index 7baa5c11..b7f662aa 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -33,6 +33,8 @@ namespace Rudzoft.ChessLib.Notation.Notations;
 
 public sealed class IccfNotation : Notation
 {
+    private static readonly int[] PieceTypeToValueIndex = [0, 0, 4, 3, 2, 1];
+
     public IccfNotation(ObjectPool<MoveList> moveLists) : base(moveLists) { }
 
     /// <summary>
@@ -50,31 +52,18 @@ public override string Convert(IPosition pos, Move move)
         Span<char> re = stackalloc char[5];
         var i = 0;
 
-        re[i++] = (char)('1' + from.File);
-        re[i++] = (char)('1' + from.Rank);
-        re[i++] = (char)('1' + to.File);
-        re[i++] = (char)('1' + to.Rank);
+        re[i++] = (char)('1' + from.File.Value);
+        re[i++] = (char)('1' + from.Rank.Value);
+        re[i++] = (char)('1' + to.File.Value);
+        re[i++] = (char)('1' + to.Rank.Value);
 
         // ReSharper disable once InvertIf
         if (move.IsPromotionMove())
         {
-            var c = PieceTypeToValue(move.PromotedPieceType());
+            var c = PieceTypeToValueIndex[move.PromotedPieceType()];
             re[i++] = (char)('0' + c);
         }
 
         return new(re[..i]);
     }
-
-    private static int PieceTypeToValue(PieceType pt)
-    {
-        if (pt == PieceType.Queen)
-            return 1;
-        if (pt == PieceType.Rook)
-            return 2;
-        if (pt == PieceType.Bishop)
-            return 3;
-        if (pt == PieceType.Knight)
-            return 4;
-        throw new NotImplementedException();
-    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
index 1cb49705..5f347ed3 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
@@ -73,7 +73,7 @@ public override string Convert(IPosition pos, Move move)
             var capturedPiece = pos.GetPiece(to);
             if (capturedPiece != Piece.EmptyPiece)
             {
-                if (pt == PieceTypes.Pawn)
+                if (pt == PieceType.Pawn)
                     re[i++] = from.FileChar;
 
                 re[i++] = 'x';
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
index 59b19c98..d80ea7f4 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
@@ -128,7 +128,7 @@ private static BitBoard GetSimilarAttacks(IPosition pos, Move move)
         var from = move.FromSquare();
         var pt = pos.GetPieceType(from);
 
-        return pt == PieceTypes.Pawn || pt == PieceTypes.King
+        return pt == PieceType.Pawn || pt == PieceType.King
             ? BitBoard.Empty
             : pos.GetAttacks(move.ToSquare(), pt, pos.Pieces()) ^ from;
     }
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
index a063be92..528f6981 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
@@ -56,7 +56,7 @@ public override string Convert(IPosition pos, Move move)
 
         var pt = pos.GetPieceType(from);
 
-        if (pt != PieceTypes.Pawn)
+        if (pt != PieceType.Pawn)
             re[i++] = pt.GetPieceChar();
 
         re[i++] = from.FileChar;
@@ -73,7 +73,7 @@ public override string Convert(IPosition pos, Move move)
             var capturedPiece = pos.GetPiece(to);
             if (capturedPiece != Piece.EmptyPiece)
             {
-                if (pt == PieceTypes.Pawn)
+                if (pt == PieceType.Pawn)
                     re[i++] = from.FileChar;
 
                 re[i++] = 'x';
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
index 6927dc27..48852d25 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
@@ -56,7 +56,7 @@ public override string Convert(IPosition pos, Move move)
 
         var pt = pos.GetPieceType(from);
 
-        if (pt != PieceTypes.Pawn)
+        if (pt != PieceType.Pawn)
         {
             re[i++] = pos.GetPiece(from).GetPgnChar();
             foreach (var amb in Disambiguation(pos, move, from))
@@ -71,7 +71,7 @@ public override string Convert(IPosition pos, Move move)
         }
         else if (pos.GetPiece(to) != Piece.EmptyPiece)
         {
-            if (pt == PieceTypes.Pawn)
+            if (pt == PieceType.Pawn)
                 re[i++] = from.FileChar;
             re[i++] = 'x';
         }
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
index 7458150f..169eebac 100644
--- a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
+++ b/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
@@ -156,7 +156,7 @@ private Move ConvertMove(IPosition pos, ushort polyMove)
 
         return mm;
 
-        static PieceTypes PolyToPt(int pt) => (PieceTypes)(3 - pt);
+        static PieceType PolyToPt(int pt) => (PieceTypes)(3 - pt);
     }
 
     private static Move SelectMove(
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index c3749b58..e9a0d0d8 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -58,7 +58,7 @@ public sealed class Position : IPosition
     private readonly ObjectPool<StringBuilder> _outputObjectPool;
     private readonly ObjectPool<MoveList> _moveListPool;
     private readonly IPositionValidator _positionValidator;
-    private Player _sideToMove;
+    private Color _sideToMove;
 
     public Position(
         IBoard board,
@@ -117,7 +117,7 @@ public Position(
 
     public int Rule50 => State.ClockPly;
 
-    public Player SideToMove => _sideToMove;
+    public Color SideToMove => _sideToMove;
 
     public State State { get; private set; }
 
@@ -132,36 +132,36 @@ public void AddPiece(Piece pc, Square sq)
             PieceUpdated?.Invoke(new PieceSquareEventArgs(pc, sq));
     }
 
-    public bool AttackedByKing(Square sq, Player p)
-        => (GetAttacks(sq, PieceType.King) & GetKingSquare(p)).IsNotEmpty;
+    public bool AttackedByKing(Square sq, Color c)
+        => (GetAttacks(sq, PieceType.King) & GetKingSquare(c)).IsNotEmpty;
 
-    public bool AttackedByKnight(Square sq, Player p)
-        => (Board.Pieces(p, PieceType.Knight) & GetAttacks(sq, PieceType.Knight)).IsNotEmpty;
+    public bool AttackedByKnight(Square sq, Color c)
+        => (Board.Pieces(c, PieceType.Knight) & GetAttacks(sq, PieceType.Knight)).IsNotEmpty;
 
-    public bool AttackedByPawn(Square sq, Player p) =>
-        (Board.Pieces(p, PieceType.Pawn) & sq.PawnAttack(~p)).IsNotEmpty;
+    public bool AttackedByPawn(Square sq, Color c) =>
+        (Board.Pieces(c, PieceType.Pawn) & sq.PawnAttack(~c)).IsNotEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool AttackedBySlider(Square sq, Player p)
+    public bool AttackedBySlider(Square sq, Color c)
     {
         var occupied = Board.Pieces();
         var rookAttacks = sq.RookAttacks(in occupied);
-        if (Board.Pieces(p, PieceType.Rook) & rookAttacks)
+        if (Board.Pieces(c, PieceType.Rook) & rookAttacks)
             return true;
 
         var bishopAttacks = sq.BishopAttacks(in occupied);
-        if (Board.Pieces(p, PieceType.Bishop) & bishopAttacks)
+        if (Board.Pieces(c, PieceType.Bishop) & bishopAttacks)
             return true;
 
-        return (Board.Pieces(p, PieceType.Queen) & (bishopAttacks | rookAttacks)).IsNotEmpty;
+        return (Board.Pieces(c, PieceType.Queen) & (bishopAttacks | rookAttacks)).IsNotEmpty;
     }
 
-    public BitBoard AttacksBy(PieceType pt, Player p)
+    public BitBoard AttacksBy(PieceType pt, Color c)
     {
-        var attackers = Pieces(pt, p);
+        var attackers = Pieces(pt, c);
 
         if (pt == PieceType.Pawn)
-            return attackers.PawnAttacks(p);
+            return attackers.PawnAttacks(c);
 
         var threats = BitBoard.Empty;
         while (attackers)
@@ -186,18 +186,18 @@ public bool IsCaptureOrPromotion(Move m)
         };
     }
 
-    public bool IsPawnPassedAt(Player p, Square sq)
-        => (Board.Pieces(~p, PieceType.Pawn) & sq.PassedPawnFrontAttackSpan(p)).IsEmpty;
+    public bool IsPawnPassedAt(Color c, Square sq)
+        => (Board.Pieces(~c, PieceType.Pawn) & sq.PassedPawnFrontAttackSpan(c)).IsEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard PawnPassSpan(Player p, Square sq) => p.FrontSquares(sq) | sq.PawnAttackSpan(p);
+    public BitBoard PawnPassSpan(Color c, Square sq) => c.FrontSquares(sq) | sq.PawnAttackSpan(c);
 
     public BitBoard AttacksTo(Square sq, in BitBoard occ)
     {
         Debug.Assert(sq.IsOk);
 
-        return (sq.PawnAttack(Player.White) & Board.Pieces(Player.Black, PieceType.Pawn))
-               | (sq.PawnAttack(Player.Black) & Board.Pieces(Player.White, PieceType.Pawn))
+        return (sq.PawnAttack(Color.White) & Board.Pieces(Color.Black, PieceType.Pawn))
+               | (sq.PawnAttack(Color.Black) & Board.Pieces(Color.White, PieceType.Pawn))
                | (GetAttacks(sq, PieceType.Knight) & Board.Pieces(PieceType.Knight))
                | (GetAttacks(sq, PieceType.Rook, in occ) & Board.Pieces(PieceType.Rook, PieceType.Queen))
                | (GetAttacks(sq, PieceType.Bishop, in occ) & Board.Pieces(PieceType.Bishop, PieceType.Queen))
@@ -206,9 +206,9 @@ public BitBoard AttacksTo(Square sq, in BitBoard occ)
 
     public BitBoard AttacksTo(Square sq) => AttacksTo(sq, Board.Pieces());
 
-    public BitBoard KingBlockers(Player p) => State.BlockersForKing[p];
+    public BitBoard KingBlockers(Color c) => State.BlockersForKing[c];
 
-    public bool IsKingBlocker(Player p, Square sq) => KingBlockers(p).Contains(sq);
+    public bool IsKingBlocker(Color c, Square sq) => KingBlockers(c).Contains(sq);
 
     public BitBoard SliderBlockerOn(Square sq, BitBoard attackers, ref BitBoard pinners, ref BitBoard hidders)
     {
@@ -218,8 +218,8 @@ public BitBoard SliderBlockerOn(Square sq, BitBoard attackers, ref BitBoard pinn
         // Snipers are X-ray slider attackers at 's'
         // No need to remove direct attackers at 's' as in check no evaluation
 
-        var snipers = attackers & ((Pieces(PieceTypes.Bishop, PieceTypes.Queen) & GetAttacks(sq, PieceTypes.Bishop))
-                                   | (Pieces(PieceTypes.Rook, PieceTypes.Queen) & GetAttacks(sq, PieceTypes.Rook)));
+        var snipers = attackers & ((Pieces(PieceType.Bishop, PieceType.Queen) & GetAttacks(sq, PieceType.Bishop))
+                                   | (Pieces(PieceType.Rook, PieceType.Queen) & GetAttacks(sq, PieceType.Rook)));
 
         var mocc = Pieces() ^ snipers;
 
@@ -244,7 +244,7 @@ public BitBoard SliderBlockerOn(Square sq, BitBoard attackers, ref BitBoard pinn
 
     public bool CanCastle(CastleRight cr) => State.CastleRights.Has(cr);
 
-    public bool CanCastle(Player p) => State.CastleRights.Has(p);
+    public bool CanCastle(Color c) => State.CastleRights.Has(c);
 
     public ref BitBoard CastleKingPath(CastleRight cr) => ref _castleKingPath[cr.AsInt()];
 
@@ -271,7 +271,7 @@ public void Clear()
         _castleRookPath.Fill(BitBoard.Empty);
         _castlingRightsMask.Fill(CastleRight.None);
         _castlingRookSquare.Fill(Square.None);
-        _sideToMove = Player.White;
+        _sideToMove = Color.White;
         ChessMode = ChessMode.Normal;
     }
 
@@ -377,7 +377,7 @@ public FenData GenerateFen()
 
     public BitBoard GetAttacks(Square sq, PieceType pt, in BitBoard occ)
     {
-        Debug.Assert(pt != PieceTypes.Pawn, "Pawns need player");
+        Debug.Assert(pt != PieceType.Pawn, "Pawns need player");
 
         if (pt == PieceType.Knight || pt == PieceType.King)
             return pt.PseudoAttacks(sq);
@@ -402,7 +402,7 @@ public BitBoard GetAttacks(Square sq, PieceType pt, in BitBoard occ)
 
     IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
-    public Square GetKingSquare(Player p) => Board.Square(PieceType.King, p);
+    public Square GetKingSquare(Color c) => Board.Square(PieceType.King, c);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public HashKey GetPawnKey()
@@ -437,13 +437,13 @@ public HashKey GetKey(State state)
         if (state.EnPassantSquare != Square.None)
             result ^= Zobrist.EnPassant(state.EnPassantSquare);
 
-        if (_sideToMove == Player.Black)
+        if (_sideToMove == Color.Black)
             result ^= Zobrist.Side();
 
         return result;
     }
 
-    public Square GetPieceSquare(PieceType pt, Player p) => Board.Square(pt, p);
+    public Square GetPieceSquare(PieceType pt, Color c) => Board.Square(pt, c);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public PieceType GetPieceType(Square sq) => Board.PieceAt(sq).Type();
@@ -543,8 +543,8 @@ public bool IsDraw(int ply)
         };
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsAttacked(Square sq, Player p)
-        => AttackedBySlider(sq, p) || AttackedByKnight(sq, p) || AttackedByPawn(sq, p) || AttackedByKing(sq, p);
+    public bool IsAttacked(Square sq, Color c)
+        => AttackedBySlider(sq, c) || AttackedByKnight(sq, c) || AttackedByPawn(sq, c) || AttackedByKing(sq, c);
 
     public bool IsLegal(Move m)
     {
@@ -579,7 +579,7 @@ public bool IsLegal(Move m)
         return (KingBlockers(us) & from).IsEmpty || from.Aligned(to, ksq);
     }
 
-    private bool IsCastleMoveLegal(Move m, Square to, Square from, Player us)
+    private bool IsCastleMoveLegal(Move m, Square to, Square from, Color us)
     {
         // After castling, the rook and king final positions are the same in Chess960 as
         // they would be in standard chess.
@@ -619,7 +619,7 @@ private bool IsCastleMoveLegal(Move m, Square to, Square from, Player us)
 
         return (attacks & occupied).IsEmpty;
 
-        static (Square, Direction) GetRookSquareAndDirection(Square toSq, Square fromSq, Player us)
+        static (Square, Direction) GetRookSquareAndDirection(Square toSq, Square fromSq, Color us)
         {
             return toSq > fromSq
                 ? (Square.G1.Relative(us), Direction.West)
@@ -627,7 +627,7 @@ private bool IsCastleMoveLegal(Move m, Square to, Square from, Player us)
         }
     }
 
-    private bool IsEnPassantMoveLegal(Square to, Player us, Square from, Square ksq)
+    private bool IsEnPassantMoveLegal(Square to, Color us, Square from, Square ksq)
     {
         var captureSquare = to - us.PawnPushDistance();
         var occupied = (Board.Pieces() ^ from ^ captureSquare) | to;
@@ -750,7 +750,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
         Debug.Assert(pc.ColorOf() == us);
         Debug.Assert(
             capturedPiece == Piece.EmptyPiece || capturedPiece.ColorOf() == (!m.IsCastleMove() ? them : us));
-        Debug.Assert(capturedPiece.Type() != PieceTypes.King);
+        Debug.Assert(capturedPiece.Type() != PieceType.King);
 
         if (type == MoveTypes.Castling)
         {
@@ -776,7 +776,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 {
                     captureSquare -= us.PawnPushDistance();
 
-                    Debug.Assert(pc.Type() == PieceTypes.Pawn);
+                    Debug.Assert(pc.Type() == PieceType.Pawn);
                     Debug.Assert(to == State.EnPassantSquare);
                     Debug.Assert(to.RelativeRank(us) == Ranks.Rank6);
                     Debug.Assert(!IsOccupied(to));
@@ -840,7 +840,7 @@ public void MakeMove(Move m, in State newState, bool givesCheck)
                 var promotionPiece = m.PromotedPieceType().MakePiece(us);
 
                 Debug.Assert(to.RelativeRank(us) == Rank.Rank8);
-                Debug.Assert(promotionPiece.Type().InBetween(PieceTypes.Knight, PieceTypes.Queen));
+                Debug.Assert(promotionPiece.Type().InBetween(PieceType.Knight, PieceType.Queen));
 
                 RemovePiece(to);
                 AddPiece(promotionPiece, to);
@@ -967,20 +967,20 @@ public bool PassedPawn(Square sq)
     /// Determine if a pawn is isolated e.i. no own pawns on either of it's neighboring files
     /// </summary>
     /// <param name="sq"></param>
-    /// <param name="p"></param>
+    /// <param name="c"></param>
     /// <returns></returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool PawnIsolated(Square sq, Player p)
-        => ((sq.PawnAttackSpan(p) | sq.PawnAttackSpan(~p)) & Board.Pieces(p, PieceType.Pawn)).IsEmpty;
+    public bool PawnIsolated(Square sq, Color c)
+        => ((sq.PawnAttackSpan(c) | sq.PawnAttackSpan(~c)) & Board.Pieces(c, PieceType.Pawn)).IsEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool PieceOnFile(Square sq, Player p, PieceType pt) => (Board.Pieces(p, pt) & sq).IsNotEmpty;
+    public bool PieceOnFile(Square sq, Color c, PieceType pt) => (Board.Pieces(c, pt) & sq).IsNotEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces() => Board.Pieces();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard Pieces(Player p) => Board.Pieces(p);
+    public BitBoard Pieces(Color c) => Board.Pieces(c);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces(Piece pc) => Board.Pieces(pc.ColorOf(), pc.Type());
@@ -992,10 +992,10 @@ public bool PawnIsolated(Square sq, Player p)
     public BitBoard Pieces(PieceType pt1, PieceType pt2) => Board.Pieces(pt1, pt2);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard Pieces(PieceType pt, Player p) => Board.Pieces(p, pt);
+    public BitBoard Pieces(PieceType pt, Color c) => Board.Pieces(c, pt);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard Pieces(PieceType pt1, PieceType pt2, Player p) => Board.Pieces(p, pt1, pt2);
+    public BitBoard Pieces(PieceType pt1, PieceType pt2, Color c) => Board.Pieces(c, pt1, pt2);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int PieceCount() => Board.PieceCount();
@@ -1007,33 +1007,33 @@ public bool PawnIsolated(Square sq, Player p)
     public int PieceCount(PieceType pt) => Board.PieceCount(pt);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public int PieceCount(PieceType pt, Player p) => Board.PieceCount(pt, p);
+    public int PieceCount(PieceType pt, Color c) => Board.PieceCount(pt, c);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard PawnsOnColor(Player p, Square sq) => Pieces(PieceType.Pawn, p) & sq.Color().ColorBB();
+    public BitBoard PawnsOnColor(Color c, Square sq) => Pieces(PieceType.Pawn, c) & sq.Color().ColorBB();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool SemiOpenFileOn(Player p, Square sq)
-        => (Board.Pieces(p, PieceType.Pawn) & sq.File.BitBoardFile()).IsEmpty;
+    public bool SemiOpenFileOn(Color c, Square sq)
+        => (Board.Pieces(c, PieceType.Pawn) & sq.File.BitBoardFile()).IsEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool BishopPaired(Player p) =>
-        Board.PieceCount(PieceType.Bishop, p) >= 2
-        && (Board.Pieces(p, PieceType.Bishop) & Player.White.ColorBB()).IsNotEmpty
-        && (Board.Pieces(p, PieceType.Bishop) & Player.Black.ColorBB()).IsNotEmpty;
+    public bool BishopPaired(Color c) =>
+        Board.PieceCount(PieceType.Bishop, c) >= 2
+        && (Board.Pieces(c, PieceType.Bishop) & Color.White.ColorBB()).IsNotEmpty
+        && (Board.Pieces(c, PieceType.Bishop) & Color.Black.ColorBB()).IsNotEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool BishopOpposed() =>
         Board.PieceCount(Piece.WhiteBishop) == 1
         && Board.PieceCount(Piece.BlackBishop) == 1
-        && Board.Square(PieceType.Bishop, Player.White)
-                .IsOppositeColor(Board.Square(PieceType.Bishop, Player.Black));
+        && Board.Square(PieceType.Bishop, Color.White)
+                .IsOppositeColor(Board.Square(PieceType.Bishop, Color.Black));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard PinnedPieces(Player p)
+    public BitBoard PinnedPieces(Color c)
     {
         Debug.Assert(State != null);
-        return State.Pinners[p];
+        return State.Pinners[c];
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -1151,7 +1151,7 @@ public bool SeeGe(Move m, Value threshold)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Value NonPawnMaterial(Player p) => _nonPawnMaterial[p];
+    public Value NonPawnMaterial(Color c) => _nonPawnMaterial[c];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Value NonPawnMaterial() => _nonPawnMaterial[0] - _nonPawnMaterial[1];
@@ -1179,12 +1179,11 @@ private void SetupPieces(ReadOnlySpan<char> fenChunk)
                 if (pieceIndex == -1)
                     throw new InvalidFenException("Invalid char detected");
 
-                Player p = new(char.IsLower(PieceExtensions.PieceChars[pieceIndex]));
-
+                var us = new Color(char.IsLower(PieceExtensions.PieceChars[pieceIndex]));
                 var square = new Square(r - 1, f - 1);
-
                 var pt = new PieceType(pieceIndex);
-                var pc = pt.MakePiece(p);
+                var pc = pt.MakePiece(us);
+
                 AddPiece(pc, square);
 
                 f++;
@@ -1291,7 +1290,7 @@ public IPosition Set(
     public IPosition Set(string fen, ChessMode chessMode, in State state, bool validate = false, int searcher = 0)
         => Set(new FenData(fen), chessMode, state, validate, searcher);
 
-    public IPosition Set(ReadOnlySpan<char> code, Player p, in State state)
+    public IPosition Set(ReadOnlySpan<char> code, Color c, in State state)
     {
         Debug.Assert(code[0] == 'K' && code[1..].IndexOf('K') != -1);
         Debug.Assert(code.Length.IsBetween(0, 8));
@@ -1300,7 +1299,7 @@ public IPosition Set(ReadOnlySpan<char> code, Player p, in State state)
         var kingPos = code.LastIndexOf('K');
         var sides = new[] { code[kingPos..].ToString(), code[..kingPos].ToString() };
 
-        sides[p] = sides[p].ToLower();
+        sides[c] = sides[c].ToLower();
 
         var fenStr = $"{sides[0]}{8 - sides[0].Length}/8/8/8/8/8/8/{sides[1]}{8 - sides[1].Length} w - - 0 10";
 
@@ -1318,13 +1317,13 @@ public void TakeMove(Move m)
         var (from, to, type) = m;
 
         Debug.Assert(!IsOccupied(from) || m.IsCastleMove());
-        Debug.Assert(State.CapturedPiece != PieceTypes.King);
+        Debug.Assert(State.CapturedPiece != PieceType.King);
 
         if (type == MoveTypes.Promotion)
         {
             Debug.Assert(GetPiece(to).Type() == m.PromotedPieceType());
             Debug.Assert(to.RelativeRank(us) == Rank.Rank8);
-            Debug.Assert(m.PromotedPieceType() >= PieceTypes.Knight && m.PromotedPieceType() <= PieceTypes.Queen);
+            Debug.Assert(m.PromotedPieceType() >= PieceType.Knight && m.PromotedPieceType() <= PieceType.Queen);
 
             RemovePiece(to);
             var pc = PieceType.Pawn.MakePiece(us);
@@ -1341,7 +1340,7 @@ public void TakeMove(Move m)
             MovePiece(to, from);
 #pragma warning restore S2234 // Parameters should be passed in the correct order
 
-            if (State.CapturedPiece != PieceTypes.NoPieceType)
+            if (State.CapturedPiece != PieceType.NoPieceType)
             {
                 var captureSquare = to;
 
@@ -1350,7 +1349,7 @@ public void TakeMove(Move m)
                 {
                     captureSquare -= us.PawnPushDistance();
 
-                    // Debug.Assert(GetPiece(to).Type() == PieceTypes.Pawn);
+                    Debug.Assert(GetPiece(to).Type() == PieceType.Pawn);
                     Debug.Assert(to == State.Previous.EnPassantSquare);
                     Debug.Assert(to.RelativeRank(us) == Ranks.Rank6);
                     Debug.Assert(!IsOccupied(captureSquare));
@@ -1358,7 +1357,7 @@ public void TakeMove(Move m)
 
                 AddPiece(State.CapturedPiece.MakePiece(~_sideToMove), captureSquare);
 
-                if (State.CapturedPiece != PieceTypes.Pawn)
+                if (State.CapturedPiece != PieceType.Pawn)
                 {
                     var them = ~_sideToMove;
                     _nonPawnMaterial[them] += Values.GetPieceValue(State.CapturedPiece, Phases.Mg);
@@ -1385,7 +1384,7 @@ public void TakeNullMove()
         Debug.Assert(State != null);
         Debug.Assert(State.Previous != null);
         Debug.Assert(State.NullPly == 0);
-        Debug.Assert(State.CapturedPiece == PieceTypes.NoPieceType);
+        Debug.Assert(State.CapturedPiece == PieceType.NoPieceType);
         Debug.Assert(State.Checkers.IsEmpty);
 
         Debug.Assert(!InCheck);
@@ -1454,12 +1453,12 @@ public PositionValidationResult Validate(PositionValidationTypes type = Position
         => _positionValidator.Validate(this, type);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static CastleRights OrCastlingRight(Player c, bool isKingSide)
+    private static CastleRights OrCastlingRight(Color c, bool isKingSide)
         => (CastleRights)((int)CastleRights.WhiteKing << ((!isKingSide).AsByte() + 2 * c));
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     private void DoCastle(
-        Player us,
+        Color us,
         Square from,
         ref Square to,
         out Square rookFrom,
@@ -1519,7 +1518,7 @@ private void MovePiece(Square from, Square to)
     /// </summary>
     /// <param name="stm"></param>
     /// <param name="rookFrom"></param>
-    private void SetCastlingRight(Player stm, Square rookFrom)
+    private void SetCastlingRight(Color stm, Square rookFrom)
     {
         var kingFrom = GetKingSquare(stm);
         var isKingSide = kingFrom < rookFrom;
@@ -1540,10 +1539,10 @@ private void SetCastlingRight(Player stm, Square rookFrom)
 
     private void SetCheckInfo(in State state)
     {
-        (state.BlockersForKing[Player.White], state.Pinners[Player.Black]) =
-            SliderBlockers(Board.Pieces(Player.Black), GetKingSquare(Player.White));
-        (state.BlockersForKing[Player.Black], state.Pinners[Player.White]) =
-            SliderBlockers(Board.Pieces(Player.White), GetKingSquare(Player.Black));
+        (state.BlockersForKing[Color.White], state.Pinners[Color.Black]) =
+            SliderBlockers(Board.Pieces(Color.Black), GetKingSquare(Color.White));
+        (state.BlockersForKing[Color.Black], state.Pinners[Color.White]) =
+            SliderBlockers(Board.Pieces(Color.White), GetKingSquare(Color.Black));
 
         var ksq = GetKingSquare(~_sideToMove);
 
@@ -1564,7 +1563,7 @@ private void SetState(State state)
     {
         state.MaterialKey = HashKey.Empty;
 
-        _nonPawnMaterial[Player.White] = _nonPawnMaterial[Player.Black] = Value.ValueZero;
+        _nonPawnMaterial[Color.White] = _nonPawnMaterial[Color.Black] = Value.ValueZero;
         State.Checkers = AttacksTo(GetKingSquare(_sideToMove)) & Board.Pieces(~_sideToMove);
 
         SetCheckInfo(state);
@@ -1600,7 +1599,7 @@ private void SetupCastle(ReadOnlySpan<char> castle)
     {
         foreach (var ca in castle)
         {
-            Player c = char.IsLower(ca);
+            Color c = char.IsLower(ca);
             var rook = PieceType.Rook.MakePiece(c);
             var token = char.ToUpper(ca);
 
@@ -1686,7 +1685,7 @@ private bool ContainsMove(Move m)
     /// <param name="moved">flag if piece is moved or not</param>
     /// <returns>true if allows, otherwise false</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private bool CanEnPassant(Player us, Square epSquare, bool moved = true)
+    private bool CanEnPassant(Color us, Square epSquare, bool moved = true)
     {
         Debug.Assert(epSquare.IsOk);
         Debug.Assert(epSquare.RelativeRank(us) != Rank.Rank3);
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
index 168ccf82..a66622c4 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
@@ -97,13 +97,13 @@ public float Interval
 
     public static bool operator ==(HiResTimer left, int right) => left != null && left.Id == right;
 
-    public static bool operator ==(HiResTimer left, Player right) => left != null && left.Id == right;
+    public static bool operator ==(HiResTimer left, Color right) => left != null && left.Id == right;
 
     public static bool operator !=(HiResTimer left, HiResTimer right) => !Equals(left, right);
 
     public static bool operator !=(HiResTimer left, int right) => left != null && left.Id != right;
 
-    public static bool operator !=(HiResTimer left, Player right) => left != null && left.Id != right;
+    public static bool operator !=(HiResTimer left, Color right) => left != null && left.Id != right;
 
     public void Start()
     {
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs b/src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs
index bd8a961a..203940a0 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs
@@ -36,5 +36,5 @@ public interface IMovesToGoModel
 
     ulong WhiteTimeMilliseconds { get; set; }
 
-    ulong Time(Player p);
+    ulong Time(Color c);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
index 77f4a1c6..0ca9492e 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
@@ -52,13 +52,13 @@ public interface ISearchParameters : ISpanFormattable
 
     ulong WhiteTimeMilliseconds { get; set; }
 
-    ref Clock Clock(Player p);
-    
+    ref Clock Clock(Color c);
+
     void Clear();
 
-    ulong Inc(Player p);
+    ulong Inc(Color c);
 
-    ulong Time(Player p);
+    ulong Time(Color c);
 
     bool DecreaseMovesToGo();
 
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
index 549d0860..62a60d4c 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
@@ -54,5 +54,5 @@ public ulong BlackTimeMilliseconds
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ulong Time(Player p) => _time[p];
+    public ulong Time(Color c) => _time[c];
 }
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
index 332d8434..edf75656 100644
--- a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
+++ b/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
@@ -38,7 +38,7 @@ public sealed class SearchParameters() : ISearchParameters
 {
     private static readonly Clock ZeroClock = new(0UL, 0UL);
 
-    private readonly Clock[] _clock = new Clock[Player.Count];
+    private readonly Clock[] _clock = new Clock[Color.Count];
     private          ulong   _movesToGo;
     private          ulong   _moveTime;
 
@@ -98,43 +98,43 @@ public ulong MovesToGo
 
     public ulong WhiteTimeMilliseconds
     {
-        get => _clock[Player.White].Time;
-        set => _clock[Player.White].Time = value;
+        get => _clock[Color.White].Time;
+        set => _clock[Color.White].Time = value;
     }
 
     public ulong BlackTimeMilliseconds
     {
-        get => _clock[Player.Black].Time;
-        set => _clock[Player.Black].Time = value;
+        get => _clock[Color.Black].Time;
+        set => _clock[Color.Black].Time = value;
     }
 
     public ulong WhiteIncrementTimeMilliseconds
     {
-        get => _clock[Player.White].Inc;
-        set => _clock[Player.White].Inc = value;
+        get => _clock[Color.White].Inc;
+        set => _clock[Color.White].Inc = value;
     }
 
     public ulong BlackIncrementTimeMilliseconds
     {
-        get => _clock[Player.Black].Inc;
-        set => _clock[Player.Black].Inc = value;
+        get => _clock[Color.Black].Inc;
+        set => _clock[Color.Black].Inc = value;
     }
 
-    public ref Clock Clock(Player p)
+    public ref Clock Clock(Color c)
     {
-        return ref _clock[p];
+        return ref _clock[c];
     }
 
     public bool UseTimeManagement()
     {
-        return _clock[Player.White].Time != 0 && _clock[Player.Black].Time != 0;
+        return _clock[Color.White].Time != 0 && _clock[Color.Black].Time != 0;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ulong Time(Player p) => _clock[p].Time;
+    public ulong Time(Color c) => _clock[c].Time;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public ulong Inc(Player p) => _clock[p].Inc;
+    public ulong Inc(Color c) => _clock[c].Inc;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Clear()
@@ -193,7 +193,7 @@ public bool TryFormat(
         destination[index++] = 'e';
         destination[index++] = ' ';
 
-        _clock[Player.White].Time.TryFormat(destination[index..], out var written);
+        _clock[Color.White].Time.TryFormat(destination[index..], out var written);
         index += written;
 
         destination[index++] = ' ';
@@ -204,7 +204,7 @@ public bool TryFormat(
         destination[index++] = 'e';
         destination[index++] = ' ';
 
-        _clock[Player.Black].Time.TryFormat(destination[index..], out written);
+        _clock[Color.Black].Time.TryFormat(destination[index..], out written);
         index += written;
 
         if (MoveTime > ulong.MinValue)
@@ -225,7 +225,7 @@ public bool TryFormat(
         destination[index++] = 'c';
         destination[index++] = ' ';
 
-        _clock[Player.White].Inc.TryFormat(destination[index..], out written);
+        _clock[Color.White].Inc.TryFormat(destination[index..], out written);
         index += written;
 
         destination[index++] = ' ';
@@ -235,7 +235,7 @@ public bool TryFormat(
         destination[index++] = 'c';
         destination[index++] = ' ';
 
-        _clock[Player.Black].Inc.TryFormat(destination[index..], out written);
+        _clock[Color.Black].Inc.TryFormat(destination[index..], out written);
         index += written;
 
         if (_movesToGo == ulong.MinValue)
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 07d2e9b1..8d5ff0fb 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <TargetFramework>net8.0</TargetFramework>
         <Platforms>AnyCPU</Platforms>
         <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -28,13 +27,13 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
-        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.2" />
-        <PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
-        <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Json"/>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions"/>
+        <PackageReference Include="Microsoft.Extensions.ObjectPool"/>
+        <PackageReference Include="Microsoft.Extensions.Options"/>
+        <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions"/>
     </ItemGroup>
     <ItemGroup>
         <Folder Include="Protocol\"/>
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/Rudzoft.ChessLib/State.cs
index d368ab74..e90227d4 100644
--- a/src/Rudzoft.ChessLib/State.cs
+++ b/src/Rudzoft.ChessLib/State.cs
@@ -106,7 +106,7 @@ public void Clear()
         CheckedSquares.Fill(BitBoard.Empty);
         Pinners[0] = Pinners[1] = BitBoard.Empty;
         BlockersForKing[0] = BlockersForKing[1] = BitBoard.Empty;
-        CapturedPiece = PieceTypes.NoPieceType;
+        CapturedPiece = PieceType.NoPieceType;
         Previous = null;
     }
 
@@ -136,17 +136,17 @@ public void UpdateRepetition()
     public bool Equals(State other)
     {
         if (other is null) return false;
-        return LastMove.Equals(other.LastMove)
-               && PositionKey.Equals(other.PositionKey)
-               && PawnKey.Equals(other.PawnKey)
-               && EnPassantSquare.Equals(other.EnPassantSquare)
-               && CastleRights == other.CastleRights
-               && NullPly == other.NullPly
-               && ClockPly == other.ClockPly
-               && Pinners.Equals(other.Pinners)
-               && Checkers.Equals(other.Checkers)
-               && CapturedPiece == other.CapturedPiece
-               && Equals(Previous, other.Previous);
+        return LastMove == other.LastMove
+            && PositionKey == other.PositionKey
+            && PawnKey == other.PawnKey
+            && EnPassantSquare == other.EnPassantSquare
+            && CastleRights == other.CastleRights
+            && NullPly == other.NullPly
+            && ClockPly == other.ClockPly
+            && Pinners == other.Pinners
+            && Checkers == other.Checkers
+            && CapturedPiece == other.CapturedPiece
+            && Equals(Previous, other.Previous);
     }
 
     public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is State other && Equals(other);
@@ -155,16 +155,16 @@ public override int GetHashCode()
     {
         var hashCode = new HashCode();
         hashCode.Add(LastMove);
-        hashCode.Add(PawnKey);
-        hashCode.Add(MaterialKey);
+        hashCode.Add(PawnKey.Key);
+        hashCode.Add(MaterialKey.Key);
         hashCode.Add(NullPly);
         hashCode.Add(ClockPly);
-        hashCode.Add(PositionKey);
-        hashCode.Add(CastleRights.Rights.AsInt());
-        hashCode.Add(EnPassantSquare);
-        hashCode.Add(Checkers);
+        hashCode.Add(PositionKey.Key);
+        hashCode.Add(CastleRights.Rights);
+        hashCode.Add(EnPassantSquare.Value);
+        hashCode.Add(Checkers.Value);
         hashCode.Add(Previous);
-        hashCode.Add(CapturedPiece);
+        hashCode.Add(CapturedPiece.Value);
         hashCode.Add(Pinners);
         hashCode.Add(CheckedSquares);
         return hashCode.ToHashCode();
diff --git a/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs b/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
index 6639f8c6..dac7667a 100644
--- a/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
+++ b/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
@@ -35,33 +35,33 @@ public sealed class HistoryHeuristic : IHistoryHeuristic
 
     public HistoryHeuristic()
     {
-        _table = new int[Player.Count][][];
-        Initialize(Player.White);
-        Initialize(Player.Black);
+        _table = new int[Color.Count][][];
+        Initialize(Color.White);
+        Initialize(Color.Black);
     }
 
     public void Clear()
     {
-        ClearTable(Player.White);
-        ClearTable(Player.Black);
+        ClearTable(Color.White);
+        ClearTable(Color.Black);
     }
 
-    public void Set(Player p, Square from, Square to, int value)
-        => _table[p][from][to] = value;
+    public void Set(Color c, Square from, Square to, int value)
+        => _table[c][from][to] = value;
 
-    public int Retrieve(Player p, Square from, Square to)
-        => _table[p][from][to];
+    public int Retrieve(Color c, Square from, Square to)
+        => _table[c][from][to];
 
-    private void Initialize(Player p)
+    private void Initialize(Color c)
     {
-        _table[p] = new int[Square.Count][];
-        for (var i = 0; i < _table[p].Length; ++i)
-            _table[p][i] = new int[Square.Count];
+        _table[c] = new int[Square.Count][];
+        for (var i = 0; i < _table[c].Length; ++i)
+            _table[c][i] = new int[Square.Count];
     }
 
-    private void ClearTable(Player p)
+    private void ClearTable(Color c)
     {
-        for (var i = 0; i < _table[p].Length; i++)
-            _table[p][i].Clear();
+        for (var i = 0; i < _table[c].Length; i++)
+            _table[c][i].Clear();
     }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs b/src/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs
index b0b6411d..a5d43572 100644
--- a/src/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs
+++ b/src/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs
@@ -32,7 +32,7 @@ public interface IHistoryHeuristic
 {
     void Clear();
 
-    void Set(Player p, Square from, Square to, int value);
+    void Set(Color c, Square from, Square to, int value);
 
-    int Retrieve(Player p, Square from, Square to);
+    int Retrieve(Color c, Square from, Square to);
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/Rudzoft.ChessLib/Types/BitBoard.cs
index f24708e7..1968544e 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoard.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoard.cs
@@ -199,8 +199,7 @@ public BitBoard OrAll(ReadOnlySpan<Square> sqs)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool MoreThanOne()
     {
-        var v = Value;
-        return (v & (v - 1)) > 0;
+        return (Value & (Value - 1)) > 0;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 1caf5401..347b8ffe 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -150,15 +150,15 @@ public static class BitBoards
     /// </summary>
     private static readonly BitBoard[][] PseudoAttacksBB = new BitBoard[PieceType.Count][];
 
-    private static readonly BitBoard[][] PawnAttackSpanBB = new BitBoard[Player.Count][];
+    private static readonly BitBoard[][] PawnAttackSpanBB = new BitBoard[Color.Count][];
 
-    private static readonly BitBoard[][] PassedPawnMaskBB = new BitBoard[Player.Count][];
+    private static readonly BitBoard[][] PassedPawnMaskBB = new BitBoard[Color.Count][];
 
-    private static readonly BitBoard[][] ForwardRanksBB = new BitBoard[Player.Count][];
+    private static readonly BitBoard[][] ForwardRanksBB = new BitBoard[Color.Count][];
 
-    private static readonly BitBoard[][] ForwardFileBB = new BitBoard[Player.Count][];
+    private static readonly BitBoard[][] ForwardFileBB = new BitBoard[Color.Count][];
 
-    private static readonly BitBoard[][] KingRingBB = new BitBoard[Player.Count][];
+    private static readonly BitBoard[][] KingRingBB = new BitBoard[Color.Count][];
 
     private static readonly BitBoard[][] BetweenBB = new BitBoard[Square.Count][];
 
@@ -174,7 +174,12 @@ public static class BitBoards
 
     private static readonly BitBoard[][] DistanceRingBB = new BitBoard[Square.Count][];
 
-    private static readonly BitBoard[] SlotFileBB;
+    private static readonly BitBoard[] SlotFileBB =
+    [
+        FileEBB | FileFBB | FileGBB | FileHBB, // King
+        FileABB | FileBBB | FileCBB | FileDBB, // Queen
+        FileCBB | FileDBB | FileEBB | FileFBB  // Center
+    ];
 
     private static readonly Direction[] PawnPushDirections = [Direction.North, Direction.South];
 
@@ -201,87 +206,69 @@ static BitBoards()
         KingRingBB[1] = new BitBoard[Square.Count];
 
         for (var i = 0; i < BetweenBB.Length; i++)
+        {
             BetweenBB[i] = new BitBoard[Square.Count];
-
-        for (var i = 0; i < LineBB.Length; i++)
             LineBB[i] = new BitBoard[Square.Count];
+        }
 
         // ForwardRanksBB population loop idea from sf
-        for (var r = Rank.Rank1; r <= Rank.Rank8; r++)
-        {
+        foreach (var r in Rank.All)
             ForwardRanksBB[0][r] = ~(ForwardRanksBB[1][r + 1] = ForwardRanksBB[1][r] | r.BitBoardRank());
-        }
 
-        foreach (var p in Player.AllPlayers.AsSpan())
+        foreach (var c in Color.AllColors.AsSpan())
         {
             foreach (var sq in Square.All.AsSpan())
             {
                 var file = sq.File;
-                ForwardFileBB[p][sq] = ForwardRanksBB[p][sq.Rank] & file.BitBoardFile();
-                PawnAttackSpanBB[p][sq] = ForwardRanksBB[p][sq.Rank] & AdjacentFilesBB[file];
-                PassedPawnMaskBB[p][sq] = ForwardFileBB[p][sq] | PawnAttackSpanBB[p][sq];
+                ForwardFileBB[c][sq] = ForwardRanksBB[c][sq.Rank] & file.BitBoardFile();
+                PawnAttackSpanBB[c][sq] = ForwardRanksBB[c][sq.Rank] & AdjacentFilesBB[file];
+                PassedPawnMaskBB[c][sq] = ForwardFileBB[c][sq] | PawnAttackSpanBB[c][sq];
             }
         }
 
         // have to compute here before we access the BitBoards
-        for (var s1 = Squares.a1; s1 <= Squares.h8; s1++)
+        foreach (var sq1 in Square.All.AsSpan())
         {
-            SquareDistance[s1.AsInt()] = new int[64];
-            DistanceRingBB[s1.AsInt()] = new BitBoard[8];
-            for (var s2 = Squares.a1; s2 <= Squares.h8; s2++)
+            SquareDistance[sq1] = new int[Square.Count];
+            DistanceRingBB[sq1] = new BitBoard[8];
+            foreach (var sq2 in Square.All.AsSpan())
             {
-                var dist = Math.Max(distanceFile(s1, s2), distanceRank(s1, s2));
-                SquareDistance[s1.AsInt()][s2.AsInt()] = dist;
-                DistanceRingBB[s1.AsInt()][dist] |= s2;
+                var dist = Math.Max(distanceFile(sq1, sq2), distanceRank(sq1, sq2));
+                SquareDistance[sq1][sq2] = dist;
+                DistanceRingBB[sq1][dist] |= sq2;
             }
         }
 
         // mini local helpers
         Span<PieceType> validMagicPieces = stackalloc PieceType[] { PieceType.Bishop, PieceType.Rook };
 
-        var bb = AllSquares;
         // Pseudo attacks for all pieces
-        while (bb)
+        foreach (var sq1 in Square.All)
         {
-            var sq = PopLsb(ref bb);
-
-            InitializePseudoAttacks(sq);
+            InitializePseudoAttacks(sq1);
 
             // Compute lines and betweens
-            foreach (var validMagicPiece in validMagicPieces)
+            foreach (var pt in validMagicPieces)
             {
-                var pt = validMagicPiece;
-                var bb3 = AllSquares;
-                while (bb3)
+                foreach (var sq2 in Square.All)
                 {
-                    var s2 = PopLsb(ref bb3);
-                    if ((PseudoAttacksBB[pt][sq] & s2).IsEmpty)
+                    if ((PseudoAttacksBB[pt][sq1] & sq2).IsEmpty)
                         continue;
 
-                    var sq2 = s2.AsInt();
-
-                    LineBB[sq][sq2] = (GetAttacks(sq, validMagicPiece, EmptyBitBoard) &
-                                       GetAttacks(s2, validMagicPiece, EmptyBitBoard)) | sq | s2;
-                    BetweenBB[sq][sq2] = GetAttacks(sq, validMagicPiece, BbSquares[sq2]) &
-                                         GetAttacks(s2, validMagicPiece, BbSquares[sq]);
+                    LineBB[sq1][sq2] = (GetAttacks(sq1, pt, EmptyBitBoard) &
+                                      GetAttacks(sq2, pt, EmptyBitBoard)) | sq1 | sq2;
+                    BetweenBB[sq1][sq2] = GetAttacks(sq1, pt, BbSquares[sq2]) &
+                                        GetAttacks(sq2, pt, BbSquares[sq1]);
                 }
             }
 
             // Compute KingRings
-            InitializeKingRing(sq);
+            InitializeKingRing(sq1);
         }
 
-        SlotFileBB =
-        [
-            FileEBB | FileFBB | FileGBB | FileHBB, // King
-            FileABB | FileBBB | FileCBB | FileDBB, // Queen
-            FileCBB | FileDBB | FileEBB | FileFBB  // Center
-        ];
-
         return;
 
         static int distanceRank(Square x, Square y) => distance(x.Rank, y.Rank);
-
         static int distanceFile(Square x, Square y) => distance(x.File, y.File);
 
         // local helper functions to calculate distance
@@ -315,29 +302,26 @@ private static void InitializePseudoAttacks(Square sq)
         PseudoAttacksBB[PieceType.Bishop][sq] = bishopAttacks;
         PseudoAttacksBB[PieceType.Rook][sq] = rookAttacks;
         PseudoAttacksBB[PieceType.Queen][sq] = bishopAttacks | rookAttacks;
-        PseudoAttacksBB[PieceType.King][sq] = b.NorthOne() | b.SouthOne() | b.EastOne()
-                                              | b.WestOne() | b.NorthEastOne() | b.NorthWestOne()
-                                              | b.SouthEastOne() | b.SouthWestOne();
+        PseudoAttacksBB[PieceType.King][sq] = b.NorthOne() | b.SouthOne() | b.EastOne() | b.WestOne() |
+                                              PseudoAttacksBB[0][sq] | PseudoAttacksBB[1][sq];
     }
 
     private static void InitializeKingRing(Square sq)
     {
-        const int pt = (int)PieceTypes.King;
         var file = sq.File;
 
-        // TODO : Change to basic for-loop
-        foreach (var p in Player.AllPlayers)
+        foreach (var c in Color.AllColors)
         {
-            KingRingBB[p][sq] = PseudoAttacksBB[pt][sq];
-            if (sq.RelativeRank(p) == Ranks.Rank1)
-                KingRingBB[p][sq] |= KingRingBB[p][sq].Shift(PawnPushDirections[p]);
+            KingRingBB[c][sq] = PseudoAttacksBB[PieceType.King][sq];
+            if (sq.RelativeRank(c) == Ranks.Rank1)
+                KingRingBB[c][sq] |= KingRingBB[c][sq].Shift(PawnPushDirections[c]);
 
             if (file == Files.FileH)
-                KingRingBB[p][sq] |= KingRingBB[p][sq].WestOne();
+                KingRingBB[c][sq] |= KingRingBB[c][sq].WestOne();
             else if (file == Files.FileA)
-                KingRingBB[p][sq] |= KingRingBB[p][sq].EastOne();
+                KingRingBB[c][sq] |= KingRingBB[c][sq].EastOne();
 
-            Debug.Assert(!KingRingBB[p.Side][sq].IsEmpty);
+            Debug.Assert(!KingRingBB[c][sq].IsEmpty);
         }
     }
 
@@ -357,22 +341,22 @@ public static BitBoard FileBB(File first, File last)
     public static BitBoard RankBB(this Rank r) => RanksBB[r];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ColorBB(this Player p) => ColorsBB[p];
+    public static BitBoard ColorBB(this Color c) => ColorsBB[c];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard FirstRank(Player p) => Ranks1[p];
+    public static BitBoard FirstRank(Color c) => Ranks1[c];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ThirdRank(Player p) => Ranks3BB[p];
+    public static BitBoard ThirdRank(Color c) => Ranks3BB[c];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SeventhRank(Player p) => Ranks7BB[p];
+    public static BitBoard SeventhRank(Color c) => Ranks7BB[c];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SixthAndSeventhRank(Player p) => Ranks6And7BB[p];
+    public static BitBoard SixthAndSeventhRank(Color c) => Ranks6And7BB[c];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SeventhAndEightsRank(Player p) => Ranks7And8BB[p];
+    public static BitBoard SeventhAndEightsRank(Color c) => Ranks7And8BB[c];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard PseudoAttacks(this PieceType pt, Square sq) => PseudoAttacksBB[pt][sq];
@@ -387,10 +371,10 @@ public static BitBoard FileBB(File first, File last)
     /// Attack for pawn.
     /// </summary>
     /// <param name="sq">The square</param>
-    /// <param name="p">The player side</param>
+    /// <param name="c">The player side</param>
     /// <returns>ref to bitboard of attack</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnAttack(this Square sq, Player p) => PseudoAttacksBB[p][sq];
+    public static BitBoard PawnAttack(this Square sq, Color c) => PseudoAttacksBB[c][sq];
 
     /// <summary>
     /// Returns the bitboard representation of the rank of which the square is located.
@@ -427,36 +411,36 @@ public static BitBoard FileBB(File first, File last)
     /// <summary>
     /// Returns all squares in front of the square in the same file as bitboard
     /// </summary>
-    /// <param name="sq">The square</param>
-    /// <param name="p">The side, white is north and black is south</param>
+    /// <param name="s">The square</param>
+    /// <param name="c">The side, white is north and black is south</param>
     /// <returns>The bitboard of all forward file squares</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ForwardFile(this Square sq, Player p) => ForwardFileBB[p][sq];
+    public static BitBoard ForwardFile(this Square s, Color c) => ForwardFileBB[c][s];
 
     /// <summary>
     /// Returns all squares in pawn attack pattern in front of the square.
     /// </summary>
-    /// <param name="sq">The square</param>
-    /// <param name="p">White = north, Black = south</param>
+    /// <param name="s">The square</param>
+    /// <param name="c">White = north, Black = south</param>
     /// <returns>The bitboard representation</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnAttackSpan(this Square sq, Player p) => PawnAttackSpanBB[p][sq];
+    public static BitBoard PawnAttackSpan(this Square s, Color c) => PawnAttackSpanBB[c][s];
 
     /// <summary>
     /// Returns all square of both file and pawn attack pattern in front of square. This is the
     /// same as ForwardFile() | PawnAttackSpan().
     /// </summary>
-    /// <param name="sq">The square</param>
-    /// <param name="p">White = north, Black = south</param>
+    /// <param name="s">The square</param>
+    /// <param name="c">White = north, Black = south</param>
     /// <returns>The bitboard representation</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PassedPawnFrontAttackSpan(this Square sq, Player p) => PassedPawnMaskBB[p][sq];
+    public static BitBoard PassedPawnFrontAttackSpan(this Square s, Color c) => PassedPawnMaskBB[c][s];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard ForwardRanks(this Square sq, Player p) => ForwardRanksBB[p][sq];
+    public static BitBoard ForwardRanks(this Square s, Color c) => ForwardRanksBB[c][s];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard BitboardBetween(this Square sq1, Square sq2) => BetweenBB[sq1][sq2];
+    public static BitBoard BitboardBetween(this Square s1, Square s2) => BetweenBB[s1][s2];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Square Get(this in BitBoard bb, int pos) => (int)(bb.Value >> pos) & 0x1;
@@ -474,7 +458,7 @@ public static BitBoard FileBB(File first, File last)
     public static BitBoard Line(this Square sq1, Square sq2) => LineBB[sq1][sq2];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SlotFile(CastleSides cs) => SlotFileBB[cs.AsInt()];
+    public static BitBoard SlotFile(CastleSide cs) => SlotFileBB[cs];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static BitBoard AdjacentFiles(File f) => AdjacentFilesBB[f];
@@ -483,11 +467,11 @@ public static BitBoard FileBB(File first, File last)
     public static bool Aligned(this Square sq1, Square sq2, Square sq3) => (Line(sq1, sq2) & sq3).IsNotEmpty;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard FrontSquares(this Player p, Square sq)
-        => ForwardRanksBB[p][sq] & sq.BitBoardFile();
+    public static BitBoard FrontSquares(this Color c, Square sq)
+        => ForwardRanksBB[c][sq] & sq.BitBoardFile();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard KingRing(this Square sq, Player p) => KingRingBB[p][sq];
+    public static BitBoard KingRing(this Square sq, Color c) => KingRingBB[c][sq];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static int Distance(this Square sq1, Square sq2) => SquareDistance[sq1][sq2];
@@ -496,7 +480,7 @@ public static BitBoard FrontSquares(this Player p, Square sq)
     public static BitBoard DistanceRing(this Square sq, int length) => DistanceRingBB[sq][length];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PromotionRank(this Player p) => PromotionRanks[p];
+    public static BitBoard PromotionRank(this Color c) => PromotionRanks[c];
 
     public static string StringifyRaw(in ulong bb, string title = "") => Stringify(BitBoard.Create(bb), title);
 
@@ -568,7 +552,7 @@ public static string Stringify(in BitBoard bb, string title = "")
     public static Square Msb(this in BitBoard bb) => new(63 ^ BitOperations.LeadingZeroCount(bb.Value));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square FrontMostSquare(in BitBoard bb, Player p) => p.IsWhite ? Lsb(in bb) : Msb(in bb);
+    public static Square FrontMostSquare(in BitBoard bb, Color c) => c.IsWhite ? Lsb(in bb) : Msb(in bb);
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     public static BitBoard NorthOne(this BitBoard bb) => bb << 8;
@@ -616,10 +600,10 @@ public static BitBoard SouthFill(this BitBoard bb)
     /// Shorthand method for north or south fill of bitboard depending on color
     /// </summary>
     /// <param name="bb">The bitboard to fill</param>
-    /// <param name="p">The direction to fill in, white = north, black = south</param>
+    /// <param name="c">The direction to fill in, white = north, black = south</param>
     /// <returns>Filled bitboard</returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Fill(this in BitBoard bb, Player p) => FillFuncs[p](bb);
+    public static BitBoard Fill(this in BitBoard bb, Color c) => FillFuncs[c](bb);
 
     [MethodImpl(MethodImplOptions.AggressiveOptimization)]
     public static BitBoard Shift(this in BitBoard bb, Direction d)
@@ -654,24 +638,24 @@ public static BitBoard Shift(this in BitBoard bb, Direction d)
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnEastAttack(this in BitBoard bb, Player p) => Shift(in bb, p.PawnEastAttackDistance());
+    public static BitBoard PawnEastAttack(this in BitBoard bb, Color c) => Shift(in bb, c.PawnEastAttackDistance());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnWestAttack(this in BitBoard bb, Player p) => Shift(in bb, p.PawnWestAttackDistance());
+    public static BitBoard PawnWestAttack(this in BitBoard bb, Color c) => Shift(in bb, c.PawnWestAttackDistance());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnAttacks(this in BitBoard bb, Player p)
-        => PawnEastAttack(in bb, p) | PawnWestAttack(in bb, p);
+    public static BitBoard PawnAttacks(this in BitBoard bb, Color c)
+        => PawnEastAttack(in bb, c) | PawnWestAttack(in bb, c);
 
     /// <summary>
     /// Compute all attack squares where two pawns attack the square
     /// </summary>
     /// <param name="bb">The squares to compute attacks from</param>
-    /// <param name="p">The side</param>
+    /// <param name="c">The side</param>
     /// <returns></returns>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard PawnDoubleAttacks(this in BitBoard bb, Player p)
-        => PawnEastAttack(in bb, p) & PawnWestAttack(in bb, p);
+    public static BitBoard PawnDoubleAttacks(this in BitBoard bb, Color c)
+        => PawnEastAttack(in bb, c) & PawnWestAttack(in bb, c);
 
     /// <summary>
     /// Reset the least significant bit in-place
@@ -712,10 +696,10 @@ public static int PopLsb(ref int v)
     public static int PopCount(in BitBoard bb) => BitOperations.PopCount(bb.Value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Rank7(this Player p) => Ranks7BB[p];
+    public static BitBoard Rank7(this Color c) => Ranks7BB[c];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard Rank3(this Player p) => Ranks3BB[p];
+    public static BitBoard Rank3(this Color c) => Ranks3BB[c];
 
     /// <summary>
     /// Generate a bitboard based on a square.
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/Rudzoft.ChessLib/Types/CastleRight.cs
index 83b8c147..639821be 100644
--- a/src/Rudzoft.ChessLib/Types/CastleRight.cs
+++ b/src/Rudzoft.ChessLib/Types/CastleRight.cs
@@ -29,38 +29,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 namespace Rudzoft.ChessLib.Types;
 
 [Flags]
-public enum CastleRights
+public enum CastleRights : byte
 {
-    None = 0,                           // 0000
-    WhiteKing = 1,                      // 0001
-    WhiteQueen = WhiteKing << 1,        // 0010
-    BlackKing = WhiteKing << 2,         // 0100
-    BlackQueen = WhiteKing << 3,        // 1000
-
-    King = WhiteKing | BlackKing,       // 0101
-    Queen = WhiteQueen | BlackQueen,    // 1010
-    White = WhiteKing | WhiteQueen,     // 0011
-    Black = BlackKing | BlackQueen,     // 1100
-    Any = White | Black,                // 1111
+    None = 0,                    // 0000
+    WhiteKing = 1,               // 0001
+    WhiteQueen = WhiteKing << 1, // 0010
+    BlackKing = WhiteKing << 2,  // 0100
+    BlackQueen = WhiteKing << 3, // 1000
+
+    King = WhiteKing | BlackKing,    // 0101
+    Queen = WhiteQueen | BlackQueen, // 1010
+    White = WhiteKing | WhiteQueen,  // 0011
+    Black = BlackKing | BlackQueen,  // 1100
+    Any = White | Black,             // 1111
 
     Count = 16
 }
 
-public enum CastleSides
+public enum CastleSides : byte
 {
     King = 0,
     Queen = 1,
-    Center = 2,
-    Count = 3
-}
-
-public static class CastleSidesExtensions
-{
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int AsInt(this CastleSides cs) => (int)cs;
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard SlotFile(this CastleSides cs) => BitBoards.SlotFile(cs);
+    Center = 2
 }
 
 public enum CastlePerform
@@ -74,9 +64,6 @@ public static class CastleExtensions
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static string GetCastleString(Square toSquare, Square fromSquare) => toSquare < fromSquare ? "O-O-O" : "O-O";
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool HasFlagFast(this CastleRights value, CastleRights flag) => (value & flag) != 0;
-
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static int AsInt(this CastleRights value) => (int)value;
 
@@ -84,10 +71,10 @@ public static class CastleExtensions
     public static CastleRights Without(this CastleRights @this, CastleRights remove) => @this & ~remove;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRights MakeCastleRights(this CastleRights cs, Player p)
+    public static CastleRights MakeCastleRights(this CastleRights cs, Color c)
     {
         var isQueen = cs == CastleRights.Queen;
-        if (p.IsWhite)
+        if (c.IsWhite)
             return isQueen
                 ? CastleRights.WhiteQueen
                 : CastleRights.WhiteKing;
@@ -97,6 +84,31 @@ public static CastleRights MakeCastleRights(this CastleRights cs, Player p)
     }
 }
 
+public readonly record struct CastleSide(CastleSides Sides)
+{
+    public static CastleSide King { get; } = new(CastleSides.King);
+    public static CastleSide Queen { get; } = new(CastleSides.Queen);
+    public static CastleSide Center { get; } = new(CastleSides.Center);
+
+    public static int Count => 3;
+
+    public BitBoard SlotFile() => BitBoards.SlotFile(this);
+
+    public static CastleSide[] AllCastleSides =>
+    [
+        King, Queen, Center
+    ];
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator ==(CastleSide left, CastleSides right) => left.Sides == right;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool operator !=(CastleSide left, CastleSides right) => left.Sides != right;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator byte(CastleSide r) => (byte)r.Sides;
+}
+
 public readonly record struct CastleRight(CastleRights Rights)
 {
     private CastleRight(int cr) : this((CastleRights)cr) { }
@@ -114,16 +126,21 @@ private CastleRight(int cr) : this((CastleRights)cr) { }
     public static CastleRight Black { get; } = new(CastleRights.Black);
     public static CastleRight Any { get; } = new(CastleRights.Any);
 
+    public static CastleRight[] AllCastleRight =
+    [
+        None, WhiteKing, BlackKing, WhiteQueen, BlackQueen, King, Queen, White, Black, Any
+    ];
+
     public const int Count = (int)CastleRights.Count;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator CastleRight(CastleRights cr) => new(cr);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator CastleRight(Player p) => Create(p);
+    public static implicit operator CastleRight(Color c) => Create(c);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static CastleRight Create(Player p) => new((CastleRights)((int)CastleRights.White << (p << 1)));
+    public static CastleRight Create(Color c) => new((CastleRights)((int)CastleRights.White << (c << 1)));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Equals(CastleRight other) => Rights == other.Rights;
@@ -159,10 +176,10 @@ private CastleRight(int cr) : this((CastleRights)cr) { }
     public static CastleRight operator ~(CastleRight cr) => new(~cr.Rights);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Has(CastleRights cr) => Rights.HasFlagFast(cr);
+    public bool Has(CastleRights cr) => (Rights & cr) != 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Has(CastleRight cr) => Rights.HasFlagFast(cr.Rights);
+    public bool Has(CastleRight cr) => (Rights & cr.Rights) != 0;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public CastleRight Not(CastleRights cr) => new(Rights & ~cr);
diff --git a/src/Rudzoft.ChessLib/Types/Player.cs b/src/Rudzoft.ChessLib/Types/Color.cs
similarity index 75%
rename from src/Rudzoft.ChessLib/Types/Player.cs
rename to src/Rudzoft.ChessLib/Types/Color.cs
index c9821cd0..ddfd9c0d 100644
--- a/src/Rudzoft.ChessLib/Types/Player.cs
+++ b/src/Rudzoft.ChessLib/Types/Color.cs
@@ -30,7 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Types;
 
-public enum Players
+public enum Colors
 {
     White = 0,
     Black = 1,
@@ -43,29 +43,23 @@ public enum PlayerTypes
     Human = 1
 }
 
-public readonly record struct Player(byte Side) : ISpanFormattable, IMinMaxValue<Player>
+public readonly record struct Color(byte Side) : ISpanFormattable, IMinMaxValue<Color>
 {
     private static readonly Direction[] PawnPushDist = [Direction.North, Direction.South];
-
     private static readonly Direction[] PawnDoublePushDist = [Direction.NorthDouble, Direction.SouthDouble];
-
     private static readonly Direction[] PawnWestAttackDist = [Direction.NorthEast, Direction.SouthEast];
-
     private static readonly Direction[] PawnEastAttackDist = [Direction.NorthWest, Direction.SouthWest];
 
     private static readonly string[] PlayerColors = ["White", "Black"];
-
     private static readonly char[] PlayerFen = ['w', 'b'];
-
     private static readonly Func<BitBoard, BitBoard>[] PawnPushModifiers = [BitBoards.NorthOne, BitBoards.SouthOne];
-
     private static readonly int[] ScoreSign = [1, -1];
 
-    public Player(Player p)
-        : this(p.Side) { }
+    public Color(Color c)
+        : this(c.Side) { }
 
-    public Player(Players p)
-        : this((byte)p) { }
+    public Color(Colors c)
+        : this((byte)c) { }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Deconstruct(out byte side) => side = Side;
@@ -74,49 +68,49 @@ public Player(Players p)
     public        bool     IsBlack    => Side != byte.MinValue;
     public        int      Sign       => ScoreSign[Side];
     public        char     Fen        => PlayerFen[Side];
-    public static Player   White      { get; } = new(Players.White);
-    public static Player   Black      { get; } = new(Players.Black);
-    public static Player[] AllPlayers { get; } = [White, Black];
-    public static Player   MaxValue   => White;
-    public static Player   MinValue   => Black;
+    public static Color   White      { get; } = new(Colors.White);
+    public static Color   Black      { get; } = new(Colors.Black);
+    public static Color[] AllColors { get; } = [White, Black];
+    public static Color   MaxValue   => White;
+    public static Color   MinValue   => Black;
 
-    public const int Count = (int)Players.PlayerNb;
+    public const int Count = (int)Colors.PlayerNb;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(int value) => new((byte)value);
+    public static implicit operator Color(int value) => new((byte)value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(byte value) => new(value);
+    public static implicit operator Color(byte value) => new(value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(uint value) => new((byte)value);
+    public static implicit operator Color(uint value) => new((byte)value);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(Players p) => new(p);
+    public static implicit operator Color(Colors c) => new(c);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator Player(bool value) => new(value.AsByte());
+    public static implicit operator Color(bool value) => new(value.AsByte());
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Player Create(Players p) => new(p);
+    public static Color Create(Colors c) => new(c);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Player operator ~(Player p) => new(p ^ 1);
+    public static Color operator ~(Color c) => new(c.Side ^ 1);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator <<(Player left, int right) => left.Side << right;
+    public static int operator <<(Color left, int right) => left.Side << right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static int operator >>(Player left, int right) => left.Side >> right;
+    public static int operator >>(Color left, int right) => left.Side >> right;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Pieces operator +(PieceTypes pieceType, Player side) => (Pieces)pieceType + (byte)(side << 3);
+    public static Piece operator +(PieceType pt, Color c) => new((Pieces)(pt.Value + (byte)(c << 3)));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator byte(Player p) => p.Side;
+    public static implicit operator byte(Color c) => c.Side;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool Equals(Player other) => Side == other.Side;
+    public bool Equals(Color c) => Side == c.Side;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override int GetHashCode() => Side << 24;
@@ -154,5 +148,5 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
     public Direction PawnEastAttackDistance() => PawnEastAttackDist[Side];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public BitBoard PawnPush(BitBoard bb) => PawnPushModifiers[Side](bb);
+    public BitBoard PawnPush(in BitBoard bb) => PawnPushModifiers[Side](bb);
 }
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index db1758c2..664716ed 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -167,7 +167,7 @@ public File(File f) : this(f.Value)
     public static bool operator false(File f) => !f.IsOk;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator int(File f) => (int)f.Value;
+    public static implicit operator byte(File f) => (byte)f.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int AsInt() => (int)Value;
diff --git a/src/Rudzoft.ChessLib/Types/HashKey.cs b/src/Rudzoft.ChessLib/Types/HashKey.cs
index a6c28673..b44d0991 100644
--- a/src/Rudzoft.ChessLib/Types/HashKey.cs
+++ b/src/Rudzoft.ChessLib/Types/HashKey.cs
@@ -51,7 +51,7 @@ private HashKey(uint key32)
     [field: FieldOffset(4)] public uint UpperKey { get; }
 
     public bool IsEmpty => Key == 0;
-    
+
     public static HashKey Empty => new();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -75,6 +75,9 @@ private HashKey(uint key32)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static HashKey operator &(HashKey left, int right) => new(left.Key & (ulong)right);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static HashKey operator &(HashKey left, ulong right) => new(left.Key & (ulong)right);
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static HashKey operator ^(HashKey left, int right) => new(left.Key ^ (ulong)right);
 
@@ -84,6 +87,9 @@ private HashKey(uint key32)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static HashKey operator ^(HashKey left, ulong right) => new(left.Key ^ right);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator ulong(HashKey hk) => hk.Key;
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Equals(HashKey other) => Key == other.Key;
 
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/Rudzoft.ChessLib/Types/MagicBB.cs
index bdf24a08..18c8bd02 100644
--- a/src/Rudzoft.ChessLib/Types/MagicBB.cs
+++ b/src/Rudzoft.ChessLib/Types/MagicBB.cs
@@ -133,6 +133,7 @@ IRKiss rk
         var rankMask = Rank.Rank1.RankBB() | Rank.Rank8.RankBB();
         var fileMask = File.FileA.FileBB() | File.FileH.FileBB();
         ref var magicsRef = ref MemoryMarshal.GetArrayDataReference(magics);
+        var seedsSpan = Seeds.AsSpan();
 
         for (var s = 0; s < 64; s++)
         {
@@ -177,7 +178,7 @@ IRKiss rk
             if (Bmi2.X64.IsSupported)
                 continue;
 
-            rk.Seed = Seeds[rank];
+            rk.Seed = seedsSpan[rank];
 
             // Find a magic for square 's' picking up an (almost) random number
             // until we find the one that passes the verification test.
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/Rudzoft.ChessLib/Types/Move.cs
index 391bf22d..4b6de9d5 100644
--- a/src/Rudzoft.ChessLib/Types/Move.cs
+++ b/src/Rudzoft.ChessLib/Types/Move.cs
@@ -51,16 +51,17 @@ public readonly record struct Move(ushort Data) : ISpanFormattable
 {
     private const int MaxMoveStringSize = 5;
 
-    public Move(Square from, Square to) : this((ushort)(to | (from << 6)))
+    private Move(Square from, Square to) : this((ushort)(to | (from << 6)))
     {
     }
 
-    public Move(Square from, Square to, MoveTypes moveType, PieceTypes promoPt = PieceTypes.Knight)
-        : this((ushort)(to | (from << 6) | moveType.AsInt() | ((promoPt - PieceTypes.Knight) << 12)))
+    private Move(Square from, Square to, MoveTypes moveType, PieceType promoPt)
+        : this((ushort)(to | (from << 6) | moveType.AsInt() | ((promoPt.Value - PieceType.Knight.Value) << 12)))
     {
     }
 
     public static readonly Move EmptyMove = new();
+    public static readonly Move NullMove = new(65);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Deconstruct(out Square from, out Square to)
@@ -106,9 +107,12 @@ public static int Create(ref ValMove moves, int index, Square from, ref BitBoard
         return index;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Move Create(Square from, Square to, MoveTypes moveType)
+        => new(from, to, moveType, PieceType.Knight);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Move Create(Square from, Square to, MoveTypes moveType, PieceTypes promoPt = PieceTypes.Knight)
+    public static Move Create(Square from, Square to, MoveTypes moveType, PieceType promoPt)
         => new(from, to, moveType, promoPt);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -121,7 +125,7 @@ public static Move Create(Square from, Square to, MoveTypes moveType, PieceTypes
     public PieceType PromotedPieceType() => (PieceTypes)(((Data >> 12) & 3) + 2);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsQueenPromotion() => PromotedPieceType() == PieceTypes.Queen;
+    public bool IsQueenPromotion() => PromotedPieceType() == PieceType.Queen;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public MoveTypes MoveType() => (MoveTypes)(Data & (3 << 14));
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/Rudzoft.ChessLib/Types/Piece.cs
index 66eb99ef..cbfe75a5 100644
--- a/src/Rudzoft.ChessLib/Types/Piece.cs
+++ b/src/Rudzoft.ChessLib/Types/Piece.cs
@@ -93,14 +93,14 @@ private Piece(Piece pc) : this(pc.Value) { }
 
     public static Range BlackPieces => new(6, 11);
 
-    public static PieceTypes[] AllTypes { get; } =
+    public static PieceType[] AllTypes { get; } =
     [
-        PieceTypes.Pawn,
-        PieceTypes.Knight,
-        PieceTypes.Bishop,
-        PieceTypes.Rook,
-        PieceTypes.Queen,
-        PieceTypes.King
+        PieceType.Pawn,
+        PieceType.Knight,
+        PieceType.Bishop,
+        PieceType.Rook,
+        PieceType.Queen,
+        PieceType.King
     ];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -116,7 +116,7 @@ private Piece(Piece pc) : this(pc.Value) { }
     public static Piece operator ~(Piece pc) => new(pc ^ 8);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Piece operator +(Piece left, Player right) => new(left.Value + (byte)(right << 3));
+    public static Piece operator +(Piece left, Color right) => new(left.Value + (byte)(right << 3));
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Piece operator >> (Piece left, int right) => new((int)left.Value >> right);
@@ -155,10 +155,10 @@ private Piece(Piece pc) : this(pc.Value) { }
     public static bool operator false(Piece pc) => pc == EmptyPiece;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator int(Piece pc) => (int)pc.Value;
+    public static implicit operator byte(Piece pc) => (byte)pc.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Player ColorOf() => new((int)Value >> 3);
+    public Color ColorOf() => new((int)Value >> 3);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool Equals(Piece other) => Value == other.Value;
@@ -169,21 +169,21 @@ private Piece(Piece pc) : this(pc.Value) { }
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public override string ToString() => this.GetPieceString();
 
-    public static Piece GetPiece(char c)
+    public static Piece GetPiece(char t)
     {
-        var pcIndex = PieceExtensions.PieceChars.IndexOf(c);
+        var pcIndex = PieceExtensions.PieceChars.IndexOf(t);
         if (pcIndex == -1)
             return EmptyPiece;
 
-        Player p = new(char.IsLower(PieceExtensions.PieceChars[pcIndex]));
-        return new PieceType(pcIndex).MakePiece(p);
+        Color c = new(char.IsLower(PieceExtensions.PieceChars[pcIndex]));
+        return new PieceType(pcIndex).MakePiece(c);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void Deconstruct(out PieceType pt, out Player p)
+    public void Deconstruct(out PieceType pt, out Color c)
     {
         pt = Type();
-        p = ColorOf();
+        c = ColorOf();
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Rudzoft.ChessLib/Types/PieceType.cs b/src/Rudzoft.ChessLib/Types/PieceType.cs
index 16f962c1..05718a06 100644
--- a/src/Rudzoft.ChessLib/Types/PieceType.cs
+++ b/src/Rudzoft.ChessLib/Types/PieceType.cs
@@ -85,11 +85,15 @@ private PieceType(PieceType pt) : this(pt.Value) { }
     // public int AsInt() => (int)Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Piece MakePiece(Player side) => (int)Value | (side << 3);
+    public Piece MakePiece(Color c) => (int)Value | (c << 3);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool IsSlider() => InBetween(PieceTypes.Bishop, PieceTypes.Queen);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool InBetween(PieceType min, PieceType max) =>
+        (uint)Value - (uint)min.Value <= (uint)max.Value - (uint)min.Value;
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool InBetween(PieceTypes min, PieceTypes max) =>
         (uint)Value - (uint)min <= (uint)max - (uint)min;
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/Rudzoft.ChessLib/Types/Rank.cs
index 2ca2b76d..18d99ed3 100644
--- a/src/Rudzoft.ChessLib/Types/Rank.cs
+++ b/src/Rudzoft.ChessLib/Types/Rank.cs
@@ -46,7 +46,7 @@ public enum Ranks
 public static class RanksExtensions
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Rank RelativeRank(this Ranks r, Player p) => new((Ranks)(r.AsInt() ^ (p * 7)));
+    public static Rank RelativeRank(this Ranks r, Color c) => new((Ranks)(r.AsInt() ^ (c * 7)));
 
     public static int AsInt(this Ranks r) => (int)r;
 }
@@ -195,7 +195,7 @@ public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan
     public override int GetHashCode() => AsInt();
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank Relative(Player p) => new(AsInt() ^ (p * 7));
+    public Rank Relative(Color c) => new(AsInt() ^ (c.Side * 7));
 
     /// <summary>
     /// Fold rank [12345678] to rank [12344321]
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/Rudzoft.ChessLib/Types/Square.cs
index 2ee34f35..142fc3d6 100644
--- a/src/Rudzoft.ChessLib/Types/Square.cs
+++ b/src/Rudzoft.ChessLib/Types/Square.cs
@@ -48,12 +48,6 @@ public enum Squares : byte
 
 public static class SquaresExtensions
 {
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static BitBoard BitBoardSquare(this Squares sq) => BitBoards.BbSquares[sq.AsInt()];
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static Square RelativeSquare(this Squares sq, Player p) => sq.AsInt() ^ (p * 56);
-
     public static int AsInt(this Squares sq) => (int)sq;
 }
 
@@ -110,7 +104,7 @@ public void Deconstruct(out Rank r, out File f)
 
     public bool IsPromotionRank => (BitBoards.PromotionRanksBB & this).IsNotEmpty;
 
-    public bool IsDark => (Player.Black.ColorBB() & this).IsNotEmpty;
+    public bool IsDark => (Types.Color.Black.ColorBB() & this).IsNotEmpty;
 
     public static Square None { get; } = new(Squares.none);
 
@@ -295,10 +289,10 @@ public void Deconstruct(out Rank r, out File f)
     public static bool operator false(Square sq) => !sq.IsOk;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator int(Square sq) => (int)sq.Value;
+    public static implicit operator byte(Square sq) => (byte)sq.Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Square Relative(Player p) => (int)Value ^ (p.Side * 56);
+    public Square Relative(Color c) => (int)Value ^ (c.Side * 56);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Square Max(Square other) => Value > other.Value ? this : other;
@@ -338,7 +332,7 @@ public bool TryFormat(
     public BitBoard AsBb() => BitBoards.BbSquares[AsInt()];
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public Rank RelativeRank(Player p) => Rank.Relative(p);
+    public Rank RelativeRank(Color c) => Rank.Relative(c);
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool IsOppositeColor(Square other)
@@ -364,5 +358,5 @@ public int CompareTo(Square other)
     /// <returns>Flipped square by Rank</returns>
     public Square FlipRank() => AsInt() ^ Squares.a8.AsInt();
 
-    public Player Color() => ((AsInt() + Rank) ^ 1) & 1;
+    public Color Color() => ((AsInt() + Rank) ^ 1) & 1;
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Value.cs b/src/Rudzoft.ChessLib/Types/Value.cs
index 22128f98..a0f160f0 100644
--- a/src/Rudzoft.ChessLib/Types/Value.cs
+++ b/src/Rudzoft.ChessLib/Types/Value.cs
@@ -101,7 +101,7 @@ public Value(int value) : this((DefaultPieceValues)value) { }
 
     public static bool operator false(Value value) => value.Raw <= 0;
 
-    public Value ForColor(Player p) => p.IsWhite ? this : new(-(int)Raw);
+    public Value ForColor(Color c) => c.IsWhite ? this : new(-(int)Raw);
 
     public bool Equals(Value other) => Raw == other.Raw;
 
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
index 8f4dc290..a3ccbe34 100644
--- a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
+++ b/src/Rudzoft.ChessLib/Validation/PositionValidator.cs
@@ -51,15 +51,8 @@ public static bool HasFlagFast(this PositionValidationTypes @this, PositionValid
         => (@this & flag) != PositionValidationTypes.None;
 }
 
-public sealed class PositionValidator : IPositionValidator
+public sealed class PositionValidator(IZobrist zobrist) : IPositionValidator
 {
-    private readonly IZobrist _zobrist;
-
-    public PositionValidator(IZobrist zobrist)
-    {
-        _zobrist = zobrist;
-    }
-
     public PositionValidationResult Validate(in IPosition pos, PositionValidationTypes type = PositionValidationTypes.All)
     {
         var errors = new List<string>();
@@ -94,13 +87,13 @@ public PositionValidationResult Validate(in IPosition pos, PositionValidationTyp
 
     private static IEnumerable<string> ValidateBasic(IPosition pos)
     {
-        if (pos.SideToMove != Player.White && pos.SideToMove != Player.Black)
+        if (pos.SideToMove != Color.White && pos.SideToMove != Color.Black)
             yield return $"{nameof(pos.SideToMove)} is not a valid";
 
-        if (pos.GetPiece(pos.GetKingSquare(Player.White)) != Piece.WhiteKing)
+        if (pos.GetPiece(pos.GetKingSquare(Color.White)) != Piece.WhiteKing)
             yield return "white king position is not a white king";
 
-        if (pos.GetPiece(pos.GetKingSquare(Player.Black)) != Piece.BlackKing)
+        if (pos.GetPiece(pos.GetKingSquare(Color.Black)) != Piece.BlackKing)
             yield return "black king position is not a black king";
 
         if (pos.EnPassantSquare != Square.None && pos.EnPassantSquare.RelativeRank(pos.SideToMove) != Ranks.Rank6)
@@ -111,12 +104,12 @@ private static IEnumerable<string> ValidateCastle(IPosition pos)
     {
         var crs = new[] { CastleRight.None, CastleRight.None };
 
-        foreach (var p in Player.AllPlayers)
+        foreach (var c in Color.AllColors)
         {
-            crs[0] = CastleRights.King.MakeCastleRights(p);
-            crs[1] = CastleRights.Queen.MakeCastleRights(p);
+            crs[0] = CastleRights.King.MakeCastleRights(c);
+            crs[1] = CastleRights.Queen.MakeCastleRights(c);
 
-            var ourRook = PieceType.Rook.MakePiece(p);
+            var ourRook = PieceType.Rook.MakePiece(c);
             foreach (var cr in crs)
             {
                 if (!pos.CanCastle(cr))
@@ -125,24 +118,24 @@ private static IEnumerable<string> ValidateCastle(IPosition pos)
                 var rookSq = pos.CastlingRookSquare(cr);
 
                 if (pos.GetPiece(rookSq) != ourRook)
-                    yield return $"rook does not appear on its position for {p}";
+                    yield return $"rook does not appear on its position for {c}";
 
                 if (pos.GetCastleRightsMask(rookSq) != cr)
-                    yield return $"castle rights mask at {rookSq} does not match for player {p}";
+                    yield return $"castle rights mask at {rookSq} does not match for player {c}";
 
-                if ((pos.GetCastleRightsMask(pos.GetKingSquare(p)) & cr) != cr)
-                    yield return $"castle rights mask at {pos.GetKingSquare(p)} does not match for player {p}";
+                if ((pos.GetCastleRightsMask(pos.GetKingSquare(c)) & cr) != cr)
+                    yield return $"castle rights mask at {pos.GetKingSquare(c)} does not match for player {c}";
             }
         }
     }
 
     private static IEnumerable<string> ValidateKings(IPosition pos)
     {
-        foreach (var player in Player.AllPlayers)
+        foreach (var c in Color.AllColors)
         {
-            var count = pos.PieceCount(PieceTypes.King, player);
+            var count = pos.PieceCount(PieceType.King, c);
             if (count != 1)
-                yield return $"king count for player {player} was {count}";
+                yield return $"king count for player {c} was {count}";
         }
 
         if ((pos.AttacksTo(pos.GetKingSquare(~pos.SideToMove)) & pos.Pieces(pos.SideToMove)).IsNotEmpty)
@@ -151,28 +144,28 @@ private static IEnumerable<string> ValidateKings(IPosition pos)
 
     private static IEnumerable<string> ValidatePawns(IPosition pos)
     {
-        if ((pos.Pieces(PieceTypes.Pawn) & (Rank.Rank1.RankBB() | Rank.Rank8.RankBB())).IsNotEmpty)
+        if ((pos.Pieces(PieceType.Pawn) & (Rank.Rank1.RankBB() | Rank.Rank8.RankBB())).IsNotEmpty)
             yield return "pawns exists on rank 1 or rank 8";
 
-        if (pos.PieceCount(PieceTypes.Pawn, Player.White) > 8)
+        if (pos.PieceCount(PieceType.Pawn, Color.White) > 8)
             yield return "white side has more than 8 pawns";
 
-        if (pos.PieceCount(PieceTypes.Pawn, Player.Black) > 8)
+        if (pos.PieceCount(PieceType.Pawn, Color.Black) > 8)
             yield return "black side has more than 8 pawns";
     }
 
     private static IEnumerable<string> ValidatePieceConsistency(IPosition pos)
     {
-        if ((pos.Pieces(Player.White) & pos.Pieces(Player.Black)).IsNotEmpty)
+        if ((pos.Pieces(Color.White) & pos.Pieces(Color.Black)).IsNotEmpty)
             yield return "white and black pieces overlap";
 
-        if ((pos.Pieces(Player.White) | pos.Pieces(Player.Black)) != pos.Pieces())
+        if ((pos.Pieces(Color.White) | pos.Pieces(Color.Black)) != pos.Pieces())
             yield return "white and black pieces do not match all pieces";
 
-        if (pos.Pieces(Player.White).Count > 16)
+        if (pos.Pieces(Color.White).Count > 16)
             yield return "white side has more than 16 pieces";
 
-        if (pos.Pieces(Player.Black).Count > 16)
+        if (pos.Pieces(Color.Black).Count > 16)
             yield return "black side has more than 16 pieces";
     }
 
@@ -193,7 +186,7 @@ private IEnumerable<string> ValidateState(IPosition pos)
         if (state.PositionKey == HashKey.Empty && pos.PieceCount() != 0)
             yield return "state key is invalid";
 
-        if (pos.PieceCount(PieceTypes.Pawn) == 0 && state.PawnKey != _zobrist.ZobristNoPawn)
+        if (pos.PieceCount(PieceType.Pawn) == 0 && state.PawnKey != zobrist.ZobristNoPawn)
             yield return "empty pawn key is invalid";
 
         if (state.Repetition < 0)
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
index 10255bcb..d072ebfb 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -2,19 +2,18 @@
 
     <PropertyGroup>
         <OutputType>Exe</OutputType>
-        <IsPackable>false</IsPackable>
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Akka" Version="1.5.17.1" />
-      <PackageReference Include="Akka.DependencyInjection" Version="1.5.17.1" />
-      <PackageReference Include="CommandLineParser" Version="2.9.1" />
-      <PackageReference Include="Serilog" Version="3.1.1" />
-      <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
-      <PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/>
-      <PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
-      <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/>
-      <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
+      <PackageReference Include="Akka"/>
+      <PackageReference Include="Akka.DependencyInjection"/>
+      <PackageReference Include="CommandLineParser"/>
+      <PackageReference Include="Serilog"/>
+      <PackageReference Include="Serilog.Sinks.Console"/>
+      <PackageReference Include="Serilog.Sinks.File"/>
+      <PackageReference Include="Serilog.Settings.Configuration"/>
+      <PackageReference Include="Serilog.Enrichers.Thread"/>
+      <PackageReference Include="Microsoft.Extensions.Hosting"/>
     </ItemGroup>
 
     <ItemGroup>
@@ -32,9 +31,4 @@
         </Content>
     </ItemGroup>
 
-    <ItemGroup>
-      <Folder Include="Actors\Perft\" />
-    </ItemGroup>
-
-
 </Project>

From 5285b3a694e63f4aec4008452fe100bc3f02869e Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Jun 2024 23:09:00 +0200
Subject: [PATCH 098/119] simplify bitboard static ctor a bit

---
 src/Rudzoft.ChessLib/Types/BitBoards.cs | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/Rudzoft.ChessLib/Types/BitBoards.cs
index 347b8ffe..1e67621c 100644
--- a/src/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -215,9 +215,11 @@ static BitBoards()
         foreach (var r in Rank.All)
             ForwardRanksBB[0][r] = ~(ForwardRanksBB[1][r + 1] = ForwardRanksBB[1][r] | r.BitBoardRank());
 
+        var squares = Square.All.AsSpan();
+
         foreach (var c in Color.AllColors.AsSpan())
         {
-            foreach (var sq in Square.All.AsSpan())
+            foreach (var sq in squares)
             {
                 var file = sq.File;
                 ForwardFileBB[c][sq] = ForwardRanksBB[c][sq.Rank] & file.BitBoardFile();
@@ -227,7 +229,7 @@ static BitBoards()
         }
 
         // have to compute here before we access the BitBoards
-        foreach (var sq1 in Square.All.AsSpan())
+        foreach (var sq1 in squares)
         {
             SquareDistance[sq1] = new int[Square.Count];
             DistanceRingBB[sq1] = new BitBoard[8];
@@ -243,14 +245,14 @@ static BitBoards()
         Span<PieceType> validMagicPieces = stackalloc PieceType[] { PieceType.Bishop, PieceType.Rook };
 
         // Pseudo attacks for all pieces
-        foreach (var sq1 in Square.All)
+        foreach (var sq1 in squares)
         {
             InitializePseudoAttacks(sq1);
 
             // Compute lines and betweens
             foreach (var pt in validMagicPieces)
             {
-                foreach (var sq2 in Square.All)
+                foreach (var sq2 in squares)
                 {
                     if ((PseudoAttacksBB[pt][sq1] & sq2).IsEmpty)
                         continue;

From 4c327fd6c5e1bdfd9bc195f8d4456b16023aaf5c Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Jun 2024 23:09:16 +0200
Subject: [PATCH 099/119] added methodimpl attributes to Board and updated File

---
 src/Rudzoft.ChessLib/Board.cs      | 22 ++++++++++++++++++++++
 src/Rudzoft.ChessLib/Types/File.cs |  4 ++--
 2 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/Rudzoft.ChessLib/Board.cs
index c043e255..ff5cda9c 100644
--- a/src/Rudzoft.ChessLib/Board.cs
+++ b/src/Rudzoft.ChessLib/Board.cs
@@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System.Collections;
 using System.Diagnostics;
+using System.Runtime.CompilerServices;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib;
@@ -40,6 +41,7 @@ public sealed class Board : IBoard
 
     private readonly int[] _pieceCount = new int[Piece.Count];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Clear()
     {
         Array.Fill(_board, Piece.EmptyPiece);
@@ -48,10 +50,13 @@ public void Clear()
         Array.Fill(_pieceCount, 0);
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Piece PieceAt(Square sq) => _board[sq];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool IsEmpty(Square sq) => _board[sq] == Piece.EmptyPiece;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void AddPiece(Piece pc, Square sq)
     {
         Debug.Assert(sq.IsOk);
@@ -67,6 +72,7 @@ public void AddPiece(Piece pc, Square sq)
         _pieceCount[PieceType.AllPieces.MakePiece(color)]++;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void RemovePiece(Square sq)
     {
         Debug.Assert(sq.IsOk);
@@ -80,9 +86,11 @@ public void RemovePiece(Square sq)
         _pieceCount[PieceType.AllPieces.MakePiece(pc.ColorOf())]--;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void ClearPiece(Square sq)
         => _board[sq] = Piece.EmptyPiece;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void MovePiece(Square from, Square to)
     {
         Debug.Assert(from.IsOk && to.IsOk);
@@ -96,38 +104,52 @@ public void MovePiece(Square from, Square to)
         _board[to] = pc;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Piece MovedPiece(Move m) => PieceAt(m.FromSquare());
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces() => _byType[PieceType.AllPieces];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces(PieceType pt) => _byType[pt];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces(PieceType pt1, PieceType pt2) => _byType[pt1] | _byType[pt2];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces(Color c) => _bySide[c];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces(Color c, PieceType pt) => _bySide[c] & _byType[pt];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public BitBoard Pieces(Color c, PieceType pt1, PieceType pt2)
         => _bySide[c] & (_byType[pt1] | _byType[pt2]);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Square Square(PieceType pt, Color c)
     {
         Debug.Assert(_pieceCount[pt.MakePiece(c)] == 1);
         return Pieces(c, pt).Lsb();
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int PieceCount(Piece pc) => _pieceCount[pc];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int PieceCount(PieceType pt, Color c) => PieceCount(pt.MakePiece(c));
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int PieceCount(PieceType pt)
         => _pieceCount[pt.MakePiece(Color.White)] + _pieceCount[pt.MakePiece(Color.Black)];
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public int PieceCount() => PieceCount(PieceType.AllPieces);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public IEnumerator<Piece> GetEnumerator()
         => _board.Where(static pc => pc != Piece.EmptyPiece).GetEnumerator();
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/Rudzoft.ChessLib/Types/File.cs
index 664716ed..4609f2a5 100644
--- a/src/Rudzoft.ChessLib/Types/File.cs
+++ b/src/Rudzoft.ChessLib/Types/File.cs
@@ -33,7 +33,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Types;
 
-public enum Files
+public enum Files : byte
 {
     FileA = 0,
     FileB = 1,
@@ -79,7 +79,7 @@ public File(File f) : this(f.Value)
     {
     }
 
-    public char Char => (char)('a' + Value);
+    public char Char => (char)('a' + (int)Value);
 
     public bool IsOk => Value.AsInt().IsBetween(Files.FileA.AsInt(), Files.FileH.AsInt());
 

From 9946645d9fcdd48779a0b4bf244403e4b8cb8ebc Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Jun 2024 23:10:59 +0200
Subject: [PATCH 100/119] remove redundant method call when updating board
 position

---
 src/Rudzoft.ChessLib/Position.cs | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index e9a0d0d8..9c60d30e 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1485,8 +1485,6 @@ private void DoCastle(
         {
             RemovePiece(from);
             RemovePiece(rookFrom);
-            Board.ClearPiece(from);
-            Board.ClearPiece(rookFrom);
             AddPiece(PieceType.King.MakePiece(us), to);
             AddPiece(PieceType.Rook.MakePiece(us), rookTo);
         }
@@ -1494,8 +1492,6 @@ private void DoCastle(
         {
             RemovePiece(to);
             RemovePiece(rookTo);
-            Board.ClearPiece(to);
-            Board.ClearPiece(rookTo);
             AddPiece(PieceType.King.MakePiece(us), from);
             AddPiece(PieceType.Rook.MakePiece(us), rookFrom);
         }

From 0a0010fbf73615f4bca48dcc99ffeb944496d55e Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Jun 2024 23:11:23 +0200
Subject: [PATCH 101/119] SetupPieces uses correct types for rank and file

---
 src/Rudzoft.ChessLib/Position.cs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 9c60d30e..0b69fe3d 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1158,8 +1158,8 @@ public bool SeeGe(Move m, Value threshold)
 
     private void SetupPieces(ReadOnlySpan<char> fenChunk)
     {
-        var f = 1; // file (column)
-        var r = 8; // rank (row)
+        var f = File.FileA;
+        var r = Rank.Rank8;
 
         ref var fenChunkSpace = ref MemoryMarshal.GetReference(fenChunk);
         for (var i = 0; i < fenChunk.Length; i++)
@@ -1170,7 +1170,7 @@ private void SetupPieces(ReadOnlySpan<char> fenChunk)
             else if (c == '/')
             {
                 r--;
-                f = 1;
+                f = File.FileA;
             }
             else
             {
@@ -1180,7 +1180,7 @@ private void SetupPieces(ReadOnlySpan<char> fenChunk)
                     throw new InvalidFenException("Invalid char detected");
 
                 var us = new Color(char.IsLower(PieceExtensions.PieceChars[pieceIndex]));
-                var square = new Square(r - 1, f - 1);
+                var square = new Square(r, f);
                 var pt = new PieceType(pieceIndex);
                 var pc = pt.MakePiece(us);
 

From 86cc2f129a31affd7659feba42a0e362c42991f3 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Jun 2024 23:11:35 +0200
Subject: [PATCH 102/119] added more methodimpl to position

---
 src/Rudzoft.ChessLib/Position.cs             | 5 +++++
 src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj | 1 +
 2 files changed, 6 insertions(+)

diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/Rudzoft.ChessLib/Position.cs
index 0b69fe3d..00f67ca0 100644
--- a/src/Rudzoft.ChessLib/Position.cs
+++ b/src/Rudzoft.ChessLib/Position.cs
@@ -1514,6 +1514,7 @@ private void MovePiece(Square from, Square to)
     /// </summary>
     /// <param name="stm"></param>
     /// <param name="rookFrom"></param>
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private void SetCastlingRight(Color stm, Square rookFrom)
     {
         var kingFrom = GetKingSquare(stm);
@@ -1533,6 +1534,7 @@ private void SetCastlingRight(Color stm, Square rookFrom)
         _castleRookPath[cr.AsInt()] = (kingPath | (rookFrom.BitboardBetween(rookTo) | rookTo)) & ~(kingFrom | rookFrom);
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private void SetCheckInfo(in State state)
     {
         (state.BlockersForKing[Color.White], state.Pinners[Color.Black]) =
@@ -1555,6 +1557,7 @@ private void SetCheckInfo(in State state)
     private void CopyState(in State newState)
         => State = State.CopyTo(newState);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private void SetState(State state)
     {
         state.MaterialKey = HashKey.Empty;
@@ -1591,6 +1594,7 @@ private void SetState(State state)
         }
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private void SetupCastle(ReadOnlySpan<char> castle)
     {
         foreach (var ca in castle)
@@ -1622,6 +1626,7 @@ private Square RookSquare(Square startSq, Piece rook)
         return targetSq;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private (BitBoard, BitBoard) SliderBlockers(in BitBoard sliders, Square s)
     {
         var result = (blockers: BitBoard.Empty, pinners: BitBoard.Empty);
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 8d5ff0fb..2e0f2d41 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -24,6 +24,7 @@
         <RepositoryType>git</RepositoryType>
         <Product>Rudzoft.ChessLib</Product>
         <IsPackable>true</IsPackable>
+        <PublishAot>true</PublishAot>
     </PropertyGroup>
 
     <ItemGroup>

From 4fa0f204f0aea907838d537cb381a7a0dc717ab9 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Jun 2024 23:12:04 +0200
Subject: [PATCH 103/119] update akka version

---
 Directory.Packages.props | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2d3e5714..fd3319e7 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -30,8 +30,9 @@
     </PackageVersion>
   </ItemGroup>
   <ItemGroup Label="Perft">
-    <PackageVersion Include="Akka" Version="1.5.25" />
-    <PackageVersion Include="Akka.DependencyInjection" Version="1.5.25" />
+    <PackageVersion Include="Akka" Version="1.5.26" />
+    <PackageVersion Include="Akka.DependencyInjection" Version="1.5.26" />
+    <PackageVersion Include="Akka.Hosting" Version="1.5.25" />
     <PackageVersion Include="CommandLineParser" Version="2.9.1" />
     <PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
     <PackageVersion Include="Serilog" Version="4.0.0" />

From 67372321659155e1b630939d6e727d8a54175019 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Jun 2024 23:13:36 +0200
Subject: [PATCH 104/119] fix for updating File size

---
 src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs     | 4 ++--
 src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs | 2 +-
 src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs    | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs b/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
index b25b0b08..ee475e75 100644
--- a/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
+++ b/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
@@ -46,7 +46,7 @@ public string StringFromArray(File f)
     [ArgumentsSource(nameof(FileParams))]
     public string StringFromCreate(File f)
     {
-        return string.Create(1, f.Value, static (span, v) => span[0] = (char)('a' + v));
+        return string.Create(1, f.Value, static (span, v) => span[0] = (char)('a' + (int)v));
     }
 
     [Benchmark]
@@ -55,7 +55,7 @@ public string StringFromCharConvert(File f)
     {
         return new string((char)('a' + f.AsInt()), 1);
     }
-    
+
     [Benchmark]
     [ArgumentsSource(nameof(FileParams))]
     public string StringFromChar(File f)
diff --git a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
index 0e4baa4f..568ed714 100644
--- a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
+++ b/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
@@ -67,7 +67,7 @@ public void RankSize()
     [Fact]
     public void FileSize()
     {
-        const int expected = 4;
+        const int expected = 1;
         var actual = Unsafe.SizeOf<File>();
         Assert.Equal(expected, actual);
     }
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
index b7f662aa..991a451e 100644
--- a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
+++ b/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
@@ -52,9 +52,9 @@ public override string Convert(IPosition pos, Move move)
         Span<char> re = stackalloc char[5];
         var i = 0;
 
-        re[i++] = (char)('1' + from.File.Value);
+        re[i++] = (char)('1' + (int)from.File.Value);
         re[i++] = (char)('1' + from.Rank.Value);
-        re[i++] = (char)('1' + to.File.Value);
+        re[i++] = (char)('1' + (int)to.File.Value);
         re[i++] = (char)('1' + to.Rank.Value);
 
         // ReSharper disable once InvertIf

From d7b765061de80139f243ee12658028181e0e7d98 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Jun 2024 23:19:32 +0200
Subject: [PATCH 105/119] update location of perft + using akka host

---
 Rudzoft.ChessLib.sln                          | 19 ++--
 src/Rudzoft.Perft/Actors/PerftActor.cs        | 33 -------
 src/Rudzoft.Perft/Actors/PerftRunnerActor.cs  | 50 -----------
 src/Rudzoft.Perft/Program.cs                  | 77 ----------------
 src/Rudzoft.Perft/Services/PerftService.cs    | 61 -------------
 .../Rudzoft.Perft.Domain.csproj               |  9 ++
 src/perft/Rudzoft.Perft.Domain/RunPerft.cs    |  3 +
 .../Rudzoft.Perft/Actors/FenTargetActor.cs    |  0
 src/perft/Rudzoft.Perft/Actors/OutputActor.cs | 25 ++++++
 src/perft/Rudzoft.Perft/Actors/PerftActor.cs  | 35 ++++++++
 .../Rudzoft.Perft/Actors/ProgressActor.cs     |  0
 src/{ => perft}/Rudzoft.Perft/GlobalUsings.cs |  0
 .../Rudzoft.Perft/Models/CommandLineArgs.cs   |  0
 .../Rudzoft.Perft/Models/PerftResult.cs       |  0
 .../Rudzoft.Perft/Options/EpdOptions.cs       |  0
 src/{ => perft}/Rudzoft.Perft/Options/Fens.cs |  0
 .../Rudzoft.Perft/Options/IOptions.cs         |  0
 .../Rudzoft.Perft/Options/IOptionsFactory.cs  |  0
 .../Rudzoft.Perft/Options/OptionType.cs       |  0
 .../Rudzoft.Perft/Options/OptionsFactory.cs   |  0
 .../Rudzoft.Perft/Options/TTOptions.cs        |  0
 .../Rudzoft.Perft/Parsers/EpdParser.cs        |  0
 .../Parsers/EpdParserSettings.cs              |  0
 .../Rudzoft.Perft/Parsers/EpdSet.cs           |  0
 .../Rudzoft.Perft/Parsers/IEpdParser.cs       |  0
 .../Parsers/IEpdParserSettings.cs             |  0
 .../Rudzoft.Perft/Parsers/IEpdSet.cs          |  0
 src/perft/Rudzoft.Perft/Program.cs            | 90 +++++++++++++++++++
 .../Rudzoft.Perft/Rudzoft.Perft.csproj        |  6 +-
 .../Rudzoft.Perft/Services/IPerftRunner.cs    |  0
 .../Rudzoft.Perft/Services/PerftRunner.cs     |  0
 .../Rudzoft.Perft/Services/PerftService.cs    | 48 ++++++++++
 .../Rudzoft.Perft/appsettings.json            |  0
 33 files changed, 227 insertions(+), 229 deletions(-)
 delete mode 100644 src/Rudzoft.Perft/Actors/PerftActor.cs
 delete mode 100644 src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
 delete mode 100644 src/Rudzoft.Perft/Program.cs
 delete mode 100644 src/Rudzoft.Perft/Services/PerftService.cs
 create mode 100644 src/perft/Rudzoft.Perft.Domain/Rudzoft.Perft.Domain.csproj
 create mode 100644 src/perft/Rudzoft.Perft.Domain/RunPerft.cs
 rename src/{ => perft}/Rudzoft.Perft/Actors/FenTargetActor.cs (100%)
 create mode 100644 src/perft/Rudzoft.Perft/Actors/OutputActor.cs
 create mode 100644 src/perft/Rudzoft.Perft/Actors/PerftActor.cs
 rename src/{ => perft}/Rudzoft.Perft/Actors/ProgressActor.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/GlobalUsings.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Models/CommandLineArgs.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Models/PerftResult.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Options/EpdOptions.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Options/Fens.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Options/IOptions.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Options/IOptionsFactory.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Options/OptionType.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Options/OptionsFactory.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Options/TTOptions.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Parsers/EpdParser.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Parsers/EpdParserSettings.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Parsers/EpdSet.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Parsers/IEpdParser.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Parsers/IEpdParserSettings.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Parsers/IEpdSet.cs (100%)
 create mode 100644 src/perft/Rudzoft.Perft/Program.cs
 rename src/{ => perft}/Rudzoft.Perft/Rudzoft.Perft.csproj (72%)
 rename src/{ => perft}/Rudzoft.Perft/Services/IPerftRunner.cs (100%)
 rename src/{ => perft}/Rudzoft.Perft/Services/PerftRunner.cs (100%)
 create mode 100644 src/perft/Rudzoft.Perft/Services/PerftService.cs
 rename src/{ => perft}/Rudzoft.Perft/appsettings.json (100%)

diff --git a/Rudzoft.ChessLib.sln b/Rudzoft.ChessLib.sln
index 73bfd7ad..18405279 100644
--- a/Rudzoft.ChessLib.sln
+++ b/Rudzoft.ChessLib.sln
@@ -34,7 +34,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		Directory.Packages.props = Directory.Packages.props
 	EndProjectSection
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.Perft", "src\Rudzoft.Perft\Rudzoft.Perft.csproj", "{5E02C821-DB31-4D63-9E0D-681B08BD22E1}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.Perft.Domain", "src\perft\Rudzoft.Perft.Domain\Rudzoft.Perft.Domain.csproj", "{374C6338-57B4-44CD-9A40-B13EBC25BCF6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.Perft", "src\perft\Rudzoft.Perft\Rudzoft.Perft.csproj", "{279378EB-73AF-450C-909C-7145DBE463E0}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -74,10 +76,14 @@ Global
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.Build.0 = Release|Any CPU
-		{5E02C821-DB31-4D63-9E0D-681B08BD22E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{5E02C821-DB31-4D63-9E0D-681B08BD22E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{5E02C821-DB31-4D63-9E0D-681B08BD22E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{5E02C821-DB31-4D63-9E0D-681B08BD22E1}.Release|Any CPU.Build.0 = Release|Any CPU
+		{374C6338-57B4-44CD-9A40-B13EBC25BCF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{374C6338-57B4-44CD-9A40-B13EBC25BCF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{374C6338-57B4-44CD-9A40-B13EBC25BCF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{374C6338-57B4-44CD-9A40-B13EBC25BCF6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{279378EB-73AF-450C-909C-7145DBE463E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{279378EB-73AF-450C-909C-7145DBE463E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{279378EB-73AF-450C-909C-7145DBE463E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{279378EB-73AF-450C-909C-7145DBE463E0}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -91,7 +97,8 @@ Global
 		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8} = {F82A6A7E-4551-4667-9E0A-57036E1B5117}
 		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
 		{556ADC0D-074D-4B8F-9E45-1275342FCB74} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
-		{5E02C821-DB31-4D63-9E0D-681B08BD22E1} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
+		{374C6338-57B4-44CD-9A40-B13EBC25BCF6} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
+		{279378EB-73AF-450C-909C-7145DBE463E0} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {0B507B2E-EC45-4DDA-94DD-B7CD5A2B8CF3}
diff --git a/src/Rudzoft.Perft/Actors/PerftActor.cs b/src/Rudzoft.Perft/Actors/PerftActor.cs
deleted file mode 100644
index 7e1e5bed..00000000
--- a/src/Rudzoft.Perft/Actors/PerftActor.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Collections.Immutable;
-using Akka.Actor;
-using Microsoft.Extensions.DependencyInjection;
-using Rudzoft.Perft.Options;
-
-namespace Rudzoft.Perft.Actors;
-
-// ReSharper disable once ClassNeverInstantiated.Global
-public sealed class PerftActor : ReceiveActor
-{
-    public sealed record StartPerft;
-
-    private readonly IServiceProvider _sp;
-
-    public PerftActor(IServiceProvider sp)
-    {
-        _sp = sp;
-        var optionsFactory = _sp.GetRequiredService<IOptionsFactory>();
-        var options = optionsFactory.Parse().ToImmutableArray();
-
-        Receive<StartPerft>(_ =>
-        {
-            var runner = Context.ActorOf(PerftRunnerActor.Prop(_sp), "runner-actor");
-            runner.Tell(new PerftRunnerActor.RunOptions(options));
-            runner.Tell(new PerftRunnerActor.Run());
-        });
-    }
-
-    public static Props Prop(IServiceProvider sp)
-    {
-        return Props.Create<PerftActor>(sp);
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs b/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
deleted file mode 100644
index 582888cf..00000000
--- a/src/Rudzoft.Perft/Actors/PerftRunnerActor.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System.Collections.Immutable;
-using Akka.Actor;
-using Microsoft.Extensions.DependencyInjection;
-using Rudzoft.Perft.Options;
-using Rudzoft.Perft.Services;
-
-namespace Rudzoft.Perft.Actors;
-
-// ReSharper disable once ClassNeverInstantiated.Global
-public sealed class PerftRunnerActor : ReceiveActor
-{
-    public sealed record RunOptions(ImmutableArray<PerftOption> Options);
-
-    public sealed record Run;
-
-    private readonly IPerftRunner _perftRunner;
-
-    public PerftRunnerActor(IServiceProvider sp)
-    {
-        var scope = sp.CreateScope();
-        _perftRunner = scope.ServiceProvider.GetRequiredService<IPerftRunner>();
-        Become(OptionsBehaviour);
-    }
-
-    public static Props Prop(IServiceProvider sp)
-    {
-        return Props.Create<PerftRunnerActor>(sp);
-    }
-
-    private void RunBehaviour()
-    {
-        ReceiveAsync<Run>(_ => _perftRunner.Run());
-    }
-
-    private void OptionsBehaviour()
-    {
-        Receive<RunOptions>(options =>
-        {
-            foreach (var option in options.Options)
-            {
-                if (option.Type == OptionType.TTOptions)
-                    _perftRunner.TranspositionTableOptions = option.PerftOptions;
-                else
-                    _perftRunner.Options = option.PerftOptions;
-            }
-
-            Become(RunBehaviour);
-        });
-    }
-}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Program.cs b/src/Rudzoft.Perft/Program.cs
deleted file mode 100644
index fc0ef6d6..00000000
--- a/src/Rudzoft.Perft/Program.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.ObjectPool;
-using Rudzoft.ChessLib.Extensions;
-using Rudzoft.ChessLib.Perft;
-using Rudzoft.ChessLib.Perft.Interfaces;
-using Rudzoft.Perft.Models;
-using Rudzoft.Perft.Options;
-using Rudzoft.Perft.Parsers;
-using Rudzoft.Perft.Services;
-using Serilog;
-
-var host = new HostBuilder()
-    .ConfigureServices((_, services) =>
-    {
-        var configuration = ConfigurationBuilder().Build();
-        services.AddSingleton(configuration);
-        services.AddSingleton(new CommandLineArgs(args));
-
-        services.AddChessLib(configuration);
-
-        services.AddSingleton(ConfigureLogger(configuration));
-
-        services.AddTransient<IPerft, Perft>();
-        services.AddTransient<IPerftRunner, PerftRunner>();
-        services.AddSingleton<IOptionsFactory, OptionsFactory>();
-
-        services.AddSingleton<IEpdParserSettings, EpdParserSettings>();
-        services.AddTransient<IEpdSet, EpdSet>();
-        services.AddSingleton<IEpdParser, EpdParser>();
-
-        services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
-        services.TryAddSingleton(serviceProvider =>
-        {
-            var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-            var policy = new DefaultPooledObjectPolicy<PerftResult>();
-            return provider.Create(policy);
-        });
-
-        services.AddHostedService<PerftService>();
-    })
-    .Build();
-
-host.Run();
-return;
-
-static ILogger ConfigureLogger(IConfiguration configuration)
-{
-    // Apply the config to the logger
-    Log.Logger = new LoggerConfiguration()
-        .ReadFrom.Configuration(configuration)
-        .Enrich.WithThreadId()
-        .Enrich.FromLogContext()
-        .CreateLogger();
-    AppDomain.CurrentDomain.ProcessExit += static (_, _) => Log.CloseAndFlush();
-    return Log.Logger;
-}
-
-static IConfigurationBuilder ConfigurationBuilder()
-{
-#if RELEASE
-    const string envName = "Production";
-#else
-    const string envName = "Development";
-#endif
-    // Create our configuration sources
-    return new ConfigurationBuilder()
-        // Add environment variables
-        .AddEnvironmentVariables()
-        // Set base path for Json files as the startup location of the application
-        .SetBasePath(Directory.GetCurrentDirectory())
-        // Add application settings json files
-        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
-        .AddJsonFile($"appsettings.{envName}.json", optional: true, reloadOnChange: false);
-}
diff --git a/src/Rudzoft.Perft/Services/PerftService.cs b/src/Rudzoft.Perft/Services/PerftService.cs
deleted file mode 100644
index 695bbeda..00000000
--- a/src/Rudzoft.Perft/Services/PerftService.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System.Diagnostics;
-using Akka.Actor;
-using Akka.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Rudzoft.Perft.Actors;
-using Serilog;
-
-namespace Rudzoft.Perft.Services;
-
-public sealed class PerftService : IHostedService
-{
-    private const string Version = "v0.2.0";
-
-    private static readonly ILogger Log = Serilog.Log.ForContext<PerftService>();
-
-    private readonly IServiceProvider _serviceProvider;
-    private readonly IHostApplicationLifetime _applicationLifetime;
-
-    private ActorSystem _actorSystem;
-    private IActorRef _perftActor;
-
-    public PerftService(
-        IServiceProvider serviceProvider,
-        IHostApplicationLifetime applicationLifetime)
-    {
-        _serviceProvider = serviceProvider;
-        _applicationLifetime = applicationLifetime;
-    }
-
-    public Task StartAsync(CancellationToken cancellationToken)
-    {
-        Log.Information("ChessLib Perft test program {Version}", Version);
-        Log.Information("High timer resolution : {HighRes}", Stopwatch.IsHighResolution);
-        Log.Information("Initializing..");
-
-        var bootstrap = BootstrapSetup.Create();
-
-        // enable DI support inside this ActorSystem, if needed
-        var diSetup = DependencyResolverSetup.Create(_serviceProvider);
-
-        // merge this setup (and any others) together into ActorSystemSetup
-        var actorSystemSetup = bootstrap.And(diSetup);
-
-        // start ActorSystem
-        _actorSystem = ActorSystem.Create("perft", actorSystemSetup);
-        _perftActor = _actorSystem.ActorOf(PerftActor.Prop(_serviceProvider));
-
-        _perftActor.Tell(new PerftActor.StartPerft());
-
-        // add a continuation task that will guarantee shutdown of application if ActorSystem terminates
-        _actorSystem.WhenTerminated.ContinueWith(_ => { _applicationLifetime.StopApplication(); }, cancellationToken);
-        return Task.CompletedTask;
-    }
-
-    public async Task StopAsync(CancellationToken cancellationToken)
-    {
-        // strictly speaking this may not be necessary - terminating the ActorSystem would also work
-        // but this call guarantees that the shutdown of the cluster is graceful regardless
-        await CoordinatedShutdown.Get(_actorSystem).Run(CoordinatedShutdown.ClrExitReason.Instance);
-    }
-}
\ No newline at end of file
diff --git a/src/perft/Rudzoft.Perft.Domain/Rudzoft.Perft.Domain.csproj b/src/perft/Rudzoft.Perft.Domain/Rudzoft.Perft.Domain.csproj
new file mode 100644
index 00000000..3a635329
--- /dev/null
+++ b/src/perft/Rudzoft.Perft.Domain/Rudzoft.Perft.Domain.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net8.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+    </PropertyGroup>
+
+</Project>
diff --git a/src/perft/Rudzoft.Perft.Domain/RunPerft.cs b/src/perft/Rudzoft.Perft.Domain/RunPerft.cs
new file mode 100644
index 00000000..eed8d1c4
--- /dev/null
+++ b/src/perft/Rudzoft.Perft.Domain/RunPerft.cs
@@ -0,0 +1,3 @@
+namespace Rudzoft.Perft.Domain;
+
+public sealed record RunPerft;
diff --git a/src/Rudzoft.Perft/Actors/FenTargetActor.cs b/src/perft/Rudzoft.Perft/Actors/FenTargetActor.cs
similarity index 100%
rename from src/Rudzoft.Perft/Actors/FenTargetActor.cs
rename to src/perft/Rudzoft.Perft/Actors/FenTargetActor.cs
diff --git a/src/perft/Rudzoft.Perft/Actors/OutputActor.cs b/src/perft/Rudzoft.Perft/Actors/OutputActor.cs
new file mode 100644
index 00000000..e94f92c5
--- /dev/null
+++ b/src/perft/Rudzoft.Perft/Actors/OutputActor.cs
@@ -0,0 +1,25 @@
+using Akka.Actor;
+using Serilog;
+
+namespace Rudzoft.Perft.Actors;
+
+public sealed class OutputActor : UntypedActor
+{
+    private static readonly ILogger Log = Serilog.Log.ForContext<OutputActor>();
+
+    public sealed record Output(string Out);
+
+
+
+    protected override void OnReceive(object message)
+    {
+        if (message is Output output)
+        {
+            Console.WriteLine(output.Out);
+        }
+        else
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/perft/Rudzoft.Perft/Actors/PerftActor.cs b/src/perft/Rudzoft.Perft/Actors/PerftActor.cs
new file mode 100644
index 00000000..2b0c0610
--- /dev/null
+++ b/src/perft/Rudzoft.Perft/Actors/PerftActor.cs
@@ -0,0 +1,35 @@
+using System.Collections.Frozen;
+using Akka.Actor;
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.Perft.Domain;
+using Rudzoft.Perft.Options;
+using Rudzoft.Perft.Services;
+
+namespace Rudzoft.Perft.Actors;
+
+// ReSharper disable once ClassNeverInstantiated.Global
+public sealed class PerftActor : ReceiveActor
+{
+    private readonly IPerftRunner _perftRunner;
+    private readonly IActorRef _outputActor;
+
+    public PerftActor(IServiceProvider sp)
+    {
+        _perftRunner = sp.GetRequiredService<IPerftRunner>();
+        var props = Props.Create<OutputActor>();
+
+        var optionsFactory = sp.GetRequiredService<IOptionsFactory>();
+        var options = optionsFactory.Parse().ToFrozenSet();
+
+        foreach (var option in options)
+        {
+            if (option.Type == OptionType.TTOptions)
+                _perftRunner.TranspositionTableOptions = option.PerftOptions;
+            else
+                _perftRunner.Options = option.PerftOptions;
+        }
+
+        _outputActor = Context.ActorOf(props, "output-actor");
+        ReceiveAsync<RunPerft>(_ => _perftRunner.Run());
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Actors/ProgressActor.cs b/src/perft/Rudzoft.Perft/Actors/ProgressActor.cs
similarity index 100%
rename from src/Rudzoft.Perft/Actors/ProgressActor.cs
rename to src/perft/Rudzoft.Perft/Actors/ProgressActor.cs
diff --git a/src/Rudzoft.Perft/GlobalUsings.cs b/src/perft/Rudzoft.Perft/GlobalUsings.cs
similarity index 100%
rename from src/Rudzoft.Perft/GlobalUsings.cs
rename to src/perft/Rudzoft.Perft/GlobalUsings.cs
diff --git a/src/Rudzoft.Perft/Models/CommandLineArgs.cs b/src/perft/Rudzoft.Perft/Models/CommandLineArgs.cs
similarity index 100%
rename from src/Rudzoft.Perft/Models/CommandLineArgs.cs
rename to src/perft/Rudzoft.Perft/Models/CommandLineArgs.cs
diff --git a/src/Rudzoft.Perft/Models/PerftResult.cs b/src/perft/Rudzoft.Perft/Models/PerftResult.cs
similarity index 100%
rename from src/Rudzoft.Perft/Models/PerftResult.cs
rename to src/perft/Rudzoft.Perft/Models/PerftResult.cs
diff --git a/src/Rudzoft.Perft/Options/EpdOptions.cs b/src/perft/Rudzoft.Perft/Options/EpdOptions.cs
similarity index 100%
rename from src/Rudzoft.Perft/Options/EpdOptions.cs
rename to src/perft/Rudzoft.Perft/Options/EpdOptions.cs
diff --git a/src/Rudzoft.Perft/Options/Fens.cs b/src/perft/Rudzoft.Perft/Options/Fens.cs
similarity index 100%
rename from src/Rudzoft.Perft/Options/Fens.cs
rename to src/perft/Rudzoft.Perft/Options/Fens.cs
diff --git a/src/Rudzoft.Perft/Options/IOptions.cs b/src/perft/Rudzoft.Perft/Options/IOptions.cs
similarity index 100%
rename from src/Rudzoft.Perft/Options/IOptions.cs
rename to src/perft/Rudzoft.Perft/Options/IOptions.cs
diff --git a/src/Rudzoft.Perft/Options/IOptionsFactory.cs b/src/perft/Rudzoft.Perft/Options/IOptionsFactory.cs
similarity index 100%
rename from src/Rudzoft.Perft/Options/IOptionsFactory.cs
rename to src/perft/Rudzoft.Perft/Options/IOptionsFactory.cs
diff --git a/src/Rudzoft.Perft/Options/OptionType.cs b/src/perft/Rudzoft.Perft/Options/OptionType.cs
similarity index 100%
rename from src/Rudzoft.Perft/Options/OptionType.cs
rename to src/perft/Rudzoft.Perft/Options/OptionType.cs
diff --git a/src/Rudzoft.Perft/Options/OptionsFactory.cs b/src/perft/Rudzoft.Perft/Options/OptionsFactory.cs
similarity index 100%
rename from src/Rudzoft.Perft/Options/OptionsFactory.cs
rename to src/perft/Rudzoft.Perft/Options/OptionsFactory.cs
diff --git a/src/Rudzoft.Perft/Options/TTOptions.cs b/src/perft/Rudzoft.Perft/Options/TTOptions.cs
similarity index 100%
rename from src/Rudzoft.Perft/Options/TTOptions.cs
rename to src/perft/Rudzoft.Perft/Options/TTOptions.cs
diff --git a/src/Rudzoft.Perft/Parsers/EpdParser.cs b/src/perft/Rudzoft.Perft/Parsers/EpdParser.cs
similarity index 100%
rename from src/Rudzoft.Perft/Parsers/EpdParser.cs
rename to src/perft/Rudzoft.Perft/Parsers/EpdParser.cs
diff --git a/src/Rudzoft.Perft/Parsers/EpdParserSettings.cs b/src/perft/Rudzoft.Perft/Parsers/EpdParserSettings.cs
similarity index 100%
rename from src/Rudzoft.Perft/Parsers/EpdParserSettings.cs
rename to src/perft/Rudzoft.Perft/Parsers/EpdParserSettings.cs
diff --git a/src/Rudzoft.Perft/Parsers/EpdSet.cs b/src/perft/Rudzoft.Perft/Parsers/EpdSet.cs
similarity index 100%
rename from src/Rudzoft.Perft/Parsers/EpdSet.cs
rename to src/perft/Rudzoft.Perft/Parsers/EpdSet.cs
diff --git a/src/Rudzoft.Perft/Parsers/IEpdParser.cs b/src/perft/Rudzoft.Perft/Parsers/IEpdParser.cs
similarity index 100%
rename from src/Rudzoft.Perft/Parsers/IEpdParser.cs
rename to src/perft/Rudzoft.Perft/Parsers/IEpdParser.cs
diff --git a/src/Rudzoft.Perft/Parsers/IEpdParserSettings.cs b/src/perft/Rudzoft.Perft/Parsers/IEpdParserSettings.cs
similarity index 100%
rename from src/Rudzoft.Perft/Parsers/IEpdParserSettings.cs
rename to src/perft/Rudzoft.Perft/Parsers/IEpdParserSettings.cs
diff --git a/src/Rudzoft.Perft/Parsers/IEpdSet.cs b/src/perft/Rudzoft.Perft/Parsers/IEpdSet.cs
similarity index 100%
rename from src/Rudzoft.Perft/Parsers/IEpdSet.cs
rename to src/perft/Rudzoft.Perft/Parsers/IEpdSet.cs
diff --git a/src/perft/Rudzoft.Perft/Program.cs b/src/perft/Rudzoft.Perft/Program.cs
new file mode 100644
index 00000000..9dbdcf05
--- /dev/null
+++ b/src/perft/Rudzoft.Perft/Program.cs
@@ -0,0 +1,90 @@
+using Akka.Actor;
+using Akka.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.Perft;
+using Rudzoft.ChessLib.Perft.Interfaces;
+using Rudzoft.Perft.Actors;
+using Rudzoft.Perft.Models;
+using Rudzoft.Perft.Options;
+using Rudzoft.Perft.Parsers;
+using Rudzoft.Perft.Services;
+using Serilog;
+
+var host = new HostBuilder()
+           .ConfigureServices((_, services) =>
+           {
+               var configuration = ConfigurationBuilder().Build();
+               services.AddSingleton(configuration);
+               services.AddSingleton(new CommandLineArgs(args));
+
+               services.AddChessLib(configuration);
+
+               services.AddSingleton(ConfigureLogger(configuration));
+
+               services.AddTransient<IPerft, Perft>();
+               services.AddTransient<IPerftRunner, PerftRunner>();
+               services.AddSingleton<IOptionsFactory, OptionsFactory>();
+
+               services.AddSingleton<IEpdParserSettings, EpdParserSettings>();
+               services.AddTransient<IEpdSet, EpdSet>();
+               services.AddSingleton<IEpdParser, EpdParser>();
+
+               services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
+               services.TryAddSingleton(serviceProvider =>
+               {
+                   var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                   var policy = new DefaultPooledObjectPolicy<PerftResult>();
+                   return provider.Create(policy);
+               });
+
+               services.AddAkka("perft-system", (builder, sp) =>
+               {
+                   builder.WithActors((system, registry) =>
+                   {
+                       var props = Props.Create<PerftActor>(sp);
+                       var perftActor = system.ActorOf(props, "perft-actor");
+                       registry.Register<PerftActor>(perftActor);
+                   });
+               });
+
+               services.AddHostedService<PerftService>();
+           })
+           .Build();
+
+host.Run();
+return;
+
+static ILogger ConfigureLogger(IConfiguration configuration)
+{
+    // Apply the config to the logger
+    Log.Logger = new LoggerConfiguration()
+                 .ReadFrom.Configuration(configuration)
+                 .Enrich.WithThreadId()
+                 .Enrich.FromLogContext()
+                 .CreateLogger();
+    AppDomain.CurrentDomain.ProcessExit += static (_, _) => Log.CloseAndFlush();
+    return Log.Logger;
+}
+
+static IConfigurationBuilder ConfigurationBuilder()
+{
+#if RELEASE
+    const string envName = "Production";
+#else
+    const string envName = "Development";
+#endif
+    // Create our configuration sources
+    return new ConfigurationBuilder()
+           // Add environment variables
+           .AddEnvironmentVariables()
+           // Set base path for Json files as the startup location of the application
+           .SetBasePath(Directory.GetCurrentDirectory())
+           // Add application settings json files
+           .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
+           .AddJsonFile($"appsettings.{envName}.json", optional: true, reloadOnChange: false);
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/perft/Rudzoft.Perft/Rudzoft.Perft.csproj
similarity index 72%
rename from src/Rudzoft.Perft/Rudzoft.Perft.csproj
rename to src/perft/Rudzoft.Perft/Rudzoft.Perft.csproj
index d072ebfb..df235a4c 100644
--- a/src/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/perft/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -7,6 +7,7 @@
     <ItemGroup>
       <PackageReference Include="Akka"/>
       <PackageReference Include="Akka.DependencyInjection"/>
+      <PackageReference Include="Akka.Hosting" />
       <PackageReference Include="CommandLineParser"/>
       <PackageReference Include="Serilog"/>
       <PackageReference Include="Serilog.Sinks.Console"/>
@@ -17,8 +18,9 @@
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
-      <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
+      <ProjectReference Include="..\..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
+      <ProjectReference Include="..\..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
+      <ProjectReference Include="..\Rudzoft.Perft.Domain\Rudzoft.Perft.Domain.csproj" />
     </ItemGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.Perft/Services/IPerftRunner.cs b/src/perft/Rudzoft.Perft/Services/IPerftRunner.cs
similarity index 100%
rename from src/Rudzoft.Perft/Services/IPerftRunner.cs
rename to src/perft/Rudzoft.Perft/Services/IPerftRunner.cs
diff --git a/src/Rudzoft.Perft/Services/PerftRunner.cs b/src/perft/Rudzoft.Perft/Services/PerftRunner.cs
similarity index 100%
rename from src/Rudzoft.Perft/Services/PerftRunner.cs
rename to src/perft/Rudzoft.Perft/Services/PerftRunner.cs
diff --git a/src/perft/Rudzoft.Perft/Services/PerftService.cs b/src/perft/Rudzoft.Perft/Services/PerftService.cs
new file mode 100644
index 00000000..5d5e9754
--- /dev/null
+++ b/src/perft/Rudzoft.Perft/Services/PerftService.cs
@@ -0,0 +1,48 @@
+using System.Diagnostics;
+using Akka.Actor;
+using Akka.Hosting;
+using Microsoft.Extensions.Hosting;
+using Rudzoft.Perft.Actors;
+using Rudzoft.Perft.Domain;
+using Serilog;
+
+namespace Rudzoft.Perft.Services;
+
+public sealed class PerftService : IHostedService
+{
+    private const string Version = "v0.2.0";
+
+    private static readonly ILogger Log = Serilog.Log.ForContext<PerftService>();
+
+    private readonly ActorSystem _actorSystem;
+    private readonly IRequiredActor<PerftActor> _perftActor;
+
+    public PerftService(
+        IHostApplicationLifetime applicationLifetime,
+        ActorSystem actorSystem,
+        IRequiredActor<PerftActor> perftActor)
+    {
+        _actorSystem = actorSystem;
+        _perftActor = perftActor;
+
+        actorSystem.WhenTerminated.ContinueWith(_ => { applicationLifetime.StopApplication(); });
+    }
+
+    public async Task StartAsync(CancellationToken cancellationToken)
+    {
+        Log.Information("ChessLib Perft test program {Version}", Version);
+        Log.Information("High timer resolution : {HighRes}", Stopwatch.IsHighResolution);
+        Log.Information("Initializing..");
+
+        var perftActor = await _perftActor.GetAsync(cancellationToken);
+
+        perftActor.Tell(new RunPerft());
+    }
+
+    public async Task StopAsync(CancellationToken cancellationToken)
+    {
+        // strictly speaking this may not be necessary - terminating the ActorSystem would also work
+        // but this call guarantees that the shutdown of the cluster is graceful regardless
+        await CoordinatedShutdown.Get(_actorSystem).Run(CoordinatedShutdown.ClrExitReason.Instance);
+    }
+}
\ No newline at end of file
diff --git a/src/Rudzoft.Perft/appsettings.json b/src/perft/Rudzoft.Perft/appsettings.json
similarity index 100%
rename from src/Rudzoft.Perft/appsettings.json
rename to src/perft/Rudzoft.Perft/appsettings.json

From 790eee67b7fbbceb1d66109c1f1679a23d40b7ac Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Thu, 27 Jun 2024 23:36:10 +0200
Subject: [PATCH 106/119] re-arrange src files structure in a more vertical
 friendly way

---
 Rudzoft.ChessLib.sln                          | 119 ++---
 .../Icon => icon}/ChessLib.png                | Bin
 .../BenchBitOp1.txt                           |   0
 .../Rudzoft.ChessLib.Benchmark/BitOpBench.cs  |   0
 .../CharConvertBenchmark.cs                   |   0
 .../FenBenchmark.cs                           |   0
 .../InBetweenBenchmark.cs                     |   0
 .../IsNumericBenchmark.cs                     |   0
 .../IterateBenchmark.cs                       |   0
 .../KeyBenchmarks.cs                          |   0
 .../KnightMoveGenerateBenchmark.cs            |   0
 .../MagicBBInitBenchmark.cs                   |   0
 .../MoveGeneratorBenchmark.cs                 |   0
 .../MoveGeneratorBenchmarkInfo.md             |   0
 .../NumberCompactingBenchmark.cs              |   0
 .../Rudzoft.ChessLib.Benchmark/PerftBench.cs  |   0
 .../Rudzoft.ChessLib.Benchmark/Program.cs     |   0
 .../Rudzoft.ChessLib.Benchmark.csproj         |   4 +-
 .../ShiftFuncBench.cs                         |   0
 .../SquareIterateBenchmark.cs                 |   0
 .../StringBenchmarks.cs                       |   0
 .../Rudzoft.ChessLib.Benchmark/Usings.cs      |   0
 .../PgnMoveNotationTests/SanToMoveTests.cs    |   0
 .../PgnTests/PgnTests.cs                      |   0
 .../Rudzoft.ChessLib.PGN.Test.csproj          |   2 +-
 .../Rudzoft.ChessLib.PGN.Test/Usings.cs       |   0
 .../samples/sample.pgn                        |   0
 .../Rudzoft.ChessLib.PGN/IPgnParser.cs        |   0
 .../Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs |   0
 .../Rudzoft.ChessLib.PGN/PgnGame.cs           |   0
 .../Rudzoft.ChessLib.PGN/PgnGenerator.cs      |   0
 .../Rudzoft.ChessLib.PGN/PgnMove.cs           |   0
 .../PgnParserServiceCollectionExtensions.cs   |   0
 .../Rudzoft.ChessLib.PGN/RegexPgnParser.cs    |   0
 .../Rudzoft.ChessLib.PGN.csproj               |   0
 .../IPerft.cs                                 |   0
 .../PerftPosition.cs                          |   0
 .../Rudzoft.ChessLib.Perft.Interfaces.csproj  |   0
 .../Rudzoft.ChessLib.Perft/Perft.cs           |   0
 .../PerftPositionFactory.cs                   |   0
 .../Rudzoft.ChessLib.Perft/PerftTable.cs      |   0
 .../Rudzoft.ChessLib.Perft.csproj             |   0
 .../BitboardTests/BitboardDataTests.cs        |   0
 .../BitboardTests/BitboardTests.cs            |   0
 .../BitboardTests/LineTests.cs                |   0
 .../BitboardTests/MbbTests.cs                 |   0
 .../BoardTests/BoardTests.cs                  | 346 +++++++-------
 .../BookTests/BookFixture.cs                  |   0
 .../BookTests/PolyglotTests.cs                |   0
 .../BookTests/gm2600.bin                      | Bin
 .../CastleTests/BasicCastleTests.cs           |   0
 .../CastleTests/CastleRightTests.cs           |   0
 .../DataTests/DataTests.cs                    |   0
 .../DataTests/PieceSquareTests.cs             |   0
 .../DataTests/RecordHashTests.cs              |   0
 .../DataTests/StringExtensionsTests.cs        |   0
 .../EvaluationTests/KpkBitBaseTests.cs        |   0
 .../FENTests/FenTests.cs                      |   0
 .../FenceTests/FenceTests.cs                  |   0
 .../FileTests/FileEdgeDistanceTests.cs        |   0
 .../FileTests/FileTests.cs                    |   0
 .../GameplayTests/FoolsCheckMateTests.cs      |   0
 .../MateTests/MateTests.cs                    |   0
 .../MaterialTests/MaterialTests.cs            |   0
 .../MoveTests/MoveGen_49.cs                   |   0
 .../MoveTests/MoveGeneratorTests.cs           |   0
 .../MoveTests/MoveTests.cs                    |   0
 .../NotationTests/FanTests.cs                 | 270 +++++------
 .../NotationTests/IccfTests.cs                | 182 ++++----
 .../NotationTests/RanTests.cs                 | 190 ++++----
 .../NotationTests/SanTests.cs                 | 436 +++++++++---------
 .../Rudzoft.ChessLib.Test/NumberTests.cs      |   0
 .../PerftTests/PerftTest.cs                   |   0
 .../PerftTests/PerftTheoryData.cs             |   0
 .../PerftTests/PerftVerify.cs                 |   0
 .../PiecesTests/PawnDoubleAttackTests.cs      |   0
 .../PiecesTests/PawnPushTests.cs              |   0
 .../PiecesTests/PieceAttacks.cs               |   0
 .../PiecesTests/PieceAttacksBishopTests.cs    |   0
 .../PiecesTests/PieceAttacksKingTests.cs      |   0
 .../PiecesTests/PieceAttacksKnightTests.cs    |   0
 .../PiecesTests/PieceAttacksPawnTests.cs      |   0
 .../PiecesTests/PieceAttacksRookTests.cs      |   0
 .../PiecesTests/RegularMobilityFixture.cs     |   0
 .../PiecesTests/SliderMobilityFixture.cs      |   0
 .../PiecesTests/SliderMobilityTests.cs        |   0
 .../PositionTests/EnPassantFenTests.cs        |   0
 .../PositionTests/PositionTests.cs            |   0
 .../PositionTests/ValidationTests.cs          |   0
 .../ProtocolTests/OptionsTests.cs             | 116 ++---
 .../ProtocolTests/SearchParameterTests.cs     |   0
 .../ProtocolTests/UciTests.cs                 |   0
 .../RankTests/RankEdgeDistanceTests.cs        |   0
 .../Rudzoft.ChessLib.Test.csproj              |   0
 .../ScoreTests/ScoreTests.cs                  |   0
 .../SizesTests/BaseTypesSizeTests.cs          |   0
 .../SquareTests/DistanceTests.cs              |   0
 .../SquareTests/SquareFromStringTests.cs      |   0
 .../SquareTests/SquareRangeTests.cs           |   0
 .../SquareTests/SquareTests.cs                |   0
 .../TablesTests/KillerMovesTests.cs           |   0
 .../Rudzoft.ChessLib.Test/Usings.cs           |   0
 .../ZobristTests/ZobristHashTests.cs          |   0
 .../Rudzoft.ChessLib.Test/xunit.runner.json   |   0
 .../Rudzoft.ChessLib/Blockage.cs              |   0
 src/{ => chess-lib}/Rudzoft.ChessLib/Board.cs |   0
 .../Rudzoft.ChessLib/Cuckoo.cs                |   0
 .../Rudzoft.ChessLib/Enums/ChessMode.cs       |   0
 .../Rudzoft.ChessLib/Enums/GameEndTypes.cs    |   0
 .../Rudzoft.ChessLib/Enums/GameResults.cs     |   0
 .../Rudzoft.ChessLib/Enums/MoveAmbiguities.cs |   0
 .../Enums/MoveGenerationTypes.cs              |   0
 .../Rudzoft.ChessLib/Enums/Phases.cs          |   0
 .../Evaluation/IKpkBitBase.cs                 |   0
 .../Rudzoft.ChessLib/Evaluation/KpkBitBase.cs |   0
 .../Exceptions/InvalidFenException.cs         |   0
 .../Exceptions/InvalidMove.cs                 |   0
 .../Exceptions/InvalidSquare.cs               |   0
 .../Exceptions/TranspositionTableFailure.cs   |   0
 .../Extensions/ArrayExtensions.cs             |   0
 .../ChessLibServiceCollectionExtensions.cs    |   0
 .../Extensions/MathExtensions.cs              |   0
 .../Rudzoft.ChessLib/Extensions/Maths.cs      |   0
 .../Extensions/PieceExtensions.cs             |   0
 .../Extensions/SpanExtensions.cs              |   0
 .../Extensions/StringExtensions.cs            |   0
 .../Factories/IKillerMovesFactory.cs          |   0
 .../Factories/IPolyglotBookFactory.cs         |   0
 .../Factories/IServiceFactory.cs              |   0
 .../Factories/KillerMovesFactory.cs           |   0
 .../Factories/PolyglotBookFactory.cs          |   0
 .../Factories/ServiceFactory.cs               |   0
 .../Rudzoft.ChessLib/Fen/Fen.cs               |   0
 .../Rudzoft.ChessLib/Fen/FenData.cs           |   0
 .../Rudzoft.ChessLib/Fen/IFenData.cs          |   0
 src/{ => chess-lib}/Rudzoft.ChessLib/Game.cs  |   0
 .../Rudzoft.ChessLib/GlobalSuppressions.cs    |   0
 .../Rudzoft.ChessLib/Hash/IRKiss.cs           |   0
 .../Rudzoft.ChessLib/Hash/IZobrist.cs         |   0
 .../Rudzoft.ChessLib/Hash/RKiss.cs            |   0
 .../Hash/Tables/Transposition/Bound.cs        |   0
 .../Transposition/ITranspositionTable.cs      |   0
 .../Hash/Tables/Transposition/PertT.cs        |   0
 .../Transposition/TranspositionTable.cs       |   0
 .../TranspositionTableConfiguration.cs        |   0
 .../Transposition/TranspositionTableEntry.cs  |   0
 .../Rudzoft.ChessLib/Hash/Zobrist.cs          |   0
 .../Rudzoft.ChessLib/IBlockage.cs             |   0
 .../Rudzoft.ChessLib/IBoard.cs                |   0
 .../Rudzoft.ChessLib/ICuckoo.cs               |   0
 src/{ => chess-lib}/Rudzoft.ChessLib/IGame.cs |   0
 .../Rudzoft.ChessLib/IPosition.cs             |   0
 .../Rudzoft.ChessLib/IValues.cs               |   0
 .../MoveGeneration/MoveFactory.cs             |   0
 .../MoveGeneration/MoveGenerator.cs           |   0
 .../MoveGeneration/MoveList.cs                |   0
 .../Notation/IMoveNotation.cs                 |   0
 .../Notation/INotationToMove.cs               |   0
 .../Rudzoft.ChessLib/Notation/MoveNotation.cs |   0
 .../Notation/NotationToMove.cs                |   0
 .../Notation/Notations/CoordinateNotation.cs  |   0
 .../Notation/Notations/FanNotation.cs         |   0
 .../Notation/Notations/INotation.cs           |   0
 .../Notation/Notations/IccfNotation.cs        |   0
 .../Notation/Notations/LanNotation.cs         |   0
 .../Notation/Notations/Notation.cs            |   0
 .../Notation/Notations/RanNotation.cs         |   0
 .../Notation/Notations/SanNotation.cs         |   0
 .../Notation/Notations/SmithNotation.cs       |   0
 .../Notation/Notations/UciNotation.cs         |   0
 .../Polyglot/IPolyglotBook.cs                 |   0
 .../Rudzoft.ChessLib/Polyglot/PolyglotBook.cs |   0
 .../Polyglot/PolyglotBookConfiguration.cs     |   0
 .../Polyglot/PolyglotBookEntry.cs             |   0
 .../Polyglot/PolyglotBookZobrist.cs           |   0
 .../ReverseEndianBinaryStreamReader.cs        |   0
 .../Rudzoft.ChessLib/Position.cs              |   0
 .../Rudzoft.ChessLib/Protocol/UCI/Clock.cs    |   0
 .../Protocol/UCI/CopyProtections.cs           |   0
 .../Rudzoft.ChessLib/Protocol/UCI/Cpu.cs      |   0
 .../Protocol/UCI/HiResTimer.cs                |   0
 .../Protocol/UCI/HiResTimerArgs.cs            |   0
 .../Rudzoft.ChessLib/Protocol/UCI/ICpu.cs     |   0
 .../Protocol/UCI/IHiResTimer.cs               |   0
 .../Protocol/UCI/IInputOutput.cs              |   0
 .../Protocol/UCI/IMovesToGoModel.cs           |   0
 .../Rudzoft.ChessLib/Protocol/UCI/IOption.cs  |   0
 .../Protocol/UCI/ISearchParameters.cs         |   0
 .../Rudzoft.ChessLib/Protocol/UCI/IUci.cs     |   0
 .../Protocol/UCI/InputOutput.cs               |   0
 .../Protocol/UCI/InputOutputAction.cs         |   0
 .../Protocol/UCI/MovesToGoModel.cs            |   0
 .../Rudzoft.ChessLib/Protocol/UCI/Option.cs   |   0
 .../Protocol/UCI/OptionComparer.cs            |   0
 .../Protocol/UCI/SearchParameters.cs          |   0
 .../Protocol/UCI/UCIProtocol.html             |   0
 .../Rudzoft.ChessLib/Protocol/UCI/Uci.cs      |   0
 .../Protocol/UCI/UciOptionType.cs             |   0
 .../Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |  16 +-
 src/{ => chess-lib}/Rudzoft.ChessLib/State.cs |   0
 .../Rudzoft.ChessLib/Tables/HashTable.cs      |   0
 .../Tables/History/HistoryHeuristic.cs        |   0
 .../Tables/History/IHistoryHeuristic.cs       |   0
 .../Rudzoft.ChessLib/Tables/IHashTable.cs     |   0
 .../Rudzoft.ChessLib/Tables/ITableEntry.cs    |   0
 .../Tables/KillerMoves/IKillerMoves.cs        |   0
 .../Tables/KillerMoves/KillerMoves.cs         |   0
 .../Tables/Perft/IPerftTableEntry.cs          |   0
 .../Tables/Perft/PerftTable.cs                |   0
 src/{ => chess-lib}/Rudzoft.ChessLib/ToDo.md  |   0
 .../Rudzoft.ChessLib/TroubledFens.txt         |   0
 .../Rudzoft.ChessLib/Types/BitBoard.cs        |   0
 .../Rudzoft.ChessLib/Types/BitBoards.cs       |   0
 .../Rudzoft.ChessLib/Types/CastleRight.cs     |   0
 .../Rudzoft.ChessLib/Types/Color.cs           |   0
 .../Rudzoft.ChessLib/Types/Depth.cs           |   0
 .../Rudzoft.ChessLib/Types/Direction.cs       |   0
 .../Rudzoft.ChessLib/Types/File.cs            |   0
 .../Rudzoft.ChessLib/Types/HashKey.cs         |   0
 .../Rudzoft.ChessLib/Types/IPieceSquare.cs    |   0
 .../Rudzoft.ChessLib/Types/IValidationType.cs |   0
 .../Rudzoft.ChessLib/Types/MagicBB.cs         |   0
 .../Rudzoft.ChessLib/Types/Move.cs            |   0
 .../Rudzoft.ChessLib/Types/Piece.cs           |   0
 .../Rudzoft.ChessLib/Types/PieceSquare.cs     |   6 +-
 .../Types/PieceSquareEventArgs.cs             |   0
 .../Rudzoft.ChessLib/Types/PieceType.cs       |   0
 .../Rudzoft.ChessLib/Types/Rank.cs            |   0
 .../Rudzoft.ChessLib/Types/RootMove.cs        |  88 ++--
 .../Rudzoft.ChessLib/Types/Score.cs           |   0
 .../Rudzoft.ChessLib/Types/Square.cs          |   0
 .../Rudzoft.ChessLib/Types/StateStack.cs      |   0
 .../Rudzoft.ChessLib/Types/ValMove.cs         |   0
 .../Rudzoft.ChessLib/Types/Value.cs           |   0
 .../Rudzoft.ChessLib/Types/Values.cs          |   0
 .../Validation/IPositionValidator.cs          |   0
 .../Validation/PositionValidationResult.cs    |   0
 .../Validation/PositionValidator.cs           |   0
 src/perft/Rudzoft.Perft/Rudzoft.Perft.csproj  |   5 +-
 .../Rudzoft.ChessLib.WebApi/.dockerignore     |   0
 .../Controllers/MovesController.cs            |   0
 .../Controllers/PgnController.cs              |   0
 .../Rudzoft.ChessLib.WebApi/Dockerfile        |   0
 .../Rudzoft.ChessLib.WebApi/Moves.cs          |   0
 .../Rudzoft.ChessLib.WebApi/Program.cs        |   0
 .../Properties/launchSettings.json            |   0
 .../Queries/MoveQuery.cs                      |   0
 .../Rudzoft.ChessLib.WebApi.csproj            |   7 +-
 .../Services/IMoveGeneratorService.cs         |   0
 .../Services/MoveGeneratorService.cs          |   0
 .../appsettings.Development.json              |   0
 .../Rudzoft.ChessLib.WebApi/appsettings.json  |   0
 252 files changed, 898 insertions(+), 889 deletions(-)
 rename {src/Rudzoft.ChessLib/Icon => icon}/ChessLib.png (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/BenchBitOp1.txt (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/BitOpBench.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/KnightMoveGenerateBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/MagicBBInitBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmarkInfo.md (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/NumberCompactingBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/PerftBench.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/Program.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj (56%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs (100%)
 rename src/{ => chess-lib.bench}/Rudzoft.ChessLib.Benchmark/Usings.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj (88%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN.Test/Usings.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN/IPgnParser.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN/PgnGame.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN/PgnGenerator.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN/PgnMove.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN/PgnParserServiceCollectionExtensions.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN/RegexPgnParser.cs (100%)
 rename src/{ => chess-lib.pgn}/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Perft/Perft.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Perft/PerftTable.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/BitboardTests/LineTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/BitboardTests/MbbTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs (97%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/BookTests/gm2600.bin (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/CastleTests/CastleRightTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/DataTests/DataTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/DataTests/PieceSquareTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/FENTests/FenTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/FileTests/FileTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/MateTests/MateTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/MaterialTests/MaterialTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs (97%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs (97%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs (97%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs (97%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/NumberTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/PawnPushTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs (97%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/ProtocolTests/SearchParameterTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/SquareTests/DistanceTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/SquareTests/SquareFromStringTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/Usings.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib.Test/xunit.runner.json (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Blockage.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Board.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Cuckoo.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Enums/ChessMode.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Enums/GameEndTypes.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Enums/GameResults.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Enums/Phases.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Exceptions/InvalidMove.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Extensions/MathExtensions.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Extensions/Maths.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Extensions/PieceExtensions.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Extensions/SpanExtensions.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Extensions/StringExtensions.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Factories/IKillerMovesFactory.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Factories/IServiceFactory.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Factories/KillerMovesFactory.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Factories/ServiceFactory.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Fen/Fen.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Fen/FenData.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Fen/IFenData.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Game.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/GlobalSuppressions.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/IRKiss.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/IZobrist.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/RKiss.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Hash/Zobrist.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/IBlockage.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/IBoard.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/ICuckoo.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/IGame.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/IPosition.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/IValues.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/MoveGeneration/MoveList.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/IMoveNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/INotationToMove.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/MoveNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/NotationToMove.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/INotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/Notation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Position.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/Clock.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/CopyProtections.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/HiResTimerArgs.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/IHiResTimer.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/IOption.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/IUci.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/InputOutputAction.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/Option.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/UCIProtocol.html (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/Uci.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Protocol/UCI/UciOptionType.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj (85%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/State.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Tables/HashTable.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Tables/IHashTable.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Tables/ITableEntry.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/ToDo.md (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/TroubledFens.txt (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/BitBoard.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/BitBoards.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/CastleRight.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Color.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Depth.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Direction.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/File.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/HashKey.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/IPieceSquare.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/IValidationType.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/MagicBB.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Move.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Piece.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/PieceSquare.cs (97%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/PieceType.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Rank.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/RootMove.cs (97%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Score.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Square.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/StateStack.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/ValMove.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Value.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Types/Values.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Validation/IPositionValidator.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Validation/PositionValidationResult.cs (100%)
 rename src/{ => chess-lib}/Rudzoft.ChessLib/Validation/PositionValidator.cs (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/.dockerignore (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Controllers/MovesController.cs (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Controllers/PgnController.cs (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Dockerfile (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Moves.cs (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Program.cs (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Properties/launchSettings.json (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj (78%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Services/IMoveGeneratorService.cs (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/appsettings.Development.json (100%)
 rename src/{ => web-api}/Rudzoft.ChessLib.WebApi/appsettings.json (100%)

diff --git a/Rudzoft.ChessLib.sln b/Rudzoft.ChessLib.sln
index 18405279..64869614 100644
--- a/Rudzoft.ChessLib.sln
+++ b/Rudzoft.ChessLib.sln
@@ -3,28 +3,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 17
 VisualStudioVersion = 17.0.32112.339
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.ChessLib", "src\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj", "{44D2509D-D267-4416-92FB-FDE8381A3F46}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.ChessLib.Test", "src\Rudzoft.ChessLib.Test\Rudzoft.ChessLib.Test.csproj", "{45A5E2D9-97E5-49FA-8066-9FEA4609A312}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.ChessLib.Benchmark", "src\Rudzoft.ChessLib.Benchmark\Rudzoft.ChessLib.Benchmark.csproj", "{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.ChessLib.Perft", "src\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj", "{F253BA49-993B-4119-8226-A6C6DF0B556D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.ChessLib.Perft.Interfaces", "src\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj", "{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rudzoft.ChessLib.WebApi", "src\Rudzoft.ChessLib.WebApi\Rudzoft.ChessLib.WebApi.csproj", "{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}"
-EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chess-lib", "chess-lib", "{9F0623CB-4463-40FE-874F-4A18D1871A91}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perft", "perft", "{96596E23-0DAC-46B7-AEE2-24F55C837AFF}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web-api", "web-api", "{F82A6A7E-4551-4667-9E0A-57036E1B5117}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN", "src\Rudzoft.ChessLib.PGN\Rudzoft.ChessLib.PGN.csproj", "{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN.Test", "src\Rudzoft.ChessLib.PGN.Test\Rudzoft.ChessLib.PGN.Test.csproj", "{556ADC0D-074D-4B8F-9E45-1275342FCB74}"
-EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5B4D0B75-CE2C-45E2-92D3-987704C20001}"
 	ProjectSection(SolutionItems) = preProject
 		README.md = README.md
@@ -32,50 +16,41 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		global.json = global.json
 		.editorconfig = .editorconfig
 		Directory.Packages.props = Directory.Packages.props
+		LICENSE = LICENSE
+		.gitignore = .gitignore
+		icon\ChessLib.png = icon\ChessLib.png
 	EndProjectSection
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.Perft.Domain", "src\perft\Rudzoft.Perft.Domain\Rudzoft.Perft.Domain.csproj", "{374C6338-57B4-44CD-9A40-B13EBC25BCF6}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.Perft", "src\perft\Rudzoft.Perft\Rudzoft.Perft.csproj", "{279378EB-73AF-450C-909C-7145DBE463E0}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chess-lib.bench", "chess-lib.bench", "{74DC6FDD-D550-4185-9635-1201508E8F22}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chess-lib.pgn", "chess-lib.pgn", "{CFADCDB7-1278-42ED-A25C-9B3FBB58C520}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib", "src\chess-lib\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj", "{CD9A0992-9687-41A1-8C02-DE4063F489DF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.Test", "src\chess-lib\Rudzoft.ChessLib.Test\Rudzoft.ChessLib.Test.csproj", "{71E13772-CD99-44F7-8F5A-BC7E96300280}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.Perft.Interfaces", "src\chess-lib\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj", "{9FFDC3AE-B0B2-403E-AA1E-C1CB04A2F5BE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.Perft", "src\chess-lib\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj", "{D19AFEC7-0EF0-4442-9AB8-2ECFCA2D6246}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.Benchmark", "src\chess-lib.bench\Rudzoft.ChessLib.Benchmark\Rudzoft.ChessLib.Benchmark.csproj", "{18BB06F4-F9B5-4999-8FC5-D5D479FEA82B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN", "src\chess-lib.pgn\Rudzoft.ChessLib.PGN\Rudzoft.ChessLib.PGN.csproj", "{289BE06C-D071-4620-B7A7-E119F34A40A6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.PGN.Test", "src\chess-lib.pgn\Rudzoft.ChessLib.PGN.Test\Rudzoft.ChessLib.PGN.Test.csproj", "{BF3D8F4C-743B-4F9F-A227-2763BF8DA2A6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rudzoft.ChessLib.WebApi", "src\web-api\Rudzoft.ChessLib.WebApi\Rudzoft.ChessLib.WebApi.csproj", "{F283CA88-BE8D-4A89-8402-357B375877B8}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Release|Any CPU = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{44D2509D-D267-4416-92FB-FDE8381A3F46}.Release|Any CPU.Build.0 = Release|Any CPU
-		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{45A5E2D9-97E5-49FA-8066-9FEA4609A312}.Release|Any CPU.Build.0 = Release|Any CPU
-		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F253BA49-993B-4119-8226-A6C6DF0B556D}.Release|Any CPU.Build.0 = Release|Any CPU
-		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C}.Release|Any CPU.Build.0 = Release|Any CPU
-		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8}.Release|Any CPU.Build.0 = Release|Any CPU
-		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2}.Release|Any CPU.Build.0 = Release|Any CPU
-		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{556ADC0D-074D-4B8F-9E45-1275342FCB74}.Release|Any CPU.Build.0 = Release|Any CPU
 		{374C6338-57B4-44CD-9A40-B13EBC25BCF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{374C6338-57B4-44CD-9A40-B13EBC25BCF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{374C6338-57B4-44CD-9A40-B13EBC25BCF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -84,21 +59,53 @@ Global
 		{279378EB-73AF-450C-909C-7145DBE463E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{279378EB-73AF-450C-909C-7145DBE463E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{279378EB-73AF-450C-909C-7145DBE463E0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{CD9A0992-9687-41A1-8C02-DE4063F489DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CD9A0992-9687-41A1-8C02-DE4063F489DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CD9A0992-9687-41A1-8C02-DE4063F489DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CD9A0992-9687-41A1-8C02-DE4063F489DF}.Release|Any CPU.Build.0 = Release|Any CPU
+		{71E13772-CD99-44F7-8F5A-BC7E96300280}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{71E13772-CD99-44F7-8F5A-BC7E96300280}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{71E13772-CD99-44F7-8F5A-BC7E96300280}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{71E13772-CD99-44F7-8F5A-BC7E96300280}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9FFDC3AE-B0B2-403E-AA1E-C1CB04A2F5BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9FFDC3AE-B0B2-403E-AA1E-C1CB04A2F5BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9FFDC3AE-B0B2-403E-AA1E-C1CB04A2F5BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9FFDC3AE-B0B2-403E-AA1E-C1CB04A2F5BE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D19AFEC7-0EF0-4442-9AB8-2ECFCA2D6246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D19AFEC7-0EF0-4442-9AB8-2ECFCA2D6246}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D19AFEC7-0EF0-4442-9AB8-2ECFCA2D6246}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D19AFEC7-0EF0-4442-9AB8-2ECFCA2D6246}.Release|Any CPU.Build.0 = Release|Any CPU
+		{18BB06F4-F9B5-4999-8FC5-D5D479FEA82B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{18BB06F4-F9B5-4999-8FC5-D5D479FEA82B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{18BB06F4-F9B5-4999-8FC5-D5D479FEA82B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{18BB06F4-F9B5-4999-8FC5-D5D479FEA82B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{289BE06C-D071-4620-B7A7-E119F34A40A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{289BE06C-D071-4620-B7A7-E119F34A40A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{289BE06C-D071-4620-B7A7-E119F34A40A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{289BE06C-D071-4620-B7A7-E119F34A40A6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{BF3D8F4C-743B-4F9F-A227-2763BF8DA2A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BF3D8F4C-743B-4F9F-A227-2763BF8DA2A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BF3D8F4C-743B-4F9F-A227-2763BF8DA2A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BF3D8F4C-743B-4F9F-A227-2763BF8DA2A6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F283CA88-BE8D-4A89-8402-357B375877B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F283CA88-BE8D-4A89-8402-357B375877B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F283CA88-BE8D-4A89-8402-357B375877B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F283CA88-BE8D-4A89-8402-357B375877B8}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
-		{44D2509D-D267-4416-92FB-FDE8381A3F46} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
-		{45A5E2D9-97E5-49FA-8066-9FEA4609A312} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
-		{BF9E9B6E-947E-40E3-956F-AE6A82D5C237} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
-		{F253BA49-993B-4119-8226-A6C6DF0B556D} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
-		{4AD06BC1-F944-41E8-AAF8-3AA02642E18C} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
-		{CFC3DABB-CC74-4B7E-A434-B0F3DD621DE8} = {F82A6A7E-4551-4667-9E0A-57036E1B5117}
-		{07F55E9A-ACF2-4F93-AD03-9E6F41FF78A2} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
-		{556ADC0D-074D-4B8F-9E45-1275342FCB74} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
 		{374C6338-57B4-44CD-9A40-B13EBC25BCF6} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
 		{279378EB-73AF-450C-909C-7145DBE463E0} = {96596E23-0DAC-46B7-AEE2-24F55C837AFF}
+		{CD9A0992-9687-41A1-8C02-DE4063F489DF} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
+		{71E13772-CD99-44F7-8F5A-BC7E96300280} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
+		{9FFDC3AE-B0B2-403E-AA1E-C1CB04A2F5BE} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
+		{D19AFEC7-0EF0-4442-9AB8-2ECFCA2D6246} = {9F0623CB-4463-40FE-874F-4A18D1871A91}
+		{18BB06F4-F9B5-4999-8FC5-D5D479FEA82B} = {74DC6FDD-D550-4185-9635-1201508E8F22}
+		{289BE06C-D071-4620-B7A7-E119F34A40A6} = {CFADCDB7-1278-42ED-A25C-9B3FBB58C520}
+		{BF3D8F4C-743B-4F9F-A227-2763BF8DA2A6} = {CFADCDB7-1278-42ED-A25C-9B3FBB58C520}
+		{F283CA88-BE8D-4A89-8402-357B375877B8} = {F82A6A7E-4551-4667-9E0A-57036E1B5117}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {0B507B2E-EC45-4DDA-94DD-B7CD5A2B8CF3}
diff --git a/src/Rudzoft.ChessLib/Icon/ChessLib.png b/icon/ChessLib.png
similarity index 100%
rename from src/Rudzoft.ChessLib/Icon/ChessLib.png
rename to icon/ChessLib.png
diff --git a/src/Rudzoft.ChessLib.Benchmark/BenchBitOp1.txt b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/BenchBitOp1.txt
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/BenchBitOp1.txt
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/BenchBitOp1.txt
diff --git a/src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/BitOpBench.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/CharConvertBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/FenBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/InBetweenBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/IsNumericBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/IterateBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/KnightMoveGenerateBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/KnightMoveGenerateBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/KnightMoveGenerateBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/KnightMoveGenerateBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/MagicBBInitBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/MagicBBInitBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/MagicBBInitBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/MagicBBInitBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmarkInfo.md b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmarkInfo.md
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmarkInfo.md
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/MoveGeneratorBenchmarkInfo.md
diff --git a/src/Rudzoft.ChessLib.Benchmark/NumberCompactingBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/NumberCompactingBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/NumberCompactingBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/NumberCompactingBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/PerftBench.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/PerftBench.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/PerftBench.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/Program.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Program.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/Program.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Program.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
similarity index 56%
rename from src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 47411d0d..147d10d7 100644
--- a/src/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -11,8 +11,10 @@
     </ItemGroup>
 
     <ItemGroup>
+        <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
+        <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
+        <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
         <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj"/>
-        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
     </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/ShiftFuncBench.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/SquareIterateBenchmark.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/StringBenchmarks.cs
diff --git a/src/Rudzoft.ChessLib.Benchmark/Usings.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Usings.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Benchmark/Usings.cs
rename to src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Usings.cs
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/PgnMoveNotationTests/SanToMoveTests.cs
diff --git a/src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/PgnTests/PgnTests.cs
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
similarity index 88%
rename from src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
index 43f7b0c1..a953d738 100644
--- a/src/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
+++ b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/Rudzoft.ChessLib.PGN.Test.csproj
@@ -14,8 +14,8 @@
     </ItemGroup>
 
     <ItemGroup>
+        <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
         <ProjectReference Include="..\Rudzoft.ChessLib.PGN\Rudzoft.ChessLib.PGN.csproj"/>
-        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
     </ItemGroup>
 
     <ItemGroup>
diff --git a/src/Rudzoft.ChessLib.PGN.Test/Usings.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/Usings.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN.Test/Usings.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/Usings.cs
diff --git a/src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN.Test/samples/sample.pgn
diff --git a/src/Rudzoft.ChessLib.PGN/IPgnParser.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN/IPgnParser.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN/IPgnParser.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN/IPgnParser.cs
diff --git a/src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN/NonRegexPgnParser.cs
diff --git a/src/Rudzoft.ChessLib.PGN/PgnGame.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN/PgnGame.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN/PgnGame.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN/PgnGame.cs
diff --git a/src/Rudzoft.ChessLib.PGN/PgnGenerator.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN/PgnGenerator.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN/PgnGenerator.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN/PgnGenerator.cs
diff --git a/src/Rudzoft.ChessLib.PGN/PgnMove.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN/PgnMove.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN/PgnMove.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN/PgnMove.cs
diff --git a/src/Rudzoft.ChessLib.PGN/PgnParserServiceCollectionExtensions.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN/PgnParserServiceCollectionExtensions.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN/PgnParserServiceCollectionExtensions.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN/PgnParserServiceCollectionExtensions.cs
diff --git a/src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN/RegexPgnParser.cs
diff --git a/src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj b/src/chess-lib.pgn/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
similarity index 100%
rename from src/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
rename to src/chess-lib.pgn/Rudzoft.ChessLib.PGN/Rudzoft.ChessLib.PGN.csproj
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs b/src/chess-lib/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
rename to src/chess-lib/Rudzoft.ChessLib.Perft.Interfaces/IPerft.cs
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs b/src/chess-lib/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
rename to src/chess-lib/Rudzoft.ChessLib.Perft.Interfaces/PerftPosition.cs
diff --git a/src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj b/src/chess-lib/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
similarity index 100%
rename from src/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
rename to src/chess-lib/Rudzoft.ChessLib.Perft.Interfaces/Rudzoft.ChessLib.Perft.Interfaces.csproj
diff --git a/src/Rudzoft.ChessLib.Perft/Perft.cs b/src/chess-lib/Rudzoft.ChessLib.Perft/Perft.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Perft/Perft.cs
rename to src/chess-lib/Rudzoft.ChessLib.Perft/Perft.cs
diff --git a/src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs b/src/chess-lib/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs
rename to src/chess-lib/Rudzoft.ChessLib.Perft/PerftPositionFactory.cs
diff --git a/src/Rudzoft.ChessLib.Perft/PerftTable.cs b/src/chess-lib/Rudzoft.ChessLib.Perft/PerftTable.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Perft/PerftTable.cs
rename to src/chess-lib/Rudzoft.ChessLib.Perft/PerftTable.cs
diff --git a/src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/chess-lib/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
similarity index 100%
rename from src/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
rename to src/chess-lib/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/BitboardTests/BitboardDataTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/BitboardTests/BitboardTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/LineTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/BitboardTests/LineTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/BitboardTests/LineTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/BitboardTests/LineTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/BitboardTests/MbbTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/BitboardTests/MbbTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/BitboardTests/MbbTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/BitboardTests/MbbTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
similarity index 97%
rename from src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
index 0497d9da..289d80f4 100644
--- a/src/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/BoardTests/BoardTests.cs
@@ -1,174 +1,174 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.ObjectPool;
-using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Hash;
-using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.Types;
-using Rudzoft.ChessLib.Validation;
-
-namespace Rudzoft.ChessLib.Test.BoardTests;
-
-public sealed class BoardTestsTheoryData : TheoryData<string, PieceType, Color, int>
-{
-    public BoardTestsTheoryData(string[] fens, PieceType[] pts, Color[] players, int[] expectedCounts)
-    {
-        Debug.Assert(fens != null);
-        Debug.Assert(pts != null);
-        Debug.Assert(players != null);
-        Debug.Assert(expectedCounts != null);
-        Debug.Assert(fens.Length == pts.Length);
-        Debug.Assert(fens.Length == players.Length);
-        Debug.Assert(fens.Length == expectedCounts.Length);
-
-        var fensSpan = fens.AsSpan();
-        var ptsSpan = pts.AsSpan();
-        var playersSpan = players.AsSpan();
-        var expectedCountSpan = expectedCounts.AsSpan();
-
-        ref var fenSpace = ref MemoryMarshal.GetReference(fensSpan);
-        ref var ptsSpace = ref MemoryMarshal.GetReference(ptsSpan);
-        ref var playersSpace = ref MemoryMarshal.GetReference(playersSpan);
-        ref var expectedCountSpace = ref MemoryMarshal.GetReference(expectedCountSpan);
-
-        for (var i = 0; i < fens.Length; ++i)
-        {
-            var fen = Unsafe.Add(ref fenSpace, i);
-            var pt = Unsafe.Add(ref ptsSpace, i);
-            var player = Unsafe.Add(ref playersSpace, i);
-            var expectedCount = Unsafe.Add(ref expectedCountSpace, i);
-            Add(fen, pt, player, expectedCount);
-        }
-    }
-}
-
-public sealed class BoardTests
-{
-    private readonly IServiceProvider _serviceProvider;
-
-    public BoardTests()
-    {
-        _serviceProvider = new ServiceCollection()
-                           .AddTransient<IBoard, Board>()
-                           .AddSingleton<IValues, Values>()
-                           .AddSingleton<IRKiss, RKiss>()
-                           .AddSingleton<IZobrist, Zobrist>()
-                           .AddSingleton<ICuckoo, Cuckoo>()
-                           .AddSingleton<IPositionValidator, PositionValidator>()
-                           .AddTransient<IPosition, Position>()
-                           .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
-                           .AddSingleton(static serviceProvider =>
-                           {
-                               var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                               var policy = new DefaultPooledObjectPolicy<MoveList>();
-                               return provider.Create(policy);
-                           })
-                           .BuildServiceProvider();
-    }
-
-    private static readonly string[] Fens =
-    [
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
-        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53"
-    ];
-
-
-    private static readonly Color[] Player =
-    [
-        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
-        Types.Color.Black,
-        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
-        Types.Color.Black,
-        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
-        Types.Color.Black,
-        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
-        Types.Color.Black
-    ];
-
-    private static readonly int[] ExpectedCount =
-    [
-        8, 8, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
-        4, 3, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1
-    ];
-
-    private static readonly PieceType[] PieceTypes =
-    [
-        PieceType.Pawn, PieceType.Pawn, PieceType.Knight, PieceType.Knight, PieceType.Bishop, PieceType.Bishop,
-        PieceType.Rook, PieceType.Rook, PieceType.Queen, PieceType.Queen, PieceType.King, PieceType.King,
-        PieceType.Pawn, PieceType.Pawn, PieceType.Knight, PieceType.Knight, PieceType.Bishop, PieceType.Bishop,
-        PieceType.Rook, PieceType.Rook, PieceType.Queen, PieceType.Queen, PieceType.King, PieceType.King
-    ];
-
-    public static readonly BoardTestsTheoryData TheoryData = new(Fens, PieceTypes, Player, ExpectedCount);
-
-    [Theory]
-    [MemberData(nameof(TheoryData))]
-    public void BoardPieceCount(string fen, PieceType pt, Color c, int expected)
-    {
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, in state);
-
-        var board = pos.Board;
-
-        var posCount = pos.Pieces(pt, c).Count;
-        var boardCount = board.PieceCount(pt, c);
-
-        Assert.Equal(posCount, boardCount);
-        Assert.Equal(expected, boardCount);
-    }
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Hash;
+using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.Types;
+using Rudzoft.ChessLib.Validation;
+
+namespace Rudzoft.ChessLib.Test.BoardTests;
+
+public sealed class BoardTestsTheoryData : TheoryData<string, PieceType, Color, int>
+{
+    public BoardTestsTheoryData(string[] fens, PieceType[] pts, Color[] players, int[] expectedCounts)
+    {
+        Debug.Assert(fens != null);
+        Debug.Assert(pts != null);
+        Debug.Assert(players != null);
+        Debug.Assert(expectedCounts != null);
+        Debug.Assert(fens.Length == pts.Length);
+        Debug.Assert(fens.Length == players.Length);
+        Debug.Assert(fens.Length == expectedCounts.Length);
+
+        var fensSpan = fens.AsSpan();
+        var ptsSpan = pts.AsSpan();
+        var playersSpan = players.AsSpan();
+        var expectedCountSpan = expectedCounts.AsSpan();
+
+        ref var fenSpace = ref MemoryMarshal.GetReference(fensSpan);
+        ref var ptsSpace = ref MemoryMarshal.GetReference(ptsSpan);
+        ref var playersSpace = ref MemoryMarshal.GetReference(playersSpan);
+        ref var expectedCountSpace = ref MemoryMarshal.GetReference(expectedCountSpan);
+
+        for (var i = 0; i < fens.Length; ++i)
+        {
+            var fen = Unsafe.Add(ref fenSpace, i);
+            var pt = Unsafe.Add(ref ptsSpace, i);
+            var player = Unsafe.Add(ref playersSpace, i);
+            var expectedCount = Unsafe.Add(ref expectedCountSpace, i);
+            Add(fen, pt, player, expectedCount);
+        }
+    }
+}
+
+public sealed class BoardTests
+{
+    private readonly IServiceProvider _serviceProvider;
+
+    public BoardTests()
+    {
+        _serviceProvider = new ServiceCollection()
+                           .AddTransient<IBoard, Board>()
+                           .AddSingleton<IValues, Values>()
+                           .AddSingleton<IRKiss, RKiss>()
+                           .AddSingleton<IZobrist, Zobrist>()
+                           .AddSingleton<ICuckoo, Cuckoo>()
+                           .AddSingleton<IPositionValidator, PositionValidator>()
+                           .AddTransient<IPosition, Position>()
+                           .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+                           .AddSingleton(static serviceProvider =>
+                           {
+                               var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                               var policy = new DefaultPooledObjectPolicy<MoveList>();
+                               return provider.Create(policy);
+                           })
+                           .BuildServiceProvider();
+    }
+
+    private static readonly string[] Fens =
+    [
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "rnbqkbnr/1ppQpppp/p2p4/8/8/2P5/PP1PPPPP/RNB1KBNR b KQkq - 1 6",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53",
+        "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53"
+    ];
+
+
+    private static readonly Color[] Player =
+    [
+        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
+        Types.Color.Black,
+        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
+        Types.Color.Black,
+        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
+        Types.Color.Black,
+        Types.Color.White, Types.Color.Black, Types.Color.White, Types.Color.Black, Types.Color.White,
+        Types.Color.Black
+    ];
+
+    private static readonly int[] ExpectedCount =
+    [
+        8, 8, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+        4, 3, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1
+    ];
+
+    private static readonly PieceType[] PieceTypes =
+    [
+        PieceType.Pawn, PieceType.Pawn, PieceType.Knight, PieceType.Knight, PieceType.Bishop, PieceType.Bishop,
+        PieceType.Rook, PieceType.Rook, PieceType.Queen, PieceType.Queen, PieceType.King, PieceType.King,
+        PieceType.Pawn, PieceType.Pawn, PieceType.Knight, PieceType.Knight, PieceType.Bishop, PieceType.Bishop,
+        PieceType.Rook, PieceType.Rook, PieceType.Queen, PieceType.Queen, PieceType.King, PieceType.King
+    ];
+
+    public static readonly BoardTestsTheoryData TheoryData = new(Fens, PieceTypes, Player, ExpectedCount);
+
+    [Theory]
+    [MemberData(nameof(TheoryData))]
+    public void BoardPieceCount(string fen, PieceType pt, Color c, int expected)
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, in state);
+
+        var board = pos.Board;
+
+        var posCount = pos.Pieces(pt, c).Count;
+        var boardCount = board.PieceCount(pt, c);
+
+        Assert.Equal(posCount, boardCount);
+        Assert.Equal(expected, boardCount);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs b/src/chess-lib/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/BookTests/BookFixture.cs
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/BookTests/gm2600.bin b/src/chess-lib/Rudzoft.ChessLib.Test/BookTests/gm2600.bin
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/BookTests/gm2600.bin
rename to src/chess-lib/Rudzoft.ChessLib.Test/BookTests/gm2600.bin
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/CastleTests/BasicCastleTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/CastleTests/CastleRightTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/CastleTests/CastleRightTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/CastleTests/CastleRightTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/CastleTests/CastleRightTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/DataTests/DataTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/PieceSquareTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/DataTests/PieceSquareTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/DataTests/PieceSquareTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/DataTests/PieceSquareTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/DataTests/RecordHashTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/DataTests/StringExtensionsTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/EvaluationTests/KpkBitBaseTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/FENTests/FenTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/FenceTests/FenceTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/FileTests/FileEdgeDistanceTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/FileTests/FileTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/FileTests/FileTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/FileTests/FileTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/GameplayTests/FoolsCheckMateTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/MateTests/MateTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/MateTests/MateTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/MateTests/MateTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/MateTests/MateTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/MaterialTests/MaterialTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/MaterialTests/MaterialTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/MaterialTests/MaterialTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/MaterialTests/MaterialTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs b/src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveGen_49.cs
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveGeneratorTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
similarity index 97%
rename from src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
index ae0fea53..b9bc3e49 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/FanTests.cs
@@ -1,136 +1,136 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Microsoft.Extensions.DependencyInjection;
-using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Extensions;
-using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Notation;
-using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Test.NotationTests;
-
-public sealed class FanTests
-{
-    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
-                                                         .AddChessLib()
-                                                         .BuildServiceProvider();
-
-    [Theory]
-    [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.Fan, PieceTypes.Knight, Squares.d2, Squares.f2,
-        Squares.e4)]
-    public void FanRankAmbiguities(
-        string fen,
-        MoveNotations moveNotation,
-        PieceTypes movingPt,
-        Squares fromSqOne,
-        Squares fromSqTwo,
-        Squares toSq)
-    {
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var pt = new PieceType(movingPt);
-        var pc = pt.MakePiece(pos.SideToMove);
-
-        var fromOne = new Square(fromSqOne);
-        var fromTwo = new Square(fromSqTwo);
-        var to      = new Square(toSq);
-
-        Assert.True(fromOne.IsOk);
-        Assert.True(fromTwo.IsOk);
-        Assert.True(to.IsOk);
-
-        var pieceChar = pc.GetUnicodeChar();
-        var toString  = to.ToString();
-
-        var moveOne = Move.Create(fromOne, to);
-        var moveTwo = Move.Create(fromTwo, to);
-
-        var notation = _serviceProvider.GetRequiredService<IMoveNotation>();
-
-        var expectedOne = $"{pieceChar}{fromOne.FileChar}{toString}";
-        var expectedTwo = $"{pieceChar}{fromTwo.FileChar}{toString}";
-
-        var actualOne = notation.ToNotation(moveNotation).Convert(pos, moveOne);
-        var actualTwo = notation.ToNotation(moveNotation).Convert(pos, moveTwo);
-
-        Assert.Equal(expectedOne, actualOne);
-        Assert.Equal(expectedTwo, actualTwo);
-    }
-
-    [Theory]
-    [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.Fan, PieceTypes.Knight, Squares.d2, Squares.d4,
-        Squares.f3)]
-    public void FanFileAmbiguities(
-        string fen,
-        MoveNotations moveNotation,
-        PieceTypes movingPt,
-        Squares fromSqOne,
-        Squares fromSqTwo,
-        Squares toSq)
-    {
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var pt = new PieceType(movingPt);
-        var pc = pt.MakePiece(pos.SideToMove);
-
-        var fromOne = new Square(fromSqOne);
-        var fromTwo = new Square(fromSqTwo);
-        var to      = new Square(toSq);
-
-        Assert.True(fromOne.IsOk);
-        Assert.True(fromTwo.IsOk);
-        Assert.True(to.IsOk);
-
-        var pieceChar = pc.GetUnicodeChar();
-        var toString  = to.ToString();
-
-        var moveOne = Move.Create(fromOne, to);
-        var moveTwo = Move.Create(fromTwo, to);
-
-        var notation = _serviceProvider.GetRequiredService<IMoveNotation>();
-
-        var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
-        var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
-
-        var actualOne = notation.ToNotation(moveNotation).Convert(pos, moveOne);
-        var actualTwo = notation.ToNotation(moveNotation).Convert(pos, moveTwo);
-
-        Assert.Equal(expectedOne, actualOne);
-        Assert.Equal(expectedTwo, actualTwo);
-    }
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Notation;
+using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Test.NotationTests;
+
+public sealed class FanTests
+{
+    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
+                                                         .AddChessLib()
+                                                         .BuildServiceProvider();
+
+    [Theory]
+    [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.Fan, PieceTypes.Knight, Squares.d2, Squares.f2,
+        Squares.e4)]
+    public void FanRankAmbiguities(
+        string fen,
+        MoveNotations moveNotation,
+        PieceTypes movingPt,
+        Squares fromSqOne,
+        Squares fromSqTwo,
+        Squares toSq)
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
+
+        var fromOne = new Square(fromSqOne);
+        var fromTwo = new Square(fromSqTwo);
+        var to      = new Square(toSq);
+
+        Assert.True(fromOne.IsOk);
+        Assert.True(fromTwo.IsOk);
+        Assert.True(to.IsOk);
+
+        var pieceChar = pc.GetUnicodeChar();
+        var toString  = to.ToString();
+
+        var moveOne = Move.Create(fromOne, to);
+        var moveTwo = Move.Create(fromTwo, to);
+
+        var notation = _serviceProvider.GetRequiredService<IMoveNotation>();
+
+        var expectedOne = $"{pieceChar}{fromOne.FileChar}{toString}";
+        var expectedTwo = $"{pieceChar}{fromTwo.FileChar}{toString}";
+
+        var actualOne = notation.ToNotation(moveNotation).Convert(pos, moveOne);
+        var actualTwo = notation.ToNotation(moveNotation).Convert(pos, moveTwo);
+
+        Assert.Equal(expectedOne, actualOne);
+        Assert.Equal(expectedTwo, actualTwo);
+    }
+
+    [Theory]
+    [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.Fan, PieceTypes.Knight, Squares.d2, Squares.d4,
+        Squares.f3)]
+    public void FanFileAmbiguities(
+        string fen,
+        MoveNotations moveNotation,
+        PieceTypes movingPt,
+        Squares fromSqOne,
+        Squares fromSqTwo,
+        Squares toSq)
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
+
+        var fromOne = new Square(fromSqOne);
+        var fromTwo = new Square(fromSqTwo);
+        var to      = new Square(toSq);
+
+        Assert.True(fromOne.IsOk);
+        Assert.True(fromTwo.IsOk);
+        Assert.True(to.IsOk);
+
+        var pieceChar = pc.GetUnicodeChar();
+        var toString  = to.ToString();
+
+        var moveOne = Move.Create(fromOne, to);
+        var moveTwo = Move.Create(fromTwo, to);
+
+        var notation = _serviceProvider.GetRequiredService<IMoveNotation>();
+
+        var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
+        var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
+
+        var actualOne = notation.ToNotation(moveNotation).Convert(pos, moveOne);
+        var actualTwo = notation.ToNotation(moveNotation).Convert(pos, moveTwo);
+
+        Assert.Equal(expectedOne, actualOne);
+        Assert.Equal(expectedTwo, actualTwo);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
similarity index 97%
rename from src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
index e312c7b0..ba163946 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/IccfTests.cs
@@ -1,92 +1,92 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Microsoft.Extensions.DependencyInjection;
-using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Extensions;
-using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Notation;
-using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Test.NotationTests;
-
-public sealed class IccfTests
-{
-    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
-                                                         .AddChessLib()
-                                                         .BuildServiceProvider();
-
-    [Fact]
-    public void IccfRegularMove()
-    {
-        const string        fen             = "8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1";
-        const MoveNotations moveNotations   = MoveNotations.ICCF;
-        const string        expectedPrimary = "4263";
-
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var w1 = Move.Create(Square.D2, Square.F3);
-
-        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
-        var notation     = moveNotation.ToNotation(moveNotations);
-
-        var actualPrimary = notation.Convert(pos, w1);
-
-        Assert.Equal(expectedPrimary, actualPrimary);
-    }
-
-    [Theory]
-    [InlineData("8/1P4k1/8/8/8/8/1K6/8 w - - 0 1", PieceTypes.Queen, "27281")]
-    [InlineData("8/1P4k1/8/8/8/8/1K6/8 w - - 0 1", PieceTypes.Rook, "27282")]
-    [InlineData("8/1P4k1/8/8/8/8/1K6/8 w - - 0 1", PieceTypes.Bishop, "27283")]
-    [InlineData("8/1P4k1/8/8/8/8/1K6/8 w - - 0 1", PieceTypes.Knight, "27284")]
-    public void IccfPromotionMove(string fen, PieceTypes promoPt, string expected)
-    {
-        const MoveNotations moveNotations = MoveNotations.ICCF;
-
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var w1 = Move.Create(Squares.b7, Squares.b8, MoveTypes.Promotion, promoPt);
-
-        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
-        var notation     = moveNotation.ToNotation(moveNotations);
-
-        var actual = notation.Convert(pos, w1);
-
-        Assert.Equal(expected, actual);
-    }
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Notation;
+using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Test.NotationTests;
+
+public sealed class IccfTests
+{
+    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
+                                                         .AddChessLib()
+                                                         .BuildServiceProvider();
+
+    [Fact]
+    public void IccfRegularMove()
+    {
+        const string        fen             = "8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1";
+        const MoveNotations moveNotations   = MoveNotations.ICCF;
+        const string        expectedPrimary = "4263";
+
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var w1 = Move.Create(Square.D2, Square.F3);
+
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
+
+        var actualPrimary = notation.Convert(pos, w1);
+
+        Assert.Equal(expectedPrimary, actualPrimary);
+    }
+
+    [Theory]
+    [InlineData("8/1P4k1/8/8/8/8/1K6/8 w - - 0 1", PieceTypes.Queen, "27281")]
+    [InlineData("8/1P4k1/8/8/8/8/1K6/8 w - - 0 1", PieceTypes.Rook, "27282")]
+    [InlineData("8/1P4k1/8/8/8/8/1K6/8 w - - 0 1", PieceTypes.Bishop, "27283")]
+    [InlineData("8/1P4k1/8/8/8/8/1K6/8 w - - 0 1", PieceTypes.Knight, "27284")]
+    public void IccfPromotionMove(string fen, PieceTypes promoPt, string expected)
+    {
+        const MoveNotations moveNotations = MoveNotations.ICCF;
+
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var w1 = Move.Create(Squares.b7, Squares.b8, MoveTypes.Promotion, promoPt);
+
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
+
+        var actual = notation.Convert(pos, w1);
+
+        Assert.Equal(expected, actual);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
similarity index 97%
rename from src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
index 86cab0e3..31aba0f9 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/RanTests.cs
@@ -1,96 +1,96 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Microsoft.Extensions.DependencyInjection;
-using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Extensions;
-using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.Notation;
-using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Test.NotationTests;
-
-public sealed class RanTests
-{
-    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
-                                                         .AddChessLib()
-                                                         .BuildServiceProvider();
-
-    [Theory]
-    [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.Ran, PieceTypes.Knight, Squares.d2, Squares.d4,
-        Squares.f3)]
-    [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.Ran, PieceTypes.Knight, Squares.d2, Squares.f2,
-        Squares.e4)]
-    [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.Lan, PieceTypes.Knight, Squares.d2, Squares.d4,
-        Squares.f3)]
-    [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.Lan, PieceTypes.Knight, Squares.d2, Squares.f2,
-        Squares.e4)]
-    public void RanLanAmbiguities(
-        string fen,
-        MoveNotations moveNotations,
-        PieceTypes movingPt,
-        Squares fromSqOne,
-        Squares fromSqTwo,
-        Squares toSq)
-    {
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var pt = new PieceType(movingPt);
-        var pc = pt.MakePiece(pos.SideToMove);
-
-        var fromOne = new Square(fromSqOne);
-        var fromTwo = new Square(fromSqTwo);
-        var to      = new Square(toSq);
-
-        Assert.True(fromOne.IsOk);
-        Assert.True(fromTwo.IsOk);
-        Assert.True(to.IsOk);
-
-        var pieceChar = pc.GetPieceChar();
-        var toString  = to.ToString();
-
-        var moveOne = Move.Create(fromOne, to);
-        var moveTwo = Move.Create(fromTwo, to);
-
-        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
-        var notation     = moveNotation.ToNotation(moveNotations);
-
-        var expectedOne = $"{pieceChar}{fromOne}-{toString}";
-        var expectedTwo = $"{pieceChar}{fromTwo}-{toString}";
-
-        var actualOne = notation.Convert(pos, moveOne);
-        var actualTwo = notation.Convert(pos, moveTwo);
-
-        Assert.Equal(expectedOne, actualOne);
-        Assert.Equal(expectedTwo, actualTwo);
-    }
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.Notation;
+using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Test.NotationTests;
+
+public sealed class RanTests
+{
+    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
+                                                         .AddChessLib()
+                                                         .BuildServiceProvider();
+
+    [Theory]
+    [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.Ran, PieceTypes.Knight, Squares.d2, Squares.d4,
+        Squares.f3)]
+    [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.Ran, PieceTypes.Knight, Squares.d2, Squares.f2,
+        Squares.e4)]
+    [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.Lan, PieceTypes.Knight, Squares.d2, Squares.d4,
+        Squares.f3)]
+    [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.Lan, PieceTypes.Knight, Squares.d2, Squares.f2,
+        Squares.e4)]
+    public void RanLanAmbiguities(
+        string fen,
+        MoveNotations moveNotations,
+        PieceTypes movingPt,
+        Squares fromSqOne,
+        Squares fromSqTwo,
+        Squares toSq)
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
+
+        var fromOne = new Square(fromSqOne);
+        var fromTwo = new Square(fromSqTwo);
+        var to      = new Square(toSq);
+
+        Assert.True(fromOne.IsOk);
+        Assert.True(fromTwo.IsOk);
+        Assert.True(to.IsOk);
+
+        var pieceChar = pc.GetPieceChar();
+        var toString  = to.ToString();
+
+        var moveOne = Move.Create(fromOne, to);
+        var moveTwo = Move.Create(fromTwo, to);
+
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
+
+        var expectedOne = $"{pieceChar}{fromOne}-{toString}";
+        var expectedTwo = $"{pieceChar}{fromTwo}-{toString}";
+
+        var actualOne = notation.Convert(pos, moveOne);
+        var actualTwo = notation.Convert(pos, moveTwo);
+
+        Assert.Equal(expectedOne, actualOne);
+        Assert.Equal(expectedTwo, actualTwo);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
similarity index 97%
rename from src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
index 5a8473c3..054bb088 100644
--- a/src/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/NotationTests/SanTests.cs
@@ -1,219 +1,219 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Microsoft.Extensions.DependencyInjection;
-using Rudzoft.ChessLib.Enums;
-using Rudzoft.ChessLib.Extensions;
-using Rudzoft.ChessLib.Fen;
-using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.Notation;
-using Rudzoft.ChessLib.Notation.Notations;
-using Rudzoft.ChessLib.Types;
-
-namespace Rudzoft.ChessLib.Test.NotationTests;
-
-public sealed class SanTests
-{
-    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
-                                                         .AddChessLib()
-                                                         .BuildServiceProvider();
-
-    [Theory]
-    [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.San, PieceTypes.Knight, Squares.d2, Squares.f2,
-        Squares.e4)]
-    public void SanRankAmbiguities(
-        string fen,
-        MoveNotations moveNotations,
-        PieceTypes movingPt,
-        Squares fromSqOne,
-        Squares fromSqTwo,
-        Squares toSq)
-    {
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var pt = new PieceType(movingPt);
-        var pc = pt.MakePiece(pos.SideToMove);
-
-        var fromOne = new Square(fromSqOne);
-        var fromTwo = new Square(fromSqTwo);
-        var to      = new Square(toSq);
-
-        Assert.True(fromOne.IsOk);
-        Assert.True(fromTwo.IsOk);
-        Assert.True(to.IsOk);
-
-        var pieceChar = pc.GetPieceChar();
-        var toString  = to.ToString();
-
-        var moveOne = Move.Create(fromOne, to);
-        var moveTwo = Move.Create(fromTwo, to);
-
-        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
-        var notation     = moveNotation.ToNotation(moveNotations);
-
-        var expectedOne = $"{pieceChar}{fromOne.FileChar}{toString}";
-        var expectedTwo = $"{pieceChar}{fromTwo.FileChar}{toString}";
-
-        var actualOne = notation.Convert(pos, moveOne);
-        var actualTwo = notation.Convert(pos, moveTwo);
-
-        Assert.Equal(expectedOne, actualOne);
-        Assert.Equal(expectedTwo, actualTwo);
-    }
-
-    [Theory]
-    [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.San, PieceTypes.Knight, Squares.d2, Squares.d4,
-        Squares.f3)]
-    public void SanFileAmbiguities(
-        string fen, MoveNotations moveNotations, PieceTypes movingPt, Squares fromSqOne,
-        Squares fromSqTwo, Squares toSq)
-    {
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var pt = new PieceType(movingPt);
-        var pc = pt.MakePiece(pos.SideToMove);
-
-        var fromOne = new Square(fromSqOne);
-        var fromTwo = new Square(fromSqTwo);
-        var to      = new Square(toSq);
-
-        Assert.True(fromOne.IsOk);
-        Assert.True(fromTwo.IsOk);
-        Assert.True(to.IsOk);
-
-        var pieceChar = pc.GetPieceChar();
-        var toString  = to.ToString();
-
-        var moveOne = Move.Create(fromOne, to);
-        var moveTwo = Move.Create(fromTwo, to);
-
-        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
-        var notation     = moveNotation.ToNotation(moveNotations);
-
-        var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
-        var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
-
-        var actualOne = notation.Convert(pos, moveOne);
-        var actualTwo = notation.Convert(pos, moveTwo);
-
-        Assert.Equal(expectedOne, actualOne);
-        Assert.Equal(expectedTwo, actualTwo);
-    }
-
-    [Fact]
-    public void RookSanAmbiguity()
-    {
-        // Tests rook ambiguity notation for white rooks @ e1 and g2. Original author : johnathandavis
-
-        const string        fen               = "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53";
-        const MoveNotations moveNotations     = MoveNotations.San;
-        var                 expectedNotations = new[] { "Ree2", "Rge2" };
-
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var sideToMove  = pos.SideToMove;
-        var targetPiece = PieceType.Rook.MakePiece(sideToMove);
-
-        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
-        var notation     = moveNotation.ToNotation(moveNotations);
-
-        var ml = pos.GenerateMoves();
-        ml.Generate(in pos);
-
-        var sanMoves = ml
-            .GetMoves(move => pos.GetPiece(move.FromSquare()) == targetPiece)
-            .Select(m => notation.Convert(pos, m))
-            .ToArray();
-
-        foreach (var notationResult in expectedNotations)
-            Assert.Contains(sanMoves, s => s == notationResult);
-    }
-
-    [Theory]
-    [InlineData("2rr2k1/p3ppbp/b1n3p1/2p1P3/5P2/2N3P1/PP2N1BP/3R1RK1 w - - 2 18", "Rxd8+")]
-    public void SanCaptureWithCheck(string fen, string expected)
-    {
-        // author: skotz
-
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
-        var notation     = moveNotation.ToNotation(MoveNotations.San);
-
-        var move    = Move.Create(Square.D1, Square.D8, MoveTypes.Normal);
-        var sanMove = notation.Convert(pos, move);
-
-        // Capturing a piece with check
-        Assert.Equal(sanMove, expected);
-    }
-
-    [Theory]
-    [InlineData("2rR2k1/p3ppbp/b1n3p1/2p1P3/5P2/2N3P1/PP2N1BP/5RK1 b - - 0 36", "Nxd8", "Rxd8", "Bf8")]
-    public void SanRecaptureNotCheckmate(string fen, params string[] expected)
-    {
-        // author: skotz
-
-        var pos = _serviceProvider.GetRequiredService<IPosition>();
-
-        var fenData = new FenData(fen);
-        var state   = new State();
-
-        pos.Set(in fenData, ChessMode.Normal, state);
-
-        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
-        var notation     = moveNotation.ToNotation(MoveNotations.San);
-
-        var allMoves = pos.GenerateMoves().Get();
-
-        foreach (var move in allMoves)
-        {
-            var sanMove = notation.Convert(pos, move);
-
-            // Recapturing a piece to remove the check
-            Assert.Contains(sanMove, expected);
-        }
-    }
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Enums;
+using Rudzoft.ChessLib.Extensions;
+using Rudzoft.ChessLib.Fen;
+using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.Notation;
+using Rudzoft.ChessLib.Notation.Notations;
+using Rudzoft.ChessLib.Types;
+
+namespace Rudzoft.ChessLib.Test.NotationTests;
+
+public sealed class SanTests
+{
+    private readonly IServiceProvider _serviceProvider = new ServiceCollection()
+                                                         .AddChessLib()
+                                                         .BuildServiceProvider();
+
+    [Theory]
+    [InlineData("8/6k1/8/8/8/8/1K1N1N2/8 w - - 0 1", MoveNotations.San, PieceTypes.Knight, Squares.d2, Squares.f2,
+        Squares.e4)]
+    public void SanRankAmbiguities(
+        string fen,
+        MoveNotations moveNotations,
+        PieceTypes movingPt,
+        Squares fromSqOne,
+        Squares fromSqTwo,
+        Squares toSq)
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
+
+        var fromOne = new Square(fromSqOne);
+        var fromTwo = new Square(fromSqTwo);
+        var to      = new Square(toSq);
+
+        Assert.True(fromOne.IsOk);
+        Assert.True(fromTwo.IsOk);
+        Assert.True(to.IsOk);
+
+        var pieceChar = pc.GetPieceChar();
+        var toString  = to.ToString();
+
+        var moveOne = Move.Create(fromOne, to);
+        var moveTwo = Move.Create(fromTwo, to);
+
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
+
+        var expectedOne = $"{pieceChar}{fromOne.FileChar}{toString}";
+        var expectedTwo = $"{pieceChar}{fromTwo.FileChar}{toString}";
+
+        var actualOne = notation.Convert(pos, moveOne);
+        var actualTwo = notation.Convert(pos, moveTwo);
+
+        Assert.Equal(expectedOne, actualOne);
+        Assert.Equal(expectedTwo, actualTwo);
+    }
+
+    [Theory]
+    [InlineData("8/6k1/8/8/3N4/8/1K1N4/8 w - - 0 1", MoveNotations.San, PieceTypes.Knight, Squares.d2, Squares.d4,
+        Squares.f3)]
+    public void SanFileAmbiguities(
+        string fen, MoveNotations moveNotations, PieceTypes movingPt, Squares fromSqOne,
+        Squares fromSqTwo, Squares toSq)
+    {
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var pt = new PieceType(movingPt);
+        var pc = pt.MakePiece(pos.SideToMove);
+
+        var fromOne = new Square(fromSqOne);
+        var fromTwo = new Square(fromSqTwo);
+        var to      = new Square(toSq);
+
+        Assert.True(fromOne.IsOk);
+        Assert.True(fromTwo.IsOk);
+        Assert.True(to.IsOk);
+
+        var pieceChar = pc.GetPieceChar();
+        var toString  = to.ToString();
+
+        var moveOne = Move.Create(fromOne, to);
+        var moveTwo = Move.Create(fromTwo, to);
+
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
+
+        var expectedOne = $"{pieceChar}{fromOne.RankChar}{toString}";
+        var expectedTwo = $"{pieceChar}{fromTwo.RankChar}{toString}";
+
+        var actualOne = notation.Convert(pos, moveOne);
+        var actualTwo = notation.Convert(pos, moveTwo);
+
+        Assert.Equal(expectedOne, actualOne);
+        Assert.Equal(expectedTwo, actualTwo);
+    }
+
+    [Fact]
+    public void RookSanAmbiguity()
+    {
+        // Tests rook ambiguity notation for white rooks @ e1 and g2. Original author : johnathandavis
+
+        const string        fen               = "5r1k/p6p/4r1n1/3NPp2/8/8/PP4RP/4R1K1 w - - 3 53";
+        const MoveNotations moveNotations     = MoveNotations.San;
+        var                 expectedNotations = new[] { "Ree2", "Rge2" };
+
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var sideToMove  = pos.SideToMove;
+        var targetPiece = PieceType.Rook.MakePiece(sideToMove);
+
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(moveNotations);
+
+        var ml = pos.GenerateMoves();
+        ml.Generate(in pos);
+
+        var sanMoves = ml
+            .GetMoves(move => pos.GetPiece(move.FromSquare()) == targetPiece)
+            .Select(m => notation.Convert(pos, m))
+            .ToArray();
+
+        foreach (var notationResult in expectedNotations)
+            Assert.Contains(sanMoves, s => s == notationResult);
+    }
+
+    [Theory]
+    [InlineData("2rr2k1/p3ppbp/b1n3p1/2p1P3/5P2/2N3P1/PP2N1BP/3R1RK1 w - - 2 18", "Rxd8+")]
+    public void SanCaptureWithCheck(string fen, string expected)
+    {
+        // author: skotz
+
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(MoveNotations.San);
+
+        var move    = Move.Create(Square.D1, Square.D8, MoveTypes.Normal);
+        var sanMove = notation.Convert(pos, move);
+
+        // Capturing a piece with check
+        Assert.Equal(sanMove, expected);
+    }
+
+    [Theory]
+    [InlineData("2rR2k1/p3ppbp/b1n3p1/2p1P3/5P2/2N3P1/PP2N1BP/5RK1 b - - 0 36", "Nxd8", "Rxd8", "Bf8")]
+    public void SanRecaptureNotCheckmate(string fen, params string[] expected)
+    {
+        // author: skotz
+
+        var pos = _serviceProvider.GetRequiredService<IPosition>();
+
+        var fenData = new FenData(fen);
+        var state   = new State();
+
+        pos.Set(in fenData, ChessMode.Normal, state);
+
+        var moveNotation = _serviceProvider.GetRequiredService<IMoveNotation>();
+        var notation     = moveNotation.ToNotation(MoveNotations.San);
+
+        var allMoves = pos.GenerateMoves().Get();
+
+        foreach (var move in allMoves)
+        {
+            var sanMove = notation.Convert(pos, move);
+
+            // Recapturing a piece to remove the check
+            Assert.Contains(sanMove, expected);
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/NumberTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/NumberTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/NumberTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/NumberTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftTest.cs
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
diff --git a/src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PawnDoubleAttackTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PawnPushTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PawnPushTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/PawnPushTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PawnPushTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacks.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksBishopTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKingTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksKnightTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksPawnTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/PieceAttacksRookTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/RegularMobilityFixture.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityFixture.cs
diff --git a/src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PiecesTests/SliderMobilityTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PositionTests/EnPassantFenTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PositionTests/PositionTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/PositionTests/ValidationTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
similarity index 97%
rename from src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
index dcaae4b8..3432abb8 100644
--- a/src/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/ProtocolTests/OptionsTests.cs
@@ -1,59 +1,59 @@
-/*
-ChessLib, a chess data structure library
-
-MIT License
-
-Copyright (c) 2017-2023 Rudy Alex Kohn
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-using Microsoft.Extensions.ObjectPool;
-using Rudzoft.ChessLib.MoveGeneration;
-using Rudzoft.ChessLib.Protocol.UCI;
-
-namespace Rudzoft.ChessLib.Test.ProtocolTests;
-
-public sealed class OptionsTests
-{
-    [Theory]
-    [InlineData("Boolean Test", true, true, "option name Boolean Test type Check default true")]
-    [InlineData("Boolean Test", false, false, "option name Boolean Test type Check default false")]
-    public void Boolean(string name, bool value, bool expected, string uciString)
-    {
-        var provider = new DefaultObjectPoolProvider();
-        var policy = new DefaultPooledObjectPolicy<MoveList>();
-
-        IUci uci = new Uci(provider.Create(policy));
-
-        uci.Initialize();
-
-        var option = new Option(name, 0, value);
-        uci.AddOption(name, option);
-
-        var actual = option.GetBool();
-
-        Assert.Equal(expected, actual);
-        Assert.Equal(expected, option);
-
-        var t = uci.ToString();
-
-        Assert.Contains(uciString, t);
-    }
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2023 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+using Microsoft.Extensions.ObjectPool;
+using Rudzoft.ChessLib.MoveGeneration;
+using Rudzoft.ChessLib.Protocol.UCI;
+
+namespace Rudzoft.ChessLib.Test.ProtocolTests;
+
+public sealed class OptionsTests
+{
+    [Theory]
+    [InlineData("Boolean Test", true, true, "option name Boolean Test type Check default true")]
+    [InlineData("Boolean Test", false, false, "option name Boolean Test type Check default false")]
+    public void Boolean(string name, bool value, bool expected, string uciString)
+    {
+        var provider = new DefaultObjectPoolProvider();
+        var policy = new DefaultPooledObjectPolicy<MoveList>();
+
+        IUci uci = new Uci(provider.Create(policy));
+
+        uci.Initialize();
+
+        var option = new Option(name, 0, value);
+        uci.AddOption(name, option);
+
+        var actual = option.GetBool();
+
+        Assert.Equal(expected, actual);
+        Assert.Equal(expected, option);
+
+        var t = uci.ToString();
+
+        Assert.Contains(uciString, t);
+    }
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/SearchParameterTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/ProtocolTests/SearchParameterTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/ProtocolTests/SearchParameterTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/ProtocolTests/SearchParameterTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/ProtocolTests/UciTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/RankTests/RankEdgeDistanceTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/chess-lib/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
rename to src/chess-lib/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
diff --git a/src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/ScoreTests/ScoreTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/SizesTests/BaseTypesSizeTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/DistanceTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/SquareTests/DistanceTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/SquareTests/DistanceTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/SquareTests/DistanceTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareFromStringTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/SquareTests/SquareFromStringTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/SquareTests/SquareFromStringTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/SquareTests/SquareFromStringTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/SquareTests/SquareRangeTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/SquareTests/SquareTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/TablesTests/KillerMovesTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/Usings.cs b/src/chess-lib/Rudzoft.ChessLib.Test/Usings.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/Usings.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/Usings.cs
diff --git a/src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
rename to src/chess-lib/Rudzoft.ChessLib.Test/ZobristTests/ZobristHashTests.cs
diff --git a/src/Rudzoft.ChessLib.Test/xunit.runner.json b/src/chess-lib/Rudzoft.ChessLib.Test/xunit.runner.json
similarity index 100%
rename from src/Rudzoft.ChessLib.Test/xunit.runner.json
rename to src/chess-lib/Rudzoft.ChessLib.Test/xunit.runner.json
diff --git a/src/Rudzoft.ChessLib/Blockage.cs b/src/chess-lib/Rudzoft.ChessLib/Blockage.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Blockage.cs
rename to src/chess-lib/Rudzoft.ChessLib/Blockage.cs
diff --git a/src/Rudzoft.ChessLib/Board.cs b/src/chess-lib/Rudzoft.ChessLib/Board.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Board.cs
rename to src/chess-lib/Rudzoft.ChessLib/Board.cs
diff --git a/src/Rudzoft.ChessLib/Cuckoo.cs b/src/chess-lib/Rudzoft.ChessLib/Cuckoo.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Cuckoo.cs
rename to src/chess-lib/Rudzoft.ChessLib/Cuckoo.cs
diff --git a/src/Rudzoft.ChessLib/Enums/ChessMode.cs b/src/chess-lib/Rudzoft.ChessLib/Enums/ChessMode.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Enums/ChessMode.cs
rename to src/chess-lib/Rudzoft.ChessLib/Enums/ChessMode.cs
diff --git a/src/Rudzoft.ChessLib/Enums/GameEndTypes.cs b/src/chess-lib/Rudzoft.ChessLib/Enums/GameEndTypes.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Enums/GameEndTypes.cs
rename to src/chess-lib/Rudzoft.ChessLib/Enums/GameEndTypes.cs
diff --git a/src/Rudzoft.ChessLib/Enums/GameResults.cs b/src/chess-lib/Rudzoft.ChessLib/Enums/GameResults.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Enums/GameResults.cs
rename to src/chess-lib/Rudzoft.ChessLib/Enums/GameResults.cs
diff --git a/src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs b/src/chess-lib/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs
rename to src/chess-lib/Rudzoft.ChessLib/Enums/MoveAmbiguities.cs
diff --git a/src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs b/src/chess-lib/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
rename to src/chess-lib/Rudzoft.ChessLib/Enums/MoveGenerationTypes.cs
diff --git a/src/Rudzoft.ChessLib/Enums/Phases.cs b/src/chess-lib/Rudzoft.ChessLib/Enums/Phases.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Enums/Phases.cs
rename to src/chess-lib/Rudzoft.ChessLib/Enums/Phases.cs
diff --git a/src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs b/src/chess-lib/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs
rename to src/chess-lib/Rudzoft.ChessLib/Evaluation/IKpkBitBase.cs
diff --git a/src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs b/src/chess-lib/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
rename to src/chess-lib/Rudzoft.ChessLib/Evaluation/KpkBitBase.cs
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
rename to src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
rename to src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
diff --git a/src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
rename to src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
diff --git a/src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
rename to src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
diff --git a/src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs b/src/chess-lib/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
rename to src/chess-lib/Rudzoft.ChessLib/Extensions/ArrayExtensions.cs
diff --git a/src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/chess-lib/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
rename to src/chess-lib/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
diff --git a/src/Rudzoft.ChessLib/Extensions/MathExtensions.cs b/src/chess-lib/Rudzoft.ChessLib/Extensions/MathExtensions.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Extensions/MathExtensions.cs
rename to src/chess-lib/Rudzoft.ChessLib/Extensions/MathExtensions.cs
diff --git a/src/Rudzoft.ChessLib/Extensions/Maths.cs b/src/chess-lib/Rudzoft.ChessLib/Extensions/Maths.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Extensions/Maths.cs
rename to src/chess-lib/Rudzoft.ChessLib/Extensions/Maths.cs
diff --git a/src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs b/src/chess-lib/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
rename to src/chess-lib/Rudzoft.ChessLib/Extensions/PieceExtensions.cs
diff --git a/src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs b/src/chess-lib/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
rename to src/chess-lib/Rudzoft.ChessLib/Extensions/SpanExtensions.cs
diff --git a/src/Rudzoft.ChessLib/Extensions/StringExtensions.cs b/src/chess-lib/Rudzoft.ChessLib/Extensions/StringExtensions.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Extensions/StringExtensions.cs
rename to src/chess-lib/Rudzoft.ChessLib/Extensions/StringExtensions.cs
diff --git a/src/Rudzoft.ChessLib/Factories/IKillerMovesFactory.cs b/src/chess-lib/Rudzoft.ChessLib/Factories/IKillerMovesFactory.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Factories/IKillerMovesFactory.cs
rename to src/chess-lib/Rudzoft.ChessLib/Factories/IKillerMovesFactory.cs
diff --git a/src/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs b/src/chess-lib/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs
rename to src/chess-lib/Rudzoft.ChessLib/Factories/IPolyglotBookFactory.cs
diff --git a/src/Rudzoft.ChessLib/Factories/IServiceFactory.cs b/src/chess-lib/Rudzoft.ChessLib/Factories/IServiceFactory.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Factories/IServiceFactory.cs
rename to src/chess-lib/Rudzoft.ChessLib/Factories/IServiceFactory.cs
diff --git a/src/Rudzoft.ChessLib/Factories/KillerMovesFactory.cs b/src/chess-lib/Rudzoft.ChessLib/Factories/KillerMovesFactory.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Factories/KillerMovesFactory.cs
rename to src/chess-lib/Rudzoft.ChessLib/Factories/KillerMovesFactory.cs
diff --git a/src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs b/src/chess-lib/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
rename to src/chess-lib/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
diff --git a/src/Rudzoft.ChessLib/Factories/ServiceFactory.cs b/src/chess-lib/Rudzoft.ChessLib/Factories/ServiceFactory.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Factories/ServiceFactory.cs
rename to src/chess-lib/Rudzoft.ChessLib/Factories/ServiceFactory.cs
diff --git a/src/Rudzoft.ChessLib/Fen/Fen.cs b/src/chess-lib/Rudzoft.ChessLib/Fen/Fen.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Fen/Fen.cs
rename to src/chess-lib/Rudzoft.ChessLib/Fen/Fen.cs
diff --git a/src/Rudzoft.ChessLib/Fen/FenData.cs b/src/chess-lib/Rudzoft.ChessLib/Fen/FenData.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Fen/FenData.cs
rename to src/chess-lib/Rudzoft.ChessLib/Fen/FenData.cs
diff --git a/src/Rudzoft.ChessLib/Fen/IFenData.cs b/src/chess-lib/Rudzoft.ChessLib/Fen/IFenData.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Fen/IFenData.cs
rename to src/chess-lib/Rudzoft.ChessLib/Fen/IFenData.cs
diff --git a/src/Rudzoft.ChessLib/Game.cs b/src/chess-lib/Rudzoft.ChessLib/Game.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Game.cs
rename to src/chess-lib/Rudzoft.ChessLib/Game.cs
diff --git a/src/Rudzoft.ChessLib/GlobalSuppressions.cs b/src/chess-lib/Rudzoft.ChessLib/GlobalSuppressions.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/GlobalSuppressions.cs
rename to src/chess-lib/Rudzoft.ChessLib/GlobalSuppressions.cs
diff --git a/src/Rudzoft.ChessLib/Hash/IRKiss.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/IRKiss.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/IRKiss.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/IRKiss.cs
diff --git a/src/Rudzoft.ChessLib/Hash/IZobrist.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/IZobrist.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/IZobrist.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/IZobrist.cs
diff --git a/src/Rudzoft.ChessLib/Hash/RKiss.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/RKiss.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/RKiss.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/RKiss.cs
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/Bound.cs
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/ITranspositionTable.cs
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/PertT.cs
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableConfiguration.cs
diff --git a/src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTableEntry.cs
diff --git a/src/Rudzoft.ChessLib/Hash/Zobrist.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/Zobrist.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Hash/Zobrist.cs
rename to src/chess-lib/Rudzoft.ChessLib/Hash/Zobrist.cs
diff --git a/src/Rudzoft.ChessLib/IBlockage.cs b/src/chess-lib/Rudzoft.ChessLib/IBlockage.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/IBlockage.cs
rename to src/chess-lib/Rudzoft.ChessLib/IBlockage.cs
diff --git a/src/Rudzoft.ChessLib/IBoard.cs b/src/chess-lib/Rudzoft.ChessLib/IBoard.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/IBoard.cs
rename to src/chess-lib/Rudzoft.ChessLib/IBoard.cs
diff --git a/src/Rudzoft.ChessLib/ICuckoo.cs b/src/chess-lib/Rudzoft.ChessLib/ICuckoo.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/ICuckoo.cs
rename to src/chess-lib/Rudzoft.ChessLib/ICuckoo.cs
diff --git a/src/Rudzoft.ChessLib/IGame.cs b/src/chess-lib/Rudzoft.ChessLib/IGame.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/IGame.cs
rename to src/chess-lib/Rudzoft.ChessLib/IGame.cs
diff --git a/src/Rudzoft.ChessLib/IPosition.cs b/src/chess-lib/Rudzoft.ChessLib/IPosition.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/IPosition.cs
rename to src/chess-lib/Rudzoft.ChessLib/IPosition.cs
diff --git a/src/Rudzoft.ChessLib/IValues.cs b/src/chess-lib/Rudzoft.ChessLib/IValues.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/IValues.cs
rename to src/chess-lib/Rudzoft.ChessLib/IValues.cs
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs b/src/chess-lib/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs
rename to src/chess-lib/Rudzoft.ChessLib/MoveGeneration/MoveFactory.cs
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs b/src/chess-lib/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
rename to src/chess-lib/Rudzoft.ChessLib/MoveGeneration/MoveGenerator.cs
diff --git a/src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs b/src/chess-lib/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
rename to src/chess-lib/Rudzoft.ChessLib/MoveGeneration/MoveList.cs
diff --git a/src/Rudzoft.ChessLib/Notation/IMoveNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/IMoveNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/IMoveNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/IMoveNotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/INotationToMove.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/INotationToMove.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/INotationToMove.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/INotationToMove.cs
diff --git a/src/Rudzoft.ChessLib/Notation/MoveNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/MoveNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/MoveNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/MoveNotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/NotationToMove.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/NotationToMove.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/NotationToMove.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/NotationToMove.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/CoordinateNotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/FanNotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/INotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/INotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/INotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/INotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/IccfNotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/LanNotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/Notation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/Notation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/Notation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/Notation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/RanNotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/SanNotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/SmithNotation.cs
diff --git a/src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
rename to src/chess-lib/Rudzoft.ChessLib/Notation/Notations/UciNotation.cs
diff --git a/src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs b/src/chess-lib/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
rename to src/chess-lib/Rudzoft.ChessLib/Polyglot/IPolyglotBook.cs
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs b/src/chess-lib/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
rename to src/chess-lib/Rudzoft.ChessLib/Polyglot/PolyglotBook.cs
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs b/src/chess-lib/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs
rename to src/chess-lib/Rudzoft.ChessLib/Polyglot/PolyglotBookConfiguration.cs
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs b/src/chess-lib/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs
rename to src/chess-lib/Rudzoft.ChessLib/Polyglot/PolyglotBookEntry.cs
diff --git a/src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs b/src/chess-lib/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
rename to src/chess-lib/Rudzoft.ChessLib/Polyglot/PolyglotBookZobrist.cs
diff --git a/src/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs b/src/chess-lib/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs
rename to src/chess-lib/Rudzoft.ChessLib/Polyglot/ReverseEndianBinaryStreamReader.cs
diff --git a/src/Rudzoft.ChessLib/Position.cs b/src/chess-lib/Rudzoft.ChessLib/Position.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Position.cs
rename to src/chess-lib/Rudzoft.ChessLib/Position.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/Clock.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/CopyProtections.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/CopyProtections.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/CopyProtections.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/CopyProtections.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/Cpu.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/HiResTimer.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/HiResTimerArgs.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/HiResTimerArgs.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/HiResTimerArgs.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/HiResTimerArgs.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/ICpu.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IHiResTimer.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IHiResTimer.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/IHiResTimer.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IHiResTimer.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IInputOutput.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IMovesToGoModel.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IOption.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/IOption.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IOption.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/ISearchParameters.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/IUci.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/InputOutput.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/InputOutputAction.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/InputOutputAction.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/InputOutputAction.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/InputOutputAction.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/MovesToGoModel.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Option.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/Option.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/Option.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/Option.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/OptionComparer.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/SearchParameters.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/UCIProtocol.html b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/UCIProtocol.html
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/UCIProtocol.html
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/UCIProtocol.html
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/Uci.cs
diff --git a/src/Rudzoft.ChessLib/Protocol/UCI/UciOptionType.cs b/src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/UciOptionType.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Protocol/UCI/UciOptionType.cs
rename to src/chess-lib/Rudzoft.ChessLib/Protocol/UCI/UciOptionType.cs
diff --git a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
similarity index 85%
rename from src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
rename to src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 2e0f2d41..4887c251 100644
--- a/src/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -18,7 +18,6 @@
         <Title>Rudzoft.ChessLib</Title>
         <PackageReadmeFile>README.md</PackageReadmeFile>
         <PackageTags>chess bitboard datastructure movegeneration magicbb</PackageTags>
-        <PackageIcon>ChessLib.png</PackageIcon>
         <PackageId>Rudzoft.ChessLib</PackageId>
         <RepositoryUrl>https://github.com/rudzen/ChessLib</RepositoryUrl>
         <RepositoryType>git</RepositoryType>
@@ -36,17 +35,12 @@
         <PackageReference Include="Microsoft.Extensions.Options"/>
         <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions"/>
     </ItemGroup>
+
     <ItemGroup>
-        <Folder Include="Protocol\"/>
-    </ItemGroup>
-    <ItemGroup>
-        <None Include="..\..\README.md">
-            <Pack>True</Pack>
-            <PackagePath>\</PackagePath>
-        </None>
-        <None Include="..\..\LICENSE">
-            <Pack>True</Pack>
-            <PackagePath></PackagePath>
+        <None Include="..\..\..\icon\ChessLib.png">
+          <Pack>True</Pack>
+          <PackagePath></PackagePath>
+          <Link>ChessLib.png</Link>
         </None>
         <None Include="Icon\ChessLib.png" Pack="true" PackagePath=""/>
     </ItemGroup>
diff --git a/src/Rudzoft.ChessLib/State.cs b/src/chess-lib/Rudzoft.ChessLib/State.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/State.cs
rename to src/chess-lib/Rudzoft.ChessLib/State.cs
diff --git a/src/Rudzoft.ChessLib/Tables/HashTable.cs b/src/chess-lib/Rudzoft.ChessLib/Tables/HashTable.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Tables/HashTable.cs
rename to src/chess-lib/Rudzoft.ChessLib/Tables/HashTable.cs
diff --git a/src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs b/src/chess-lib/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
rename to src/chess-lib/Rudzoft.ChessLib/Tables/History/HistoryHeuristic.cs
diff --git a/src/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs b/src/chess-lib/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs
rename to src/chess-lib/Rudzoft.ChessLib/Tables/History/IHistoryHeuristic.cs
diff --git a/src/Rudzoft.ChessLib/Tables/IHashTable.cs b/src/chess-lib/Rudzoft.ChessLib/Tables/IHashTable.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Tables/IHashTable.cs
rename to src/chess-lib/Rudzoft.ChessLib/Tables/IHashTable.cs
diff --git a/src/Rudzoft.ChessLib/Tables/ITableEntry.cs b/src/chess-lib/Rudzoft.ChessLib/Tables/ITableEntry.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Tables/ITableEntry.cs
rename to src/chess-lib/Rudzoft.ChessLib/Tables/ITableEntry.cs
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs b/src/chess-lib/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs
rename to src/chess-lib/Rudzoft.ChessLib/Tables/KillerMoves/IKillerMoves.cs
diff --git a/src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs b/src/chess-lib/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
rename to src/chess-lib/Rudzoft.ChessLib/Tables/KillerMoves/KillerMoves.cs
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs b/src/chess-lib/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs
rename to src/chess-lib/Rudzoft.ChessLib/Tables/Perft/IPerftTableEntry.cs
diff --git a/src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs b/src/chess-lib/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
rename to src/chess-lib/Rudzoft.ChessLib/Tables/Perft/PerftTable.cs
diff --git a/src/Rudzoft.ChessLib/ToDo.md b/src/chess-lib/Rudzoft.ChessLib/ToDo.md
similarity index 100%
rename from src/Rudzoft.ChessLib/ToDo.md
rename to src/chess-lib/Rudzoft.ChessLib/ToDo.md
diff --git a/src/Rudzoft.ChessLib/TroubledFens.txt b/src/chess-lib/Rudzoft.ChessLib/TroubledFens.txt
similarity index 100%
rename from src/Rudzoft.ChessLib/TroubledFens.txt
rename to src/chess-lib/Rudzoft.ChessLib/TroubledFens.txt
diff --git a/src/Rudzoft.ChessLib/Types/BitBoard.cs b/src/chess-lib/Rudzoft.ChessLib/Types/BitBoard.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/BitBoard.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/BitBoard.cs
diff --git a/src/Rudzoft.ChessLib/Types/BitBoards.cs b/src/chess-lib/Rudzoft.ChessLib/Types/BitBoards.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/BitBoards.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/BitBoards.cs
diff --git a/src/Rudzoft.ChessLib/Types/CastleRight.cs b/src/chess-lib/Rudzoft.ChessLib/Types/CastleRight.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/CastleRight.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/CastleRight.cs
diff --git a/src/Rudzoft.ChessLib/Types/Color.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Color.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Color.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Color.cs
diff --git a/src/Rudzoft.ChessLib/Types/Depth.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Depth.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Depth.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Depth.cs
diff --git a/src/Rudzoft.ChessLib/Types/Direction.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Direction.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Direction.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Direction.cs
diff --git a/src/Rudzoft.ChessLib/Types/File.cs b/src/chess-lib/Rudzoft.ChessLib/Types/File.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/File.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/File.cs
diff --git a/src/Rudzoft.ChessLib/Types/HashKey.cs b/src/chess-lib/Rudzoft.ChessLib/Types/HashKey.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/HashKey.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/HashKey.cs
diff --git a/src/Rudzoft.ChessLib/Types/IPieceSquare.cs b/src/chess-lib/Rudzoft.ChessLib/Types/IPieceSquare.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/IPieceSquare.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/IPieceSquare.cs
diff --git a/src/Rudzoft.ChessLib/Types/IValidationType.cs b/src/chess-lib/Rudzoft.ChessLib/Types/IValidationType.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/IValidationType.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/IValidationType.cs
diff --git a/src/Rudzoft.ChessLib/Types/MagicBB.cs b/src/chess-lib/Rudzoft.ChessLib/Types/MagicBB.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/MagicBB.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/MagicBB.cs
diff --git a/src/Rudzoft.ChessLib/Types/Move.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Move.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Move.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Move.cs
diff --git a/src/Rudzoft.ChessLib/Types/Piece.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Piece.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Piece.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Piece.cs
diff --git a/src/Rudzoft.ChessLib/Types/PieceSquare.cs b/src/chess-lib/Rudzoft.ChessLib/Types/PieceSquare.cs
similarity index 97%
rename from src/Rudzoft.ChessLib/Types/PieceSquare.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/PieceSquare.cs
index 3ce165cf..a11d1d0b 100644
--- a/src/Rudzoft.ChessLib/Types/PieceSquare.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Types/PieceSquare.cs
@@ -1,3 +1,3 @@
-namespace Rudzoft.ChessLib.Types;
-
-public readonly record struct PieceSquare(Piece Piece, Square Square);
+namespace Rudzoft.ChessLib.Types;
+
+public readonly record struct PieceSquare(Piece Piece, Square Square);
diff --git a/src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs b/src/chess-lib/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/PieceSquareEventArgs.cs
diff --git a/src/Rudzoft.ChessLib/Types/PieceType.cs b/src/chess-lib/Rudzoft.ChessLib/Types/PieceType.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/PieceType.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/PieceType.cs
diff --git a/src/Rudzoft.ChessLib/Types/Rank.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Rank.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Rank.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Rank.cs
diff --git a/src/Rudzoft.ChessLib/Types/RootMove.cs b/src/chess-lib/Rudzoft.ChessLib/Types/RootMove.cs
similarity index 97%
rename from src/Rudzoft.ChessLib/Types/RootMove.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/RootMove.cs
index a216fbb0..9db10b45 100644
--- a/src/Rudzoft.ChessLib/Types/RootMove.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Types/RootMove.cs
@@ -1,45 +1,45 @@
-using System.Runtime.CompilerServices;
-
-namespace Rudzoft.ChessLib.Types;
-
-public sealed class RootMove : List<Move>
-{
-    public RootMove(Move m)
-    {
-        Add(m);
-    }
-    
-    public Value OldValue { get; set; }
-    
-    public Value NewValue { get; set; }
-    
-    public Depth SelDepth { get; set; }
-    
-    public int TbRank { get; set; }
-    
-    public Value TbValue { get; set; }
-    
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator RootMove(Move m) => new(m);
-
-    public static bool operator !=(RootMove left, Move right) => left.FirstOrDefault() != right;
-
-    public static bool operator ==(RootMove left, Move right) => left.FirstOrDefault() == right;
-    
-    public static bool operator <(RootMove left, RootMove right)
-        => left.NewValue > right.NewValue || left.NewValue == right.NewValue && left.OldValue > right.OldValue;
-
-    public static bool operator >(RootMove left, RootMove right)
-        => left.NewValue < right.NewValue || left.NewValue == right.NewValue && left.OldValue < right.OldValue;
-    
-    private bool Equals(RootMove other) => this.FirstOrDefault().Equals(other.FirstOrDefault());
-
-    public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is RootMove other && Equals(other);
-
-    public override int GetHashCode() => this.FirstOrDefault().GetHashCode();
-}
-
-public sealed class RootMoves : List<RootMove>
-{
-    
+using System.Runtime.CompilerServices;
+
+namespace Rudzoft.ChessLib.Types;
+
+public sealed class RootMove : List<Move>
+{
+    public RootMove(Move m)
+    {
+        Add(m);
+    }
+    
+    public Value OldValue { get; set; }
+    
+    public Value NewValue { get; set; }
+    
+    public Depth SelDepth { get; set; }
+    
+    public int TbRank { get; set; }
+    
+    public Value TbValue { get; set; }
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator RootMove(Move m) => new(m);
+
+    public static bool operator !=(RootMove left, Move right) => left.FirstOrDefault() != right;
+
+    public static bool operator ==(RootMove left, Move right) => left.FirstOrDefault() == right;
+    
+    public static bool operator <(RootMove left, RootMove right)
+        => left.NewValue > right.NewValue || left.NewValue == right.NewValue && left.OldValue > right.OldValue;
+
+    public static bool operator >(RootMove left, RootMove right)
+        => left.NewValue < right.NewValue || left.NewValue == right.NewValue && left.OldValue < right.OldValue;
+    
+    private bool Equals(RootMove other) => this.FirstOrDefault().Equals(other.FirstOrDefault());
+
+    public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is RootMove other && Equals(other);
+
+    public override int GetHashCode() => this.FirstOrDefault().GetHashCode();
+}
+
+public sealed class RootMoves : List<RootMove>
+{
+    
 }
\ No newline at end of file
diff --git a/src/Rudzoft.ChessLib/Types/Score.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Score.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Score.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Score.cs
diff --git a/src/Rudzoft.ChessLib/Types/Square.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Square.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Square.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Square.cs
diff --git a/src/Rudzoft.ChessLib/Types/StateStack.cs b/src/chess-lib/Rudzoft.ChessLib/Types/StateStack.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/StateStack.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/StateStack.cs
diff --git a/src/Rudzoft.ChessLib/Types/ValMove.cs b/src/chess-lib/Rudzoft.ChessLib/Types/ValMove.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/ValMove.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/ValMove.cs
diff --git a/src/Rudzoft.ChessLib/Types/Value.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Value.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Value.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Value.cs
diff --git a/src/Rudzoft.ChessLib/Types/Values.cs b/src/chess-lib/Rudzoft.ChessLib/Types/Values.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Types/Values.cs
rename to src/chess-lib/Rudzoft.ChessLib/Types/Values.cs
diff --git a/src/Rudzoft.ChessLib/Validation/IPositionValidator.cs b/src/chess-lib/Rudzoft.ChessLib/Validation/IPositionValidator.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Validation/IPositionValidator.cs
rename to src/chess-lib/Rudzoft.ChessLib/Validation/IPositionValidator.cs
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidationResult.cs b/src/chess-lib/Rudzoft.ChessLib/Validation/PositionValidationResult.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Validation/PositionValidationResult.cs
rename to src/chess-lib/Rudzoft.ChessLib/Validation/PositionValidationResult.cs
diff --git a/src/Rudzoft.ChessLib/Validation/PositionValidator.cs b/src/chess-lib/Rudzoft.ChessLib/Validation/PositionValidator.cs
similarity index 100%
rename from src/Rudzoft.ChessLib/Validation/PositionValidator.cs
rename to src/chess-lib/Rudzoft.ChessLib/Validation/PositionValidator.cs
diff --git a/src/perft/Rudzoft.Perft/Rudzoft.Perft.csproj b/src/perft/Rudzoft.Perft/Rudzoft.Perft.csproj
index df235a4c..2125cfaf 100644
--- a/src/perft/Rudzoft.Perft/Rudzoft.Perft.csproj
+++ b/src/perft/Rudzoft.Perft/Rudzoft.Perft.csproj
@@ -18,8 +18,9 @@
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\..\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
-      <ProjectReference Include="..\..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
+      <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
+      <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
+      <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
       <ProjectReference Include="..\Rudzoft.Perft.Domain\Rudzoft.Perft.Domain.csproj" />
     </ItemGroup>
 
diff --git a/src/Rudzoft.ChessLib.WebApi/.dockerignore b/src/web-api/Rudzoft.ChessLib.WebApi/.dockerignore
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/.dockerignore
rename to src/web-api/Rudzoft.ChessLib.WebApi/.dockerignore
diff --git a/src/Rudzoft.ChessLib.WebApi/Controllers/MovesController.cs b/src/web-api/Rudzoft.ChessLib.WebApi/Controllers/MovesController.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/Controllers/MovesController.cs
rename to src/web-api/Rudzoft.ChessLib.WebApi/Controllers/MovesController.cs
diff --git a/src/Rudzoft.ChessLib.WebApi/Controllers/PgnController.cs b/src/web-api/Rudzoft.ChessLib.WebApi/Controllers/PgnController.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/Controllers/PgnController.cs
rename to src/web-api/Rudzoft.ChessLib.WebApi/Controllers/PgnController.cs
diff --git a/src/Rudzoft.ChessLib.WebApi/Dockerfile b/src/web-api/Rudzoft.ChessLib.WebApi/Dockerfile
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/Dockerfile
rename to src/web-api/Rudzoft.ChessLib.WebApi/Dockerfile
diff --git a/src/Rudzoft.ChessLib.WebApi/Moves.cs b/src/web-api/Rudzoft.ChessLib.WebApi/Moves.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/Moves.cs
rename to src/web-api/Rudzoft.ChessLib.WebApi/Moves.cs
diff --git a/src/Rudzoft.ChessLib.WebApi/Program.cs b/src/web-api/Rudzoft.ChessLib.WebApi/Program.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/Program.cs
rename to src/web-api/Rudzoft.ChessLib.WebApi/Program.cs
diff --git a/src/Rudzoft.ChessLib.WebApi/Properties/launchSettings.json b/src/web-api/Rudzoft.ChessLib.WebApi/Properties/launchSettings.json
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/Properties/launchSettings.json
rename to src/web-api/Rudzoft.ChessLib.WebApi/Properties/launchSettings.json
diff --git a/src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs b/src/web-api/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs
rename to src/web-api/Rudzoft.ChessLib.WebApi/Queries/MoveQuery.cs
diff --git a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/web-api/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
similarity index 78%
rename from src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
rename to src/web-api/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index d48a0997..91dd3759 100644
--- a/src/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/web-api/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -21,7 +21,12 @@
     </ItemGroup>
 
     <ItemGroup>
-        <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj"/>
+      <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
+      <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
     </ItemGroup>
 
 </Project>
diff --git a/src/Rudzoft.ChessLib.WebApi/Services/IMoveGeneratorService.cs b/src/web-api/Rudzoft.ChessLib.WebApi/Services/IMoveGeneratorService.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/Services/IMoveGeneratorService.cs
rename to src/web-api/Rudzoft.ChessLib.WebApi/Services/IMoveGeneratorService.cs
diff --git a/src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs b/src/web-api/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
rename to src/web-api/Rudzoft.ChessLib.WebApi/Services/MoveGeneratorService.cs
diff --git a/src/Rudzoft.ChessLib.WebApi/appsettings.Development.json b/src/web-api/Rudzoft.ChessLib.WebApi/appsettings.Development.json
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/appsettings.Development.json
rename to src/web-api/Rudzoft.ChessLib.WebApi/appsettings.Development.json
diff --git a/src/Rudzoft.ChessLib.WebApi/appsettings.json b/src/web-api/Rudzoft.ChessLib.WebApi/appsettings.json
similarity index 100%
rename from src/Rudzoft.ChessLib.WebApi/appsettings.json
rename to src/web-api/Rudzoft.ChessLib.WebApi/appsettings.json

From 6174d6da465badcc31292c2cef8553bc3d67b2e6 Mon Sep 17 00:00:00 2001
From: rudzen <rudzen@users.noreply.github.com>
Date: Thu, 27 Jun 2024 23:42:33 +0200
Subject: [PATCH 107/119] Update test.yml

---
 .github/workflows/test.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index bce1b744..3d6e0194 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -14,7 +14,7 @@ jobs:
     - name: Setup .NET
       uses: actions/setup-dotnet@v2
       with:
-        dotnet-version: 7.0.x
+        dotnet-version: 8.0.x
     - name: Restore dependencies
       run: dotnet restore
     - name: Build

From 4cf3bc670b3fe92eb421845d6bcf0cfbdfc55f3e Mon Sep 17 00:00:00 2001
From: rudzen <rudzen@users.noreply.github.com>
Date: Thu, 27 Jun 2024 23:42:51 +0200
Subject: [PATCH 108/119] Update publish.yml

---
 .github/workflows/publish.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index e26859e6..f1e1aa41 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -14,7 +14,7 @@ jobs:
     - name: Setup .NET
       uses: actions/setup-dotnet@v2
       with:
-        dotnet-version: 7.0.x
+        dotnet-version: 8.0.x
     - name: Restore dependencies
       run: dotnet restore
     - name: Build

From 28bdad40ba6e89fba085fecd181472529da00594 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Fri, 28 Jun 2024 08:21:34 +0200
Subject: [PATCH 109/119] link some files

---
 .../Rudzoft.ChessLib/Rudzoft.ChessLib.csproj   | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index 4887c251..ada17cb9 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -13,10 +13,8 @@
         <FileVersion>0.0.4</FileVersion>
         <PackageProjectUrl>https://github.com/rudzen/ChessLib</PackageProjectUrl>
         <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
-        <PackageLicenseFile>LICENSE</PackageLicenseFile>
         <EnforceCodeStyleInBuild>False</EnforceCodeStyleInBuild>
         <Title>Rudzoft.ChessLib</Title>
-        <PackageReadmeFile>README.md</PackageReadmeFile>
         <PackageTags>chess bitboard datastructure movegeneration magicbb</PackageTags>
         <PackageId>Rudzoft.ChessLib</PackageId>
         <RepositoryUrl>https://github.com/rudzen/ChessLib</RepositoryUrl>
@@ -24,6 +22,9 @@
         <Product>Rudzoft.ChessLib</Product>
         <IsPackable>true</IsPackable>
         <PublishAot>true</PublishAot>
+        <PackageIcon>ChessLib.png</PackageIcon>
+        <PackageReadmeFile>README.md</PackageReadmeFile>
+        <PackageLicenseFile>LICENSE</PackageLicenseFile>
     </PropertyGroup>
 
     <ItemGroup>
@@ -39,10 +40,17 @@
     <ItemGroup>
         <None Include="..\..\..\icon\ChessLib.png">
           <Pack>True</Pack>
-          <PackagePath></PackagePath>
+          <PackagePath>\</PackagePath>
           <Link>ChessLib.png</Link>
         </None>
-        <None Include="Icon\ChessLib.png" Pack="true" PackagePath=""/>
+        <None Include="..\..\..\README.md">
+            <Pack>True</Pack>
+            <PackagePath>\</PackagePath>
+        </None>
+        <None Include="..\..\..\LICENSE">
+            <Pack>True</Pack>
+            <PackagePath>\</PackagePath>
+        </None>
     </ItemGroup>
-
+    
 </Project>

From 298ae5c963702f0385d6f1ccc6cb41e1145b06c5 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Fri, 28 Jun 2024 08:31:33 +0200
Subject: [PATCH 110/119] various minor issues and warnings fixed

---
 .../Rudzoft.ChessLib.Test.csproj                   |  1 +
 .../Exceptions/InvalidFenException.cs              |  6 +-----
 .../{InvalidMove.cs => InvalidMoveException.cs}    | 14 +++++---------
 ...{InvalidSquare.cs => InvalidSquareException.cs} | 14 +++++---------
 .../Exceptions/TranspositionTableFailure.cs        |  8 --------
 .../Rudzoft.ChessLib/Notation/MoveNotation.cs      |  2 +-
 src/chess-lib/Rudzoft.ChessLib/Position.cs         |  7 -------
 .../Rudzoft.ChessLib/Rudzoft.ChessLib.csproj       |  1 -
 8 files changed, 13 insertions(+), 40 deletions(-)
 rename src/chess-lib/Rudzoft.ChessLib/Exceptions/{InvalidMove.cs => InvalidMoveException.cs} (79%)
 rename src/chess-lib/Rudzoft.ChessLib/Exceptions/{InvalidSquare.cs => InvalidSquareException.cs} (78%)

diff --git a/src/chess-lib/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj b/src/chess-lib/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
index 5164cc6e..61173f94 100644
--- a/src/chess-lib/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/Rudzoft.ChessLib.Test.csproj
@@ -8,6 +8,7 @@
         <PackageReference Include="Microsoft.Extensions.DependencyInjection"/>
         <PackageReference Include="Microsoft.NET.Test.Sdk"/>
         <PackageReference Include="SimdLinq" />
+        <PackageReference Include="coverlet.collector"/>
         <PackageReference Include="xunit"/>
         <PackageReference Include="xunit.analyzers"/>
         <PackageReference Include="xunit.runner.visualstudio"/>
diff --git a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
index b387a7fb..4395689c 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
@@ -24,10 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Runtime.Serialization;
-
 namespace Rudzoft.ChessLib.Exceptions;
 
+[Serializable]
 public sealed class InvalidFenException : ArgumentException
 {
     public InvalidFenException()
@@ -38,7 +37,4 @@ public InvalidFenException(string message)
 
     public InvalidFenException(string message, Exception innerException)
         : base(message, innerException) { }
-
-    public InvalidFenException(SerializationInfo info, StreamingContext context)
-        : base(info, context) { }
 }
diff --git a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMove.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMoveException.cs
similarity index 79%
rename from src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
rename to src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMoveException.cs
index a49fc88b..e604ed15 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMove.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMoveException.cs
@@ -24,22 +24,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Runtime.Serialization;
-
 namespace Rudzoft.ChessLib.Exceptions;
 
-public sealed class InvalidMove : InvalidOperationException
+[Serializable]
+public sealed class InvalidMoveException : InvalidOperationException
 {
-    public InvalidMove()
+    public InvalidMoveException()
     {
     }
 
-    public InvalidMove(string message)
+    public InvalidMoveException(string message)
         : base(message) { }
 
-    public InvalidMove(string message, Exception innerException)
+    public InvalidMoveException(string message, Exception innerException)
         : base(message, innerException) { }
-
-    public InvalidMove(SerializationInfo info, StreamingContext context)
-        : base(info, context) { }
 }
diff --git a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquareException.cs
similarity index 78%
rename from src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
rename to src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquareException.cs
index 62fbd79d..9a005871 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquare.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquareException.cs
@@ -24,22 +24,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Runtime.Serialization;
-
 namespace Rudzoft.ChessLib.Exceptions;
 
-public sealed class InvalidSquare : InvalidOperationException
+[Serializable]
+public sealed class InvalidSquareException : InvalidOperationException
 {
-    public InvalidSquare()
+    public InvalidSquareException()
     {
     }
 
-    public InvalidSquare(string message)
+    public InvalidSquareException(string message)
         : base(message) { }
 
-    public InvalidSquare(string message, Exception innerException)
+    public InvalidSquareException(string message, Exception innerException)
         : base(message, innerException) { }
-
-    public InvalidSquare(SerializationInfo info, StreamingContext context)
-        : base(info, context) { }
 }
diff --git a/src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
index 3da66205..6614565e 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
@@ -24,8 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
-using System.Runtime.Serialization;
-
 namespace Rudzoft.ChessLib.Exceptions;
 
 [Serializable]
@@ -42,10 +40,4 @@ public TranspositionTableFailure(string message) : base(message)
     public TranspositionTableFailure(string message, Exception inner) : base(message, inner)
     {
     }
-
-    public TranspositionTableFailure(
-        SerializationInfo info,
-        StreamingContext context) : base(info, context)
-    {
-    }
 }
diff --git a/src/chess-lib/Rudzoft.ChessLib/Notation/MoveNotation.cs b/src/chess-lib/Rudzoft.ChessLib/Notation/MoveNotation.cs
index 44657188..54f71195 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Notation/MoveNotation.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Notation/MoveNotation.cs
@@ -57,7 +57,7 @@ public INotation ToNotation(MoveNotations moveNotation = MoveNotations.Fan)
         var notation = _notations[(int)moveNotation];
 
         if (notation == null)
-            throw new InvalidMove("Invalid move notation detected.");
+            throw new InvalidMoveException("Invalid move notation detected.");
 
         return notation;
     }
diff --git a/src/chess-lib/Rudzoft.ChessLib/Position.cs b/src/chess-lib/Rudzoft.ChessLib/Position.cs
index 00f67ca0..84d2157d 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Position.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Position.cs
@@ -618,13 +618,6 @@ private bool IsCastleMoveLegal(Move m, Square to, Square from, Color us)
         var occupied = Board.Pieces(them, PieceType.Rook, PieceType.Queen);
 
         return (attacks & occupied).IsEmpty;
-
-        static (Square, Direction) GetRookSquareAndDirection(Square toSq, Square fromSq, Color us)
-        {
-            return toSq > fromSq
-                ? (Square.G1.Relative(us), Direction.West)
-                : (Square.C1.Relative(us), Direction.East);
-        }
     }
 
     private bool IsEnPassantMoveLegal(Square to, Color us, Square from, Square ksq)
diff --git a/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index ada17cb9..b41ecdce 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -21,7 +21,6 @@
         <RepositoryType>git</RepositoryType>
         <Product>Rudzoft.ChessLib</Product>
         <IsPackable>true</IsPackable>
-        <PublishAot>true</PublishAot>
         <PackageIcon>ChessLib.png</PackageIcon>
         <PackageReadmeFile>README.md</PackageReadmeFile>
         <PackageLicenseFile>LICENSE</PackageLicenseFile>

From 0ac016ba891bee023730b341e7c00f6bcb1cfbc4 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Fri, 28 Jun 2024 08:33:26 +0200
Subject: [PATCH 111/119] removed attribute from exceptions

---
 src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs | 1 -
 .../Rudzoft.ChessLib/Exceptions/InvalidMoveException.cs          | 1 -
 .../Rudzoft.ChessLib/Exceptions/InvalidSquareException.cs        | 1 -
 .../Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs     | 1 -
 4 files changed, 4 deletions(-)

diff --git a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
index 4395689c..ce8d1431 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidFenException.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Exceptions;
 
-[Serializable]
 public sealed class InvalidFenException : ArgumentException
 {
     public InvalidFenException()
diff --git a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMoveException.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMoveException.cs
index e604ed15..1b62a030 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMoveException.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidMoveException.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Exceptions;
 
-[Serializable]
 public sealed class InvalidMoveException : InvalidOperationException
 {
     public InvalidMoveException()
diff --git a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquareException.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquareException.cs
index 9a005871..1897c95c 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquareException.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Exceptions/InvalidSquareException.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Exceptions;
 
-[Serializable]
 public sealed class InvalidSquareException : InvalidOperationException
 {
     public InvalidSquareException()
diff --git a/src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs b/src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
index 6614565e..a4f6f1f8 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Exceptions/TranspositionTableFailure.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 namespace Rudzoft.ChessLib.Exceptions;
 
-[Serializable]
 public sealed class TranspositionTableFailure : ArgumentException
 {
     public TranspositionTableFailure()

From 78337747c76995d9c3478ca309da031ba9e27920 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Fri, 28 Jun 2024 10:15:02 +0200
Subject: [PATCH 112/119] fix a position assert

---
 src/chess-lib/Rudzoft.ChessLib/Position.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/chess-lib/Rudzoft.ChessLib/Position.cs b/src/chess-lib/Rudzoft.ChessLib/Position.cs
index 84d2157d..d31b8a59 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Position.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Position.cs
@@ -1342,7 +1342,7 @@ public void TakeMove(Move m)
                 {
                     captureSquare -= us.PawnPushDistance();
 
-                    Debug.Assert(GetPiece(to).Type() == PieceType.Pawn);
+                    Debug.Assert(!IsOccupied(to));
                     Debug.Assert(to == State.Previous.EnPassantSquare);
                     Debug.Assert(to.RelativeRank(us) == Ranks.Rank6);
                     Debug.Assert(!IsOccupied(captureSquare));

From 7f23f1d15b2a7aff4501796f678cef37556b99d5 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Fri, 28 Jun 2024 11:02:18 +0200
Subject: [PATCH 113/119] fix several warnings and problems with projects +
 simplify configuration loading

---
 .../KeyBenchmarks.cs                          | 134 ------------------
 .../Rudzoft.ChessLib.Benchmark/PerftBench.cs  |   3 +-
 .../Rudzoft.ChessLib.Benchmark.csproj         |   1 -
 src/chess-lib/Rudzoft.ChessLib.Perft/Perft.cs |  20 ++-
 .../PerftTests/PerftTheoryData.cs             |  37 ++++-
 .../ChessLibServiceCollectionExtensions.cs    |  39 +++--
 .../Factories/PolyglotBookFactory.cs          |   6 +-
 .../Transposition/TranspositionTable.cs       |   8 +-
 .../Rudzoft.ChessLib/Rudzoft.ChessLib.csproj  |   4 +
 src/perft/Rudzoft.Perft/Actors/PerftActor.cs  |   9 +-
 src/perft/Rudzoft.Perft/appsettings.json      |   3 +
 .../Rudzoft.ChessLib.WebApi.csproj            |   5 -
 12 files changed, 87 insertions(+), 182 deletions(-)
 delete mode 100644 src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs

diff --git a/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
deleted file mode 100644
index f6887c54..00000000
--- a/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/KeyBenchmarks.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Runtime.Intrinsics;
-using System.Runtime.Intrinsics.X86;
-
-namespace Rudzoft.ChessLib.Benchmark;
-
-[MemoryDiagnoser]
-public class KeyBenchmarks
-{
-    private sealed record Affe(string Cvr, string Se, DateOnly Ksl);
-
-    private sealed record Affe2(Guid Key, DateOnly Ksl);
-
-    private const string CvrN = "12345678";
-    private const string SeN = "87654321";
-
-    private DateOnly _ksl;
-    
-    [GlobalSetup]
-    public void Setup()
-    {
-        _ksl = DateOnly.FromDateTime(DateTime.Now);
-        var doSize = Unsafe.SizeOf<DateOnly>();
-        var dtSize = Unsafe.SizeOf<DateTime>();
-
-        var a = 0;
-    }
-    
-    [Benchmark]
-    public int StringToGuid()
-    {
-        return A(CvrN, SeN, in _ksl).GetHashCode();
-    }
-
-    [Benchmark]
-    public int StringToGuid2()
-    {
-        return A2(CvrN, SeN, in _ksl).GetHashCode();
-    }
-    
-    [Benchmark(Baseline = true)]
-    public int BaseRecord()
-    {
-        return B(CvrN, SeN, in _ksl).GetHashCode();
-    }
-    
-    [SkipLocalsInit]
-    private static Affe2 A(string s1, string s2, in DateOnly dateOnly)
-    {
-        var s1Span = s1.AsSpan();
-        var s2Span = s2.AsSpan();
-        
-        var s1Bytes = MemoryMarshal.AsBytes(s1Span);
-        var s2Bytes = MemoryMarshal.AsBytes(s2Span);
-
-        Span<byte> finalBytes = stackalloc byte[16];
-        
-        s1Bytes.CopyTo(finalBytes);
-        finalBytes[8] = s2Bytes[0];
-        finalBytes[9] = s2Bytes[1];
-        finalBytes[10] = s2Bytes[2];
-        finalBytes[11] = s2Bytes[3];
-        finalBytes[12] = s2Bytes[4];
-        finalBytes[13] = s2Bytes[5];
-        finalBytes[14] = s2Bytes[6];
-        finalBytes[15] = s2Bytes[7];
-
-        return new(new(finalBytes), dateOnly);
-        
-        //var h = MemoryMarshal.TryRead(finalBytes, out ulong v);
-    }
-
-    [SkipLocalsInit]
-    private static Affe2 A2(string s1, string s2, in DateOnly dateOnly)
-    {
-        var s1Span = s1.AsSpan();
-        var s2Span = s2.AsSpan();
-        
-        var s1Bytes = MemoryMarshal.AsBytes(s1Span);
-        var s2Bytes = MemoryMarshal.AsBytes(s2Span);
-
-        Span<byte> finalBytes = stackalloc byte[s1Bytes.Length + s2Bytes.Length];
-        
-        if (Sse2.IsSupported)
-        {
-            var s1Vector = MemoryMarshal.Cast<byte, Vector128<byte>>(s1Bytes)[0];
-            var s2Vector = MemoryMarshal.Cast<byte, Vector128<byte>>(s2Bytes)[0];
-
-            MemoryMarshal.Cast<byte, Vector128<byte>>(finalBytes)[0] = s1Vector;
-            MemoryMarshal.Cast<byte, Vector128<byte>>(finalBytes[16..])[0] = s2Vector;
-        }
-        else
-        {
-            // Fall back to non-SIMD code if not supported.
-            s1Bytes.CopyTo(finalBytes);
-            s2Bytes.CopyTo(finalBytes[16..]);
-        }
-
-        return new(new Guid(finalBytes), dateOnly);
-        
-        //var h = MemoryMarshal.TryRead(finalBytes, out ulong v);
-    }
-
-    // private static Affe2 AVector(string cvr, string se, in DateOnly now)
-    // {
-    //     var cvrSpan = cvr.AsSpan();
-    //     var seSpan = se.AsSpan();
-    //     
-    //     var cvrBytes = MemoryMarshal.AsBytes(cvrSpan);
-    //     var seBytes = MemoryMarshal.AsBytes(seSpan);
-    //
-    //     Span<byte> finalBytes = stackalloc byte[16];
-    //     
-    //     cvrBytes.CopyTo(finalBytes);
-    //     finalBytes[8] = seBytes[0];
-    //     finalBytes[9] = seBytes[1];
-    //     finalBytes[10] = seBytes[2];
-    //     finalBytes[11] = seBytes[3];
-    //     finalBytes[12] = seBytes[4];
-    //     finalBytes[13] = seBytes[5];
-    //     finalBytes[14] = seBytes[6];
-    //     finalBytes[15] = seBytes[7];
-    //
-    //     
-    //             
-    //
-    // }
-
-    private static Affe B(string cvr, string se, in DateOnly now)
-    {
-        return new(cvr, se, now);
-    }
-}
\ No newline at end of file
diff --git a/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/PerftBench.cs b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/PerftBench.cs
index 90367053..09827976 100644
--- a/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/PerftBench.cs
+++ b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/PerftBench.cs
@@ -62,8 +62,7 @@ public void Setup()
             ]);
 
         var ttConfig = new TranspositionTableConfiguration { DefaultSize = 1 };
-        var options = Options.Create(ttConfig);
-        var tt = new TranspositionTable(options);
+        var tt = new TranspositionTable(ttConfig);
 
         var provider = new DefaultObjectPoolProvider();
         var policy = new DefaultPooledObjectPolicy<MoveList>();
diff --git a/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
index 147d10d7..d3e95e6d 100644
--- a/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
+++ b/src/chess-lib.bench/Rudzoft.ChessLib.Benchmark/Rudzoft.ChessLib.Benchmark.csproj
@@ -14,7 +14,6 @@
         <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib.Perft.Interfaces\Rudzoft.ChessLib.Perft.Interfaces.csproj" />
         <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj" />
         <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
-        <ProjectReference Include="..\Rudzoft.ChessLib.Perft\Rudzoft.ChessLib.Perft.csproj"/>
     </ItemGroup>
 
 </Project>
diff --git a/src/chess-lib/Rudzoft.ChessLib.Perft/Perft.cs b/src/chess-lib/Rudzoft.ChessLib.Perft/Perft.cs
index 6d5a46d1..0ea45210 100644
--- a/src/chess-lib/Rudzoft.ChessLib.Perft/Perft.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Perft/Perft.cs
@@ -50,17 +50,23 @@ Result	3	379.0 us	1.897 us	1.584 us
 Result	6	1,912,300.6 us	3,551.167 us	3,148.017 us
      */
 
-public sealed class Perft(IGame game, IEnumerable<PerftPosition> positions) : IPerft
+public sealed class Perft : IPerft
 {
+    public Perft(IGame game, IEnumerable<PerftPosition> positions)
+    {
+        Positions = positions.ToList();
+        Game = game;
+    }
+
     public Action<string>? BoardPrintCallback { get; set; }
 
     /// <summary>
     /// The positional data for the run
     /// </summary>
-    public List<PerftPosition> Positions { get; set; } = positions.ToList();
+    public List<PerftPosition> Positions { get; set; }
 
-    public IGame   Game     { get; set; } = game;
-    public int     Depth    { get; set; }
+    public IGame Game { get; set; }
+    public int Depth { get; set; }
     public UInt128 Expected { get; set; }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -76,7 +82,7 @@ public async IAsyncEnumerable<UInt128> DoPerft(int depth)
         foreach (var fd in Positions.Select(static perftPosition => new FenData(perftPosition.Fen)))
         {
             Game.Pos.Set(in fd, ChessMode.Normal, in state);
-            var baseKey = game.Pos.State.PositionKey;
+            var baseKey = Game.Pos.State.PositionKey;
             var result = Game.Perft(in baseKey, depth);
             yield return result;
         }
@@ -84,7 +90,7 @@ public async IAsyncEnumerable<UInt128> DoPerft(int depth)
 
     public UInt128 DoPerftSimple(int depth)
     {
-        var baseKey = game.Pos.State.PositionKey;
+        var baseKey = Game.Pos.State.PositionKey;
         return Game.Perft(in baseKey, depth);
     }
 
@@ -95,7 +101,7 @@ public void SetGamePosition(PerftPosition pp)
     {
         var fp = new FenData(pp.Fen);
         var state = new State();
-        game.Pos.Set(in fp, ChessMode.Normal, in state);
+        Game.Pos.Set(in fp, ChessMode.Normal, in state);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
index 53a1566b..3bb35962 100644
--- a/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftTheoryData.cs
@@ -1,4 +1,28 @@
-using System.Diagnostics.Contracts;
+/*
+ChessLib, a chess data structure library
+
+MIT License
+
+Copyright (c) 2017-2024 Rudy Alex Kohn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
 
 namespace Rudzoft.ChessLib.Test.PerftTests;
 
@@ -6,14 +30,13 @@ public sealed class PerftTheoryData : TheoryData<string, int, ulong>
 {
     public PerftTheoryData(string[] fens, int[] depths, ulong[] results)
     {
-        Contract.Assert(fens != null);
-        Contract.Assert(depths != null);
-        Contract.Assert(results != null);
-        Contract.Assert(fens.Length == depths.Length);
-        Contract.Assert(fens.Length == results.Length);
+        ArgumentNullException.ThrowIfNull(fens);
+        ArgumentNullException.ThrowIfNull(depths);
+        ArgumentNullException.ThrowIfNull(results);
+        if (fens.Length != depths.Length || fens.Length != results.Length)
+            throw new ArgumentException("The number of FENs, depths, and results must be the same.");
 
         for (var i = 0; i < fens.Length; i++)
             Add(fens[i], depths[i], results[i]);
     }
-    
 }
\ No newline at end of file
diff --git a/src/chess-lib/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs b/src/chess-lib/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
index 3d087fda..aaf138f3 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Extensions/ChessLibServiceCollectionExtensions.cs
@@ -28,6 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.ObjectPool;
+using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Evaluation;
 using Rudzoft.ChessLib.Factories;
 using Rudzoft.ChessLib.Hash;
@@ -57,19 +58,7 @@ public static IServiceCollection AddChessLib(
             serviceCollection.AddSingleton(configuration);
         }
 
-        // TODO : Add configuration "manually" to avoid IL2026 warning
-        serviceCollection.AddOptions<TranspositionTableConfiguration>().Configure<IConfiguration>(
-            static (settings, configuration)
-                => configuration
-                   .GetSection(TranspositionTableConfiguration.Section)
-                   .Bind(settings));
-
-        // TODO : Add configuration "manually" to avoid IL2026 warning
-        serviceCollection.AddOptions<PolyglotBookConfiguration>().Configure<IConfiguration>(
-            static (settings, configuration)
-                => configuration
-                   .GetSection(PolyglotBookConfiguration.Section)
-                   .Bind(settings));
+        serviceCollection.BindConfigurations(configuration);
 
         serviceCollection.TryAddSingleton<ITranspositionTable, TranspositionTable>();
         serviceCollection.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
@@ -104,9 +93,31 @@ public static IServiceCollection AddChessLib(
                .AddNotationServices();
     }
 
+    private static void BindConfigurations(this IServiceCollection serviceCollection, IConfiguration configuration)
+    {
+        var transpositionConfigurationSection = configuration.GetSection(TranspositionTableConfiguration.Section);
+        serviceCollection.Configure<TranspositionTableConfiguration>(transpositionConfigurationSection);
+
+        var polyglotBookConfiguration = configuration.GetSection(PolyglotBookConfiguration.Section);
+        serviceCollection.Configure<PolyglotBookConfiguration>(polyglotBookConfiguration);
+
+        serviceCollection.AddSingleton(provider =>
+        {
+            var ttOptions = provider.GetRequiredService<IOptions<TranspositionTableConfiguration>>();
+            return ttOptions.Value;
+        });
+
+        serviceCollection.AddSingleton(provider =>
+        {
+            var polyOptions = provider.GetRequiredService<IOptions<PolyglotBookConfiguration>>();
+            return polyOptions.Value;
+        });
+
+    }
+
     private static IConfigurationRoot LoadConfiguration(string file)
     {
-        var configurationFile     = string.IsNullOrWhiteSpace(file) ? "chesslib.json" : file;
+        var configurationFile     = string.IsNullOrWhiteSpace(file) ? "appsettings.json" : file;
         var configurationFilePath = Path.Combine(AppContext.BaseDirectory, configurationFile);
         return new ConfigurationBuilder()
                .AddJsonFile(configurationFilePath, optional: true, reloadOnChange: true)
diff --git a/src/chess-lib/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs b/src/chess-lib/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
index 001d0884..a9144deb 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Factories/PolyglotBookFactory.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System.Runtime.CompilerServices;
 using Microsoft.Extensions.ObjectPool;
-using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.MoveGeneration;
 using Rudzoft.ChessLib.Polyglot;
 
@@ -37,10 +36,9 @@ public sealed class PolyglotBookFactory : IPolyglotBookFactory
     private readonly string _path;
     private readonly ObjectPool<MoveList> _objectPool;
 
-    public PolyglotBookFactory(IOptions<PolyglotBookConfiguration> options, ObjectPool<MoveList> objectPool)
+    public PolyglotBookFactory(PolyglotBookConfiguration configuration, ObjectPool<MoveList> objectPool)
     {
-        var config = options.Value;
-        _path = string.IsNullOrWhiteSpace(config.BookPath) ? string.Empty : config.BookPath;
+        _path = string.IsNullOrWhiteSpace(configuration.BookPath) ? string.Empty : configuration.BookPath;
         _objectPool = objectPool;
     }
 
diff --git a/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs b/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
index c9114d51..0e26ab79 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Hash/Tables/Transposition/TranspositionTable.cs
@@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
-using Microsoft.Extensions.Options;
 using Rudzoft.ChessLib.Types;
 
 namespace Rudzoft.ChessLib.Hash.Tables.Transposition;
@@ -43,10 +42,9 @@ public sealed class TranspositionTable : ITranspositionTable
     private uint _sizeMask;
     private byte _generation; // Size must be not bigger then TTEntry::generation8
 
-    public TranspositionTable(IOptions<TranspositionTableConfiguration> options)
+    public TranspositionTable(TranspositionTableConfiguration configuration)
     {
-        var config = options.Value;
-        SetSize(config.DefaultSize);
+        SetSize(configuration.DefaultSize);
     }
 
     public ulong Hits { get; private set; }
@@ -141,7 +139,7 @@ public void Store(in HashKey posKey, Value v, Bound t, Depth d, Move m, Value st
             {
                 if (tte.generation8 == _generation || tte.bound == Bound.Exact)
                 {
-                    // +2 
+                    // +2
                     if (tte.depth16 < replaceTte.depth16) // +1
                         replacePos = ttePos;
                 }
diff --git a/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj b/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
index b41ecdce..0f6d5b1a 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
+++ b/src/chess-lib/Rudzoft.ChessLib/Rudzoft.ChessLib.csproj
@@ -26,6 +26,10 @@
         <PackageLicenseFile>LICENSE</PackageLicenseFile>
     </PropertyGroup>
 
+    <PropertyGroup Label="Configuration">
+        <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
+    </PropertyGroup>
+    
     <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions"/>
         <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables"/>
diff --git a/src/perft/Rudzoft.Perft/Actors/PerftActor.cs b/src/perft/Rudzoft.Perft/Actors/PerftActor.cs
index 2b0c0610..94f57d12 100644
--- a/src/perft/Rudzoft.Perft/Actors/PerftActor.cs
+++ b/src/perft/Rudzoft.Perft/Actors/PerftActor.cs
@@ -1,6 +1,7 @@
-using System.Collections.Frozen;
-using Akka.Actor;
+using Akka.Actor;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
+using Rudzoft.ChessLib.Polyglot;
 using Rudzoft.Perft.Domain;
 using Rudzoft.Perft.Options;
 using Rudzoft.Perft.Services;
@@ -18,8 +19,10 @@ public PerftActor(IServiceProvider sp)
         _perftRunner = sp.GetRequiredService<IPerftRunner>();
         var props = Props.Create<OutputActor>();
 
+        var polyConfig = sp.GetRequiredService<PolyglotBookConfiguration>();
+
         var optionsFactory = sp.GetRequiredService<IOptionsFactory>();
-        var options = optionsFactory.Parse().ToFrozenSet();
+        var options = optionsFactory.Parse();
 
         foreach (var option in options)
         {
diff --git a/src/perft/Rudzoft.Perft/appsettings.json b/src/perft/Rudzoft.Perft/appsettings.json
index c4a020f6..9876f94a 100644
--- a/src/perft/Rudzoft.Perft/appsettings.json
+++ b/src/perft/Rudzoft.Perft/appsettings.json
@@ -11,6 +11,9 @@
       "ExpectedMove": 0
     }
   },
+  "PolyglotBook" : {
+    "BookPath": "D:\\book.bin"
+  },
   "Serilog": {
     "Using": [
       "Serilog.Sinks.Console",
diff --git a/src/web-api/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj b/src/web-api/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
index 91dd3759..c4c837fe 100644
--- a/src/web-api/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
+++ b/src/web-api/Rudzoft.ChessLib.WebApi/Rudzoft.ChessLib.WebApi.csproj
@@ -22,11 +22,6 @@
 
     <ItemGroup>
       <ProjectReference Include="..\..\chess-lib\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
-      <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
-    </ItemGroup>
-
-    <ItemGroup>
-      <ProjectReference Include="..\Rudzoft.ChessLib\Rudzoft.ChessLib.csproj" />
     </ItemGroup>
 
 </Project>

From 6171174447adb9f5d9a7cdc2c1abadaa8fb9c32a Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Fri, 28 Jun 2024 11:41:24 +0200
Subject: [PATCH 114/119] tests no longer configures options for configuration

---
 src/chess-lib/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs | 3 +--
 src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs  | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/chess-lib/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
index ca5de126..4f60497d 100644
--- a/src/chess-lib/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/BookTests/PolyglotTests.cs
@@ -48,11 +48,10 @@ public sealed class PolyglotTests : IClassFixture<BookFixture>
     public PolyglotTests(BookFixture fixture)
     {
         var polyConfig = new PolyglotBookConfiguration { BookPath = string.Empty };
-        var polyOptions = Options.Create(polyConfig);
 
         _fixture = fixture;
         _serviceProvider = new ServiceCollection()
-            .AddSingleton(polyOptions)
+            .AddSingleton(polyConfig)
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddSingleton<IRKiss, RKiss>()
diff --git a/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs b/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
index 8b4db3ce..a5506a82 100644
--- a/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/PerftTests/PerftVerify.cs
@@ -17,10 +17,9 @@ public abstract class PerftVerify
     protected PerftVerify()
     {
         var ttConfig = new TranspositionTableConfiguration { DefaultSize = 1 };
-        var ttOptions = Options.Create(ttConfig);
 
         _serviceProvider = new ServiceCollection()
-            .AddSingleton(ttOptions)
+            .AddSingleton(ttConfig)
             .AddTransient<IBoard, Board>()
             .AddSingleton<IValues, Values>()
             .AddSingleton<IRKiss, RKiss>()

From 0ecf070833b242d9d7c030ed146e0a6bc36c5189 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 29 Jun 2024 13:43:01 +0200
Subject: [PATCH 115/119] remove duplicate package version declaration

---
 Directory.Packages.props | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index fd3319e7..b087e33f 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -48,7 +48,6 @@
     </PackageVersion>
     <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.6" />
-    <PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
     <PackageVersion Include="Microsoft.OpenApi" Version="1.6.15" />
     <PackageVersion Include="Swashbuckle.AspNetCore" Version="6.6.2" />
     <PackageVersion Include="Swashbuckle.AspNetCore.Swagger" Version="6.6.2" />

From 96ba919c126b2b7294321a6187bdf54f25945fde Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 29 Jun 2024 13:43:25 +0200
Subject: [PATCH 116/119] simplify some perft functionality a bit

---
 src/perft/Rudzoft.Perft/Parsers/EpdParser.cs    | 9 ++++-----
 src/perft/Rudzoft.Perft/Services/PerftRunner.cs | 4 ++--
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/perft/Rudzoft.Perft/Parsers/EpdParser.cs b/src/perft/Rudzoft.Perft/Parsers/EpdParser.cs
index 60ce547a..63e5f8c5 100644
--- a/src/perft/Rudzoft.Perft/Parsers/EpdParser.cs
+++ b/src/perft/Rudzoft.Perft/Parsers/EpdParser.cs
@@ -88,10 +88,9 @@ public async IAsyncEnumerable<IEpdSet> Parse(string epdFile)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static PerftPositionValue ParsePerftLines(string perftData)
     {
-        var s = perftData.Split(' ', StringSplitOptions.RemoveEmptyEntries);
-        var result = (depth: 0, count: ulong.MinValue);
-        Maths.ToIntegral(s[0], out result.depth);
-        Maths.ToIntegral(s[1], out result.count);
-        return new(result.depth, result.count);
+        var s = perftData.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
+        Maths.ToIntegral(s[0], out int depth);
+        Maths.ToIntegral(s[1], out ulong count);
+        return new(depth, count);
     }
 }
\ No newline at end of file
diff --git a/src/perft/Rudzoft.Perft/Services/PerftRunner.cs b/src/perft/Rudzoft.Perft/Services/PerftRunner.cs
index 80e3c189..dcbd05e7 100644
--- a/src/perft/Rudzoft.Perft/Services/PerftRunner.cs
+++ b/src/perft/Rudzoft.Perft/Services/PerftRunner.cs
@@ -44,9 +44,9 @@ namespace Rudzoft.Perft.Services;
 
 public sealed class PerftRunner : IPerftRunner
 {
-    private static readonly ILogger Log = Serilog.Log.ForContext<PerftRunner>();
+    private const string Line = "-----------------------------------------------------------------";
 
-    private static readonly string Line = new('-', 65);
+    private static readonly ILogger Log = Serilog.Log.ForContext<PerftRunner>();
 
     private static string CurrentDirectory => Environment.CurrentDirectory;
 

From 98120fcedb10dd253de9ce75c63327005aeb27e2 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 29 Jun 2024 13:43:33 +0200
Subject: [PATCH 117/119] simplify move tests a bit

---
 .../MoveTests/MoveTests.cs                    | 79 +++++++++----------
 1 file changed, 37 insertions(+), 42 deletions(-)

diff --git a/src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs b/src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
index fcab9184..dd7003ff 100644
--- a/src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
+++ b/src/chess-lib/Rudzoft.ChessLib.Test/MoveTests/MoveTests.cs
@@ -44,21 +44,21 @@ public sealed class MoveTests
     public MoveTests()
     {
         _serviceProvider = new ServiceCollection()
-            .AddTransient<IBoard, Board>()
-            .AddSingleton<IValues, Values>()
-            .AddSingleton<IRKiss, RKiss>()
-            .AddSingleton<IZobrist, Zobrist>()
-            .AddSingleton<ICuckoo, Cuckoo>()
-            .AddSingleton<IPositionValidator, PositionValidator>()
-            .AddTransient<IPosition, Position>()
-            .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
-            .AddSingleton(static serviceProvider =>
-            {
-                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
-                var policy = new DefaultPooledObjectPolicy<MoveList>();
-                return provider.Create(policy);
-            })
-            .BuildServiceProvider();
+                           .AddTransient<IBoard, Board>()
+                           .AddSingleton<IValues, Values>()
+                           .AddSingleton<IRKiss, RKiss>()
+                           .AddSingleton<IZobrist, Zobrist>()
+                           .AddSingleton<ICuckoo, Cuckoo>()
+                           .AddSingleton<IPositionValidator, PositionValidator>()
+                           .AddTransient<IPosition, Position>()
+                           .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
+                           .AddSingleton(static serviceProvider =>
+                           {
+                               var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                               var policy = new DefaultPooledObjectPolicy<MoveList>();
+                               return provider.Create(policy);
+                           })
+                           .BuildServiceProvider();
     }
 
     [Fact]
@@ -67,7 +67,7 @@ public void MoveSquares()
         var bb = BitBoards.AllSquares;
         while (bb)
         {
-            var expectedFrom =  BitBoards.PopLsb(ref bb);
+            var expectedFrom = BitBoards.PopLsb(ref bb);
             var bb2 = bb;
             while (bb2)
             {
@@ -122,29 +122,32 @@ public void AllBasicMove()
     [Fact]
     public void MoveToString()
     {
-        var moves = new List<Move>(3528);
-        var movesString = new List<MoveStrings>(3528);
+        var result = new StringBuilder(128);
+
+        var allSquares = Square.All.AsSpan();
+        var allSquares2 = allSquares[1..];
 
-        var tmp = new StringBuilder(8);
+        var moves = new List<Move>(allSquares.Length * allSquares2.Length);
+        var movesString = new List<string>(allSquares.Length * allSquares2.Length);
 
-        // build move list and expected result
-        for (Square s1 = Squares.a1; s1 <= Squares.h8; s1++)
+        //build move list and expected result
+        foreach (var s1 in allSquares)
         {
-            for (Square s2 = Squares.a2; s2 <= Squares.h8; s2++)
+            foreach (var s2 in allSquares2)
             {
                 if (s1 == s2)
                     continue;
 
                 moves.Add(Move.Create(s1, s2));
-                tmp.Clear();
-                tmp.Append(' ');
-                tmp.Append(s1.ToString());
-                tmp.Append(s2.ToString());
-                movesString.Add(new MoveStrings(tmp.ToString()));
+                result.Clear();
+                result.Append(' ');
+                result.Append(s1.ToString());
+                result.Append(s2.ToString());
+                movesString.Add(result.ToString());
             }
         }
 
-        var result = new StringBuilder(128);
+        result.Clear();
 
         var pos = _serviceProvider.GetRequiredService<IPosition>();
         var fenData = new FenData(Fen.Fen.StartPositionFen);
@@ -152,13 +155,14 @@ public void MoveToString()
         pos.Set(in fenData, ChessMode.Normal, state);
 
         var i = 0;
-        foreach (var move in CollectionsMarshal.AsSpan(moves))
+
+        var movesSpan = CollectionsMarshal.AsSpan(moves);
+        foreach (var move in movesSpan)
         {
             result.Clear();
             result.Append(' ');
             pos.MoveToString(move, in result);
-            Assert.Equal(result.ToString(), movesString[i++].ToString());
-
+            Assert.Equal(result.ToString(), movesString[i++]);
         }
     }
 
@@ -194,8 +198,8 @@ public void MoveListToStringTest()
         var state = new State();
         pos.Set(in fenData, ChessMode.Normal, state);
 
-        // generate a bitch string for them all.
-        foreach (var move in CollectionsMarshal.AsSpan(moves))
+        var movesSpan = CollectionsMarshal.AsSpan(moves);
+        foreach (var move in movesSpan)
         {
             result.Append(' ');
             pos.MoveToString(move, result);
@@ -203,13 +207,4 @@ public void MoveListToStringTest()
 
         Assert.Equal(expected.ToString(), result.ToString());
     }
-
-    private readonly struct MoveStrings
-    {
-        private readonly string _s;
-
-        public MoveStrings(string s) => _s = s;
-
-        public override string ToString() => _s;
-    }
 }
\ No newline at end of file

From 42d4f6f7611e2c2c0f2d3909e2f15096e90fd5f8 Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 29 Jun 2024 13:45:08 +0200
Subject: [PATCH 118/119] simplify bitboard to string method a bit

---
 src/chess-lib/Rudzoft.ChessLib/Types/BitBoards.cs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/chess-lib/Rudzoft.ChessLib/Types/BitBoards.cs b/src/chess-lib/Rudzoft.ChessLib/Types/BitBoards.cs
index 1e67621c..ed4dec8a 100644
--- a/src/chess-lib/Rudzoft.ChessLib/Types/BitBoards.cs
+++ b/src/chess-lib/Rudzoft.ChessLib/Types/BitBoards.cs
@@ -498,8 +498,7 @@ public static string Stringify(in BitBoard bb, string title = "")
         var idx = line.Length;
         span[idx++] = '\n';
 
-        var t = title.Length > 64 ? title.AsSpan()[..64] : title.AsSpan();
-        t.CopyTo(span[idx..]);
+        title.AsSpan(0, Math.Min(64, title.Length)).CopyTo(span[idx..]);
 
         Span<char> rank = stackalloc char[4] { '|', ' ', ' ', ' ' };
         for (var r = Ranks.Rank8; r >= Ranks.Rank1; --r)

From f43af73ed6e5fd528ea340c5d00142346b5a1c1d Mon Sep 17 00:00:00 2001
From: Rudy Alex Kohn <rudzen@gmail.com>
Date: Sat, 29 Jun 2024 13:45:21 +0200
Subject: [PATCH 119/119] removed redundant project settings for perft project

---
 .../Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj        | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/chess-lib/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj b/src/chess-lib/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
index 45be559d..f049d234 100644
--- a/src/chess-lib/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
+++ b/src/chess-lib/Rudzoft.ChessLib.Perft/Rudzoft.ChessLib.Perft.csproj
@@ -2,8 +2,6 @@
 
     <PropertyGroup>
         <Nullable>enable</Nullable>
-        <ServerGarbageCollection>true</ServerGarbageCollection>
-        <GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
     </PropertyGroup>
 
     <ItemGroup>