Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Move class with int and use 'Move' pool for MoveGenerator #174

Merged
merged 5 commits into from
Aug 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,7 @@ global.json
*.profraw

# Environment files
*.env
*.env

# VS diagnostic sessions
*.diagsession
4 changes: 4 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>

<ItemGroup>
<Using Include="System.Int32" Alias="Move" />
</ItemGroup >

<PropertyGroup>
<Version>0.10.0</Version>
<Authors>Eduardo Cáceres</Authors>
Expand Down
20 changes: 10 additions & 10 deletions src/Lynx.Benchmark/FENGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ public string Struct_FENCalculatedOnTheFly(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new StructCustomPosition(fen);
var newPosition = new StructCustomPosition(position, moves[0]);
var newPosition = new StructCustomPosition(position, moves.First());
return newPosition.FEN;
}

Expand All @@ -226,7 +226,7 @@ public string Struct_FENCalculatedWithinTheMoveConstructor(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new StructCustomPosition(fen);
var newPosition = new StructCustomPosition(position, moves[0], default);
var newPosition = new StructCustomPosition(position, moves.First(), default);

return newPosition.FEN;
}
Expand All @@ -238,7 +238,7 @@ public string ReadonlyStruct_FENCalculatedOnTheFly(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new ReadonlyStructCustomPosition(fen);
var newPosition = new ReadonlyStructCustomPosition(position, moves[0]);
var newPosition = new ReadonlyStructCustomPosition(position, moves.First());

return newPosition.FEN;
}
Expand All @@ -250,7 +250,7 @@ public string ReadonlyStruct_FENCalculatedWithinTheMoveConstructor(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new ReadonlyStructCustomPosition(fen);
var newPosition = new ReadonlyStructCustomPosition(position, moves[0], default);
var newPosition = new ReadonlyStructCustomPosition(position, moves.First(), default);

return newPosition.FEN;
}
Expand All @@ -262,7 +262,7 @@ public string Class_FENCalculatedOnTheFly(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new ClassCustomPosition(fen);
var newPosition = new ClassCustomPosition(position, moves[0]);
var newPosition = new ClassCustomPosition(position, moves.First());

return newPosition.FEN;
}
Expand All @@ -274,7 +274,7 @@ public string Class_FENCalculatedWithinTheMoveConstructor(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new ClassCustomPosition(fen);
var newPosition = new ClassCustomPosition(position, moves[0], default);
var newPosition = new ClassCustomPosition(position, moves.First(), default);

return newPosition.FEN;
}
Expand All @@ -286,7 +286,7 @@ public string RecordClass_FENCalculatedOnTheFly(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new RecordClassCustomPosition(fen);
var newPosition = new RecordClassCustomPosition(position, moves[0]);
var newPosition = new RecordClassCustomPosition(position, moves.First());

return newPosition.FEN;
}
Expand All @@ -298,7 +298,7 @@ public string RecordClass_FENCalculatedWithinTheMoveConstructor(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new RecordClassCustomPosition(fen);
var newPosition = new RecordClassCustomPosition(position, moves[0], default);
var newPosition = new RecordClassCustomPosition(position, moves.First(), default);

return newPosition.FEN;
}
Expand All @@ -310,7 +310,7 @@ public string RecordStruct_FENCalculatedOnTheFly(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new RecordStructCustomPosition(fen);
var newPosition = new RecordStructCustomPosition(position, moves[0]);
var newPosition = new RecordStructCustomPosition(position, moves.First());

return newPosition.FEN;
}
Expand All @@ -322,7 +322,7 @@ public string RecordStruct_FENCalculatedWithinTheMoveConstructor(string fen)
var moves = new Position(fen).AllPossibleMoves();

var position = new RecordStructCustomPosition(fen);
var newPosition = new RecordStructCustomPosition(position, moves[0], default);
var newPosition = new RecordStructCustomPosition(position, moves.First(), default);

return newPosition.FEN;
}
Expand Down
40 changes: 20 additions & 20 deletions src/Lynx.Benchmark/MoveGeneratorParallel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
*
*
* | Method | fen | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Allocated |
* |--------------------- |--------------------- |----------:|----------:|----------:|------:|--------:|-------:|-------:|----------:|
* | SingleThread | r2q1r(...)- 0 9 [68] | 8.650 us | 0.1549 us | 0.1293 us | 1.00 | 0.00 | 2.3346 | - | 5 KB |
Expand Down Expand Up @@ -36,7 +36,7 @@
* | SingleThreadArray | rnbqk(...)- 0 1 [56] | 7.986 us | 0.1455 us | 0.1361 us | 1.13 | 0.02 | 2.3956 | - | 5 KB |
* | ParallelForEachArray | rnbqk(...)- 0 1 [56] | 20.664 us | 0.1411 us | 0.1251 us | 2.93 | 0.04 | 1.2512 | 0.6104 | 8 KB |
* | WhenAllArray | rnbqk(...)- 0 1 [56] | 19.929 us | 0.0522 us | 0.0435 us | 2.83 | 0.03 | 3.2959 | - | 7 KB |
*
*
*/

using BenchmarkDotNet.Attributes;
Expand Down Expand Up @@ -222,14 +222,14 @@ internal static IEnumerable<Move> GeneratePawnMoves(Position position, int offse
var targetRank = (singlePushSquare / 8) + 1;
if (targetRank == 1 || targetRank == 8) // Promotion
{
yield return new Move(sourceSquare, singlePushSquare, piece, promotedPiece: (int)Piece.Q + offset);
yield return new Move(sourceSquare, singlePushSquare, piece, promotedPiece: (int)Piece.R + offset);
yield return new Move(sourceSquare, singlePushSquare, piece, promotedPiece: (int)Piece.N + offset);
yield return new Move(sourceSquare, singlePushSquare, piece, promotedPiece: (int)Piece.B + offset);
yield return MoveExtensions.Encode(sourceSquare, singlePushSquare, piece, promotedPiece: (int)Piece.Q + offset);
yield return MoveExtensions.Encode(sourceSquare, singlePushSquare, piece, promotedPiece: (int)Piece.R + offset);
yield return MoveExtensions.Encode(sourceSquare, singlePushSquare, piece, promotedPiece: (int)Piece.N + offset);
yield return MoveExtensions.Encode(sourceSquare, singlePushSquare, piece, promotedPiece: (int)Piece.B + offset);
}
else if (!capturesOnly)
{
yield return new Move(sourceSquare, singlePushSquare, piece);
yield return MoveExtensions.Encode(sourceSquare, singlePushSquare, piece);
}

// Double pawn push
Expand All @@ -240,7 +240,7 @@ internal static IEnumerable<Move> GeneratePawnMoves(Position position, int offse
if (!position.OccupancyBitBoards[2].GetBit(doublePushSquare)
&& ((sourceRank == 2 && position.Side == Side.Black) || (sourceRank == 7 && position.Side == Side.White)))
{
yield return new Move(sourceSquare, doublePushSquare, piece, isDoublePawnPush: TRUE);
yield return MoveExtensions.Encode(sourceSquare, doublePushSquare, piece, isDoublePawnPush: TRUE);
}
}
}
Expand All @@ -251,7 +251,7 @@ internal static IEnumerable<Move> GeneratePawnMoves(Position position, int offse
if (position.EnPassant != BoardSquare.noSquare && attacks.GetBit(position.EnPassant))
// We assume that position.OccupancyBitBoards[oppositeOccupancy].GetBit(targetSquare + singlePush) == true
{
yield return new Move(sourceSquare, (int)position.EnPassant, piece, isCapture: TRUE, isEnPassant: TRUE);
yield return MoveExtensions.Encode(sourceSquare, (int)position.EnPassant, piece, isCapture: TRUE, isEnPassant: TRUE);
}

// Captures
Expand All @@ -264,14 +264,14 @@ internal static IEnumerable<Move> GeneratePawnMoves(Position position, int offse
var targetRank = (targetSquare / 8) + 1;
if (targetRank == 1 || targetRank == 8) // Capture with promotion
{
yield return new Move(sourceSquare, targetSquare, piece, promotedPiece: (int)Piece.Q + offset, isCapture: TRUE);
yield return new Move(sourceSquare, targetSquare, piece, promotedPiece: (int)Piece.R + offset, isCapture: TRUE);
yield return new Move(sourceSquare, targetSquare, piece, promotedPiece: (int)Piece.N + offset, isCapture: TRUE);
yield return new Move(sourceSquare, targetSquare, piece, promotedPiece: (int)Piece.B + offset, isCapture: TRUE);
yield return MoveExtensions.Encode(sourceSquare, targetSquare, piece, promotedPiece: (int)Piece.Q + offset, isCapture: TRUE);
yield return MoveExtensions.Encode(sourceSquare, targetSquare, piece, promotedPiece: (int)Piece.R + offset, isCapture: TRUE);
yield return MoveExtensions.Encode(sourceSquare, targetSquare, piece, promotedPiece: (int)Piece.N + offset, isCapture: TRUE);
yield return MoveExtensions.Encode(sourceSquare, targetSquare, piece, promotedPiece: (int)Piece.B + offset, isCapture: TRUE);
}
else
{
yield return new Move(sourceSquare, targetSquare, piece, isCapture: TRUE);
yield return MoveExtensions.Encode(sourceSquare, targetSquare, piece, isCapture: TRUE);
}
}
}
Expand All @@ -297,7 +297,7 @@ internal static IEnumerable<Move> GenerateCastlingMoves(Position position, int o
&& !Attacks.IsSquaredAttackedBySide((int)BoardSquare.f1, position, oppositeSide)
&& !Attacks.IsSquaredAttackedBySide((int)BoardSquare.g1, position, oppositeSide))
{
yield return new Move(sourceSquare, Constants.WhiteShortCastleKingSquare, piece, isShortCastle: TRUE);
yield return MoveExtensions.Encode(sourceSquare, Constants.WhiteShortCastleKingSquare, piece, isShortCastle: TRUE);
}

if (((position.Castle & (int)CastlingRights.WQ) != default)
Expand All @@ -308,7 +308,7 @@ internal static IEnumerable<Move> GenerateCastlingMoves(Position position, int o
&& !Attacks.IsSquaredAttackedBySide((int)BoardSquare.d1, position, oppositeSide)
&& !Attacks.IsSquaredAttackedBySide((int)BoardSquare.c1, position, oppositeSide))
{
yield return new Move(sourceSquare, Constants.WhiteLongCastleKingSquare, piece, isLongCastle: TRUE);
yield return MoveExtensions.Encode(sourceSquare, Constants.WhiteLongCastleKingSquare, piece, isLongCastle: TRUE);
}
}
else
Expand All @@ -320,7 +320,7 @@ internal static IEnumerable<Move> GenerateCastlingMoves(Position position, int o
&& !Attacks.IsSquaredAttackedBySide((int)BoardSquare.f8, position, oppositeSide)
&& !Attacks.IsSquaredAttackedBySide((int)BoardSquare.g8, position, oppositeSide))
{
yield return new Move(sourceSquare, Constants.BlackShortCastleKingSquare, piece, isShortCastle: TRUE);
yield return MoveExtensions.Encode(sourceSquare, Constants.BlackShortCastleKingSquare, piece, isShortCastle: TRUE);
}

if (((position.Castle & (int)CastlingRights.BQ) != default)
Expand All @@ -331,7 +331,7 @@ internal static IEnumerable<Move> GenerateCastlingMoves(Position position, int o
&& !Attacks.IsSquaredAttackedBySide((int)BoardSquare.d8, position, oppositeSide)
&& !Attacks.IsSquaredAttackedBySide((int)BoardSquare.c8, position, oppositeSide))
{
yield return new Move(sourceSquare, Constants.BlackLongCastleKingSquare, piece, isLongCastle: TRUE);
yield return MoveExtensions.Encode(sourceSquare, Constants.BlackLongCastleKingSquare, piece, isLongCastle: TRUE);
}
}
}
Expand All @@ -358,11 +358,11 @@ internal static IEnumerable<Move> GeneratePieceMoves(int piece, Position positio

if (position.OccupancyBitBoards[(int)Side.Both].GetBit(targetSquare))
{
yield return new Move(sourceSquare, targetSquare, piece, isCapture: TRUE);
yield return MoveExtensions.Encode(sourceSquare, targetSquare, piece, isCapture: TRUE);
}
else if (!capturesOnly)
{
yield return new Move(sourceSquare, targetSquare, piece);
yield return MoveExtensions.Encode(sourceSquare, targetSquare, piece);
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/Lynx.Benchmark/PositionIdGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ public string Id(Position newPosition)

public static IEnumerable<Position> Data => new[] {
//Constants.EmptyBoardFEN,
new Position(Positions[0], Positions[0].AllPossibleMoves()[0]),
new Position(Positions[1], Positions[1].AllPossibleMoves()[0]),
new Position(Positions[2], Positions[2].AllPossibleMoves()[0]),
new Position(Positions[3], Positions[3].AllPossibleMoves()[0])
new Position(Positions[0], Positions[0].AllPossibleMoves().First()),
new Position(Positions[1], Positions[1].AllPossibleMoves().First()),
new Position(Positions[2], Positions[2].AllPossibleMoves().First()),
new Position(Positions[3], Positions[3].AllPossibleMoves().First())
};
}

Expand Down
17 changes: 8 additions & 9 deletions src/Lynx.Dev/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static Lynx.Model.Move;

//_2_GettingStarted();
//_3_PawnAttacks();
Expand Down Expand Up @@ -394,7 +393,7 @@ static void _23_Castling_Moves()
var position = new Position("rn2k2r/pppppppp/8/8/8/8/PPPPPPPP/RN2K2R w KQkq - 0 1");
position.Print();

var moves = MoveGenerator.GenerateCastlingMoves(position, Utils.PieceOffset(position.Side)).ToList();
var moves = MoveGenerator.GenerateCastlingMovesForReference(position, Utils.PieceOffset(position.Side)).ToList();

foreach (var move in moves)
{
Expand Down Expand Up @@ -440,16 +439,16 @@ static void _29_Move_List()
{
var position = new Position(TrickyPosition);
var moves = MoveGenerator.GenerateAllMoves(position);
PrintMoveList(moves);
moves.PrintMoveList();

position = new Position(TrickyPositionReversed);
moves = MoveGenerator.GenerateAllMoves(position);
PrintMoveList(moves);
moves.PrintMoveList();

position = new Position(KillerPosition);
position.Print();
moves = MoveGenerator.GenerateAllMoves(position);
PrintMoveList(moves);
moves.PrintMoveList();
}

static void _32_Make_Move()
Expand All @@ -468,13 +467,13 @@ static void _32_Make_Move()
//CastlingRightsTest(game);
//CastlingRightsTest(reversedGame);

PrintMoveList(gameWithPromotion.GetAllMoves());
MoveGenerator.GenerateAllMoves(gameWithPromotion.CurrentPosition).PrintMoveList();

GeneralMoveTest(gameWithPromotion);

static void GeneralMoveTest(Game game)
{
foreach (var move in game.GetAllMoves())
foreach (var move in MoveGenerator.GenerateAllMoves(game.CurrentPosition))
{
game.CurrentPosition.Print();

Expand All @@ -494,7 +493,7 @@ static void GeneralMoveTest(Game game)

static void CastlingRightsTest(Game game)
{
foreach (var move in game.GetAllMoves())
foreach (var move in MoveGenerator.GenerateAllMoves(game.CurrentPosition))
{
if (move.Piece() == (int)Piece.R || (move.Piece() == (int)Piece.r)
|| move.Piece() == (int)Piece.K || (move.Piece() == (int)Piece.k))
Expand Down Expand Up @@ -678,7 +677,7 @@ static void ZobristTable()
var pos = new Position(KillerPosition);
var zobristTable = InitializeZobristTable();
var hash = CalculatePositionHash(zobristTable, pos);
var updatedHash = UpdatePositionHash(zobristTable, hash, pos.AllPossibleMoves()[0]);
var updatedHash = UpdatePositionHash(zobristTable, hash, pos.AllPossibleMoves().First());

Console.WriteLine(updatedHash);
}
Expand Down
7 changes: 7 additions & 0 deletions src/Lynx/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,11 @@ public static class Constants
15, 15, 15, 15, 15, 15, 15, 15,
13, 15, 15, 15, 12, 15, 15, 14
};

/// <summary>
/// 218 or 224 seems to be the known limit
/// https://www.reddit.com/r/chess/comments/9j70dc/position_with_the_most_number_of_legal_moves/
/// </summary>
public const int MaxNumberOfPossibleMovesInAPosition = 250;

}
9 changes: 5 additions & 4 deletions src/Lynx/Model/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public sealed class Game
{
private static readonly ILogger _logger = LogManager.GetCurrentClassLogger();

public Move[] MovePool { get; } = new Move[Constants.MaxNumberOfPossibleMovesInAPosition];

public List<Move> MoveHistory { get; }
public List<Position> PositionHistory { get; }
public Dictionary<long, int> PositionHashHistory { get; }
Expand Down Expand Up @@ -35,7 +37,9 @@ public Game(string fen, List<string> movesUCIString) : this(fen)
{
foreach (var moveString in movesUCIString)
{
if (!Move.TryParseFromUCIString(moveString, GetAllMoves(), out var parsedMove))
var moveList = MoveGenerator.GenerateAllMoves(CurrentPosition, MovePool);

if (!MoveExtensions.TryParseFromUCIString(moveString, moveList, out var parsedMove))
{
_logger.Error($"Error parsing game with fen {fen} and moves {string.Join(' ', movesUCIString)}: error detected in {moveString}");
break;
Expand All @@ -45,9 +49,6 @@ public Game(string fen, List<string> movesUCIString) : this(fen)
}
}

public List<Move> GetAllMoves() => MoveGenerator.GenerateAllMoves(CurrentPosition);
public List<Move> GetAllMovesWithCaptures() => MoveGenerator.GenerateAllMoves(CurrentPosition, capturesOnly: true);

public bool MakeMove(Move moveToPlay)
{
PositionHistory.Add(CurrentPosition);
Expand Down
Loading