Axololly.Chess.MoveGenerator 1.2.3

dotnet add package Axololly.Chess.MoveGenerator --version 1.2.3                
NuGet\Install-Package Axololly.Chess.MoveGenerator -Version 1.2.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Axololly.Chess.MoveGenerator" Version="1.2.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Axololly.Chess.MoveGenerator --version 1.2.3                
#r "nuget: Axololly.Chess.MoveGenerator, 1.2.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Axololly.Chess.MoveGenerator as a Cake Addin
#addin nuget:?package=Axololly.Chess.MoveGenerator&version=1.2.3

// Install Axololly.Chess.MoveGenerator as a Cake Tool
#tool nuget:?package=Axololly.Chess.MoveGenerator&version=1.2.3                

Chess Move Generator v1.2.3

Documentation is slowly progressing. You can check it out here, and make sure to leave positive feedback. ❤️

Statistics

Here are the current file statistics for this project:

File name Lines Characters File size
Board.cs 1,230 47,912 47.99 KB
Board960.cs 1,099 43,664 43.71 KB
Bitmasks.cs 86 2,685 2.71 KB
Bitboard.cs 119 3,818 3.84 KB
Castling.cs 212 7,962 7.98 KB
Generator.cs 549 19,141 19.23 KB
MagicBitboards.cs 318 17,175 17.08 KB
PGN.cs 238 8164 8.2 KB
Perft.cs 364 11,865 11.94 KB
Square.cs 59 2,248 2.25 KB
Tables.cs 59 1,857 1.87 KB
Utilities.cs 222 7,166 7.21 KB
Zobrist.cs 80 2,113 2.14 KB
Total 4,678 179,788 180.13 KB

I'm incredibly proud of how much I've written, how much I've learnt and how far I've come. It feels amazing to write this.

PGN Support

I added PGN support now, which you can find in PGN.cs, and use under the namespace Chess.PGN. Now you can read games in PGN format. Do note that some PGNs are raising draw errors, mostly under the three-fold repetition rule. I became aware of this issue after running my code through a set of 7,000 PGNs I found here. For my experiments, I took games from Viswanatan Anand and Alireza Firouzja, then ran them through a custom-made interpreter.

Inputs and Outputs

Both inputting PGNs to a returned Board struct and outputting a PGN from a given Board struct are supported. Of course, outputting a PGN from a board can now be performed under the .PGN property.

To input a PGN:

using Chess;
using Chess.PGN;

string examplePGN;

Board outputtedBoard = PGN.InterpretPGN(examplePGN);

And to output a PGN:

using Chess;
using Chess.PGN;

Board board;

// Option 1 (cleaner)
string outputPGN = board.PGN;

// Option 2 (raw)
string outputPGN = PGN.ConvertToPGN(board);

It's as easy as that.

Results

For Anand's game set, I got these results on an i5-4210U:

Found 4162 PGNs in 110 milliseconds.

Translated 4162 PGNs in 11032 milliseconds.
Successfully translated 4108 PGNs and failed translating 54 PGNs.

This results in 377 PGNs translated every second, or one PGN every 0.0027s.

All the fails were draw-related, which has already been explained above.


For Firouzja's game set, I got these results on the same CPU:

Found 3713 PGNs in 60 milliseconds.

Translated 3713 PGNs in 6589 milliseconds.
Successfully translated 3664 PGNs and failed translating 49 PGNs.

This results in 413 PGNs translated every second, or one PGN every 0.0024 seconds.

Fails are for the same reasons.

Inner Workings

It works using regex to identify the start and end of a PGN's body - it starts with 1. and ends with either 0-1, 1-0, or 1/2-1/2. Note that this may also be present in a [Result "..."] tag, so we need to make sure that there isn't a leading quotation mark.

These can easily be compiled into regex with:

// For start
@"1\.";

// For end
@"[^""]((0-1)|(1-0)|(1/2-1/2))";

However, note that this does blindly extract moves, so if you gave it:

1. e4 e5 1. Nc6

It would interpret this as:

["e4", "e5", "Nc6"]

Then proceed to translate the moves to long algebraic notation, lookup them in the generated legal moves based on criteria extracted from the move, for example, like:

  • an "N" means it's a knight move, and this can be either a Piece.WhiteKnight or Piece.BlackKnight, which we check the .SideToMove property for.

  • if there's no piece character, it's a pawn move. For which side? We use the .SideToMove property for this again.

  • if rank differentiation is provided, search for a move where the start square's rank originates on that specified rank, written as the "target rank"

Usage

📝:wrench: Note: To use this, make sure you have .NET 8.0.

To start, include the Chess namespace. Then you can create a Board instance and go from there.

For extra information, here's a table of all the functions you'd be using, and what they do:

Function Explanation
.MakeMove() Makes a move on the board.<br>Note that the legality of the move is not verified.<br><br>To get the Move struct, use the Chess.MoveGen namespace.
.UndoMove() Undoes a move on the board.<br><br>This will raise an error if no moves were previously played on the board.
.GenerateLegalMoves() Returns a list of Move structs representing all the legal moves that can be played on the board.<br><br>If the list is empty, the board state is either checkmate or stalemate, both of which can be found out with the .InCheck property, where if true, the state is checkmate, and if false, it's stalemate.
.IsDraw Returns a boolean representing whether or not the current board state is a draw.<br><br>Note that this generalises between the 50-move rule, three-fold repetition and insufficient material.
.InCheck Returns a boolean representing whether or not the current player's king is in check or not.

Example

using Chess;

class Program
{
    static void Main()
    {
        Board board = new();

        Console.WriteLine($"Board:\n{board}\n");
        Console.WriteLine($"Next moves: [{string.Join(", ", board.GenerateLegalMoves())}]");
    }
}

Result

Board:
r n b q k b n r
p p p p p p p p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
P P P P P P P P
R N B Q K B N R

Next moves: [b1a3, b1c3, g1f3, g1h3, a2a3, b2b3, c2c3, d2d3, e2e3, f2f3, g2g3, h2h3, a2a4, b2b4, c2c4, d2d4, e2e4, f2f4, g2g4, h2h4]

Perft

I translated this file into a more readable JSON structure where each document looks like this:

{
    "FEN": "...",
    "Depths": {
        "1": "...",
        "2": "...",
        "3": "...",
        "4": "...",
        "5": "...",
        "6": "..."
    }
}

And in total, there are around 6,600 of these that you can use to verify that your engine returns the correct move counts.

If you want to perft test my own project, you can do it with the Perft and Perft960 classes, which have the available features: |Type|Method|Explanation| |:-|:-|:-| |Basic|.BasePerftTest()|Returns the number of counted nodes at the end of the function.| |Move-by-Move|.MoveByMovePerftTest()|Prints the move and number of counted nodes, returning the total at the end.| |Global|.GlobalPerftTest()|Prints the number of counted nodes until a given depth is reached.| |Output|.OutputPerftTest()|Redirects the console output into a file for further review.|

Thanks

Honestly, thank you to everyone in the Stockfish Discord server, from Analog Hors and her magic bitboards article, all the way to Disservin, who I already mentioned above for his code relating to en-passant moves.

It means a lot to me, knowing I've finally completed a project that frustrated me back when I started in June. I learnt a new language, made new friends, and, most importantly, had a lot of fun with this.

So proud of how far I've come. Time to celebrate! 🎉

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.2.3 71 11/17/2024
1.2.1 123 10/20/2024
1.2.0 126 10/19/2024
1.1.6 97 10/3/2024
1.1.5 86 10/3/2024
1.1.4 101 10/3/2024
1.1.3 98 9/30/2024
1.1.2 85 9/29/2024
1.1.1 92 9/29/2024
1.1.0 92 9/29/2024
1.0.0 91 9/24/2024