From db99487f4eeb6b51a5e234dfaac065f445042935 Mon Sep 17 00:00:00 2001
From: Liam McGuire <12057628+liamt19@users.noreply.github.com>
Date: Sat, 22 Jun 2024 16:36:34 -0400
Subject: [PATCH] General cleanups (#46)
bench: 7141099
---
Logic/Core/StateInfo.cs | 61 ++----
Logic/Data/Enums.cs | 13 ++
Logic/Data/Move.cs | 7 +-
Logic/NN/Accumulator.cs | 6 -
Logic/NN/Bucketed768.cs | 20 +-
Logic/NN/Bucketed768Unroll.cs | 3 +-
Logic/NN/FunUnrollThings.cs | 4 +-
Logic/NN/VSize.cs | 29 ---
Logic/Search/SearchInformation.cs | 5 +-
Logic/Search/SearchStackEntry.cs | 122 ++----------
Logic/Transposition/TTCluster.cs | 19 +-
Logic/Transposition/TranspositionTable.cs | 11 +-
Logic/UCI/UCIClient.cs | 34 +---
Logic/Util/FishBench.cs | 2 -
Logic/Util/SearchBench.cs | 1 -
Logic/Util/SkipStaticConstructorAttribute.cs | 11 --
Logic/Util/Utilities.cs | 196 +++++--------------
Program.cs | 4 +-
18 files changed, 136 insertions(+), 412 deletions(-)
delete mode 100644 Logic/NN/VSize.cs
delete mode 100644 Logic/Util/SkipStaticConstructorAttribute.cs
diff --git a/Logic/Core/StateInfo.cs b/Logic/Core/StateInfo.cs
index dfed24ba..e3684640 100644
--- a/Logic/Core/StateInfo.cs
+++ b/Logic/Core/StateInfo.cs
@@ -15,55 +15,20 @@ namespace Lizard.Logic.Core
public unsafe struct StateInfo
{
public static readonly nuint StateCopySize = (nuint)(sizeof(StateInfo) - sizeof(Accumulator*));
- static StateInfo()
- {
- int accOffset = ((FieldOffsetAttribute)typeof(StateInfo).GetField("Accumulator").GetCustomAttributes(typeof(FieldOffsetAttribute), true)[0]).Value;
- Assert(accOffset == (int)StateCopySize,
- $"StateInfo's Accumulator pointer is {accOffset} / {sizeof(StateInfo)}, should be {StateCopySize}");
- }
-
- [FieldOffset(0)]
- public fixed ulong CheckSquares[PieceNB];
-
- [FieldOffset(48)]
- public fixed int KingSquares[2];
-
- [FieldOffset(56)]
- public fixed ulong BlockingPieces[2];
-
- [FieldOffset(72)]
- public fixed ulong Pinners[2];
-
- [FieldOffset(88)]
- public ulong Hash = 0;
-
- [FieldOffset(96)]
- public ulong Checkers = 0;
-
- [FieldOffset(104)]
- public CastlingStatus CastleStatus = CastlingStatus.None;
-
- ///
- /// The first number in the FEN, which starts at 0 and resets to 0 every time a pawn moves or a piece is captured.
- /// If this reaches 100, the game is a draw by the 50-move rule.
- ///
- [FieldOffset(108)]
- public int HalfmoveClock = 0;
-
- [FieldOffset(112)]
- public int EPSquare = EPNone;
-
- [FieldOffset(116)]
- public int CapturedPiece = None;
-
- [FieldOffset(120)]
- public int PliesFromNull = 0;
-
- [FieldOffset(124)]
- private int _align = 0;
- [FieldOffset(128)]
- public Accumulator* Accumulator;
+ [FieldOffset( 0)] public fixed ulong CheckSquares[PieceNB];
+ [FieldOffset( 48)] public fixed ulong BlockingPieces[2];
+ [FieldOffset( 64)] public fixed ulong Pinners[2];
+ [FieldOffset( 80)] public fixed int KingSquares[2];
+ [FieldOffset( 88)] public ulong Hash = 0;
+ [FieldOffset( 96)] public ulong Checkers = 0;
+ [FieldOffset(104)] public int HalfmoveClock = 0;
+ [FieldOffset(108)] public int EPSquare = EPNone;
+ [FieldOffset(112)] public int CapturedPiece = None;
+ [FieldOffset(116)] public int PliesFromNull = 0;
+ [FieldOffset(120)] public CastlingStatus CastleStatus = CastlingStatus.None;
+ [FieldOffset(124)] private fixed byte _pad0[4];
+ [FieldOffset(128)] public Accumulator* Accumulator;
public StateInfo()
{
diff --git a/Logic/Data/Enums.cs b/Logic/Data/Enums.cs
index 4d8b865f..f77a3bba 100644
--- a/Logic/Data/Enums.cs
+++ b/Logic/Data/Enums.cs
@@ -99,6 +99,19 @@ public static class Files
public const int H = 7;
}
+ public static class Direction
+ {
+ public const int NORTH = 8;
+ public const int EAST = 1;
+ public const int SOUTH = -NORTH;
+ public const int WEST = -EAST;
+
+ public const int NORTH_EAST = NORTH + EAST;
+ public const int SOUTH_EAST = SOUTH + EAST;
+ public const int SOUTH_WEST = SOUTH + WEST;
+ public const int NORTH_WEST = NORTH + WEST;
+ }
+
public static class Squares
{
public const int A1 = 0;
diff --git a/Logic/Data/Move.cs b/Logic/Data/Move.cs
index 942a6b43..7cbeb710 100644
--- a/Logic/Data/Move.cs
+++ b/Logic/Data/Move.cs
@@ -1,8 +1,13 @@
-using System.Text;
+
+#pragma warning disable CS0660 // Type defines operator == or operator != but does not override Object.Equals(object o)
+#pragma warning disable CS0661 // Type defines operator == or operator != but does not override Object.GetHashCode()
+
+using System.Text;
namespace Lizard.Logic.Data
{
+
public unsafe struct Move
{
public static readonly Move Null = new Move();
diff --git a/Logic/NN/Accumulator.cs b/Logic/NN/Accumulator.cs
index 58fb45cf..a3d13adf 100644
--- a/Logic/NN/Accumulator.cs
+++ b/Logic/NN/Accumulator.cs
@@ -8,14 +8,8 @@ namespace Lizard.Logic.NN
///
public unsafe struct Accumulator
{
- // Intellisense incorrectly marks "var accumulation = accumulator[perspectives[p]]" in FeatureTransformer.TransformFeatures
- // as an error when this uses a primary constructor with "size" as a parameter.
- // https://github.com/dotnet/roslyn/issues/69663
-
public const int ByteSize = Bucketed768.HiddenSize * sizeof(short);
- public static int VectorCount => Bucketed768.HiddenSize / VSize.Short;
-
public readonly Vector256* White;
public readonly Vector256* Black;
diff --git a/Logic/NN/Bucketed768.cs b/Logic/NN/Bucketed768.cs
index 03ad4050..acc2965d 100644
--- a/Logic/NN/Bucketed768.cs
+++ b/Logic/NN/Bucketed768.cs
@@ -8,7 +8,6 @@
namespace Lizard.Logic.NN
{
- [SkipStaticConstructor]
public static unsafe partial class Bucketed768
{
public const int InputBuckets = 5;
@@ -18,19 +17,14 @@ public static unsafe partial class Bucketed768
public const int QA = 255;
public const int QB = 64;
- private const int QAB = QA * QB;
public const int OutputScale = 400;
- public static readonly int SIMD_CHUNKS_512 = HiddenSize / Vector512.Count;
- public static readonly int SIMD_CHUNKS_256 = HiddenSize / Vector256.Count;
-
///
/// (768x5 -> 1536)x2 -> 8
///
public const string NetworkName = "L1536x5x8_g75_s20-550.bin";
-
public static readonly short* FeatureWeights;
public static readonly short* FeatureBiases;
public static readonly short* LayerWeights;
@@ -42,7 +36,7 @@ public static unsafe partial class Bucketed768
private const int LayerWeightElements = HiddenSize * 2 * OutputBuckets;
private const int LayerBiasElements = OutputBuckets;
- public static long ExpectedNetworkSize => (FeatureWeightElements + FeatureBiasElements + LayerWeightElements + LayerBiasElements) * sizeof(short);
+ private const long ExpectedNetworkSize = (FeatureWeightElements + FeatureBiasElements + LayerWeightElements + LayerBiasElements) * sizeof(short);
private static ReadOnlySpan KingBuckets =>
[
@@ -64,7 +58,7 @@ static Bucketed768()
FeatureBiases = (short*)AlignedAllocZeroed(sizeof(short) * FeatureBiasElements);
LayerWeights = (short*)AlignedAllocZeroed(sizeof(short) * LayerWeightElements);
- LayerBiases = (short*)AlignedAllocZeroed(sizeof(short) * (nuint)Math.Max(LayerBiasElements, VSize.Short));
+ LayerBiases = (short*)AlignedAllocZeroed(sizeof(short) * (nuint)Math.Max(LayerBiasElements, Vector512.Count));
string networkToLoad = NetworkName;
@@ -88,7 +82,7 @@ public static void Initialize(string networkToLoad, bool exitIfFail = true)
if (stream.Position + toRead > stream.Length)
{
Console.WriteLine("Bucketed768's BinaryReader doesn't have enough data for all weights and biases to be read!");
- Console.WriteLine("It expects to read " + toRead + " bytes, but the stream's position is " + stream.Position + "/" + stream.Length);
+ Console.WriteLine($"It expects to read {toRead} bytes, but the stream's position is {stream.Position} / {stream.Length}");
Console.WriteLine("The file being loaded is either not a valid 768 network, or has different layer sizes than the hardcoded ones.");
if (exitIfFail)
{
@@ -242,6 +236,8 @@ public static int GetEvaluation(Position pos)
Vector256 zeroVec = Vector256.Zero;
Vector256 sum = Vector256.Zero;
+ int SimdChunks = HiddenSize / Vector256.Count;
+
// Formula from BlackMarlin
int occ = (int)popcount(pos.bb.Occupancy);
int outputBucket = Math.Min((63 - occ) * (32 - occ) / 225, 7);
@@ -251,7 +247,7 @@ public static int GetEvaluation(Position pos)
var ourWeights = (Vector256*)(LayerWeights + (outputBucket * (HiddenSize * 2)));
var theirWeights = (Vector256*)(LayerWeights + (outputBucket * (HiddenSize * 2)) + HiddenSize);
- for (int i = 0; i < SIMD_CHUNKS_256; i++)
+ for (int i = 0; i < SimdChunks; i++)
{
Vector256 clamp = Vector256.Min(maxVec, Vector256.Max(zeroVec, ourData[i]));
Vector256 mult = clamp * ourWeights[i];
@@ -262,7 +258,7 @@ public static int GetEvaluation(Position pos)
sum = Vector256.Add(sum, Vector256.Add(loMult * loClamp, hiMult * hiClamp));
}
- for (int i = 0; i < SIMD_CHUNKS_256; i++)
+ for (int i = 0; i < SimdChunks; i++)
{
Vector256 clamp = Vector256.Min(maxVec, Vector256.Max(zeroVec, theirData[i]));
Vector256 mult = clamp * theirWeights[i];
@@ -275,7 +271,7 @@ public static int GetEvaluation(Position pos)
int output = Vector256.Sum(sum);
- return (output / QA + LayerBiases[outputBucket]) * OutputScale / QAB;
+ return (output / QA + LayerBiases[outputBucket]) * OutputScale / (QA * QB);
}
private static int FeatureIndexSingle(int pc, int pt, int sq, int kingSq, int perspective)
diff --git a/Logic/NN/Bucketed768Unroll.cs b/Logic/NN/Bucketed768Unroll.cs
index 82286435..f586a702 100644
--- a/Logic/NN/Bucketed768Unroll.cs
+++ b/Logic/NN/Bucketed768Unroll.cs
@@ -11,6 +11,7 @@
using VShort = System.Runtime.Intrinsics.Vector256;
#endif
+#pragma warning disable CS0162 // Unreachable code detected
namespace Lizard.Logic.NN
{
@@ -501,7 +502,7 @@ public static int GetEvaluationUnrolled512(Position pos)
int output = NNUE.SumVectorNoHadd(sumVec);
- return (output / QA + LayerBiases[outputBucket]) * 400 / (QA * QB);
+ return (output / QA + LayerBiases[outputBucket]) * OutputScale / (QA * QB);
}
}
diff --git a/Logic/NN/FunUnrollThings.cs b/Logic/NN/FunUnrollThings.cs
index 455b4ba7..56c62d06 100644
--- a/Logic/NN/FunUnrollThings.cs
+++ b/Logic/NN/FunUnrollThings.cs
@@ -5,6 +5,8 @@
using VectorT = System.Runtime.Intrinsics.Vector256;
#endif
+#pragma warning disable CS0162 // Unreachable code detected
+
namespace Lizard.Logic.NN
{
public static unsafe class FunUnrollThings
@@ -593,4 +595,4 @@ public static void UnrollSubtract(short* src, short* dst, short* sub1)
}
}
-}
+}
\ No newline at end of file
diff --git a/Logic/NN/VSize.cs b/Logic/NN/VSize.cs
deleted file mode 100644
index 22691446..00000000
--- a/Logic/NN/VSize.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace Lizard.Logic.NN
-{
- public static class VSize
- {
- public const int Vector256Size = 32;
-
- /// == 4
- public const int Long = Vector256Size / sizeof(long);
-
- /// == 8
- public const int Int = Vector256Size / sizeof(int);
-
- /// == 16
- public const int Short = Vector256Size / sizeof(short);
-
- /// == 32
- public const int SByte = Vector256Size / sizeof(sbyte);
-
-
- /// == 8
- public const int UInt = Vector256Size / sizeof(uint);
-
- /// == 16
- public const int UShort = Vector256Size / sizeof(ushort);
-
- /// == 32
- public const int Byte = Vector256Size / sizeof(byte);
- }
-}
diff --git a/Logic/Search/SearchInformation.cs b/Logic/Search/SearchInformation.cs
index e43dc045..8bf6d2a9 100644
--- a/Logic/Search/SearchInformation.cs
+++ b/Logic/Search/SearchInformation.cs
@@ -1,4 +1,7 @@
-namespace Lizard.Logic.Search
+
+#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+
+namespace Lizard.Logic.Search
{
public struct SearchInformation
{
diff --git a/Logic/Search/SearchStackEntry.cs b/Logic/Search/SearchStackEntry.cs
index 30ad0ece..e237ddbd 100644
--- a/Logic/Search/SearchStackEntry.cs
+++ b/Logic/Search/SearchStackEntry.cs
@@ -13,112 +13,22 @@ public unsafe struct SearchStackEntry
{
public static SearchStackEntry NullEntry = new SearchStackEntry();
- ///
- /// The move that the Negamax/QSearch loop is currently on (or Move.Null for Null Move Pruning) at the current
- ///
- /// This is set before every recursive call to Negamax/QSearch.
- ///
- [FieldOffset(0)]
- public Move CurrentMove;
-
- ///
- /// When legal moves are generated, this move will be skipped.
- ///
- /// This is used in Singular Extension searches to determine if every other move is significantly worse
- /// than the excluded one, and if so we will look at the excluded one more deeply.
- ///
- [FieldOffset(4)]
- public Move Skip;
-
- ///
- /// A pointer to a 2D array of scores (short[12][64]) for a particular move.
- ///
- /// This should be updated after a move is made, and before a recursive call to Negamax/QSearch.
- ///
- [FieldOffset(8)]
- public PieceToHistory* ContinuationHistory;
-
-
-
- ///
- /// The number of times that previous moves had their search depth extended by two.
- ///
- [FieldOffset(16)]
- public int DoubleExtensions;
-
- ///
- /// The number of moves made by both players thus far, which is generally the depth of the search times two.
- ///
- [FieldOffset(20)]
- public short Ply;
-
- ///
- /// The static evaluation for the position at the current .
- ///
- [FieldOffset(22)]
- public short StaticEval;
-
- ///
- /// Whether or not the side to move is in check at the current .
- ///
- [FieldOffset(24)]
- public bool InCheck;
-
- ///
- /// Set to true for PV/Root searches, or if is
- /// and the TT entry had TTPV true when it was updated.
- ///
- [FieldOffset(25)]
- public bool TTPV;
-
- ///
- /// Set to true if there was an acceptable for the position at the current .
- ///
- [FieldOffset(26)]
- public bool TTHit;
-
- [FieldOffset(27)]
- private fixed byte _pad0[5];
-
-
-
- ///
- /// A pointer to an array of created with , which represents the current PV.
- ///
- /// This must be set on a per-thread basis, and before that thread's search begins.
- ///
- [FieldOffset(32)]
- public Move* PV;
-
- [FieldOffset(40)]
- private fixed byte _pad1[8];
-
-
-
- ///
- /// The first killer move for the current .
- ///
- [FieldOffset(48)]
- public Move Killer0;
-
- ///
- /// Killer0's score will be at this offset when casts it as a .
- ///
- [FieldOffset(52)]
- private fixed byte _pad3[4];
-
- ///
- /// The second killer move for the current ,
- /// which is given Killer0's Move before Killer0 is overwritten with a new one.
- ///
- [FieldOffset(56)]
- public Move Killer1;
-
- ///
- /// Killer1's score will be at this offset when casts it as a .
- ///
- [FieldOffset(60)]
- private fixed byte _pad4[4];
+ [FieldOffset( 0)] public Move CurrentMove;
+ [FieldOffset( 4)] public Move Skip;
+ [FieldOffset( 8)] public PieceToHistory* ContinuationHistory;
+ [FieldOffset(16)] public int DoubleExtensions;
+ [FieldOffset(20)] public short Ply;
+ [FieldOffset(22)] public short StaticEval;
+ [FieldOffset(24)] public bool InCheck;
+ [FieldOffset(25)] public bool TTPV;
+ [FieldOffset(26)] public bool TTHit;
+ [FieldOffset(27)] private fixed byte _pad0[5];
+ [FieldOffset(32)] public Move* PV;
+ [FieldOffset(40)] private fixed byte _pad1[8];
+ [FieldOffset(48)] public Move Killer0;
+ [FieldOffset(52)] private fixed byte _pad2[4];
+ [FieldOffset(56)] public Move Killer1;
+ [FieldOffset(60)] private fixed byte _pad3[4];
public SearchStackEntry()
diff --git a/Logic/Transposition/TTCluster.cs b/Logic/Transposition/TTCluster.cs
index 0d64082a..665544e5 100644
--- a/Logic/Transposition/TTCluster.cs
+++ b/Logic/Transposition/TTCluster.cs
@@ -11,17 +11,10 @@ namespace Lizard.Logic.Transposition
[StructLayout(LayoutKind.Explicit, Size = 32)]
public unsafe struct TTCluster
{
- [FieldOffset(0)]
- private TTEntry _elem0;
-
- [FieldOffset(10)]
- private TTEntry _elem1;
-
- [FieldOffset(20)]
- private TTEntry _elem2;
-
- [FieldOffset(30)]
- private fixed byte _pad[2];
+ [FieldOffset( 0)] private TTEntry _elem0;
+ [FieldOffset(10)] private TTEntry _elem1;
+ [FieldOffset(20)] private TTEntry _elem2;
+ [FieldOffset(30)] private fixed byte _pad0[2];
///
/// Initializes the memory for this TTCluster instance.
@@ -34,8 +27,8 @@ public TTCluster()
_elem1 = new TTEntry();
_elem2 = new TTEntry();
- _pad[0] = (byte)':';
- _pad[1] = (byte)')';
+ _pad0[0] = (byte)':';
+ _pad0[1] = (byte)')';
}
///
diff --git a/Logic/Transposition/TranspositionTable.cs b/Logic/Transposition/TranspositionTable.cs
index c05a0185..c4c50314 100644
--- a/Logic/Transposition/TranspositionTable.cs
+++ b/Logic/Transposition/TranspositionTable.cs
@@ -29,17 +29,12 @@ public static unsafe class TranspositionTable
///
public static byte Age = 0;
- private static bool Initialized = false;
-
static TranspositionTable()
{
- if (!Initialized)
- {
- Initialize(SearchOptions.Hash);
+ Initialize(SearchOptions.Hash);
- Assert(ClusterCount > MinTTClusters,
- $"The TT is undersized! Needs space for {MinTTClusters} clusters but only has {ClusterCount}");
- }
+ Assert(ClusterCount > MinTTClusters,
+ $"The TT is undersized! Needs space for {MinTTClusters} clusters but only has {ClusterCount}");
}
///
diff --git a/Logic/UCI/UCIClient.cs b/Logic/UCI/UCIClient.cs
index 380d84b3..5f27ce85 100644
--- a/Logic/UCI/UCIClient.cs
+++ b/Logic/UCI/UCIClient.cs
@@ -1,4 +1,7 @@
-using System.Reflection;
+
+#pragma warning disable CS0162 // Unreachable code detected
+
+using System.Reflection;
using Lizard.Logic.NN;
using Lizard.Logic.Threads;
@@ -12,13 +15,7 @@ public unsafe class UCIClient
private SearchInformation info;
private ThreadSetup setup;
- private const string LogFileName = @".\ucilog.txt";
-
- ///
- /// If this is true, then engine instances will attempt to write to their own "ucilog_#.txt" files,
- /// where # is a (hopefully) unique number for this instance.
- ///
- private const bool WriteToConcurrentLogs = false;
+ private static readonly string LogFileName = $".\\ucilog_{ProcessID}.txt";
private static Dictionary Options;
@@ -68,19 +65,6 @@ public static void LogString(string s, bool newLine = true)
{
string fileToWrite = LogFileName;
- if (IsRunningConcurrently)
- {
- if (WriteToConcurrentLogs)
- {
- fileToWrite = @".\ucilog_" + ProcessID + ".txt";
- }
- else
- {
- // Concurrent logging off, just return here.
- return;
- }
- }
-
using FileStream fs = new FileStream(fileToWrite, FileMode.Append, FileAccess.Write, FileShare.Read);
using StreamWriter sw = new StreamWriter(fs);
@@ -165,8 +149,8 @@ private void InputLoop()
if (cmd == "quit")
{
- LogString("[INFO]: Exiting with code " + 1001);
- Environment.Exit(1001);
+ LogString("[INFO]: Exiting with code " + 0);
+ Environment.Exit(0);
}
else if (cmd == "isready")
{
@@ -591,7 +575,7 @@ public static void ProcessUCIOptions()
Options = new Dictionary();
// Get all "public static" fields, and specifically exclude constant fields (which have field.IsLiteral == true)
- List? fields = typeof(SearchOptions).GetFields(BindingFlags.Public | BindingFlags.Static).Where(x => !x.IsLiteral).ToList();
+ List fields = typeof(SearchOptions).GetFields(BindingFlags.Public | BindingFlags.Static).Where(x => !x.IsLiteral).ToList();
foreach (FieldInfo field in fields)
{
@@ -610,7 +594,7 @@ public static void ProcessUCIOptions()
//SetSPSAOutputParams();
Options[nameof(Threads)].SetMinMax(1, 512);
- Options[nameof(MultiPV)].SetMinMax(1, 5);
+ Options[nameof(MultiPV)].SetMinMax(1, 256);
Options[nameof(Hash)].SetMinMax(1, 1048576);
Options[nameof(SingularExtensionsMinDepth)].SetMinMax(2, 10);
diff --git a/Logic/Util/FishBench.cs b/Logic/Util/FishBench.cs
index 5a9900d4..b89e6dea 100644
--- a/Logic/Util/FishBench.cs
+++ b/Logic/Util/FishBench.cs
@@ -1,7 +1,5 @@
namespace Lizard.Logic.Util
{
- // SkipStaticConstructor since this introduces some unnecessary strings
- [SkipStaticConstructor]
public static class FishBench
{
private static Dictionary FENDict = new Dictionary()
diff --git a/Logic/Util/SearchBench.cs b/Logic/Util/SearchBench.cs
index e9306d8a..8609cf9e 100644
--- a/Logic/Util/SearchBench.cs
+++ b/Logic/Util/SearchBench.cs
@@ -1,6 +1,5 @@
namespace Lizard.Logic.Util
{
- [SkipStaticConstructor]
public static class SearchBench
{
diff --git a/Logic/Util/SkipStaticConstructorAttribute.cs b/Logic/Util/SkipStaticConstructorAttribute.cs
deleted file mode 100644
index 62903a4f..00000000
--- a/Logic/Util/SkipStaticConstructorAttribute.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Lizard.Logic.Util
-{
- ///
- /// Placing this attribute on a class causes the static constructor of that class to be skipped during program initialization.
- ///
- /// This is important for the main classes of NNUE networks as well as their FeatureTransformers, as only one
- /// architecture can be used at a time, and initializing multiple wastes memory.
- ///
- [System.AttributeUsage(AttributeTargets.Class)]
- public sealed class SkipStaticConstructorAttribute : Attribute { }
-}
diff --git a/Logic/Util/Utilities.cs b/Logic/Util/Utilities.cs
index 9907e0a3..5858cbdb 100644
--- a/Logic/Util/Utilities.cs
+++ b/Logic/Util/Utilities.cs
@@ -53,21 +53,10 @@ public static class Utilities
public const ulong BlackQueensideMask = (1UL << B8) | (1UL << C8) | (1UL << D8);
-
public const string InitialFEN = @"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
- ///
- /// Set to true if multiple instances of this engine are running.
- ///
- /// If there are, then the log file to not be written to unless = .
- ///
- public static bool IsRunningConcurrently = false;
-
- ///
- /// The process ID for this engine instance. This is only used to determine the name of the log file to write to.
- ///
- public static int ProcessID;
+ public static int ProcessID => Environment.ProcessId;
public const bool NO_LOG_FILE = true;
@@ -82,75 +71,15 @@ public static void Log(string s)
}
else if (!NO_LOG_FILE)
{
+#pragma warning disable CS0162 // Unreachable code detected
UCIClient.LogString("[LOG]: " + s);
+#pragma warning restore CS0162 // Unreachable code detected
}
Debug.WriteLine(s);
}
- ///
- /// If there are multiple instances of this engine running, we won't write to the ucilog file.
- ///
- /// This uses a FileStream to access it and a mutex to make writes atomic, so having multiple
- /// processes all doing that at the same time is needlessly risky.
- ///
- public static void CheckConcurrency()
- {
- Process thisProc = Process.GetCurrentProcess();
- ProcessID = thisProc.Id;
-
- var selfProcs = Process.GetProcesses().Where(x => x.ProcessName == thisProc.ProcessName).ToList();
-
- int concurrencyCount = 0;
- int duplicates = 0;
-
- var thisTime = thisProc.StartTime.Ticks;
-
- for (int i = 0; i < selfProcs.Count; i++)
- {
- try
- {
- // Windows doesn't like you touching the "System Idle Process" (0) or "System" (4)
- // and will throw an error if you try to get their MainModules,
- // so in case you prefer to rename the engine binary to "Idle" this will hopefully avoid that issue :)
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && (selfProcs[i].Id == 0 || selfProcs[i].Id == 4))
- {
- continue;
- }
-
- // Ensure that the processes are exactly the same as this one.
- // This checks their entire path, since multiple engine instance concern is
- // only if they are started from the same path.
- if (selfProcs[i].MainModule?.FileName != thisProc.MainModule?.FileName)
- {
- continue;
- }
- }
- catch (Exception e)
- {
- // This will be a Win32Exception for trying to enumerate the modules of a SYSTEM process
- concurrencyCount++;
- continue;
- }
-
- duplicates++;
-
- if (selfProcs[i].StartTime.Ticks < thisTime)
- {
- // This instance was launched after another, so increase this one's count
- concurrencyCount++;
- }
- }
-
- // If this is the only instance, this should be 0
- if (concurrencyCount != 0)
- {
- Log("Running concurrently! (" + concurrencyCount + " other process" + (concurrencyCount > 1 ? "es" : string.Empty) + ")");
- IsRunningConcurrently = true;
- }
- }
-
public static string GetCompilerInfo()
{
StringBuilder sb = new StringBuilder();
@@ -166,10 +95,7 @@ public static string GetCompilerInfo()
sb.Append(IsAOTAttribute.IsAOT() ? "AOT " : string.Empty);
- if (HasSkipInit)
- {
- sb.Append("SkipInit ");
- }
+ sb.Append(HasSkipInit ? "SkipInit " : string.Empty);
sb.Append(Avx2.IsSupported ? "Avx2 " : string.Empty);
@@ -190,24 +116,13 @@ public static string GetCompilerInfo()
return sb.ToString();
}
- public static class Direction
- {
- public const int NORTH = 8;
- public const int EAST = 1;
- public const int SOUTH = -NORTH;
- public const int WEST = -EAST;
-
- public const int NORTH_EAST = NORTH + EAST;
- public const int SOUTH_EAST = SOUTH + EAST;
- public const int SOUTH_WEST = SOUTH + WEST;
- public const int NORTH_WEST = NORTH + WEST;
- }
///
/// Returns the that the pawns move in, white pawns up, black pawns down.
///
public static int ShiftUpDir(int color) => (color == Color.White) ? Direction.NORTH : Direction.SOUTH;
+
///
/// Returns a bitboard with bits set 1 "above" the bits in .
/// So Forward(Color.White) with a bitboard that has A2 set will return one with A3 set,
@@ -215,23 +130,9 @@ public static class Direction
///
public static ulong Forward(int color, ulong b)
{
- return (color == Color.White) ? Shift(Direction.NORTH, b) : Shift(Direction.SOUTH, b);
+ return Shift(ShiftUpDir(color), b);
}
- ///
- /// Returns a bitboard with bits set 1 "below" the bits in .
- /// So Backward(Color.White) with a bitboard that has A2 set will return one with A1 set,
- /// and Backward(Color.Black) returns one with A3 set instead.
- ///
- public static ulong Backward(int color, ulong b)
- {
- if (color == Color.White)
- {
- return Shift(Direction.SOUTH, b);
- }
-
- return Shift(Direction.NORTH, b);
- }
///
/// Shifts the bits in in the direction .
@@ -239,20 +140,19 @@ public static ulong Backward(int color, ulong b)
public static ulong Shift(int dir, ulong b)
{
return dir == Direction.NORTH ? b << 8
- : dir == Direction.SOUTH ? b >> 8
- : dir == Direction.NORTH + Direction.NORTH ? b << 16
- : dir == Direction.SOUTH + Direction.SOUTH ? b >> 16
- : dir == Direction.EAST ? (b & ~FileHBB) << 1
- : dir == Direction.WEST ? (b & ~FileABB) >> 1
- : dir == Direction.NORTH_EAST ? (b & ~FileHBB) << 9
- : dir == Direction.NORTH_WEST ? (b & ~FileABB) << 7
- : dir == Direction.SOUTH_EAST ? (b & ~FileHBB) >> 7
- : dir == Direction.SOUTH_WEST ? (b & ~FileABB) >> 9
- : 0;
+ : dir == Direction.SOUTH ? b >> 8
+ : dir == Direction.NORTH + Direction.NORTH ? b << 16
+ : dir == Direction.SOUTH + Direction.SOUTH ? b >> 16
+ : dir == Direction.EAST ? (b & ~FileHBB) << 1
+ : dir == Direction.WEST ? (b & ~FileABB) >> 1
+ : dir == Direction.NORTH_EAST ? (b & ~FileHBB) << 9
+ : dir == Direction.NORTH_WEST ? (b & ~FileABB) << 7
+ : dir == Direction.SOUTH_EAST ? (b & ~FileHBB) >> 7
+ : dir == Direction.SOUTH_WEST ? (b & ~FileABB) >> 9
+ : 0;
}
-
///
/// Returns a ulong with bits set along whichever file is in.
///
@@ -261,6 +161,7 @@ public static ulong GetFileBB(int idx)
return FileABB << GetIndexFile(idx);
}
+
///
/// Returns a ulong with bits set along whichever rank is on.
///
@@ -270,8 +171,6 @@ public static ulong GetRankBB(int idx)
}
-
-
///
/// Returns the opposite of .
///
@@ -290,10 +189,11 @@ public static string ColorToString(int color)
{
Color.White => nameof(Color.White),
Color.Black => nameof(Color.Black),
- _ => "None"
+ _ => "None"
};
}
+
///
/// Returns the numerical value of the .
///
@@ -303,10 +203,11 @@ public static int StringToColor(string colorName)
{
"white" => Color.White,
"black" => Color.Black,
- _ => Color.ColorNB
+ _ => Color.ColorNB
};
}
+
///
/// Returns the name of the piece of type .
///
@@ -314,16 +215,17 @@ public static string PieceToString(int n)
{
return n switch
{
- Piece.Pawn => nameof(Piece.Pawn),
+ Piece.Pawn => nameof(Piece.Pawn),
Piece.Knight => nameof(Piece.Knight),
Piece.Bishop => nameof(Piece.Bishop),
- Piece.Rook => nameof(Piece.Rook),
- Piece.Queen => nameof(Piece.Queen),
- Piece.King => nameof(Piece.King),
- _ => "None"
+ Piece.Rook => nameof(Piece.Rook),
+ Piece.Queen => nameof(Piece.Queen),
+ Piece.King => nameof(Piece.King),
+ _ => "None"
};
}
+
///
/// Returns the type of the piece called .
///
@@ -331,16 +233,17 @@ public static int StringToPiece(string pieceName)
{
return pieceName.ToLower() switch
{
- "pawn" => Piece.Pawn,
+ "pawn" => Piece.Pawn,
"knight" => Piece.Knight,
"bishop" => Piece.Bishop,
- "rook" => Piece.Rook,
- "queen" => Piece.Queen,
- "king" => Piece.King,
- _ => Piece.None
+ "rook" => Piece.Rook,
+ "queen" => Piece.Queen,
+ "king" => Piece.King,
+ _ => Piece.None
};
}
+
///
/// Returns the first letter of the name of the piece of type , so PieceToFENChar(0 [Piece.Pawn]) returns 'P'.
///
@@ -348,16 +251,17 @@ public static char PieceToFENChar(int pieceType)
{
return pieceType switch
{
- Piece.Pawn => 'P',
+ Piece.Pawn => 'P',
Piece.Knight => 'N',
Piece.Bishop => 'B',
- Piece.Rook => 'R',
- Piece.Queen => 'Q',
- Piece.King => 'K',
- _ => ' '
+ Piece.Rook => 'R',
+ Piece.Queen => 'Q',
+ Piece.King => 'K',
+ _ => ' '
};
}
+
///
/// Returns the numerical piece type of the piece given its FEN character .
///
@@ -371,12 +275,11 @@ public static int FENToPiece(char fenChar)
'r' => Piece.Rook,
'q' => Piece.Queen,
'k' => Piece.King,
- _ => Piece.None
+ _ => Piece.None
};
}
-
///
/// Returns a random ulong using the Random instance .
///
@@ -410,21 +313,25 @@ public static bool DirectionOK(int sq, int dir)
///
public static char GetFileChar(int fileNumber) => (char)(97 + fileNumber);
+
///
/// Returns the number of the file with the letter , so GetFileInt('a') returns 0.
///
public static int GetFileInt(char fileLetter) => fileLetter - 97;
+
///
/// Returns the file (x coordinate) for the index, which is between A=0 and H=7.
///
public static int GetIndexFile(int index) => index & 7;
+
///
/// Returns the rank (y coordinate) for the index, which is between 0 and 7.
///
public static int GetIndexRank(int index) => index >> 3;
+
///
/// Sets to the file of , and to its rank.
///
@@ -434,6 +341,7 @@ public static void IndexToCoord(in int index, out int x, out int y)
y = index / 8;
}
+
///
/// Returns the index of the square with the rank and file .
///
@@ -442,6 +350,7 @@ public static int CoordToIndex(int x, int y)
return (y * 8) + x;
}
+
///
/// Returns the rank and file of the square , which looks like "a1" or "e4".
///
@@ -460,7 +369,6 @@ public static int StringToIndex(string s)
}
-
///
/// Returns a text representation of the board
///
@@ -563,16 +471,19 @@ public static string CenteredString(string s, int width)
return new string(' ', leftPadding) + s + new string(' ', rightPadding);
}
+
public static bool EqualsIgnoreCase(this string s, string other)
{
return s.Equals(other, StringComparison.OrdinalIgnoreCase);
}
+
public static bool StartsWithIgnoreCase(this string s, string other)
{
return s.StartsWith(other, StringComparison.OrdinalIgnoreCase);
}
+
public static bool ContainsIgnoreCase(this string s, string other)
{
return s.Contains(other, StringComparison.OrdinalIgnoreCase);
@@ -655,6 +566,7 @@ public static string FormatSearchInformationMultiPV(ref SearchInformation info)
return sb.ToString();
}
+
///
/// Returns an appropriately formatted string representing the Score, which is either "cp #" or "mate #".
///
@@ -681,12 +593,6 @@ public static string FormatMoveScore(int score)
}
}
- public static int ConvertRange(int originalStart, int originalEnd, int newStart, int newEnd, int value)
- {
- double scale = (double)(newEnd - newStart) / (originalEnd - originalStart);
- return (int)(newStart + (value - originalStart) * scale);
- }
-
///
/// Sorts the between the starting index and last index
@@ -722,11 +628,13 @@ public static void StableSort(ref List items, int offset = 0, int end = -1
}
}
+
public static int AsInt(this bool v)
{
return v ? 1 : 0;
}
+
public static bool AsBool(this int v)
{
return (v != 0);
diff --git a/Program.cs b/Program.cs
index ac54caab..08e7b360 100644
--- a/Program.cs
+++ b/Program.cs
@@ -43,7 +43,7 @@ public static void InitializeAll()
}
- Console.CancelKeyPress += delegate (object? sender, ConsoleCancelEventArgs e) {
+ Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) {
if (!SearchPool.StopThreads)
{
// If a search is ongoing, stop it instead of closing the console.
@@ -65,8 +65,6 @@ public static void InitializeAll()
// Give the VS debugger a friendly name for the main program thread
Thread.CurrentThread.Name = "MainThread";
- Utilities.CheckConcurrency();
-
// The GC seems to drag its feet collecting some of the now unneeded memory (random strings and RunClassConstructor junk).
// This doesn't HAVE to be done now, and generally it isn't productive to force GC collections,
// but it will inevitably occur at some point later so we can take a bit of pressure off of it by doing this now.