My name is Sviatoslav and I am a CS student at UGent, coming from Belgium/Ukraine!
โจ Improving my programming skills since 2020
๐ Currently learning Typescript
๐ฃ I speak Ukrainian, Dutch and English
C# .NET 6.0 Chess Library
License: MIT License
Describe the bug
Cannot load certain En Passant positions from PGN
Try to load this PGN (a game from Ding Liren) using ChessBoard.LoadFromPgn:
1.e4 e6 2.d4 d5 3.Nc3 Nf6 4.Bg5 dxe4 5.Nxe4 Be7 6.Bxf6 gxf6 7.Nf3 f5 8.Nc3 a6
9.Qd2 b5 10.O-O-O b4 11.Na4 Bb7 12.Bc4 Bd5 13.Qe2 Nc6 14.Rhe1 Na5 15.Bxd5 Qxd5
16.b3 Nc6 17.c4 bxc3 18.Nxc3 Qa5 19.Qc4 Nb4 20.Kb1 O-O 21.Ne5 Rad8 22.g4 f4
23.Nd3 Nxd3 24.Rxd3 Bf6 25.Red1 Bg7 26.Qc5 Qxc5 27.dxc5 Rxd3 28.Rxd3 f5 29.gxf5 Bxc3
30.Rxc3 Rxf5 31.Kc2 Rg5 32.Kd3 Rg2 33.Ke2 Rxh2 34.Rd3 e5 35.Rd5 e4 36.Rg5+ Kh8
37.Rg4 e3 38.Rxf4 exf2 39.Rf7 c6 40.a4 Kg8 41.Rc7 Rh3 42.Rxc6 Rxb3 43.Rxa6 Rb2+
44.Kf1 Kf7 45.a5 Ke8 46.Ra8+ Kd7 47.a6 Ra2 48.a7 Kc7 1/2-1/2
If fails with "'Given SAN move: Qc4 has been not found with current board positions.'"
This happens because in move 17 when w takes En Passant with bxc3, if you draw the board at that moment, you can see that the board has not had the pawn on c4 removed.
lichess analysis board does handle this PGN correctly.
To Reproduce
var pgn = "1.e4 e6 2.d4 d5 3.Nc3 Nf6 4.Bg5 dxe4 5.Nxe4 Be7 6.Bxf6 gxf6 7.Nf3 f5 8.Nc3 a6\r\n9.Qd2 b5 10.O-O-O b4 11.Na4 Bb7 12.Bc4 Bd5 13.Qe2 Nc6 14.Rhe1 Na5 15.Bxd5 Qxd5\r\n16.b3 Nc6 17.c4 bxc3 18.Nxc3 Qa5 19.Qc4 Nb4 20.Kb1 O-O 21.Ne5 Rad8 22.g4 f4\r\n23.Nd3 Nxd3 24.Rxd3 Bf6 25.Red1 Bg7 26.Qc5 Qxc5 27.dxc5 Rxd3 28.Rxd3 f5 29.gxf5 Bxc3\r\n30.Rxc3 Rxf5 31.Kc2 Rg5 32.Kd3 Rg2 33.Ke2 Rxh2 34.Rd3 e5 35.Rd5 e4 36.Rg5+ Kh8\r\n37.Rg4 e3 38.Rxf4 exf2 39.Rf7 c6 40.a4 Kg8 41.Rc7 Rh3 42.Rxc6 Rxb3 43.Rxa6 Rb2+\r\n44.Kf1 Kf7 45.a5 Ke8 46.Ra8+ Kd7 47.a6 Ra2 48.a7 Kc7 1/2-1/2";
var cb = ChessBoard.LoadFromPgn(pgn);
Expected behaviour
Loading games with En Passant would work.
Additional context
I'm working around this by manually reconstructing the board and hacking around this, but not ideal.
Otherwise, I really like the library =)
Here is a demo showing
Describe the bug
In a position achieved via the given PGN, a checkmate isn't recognized. It recognizes this as just a check
To Reproduce
`// "1. e3 e5 2. d4 e4 3. Nf3 exf3 4. Qxf3 d5 5. Qxf7+ Kxf7 6. e4 Nc6 7. exd5 Qxd5 8. c4 Qe4+ 9. Kd1 Nxd4 10. Bd3 Qxd3+ 11. Ke1 Be7 12. Nc3 Nc2+";
var listOfMoves = new List<string>() {
"e2e3", "e7e5", "d2d4", "e5e4", "g1f3", "exf3",
"d1f3", "d7d5", "f3f7", "e8f7", "e3e4", "b8c6",
"e4d5", "d8d5", "c2c4", "d5e4", "e1d1", "c6d4",
"f1d3", "e4d3", "d1e1", "f8e7", "b1c3", "d4c2"};
var board = ChessBoard.LoadFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
foreach (var moveInfo in listOfMoves)
{
board.Move(moveInfo);
}
Assert.True(board.IsEndGame);`
Expected behaviour
board.IsEndGame returns true
Additional context
Working on a game and this is a legitimate position I got while playing against AI. Needless to say the whole game just stopped in this moment as Checkmate wasn't flagged and I couldn't move ๐
I found a way to achieve this
move.Parameter is MovePromotion
returns true
when the move is a promotion.
And to find out what the promoted piece is, I do
Piece p = new Piece(move.Parameter.ShortStr[1]);
Console.WriteLine(p.Type);
And to check whether a move is EnPassant, I do
move.Parameter is MoveEnPassant
I guess this works since a move can't be both En Passant and promotion at the same time. But it's not very straightforward.
It would be nice if the library supported
Move.Promotion
which returns the promoted piece or null when there is no promotion.
Also consider adding Move.IsEnPassant
and Move.IsCastling
Anyways, thanks for the amazing library.
Describe the bug
I've been having some difficulty with the Chessboard.ParseToSan
and .TryParseToSan
methods. Despite the Move
provided passing ChessBoard.IsValidMove
, the .ParseToSan
methods return null
for the SAN string (.TryParseToSan
returns true
which leads me to believe the string shouldn't be null
).
To Reproduce
I wrote a quick unit test for it (edit: which now also contains the fix I'm proposing because I didn't link to a specific commit)
Expected behaviour
I'm expecting the returned string to contain the SAN equivalent for the move, based on the state of the current board.
Additional context
Hey - hope all's well. I love the library, and I've adopted it for my current project: instantiator/consensus-chess-engine as it's lightweight, and does everything I need (notwithstanding this particular issue). I'd be glad to dig a little deeper and see if I can help figure out what's happening, but I suspect you'd be able to go faster...
Describe the bug
After a LoadFromFen()
with at least one particular string, although the resulting .FenBuilder
looks correct (and correctly gives the proper FEN when you ask for FenBuilder.ToString()
), the .ToFen()
method does not properly include the en passant square.
To Reproduce
var board = ChessBoard.LoadFromFen("rnbqkbnr/ppppp1pp/8/8/4P1pP/8/PPPP1P2/RNBQKBNR b KQkq h3 0 4");
At this point if you use the debugger, you will see that board.ToFen()
returns "rnbqkbnr/ppppp1pp/8/8/4P1pP/8/PPPP1P2/RNBQKBNR b KQkq - 0 4"
but board.FenBuilder.ToString()
returns the expected "rnbqkbnr/ppppp1pp/8/8/4P1pP/8/PPPP1P2/RNBQKBNR b KQkq h3 0 4"
Expected behaviour
.ToFen()
should reproduce the FEN that .LoadFromFen()
used to create the board, including the en passant square.
Screenshots
N/A
Additional context
NOTE: Even with the board being created using .LoadFromFen()
, the ChessBoard.Moves()
list DOES contain the available en passant capture moves. However, when doing board.Move(oneOfThoseEnPassantMoves)
the return value is false
and the board is not modified.
Leave an issue here or send to my e-mail:
[email protected]
Thanks!
When a match between engines using UCI is played, a promoting move has the format of: h7h8q. This move is not accepted by the library and I believe it should be. Of course it is trivial to convert the h7h8q to h8=Q but I believe this should be supported out of the box.
To Reproduce
var board = ChessBoard.LoadFromFen("8/4R2P/3K4/8/p7/P2b4/1P6/3k4 w - - 3 68");
board.Move("h7h8q");
Expected behaviour
The move is accepted.
I'd be willing to implement this myself if you agree that this is a bug. Thanks a lot for the great library!
Describe the bug
A move that promotes a pawn to a piece that does not deliver check is mistakenly set to IsCheck=true if there are other possible promotions (that is, to other pieces) that WOULD give check.
To Reproduce
var board = ChessBoard.LoadFromFen("k7/7P/8/8/8/8/8/K7 w - - 0 1");
var checks = board.Moves().Where(static move => move.IsCheck).ToList();
You will see that checks.Count
is 4
, when it should be 2
, and that the List includes both h8=N+
and h8=B+
when neither of these promotions actually deliver check.
Expected behaviour
Moves that promote to pieces that do not deliver check should have Move.IsCheck == false
. In the example code above, the checks
List should contain only two moves: h8=Q+
and h8=R+
Screenshots
N/A
Additional context
N/A
This library is really cool! I'm making a game around it and I believe it would benefit of having ways to detect if a draw has been reached (three fold repetition or 50 moves rule)
Describe the solution you'd like
Describe the bug
Castling move is not shown properly in ExecutedMoves. It is applicable in both short castling and long castling. Instead of e8g8, it is showing e8h8 for black short castling. Same way instead of White long castling it is showing e1a1 instead of e1c1.
Conversions / Validations / ...
To Reproduce
Steps to reproduce the behaviour or
Eventually a call stack and input data
string str = "1. e4 c5 2. Nf3 d6 3. Nc3 Nc6 4. Bb5 Nf6 5. d3 a6 6. Bxc6+ bxc6 7. Bg5 g6 8. Qd2 Bg7 9. Bh6 O-O 10. Bxg7 Kxg7 11. O-O-O Rb8 12. Ng5 Qb6 13. Na4 Qb5 14. b3 d5 15. e5 Nd7 16. e6 Nf6 17. exf7 h6 18. Nf3 Be6 19. Ne5 Bxf7 20. Nxf7 Rxf7 21. f3 c4 22. dxc4 dxc4 23. Qc3 cxb3 24. axb3 Qg5+ 25. Kb1 e6 26. Nc5 Kg8 27. g4 Rb5 28. Rd8+ Rf8 29. Rxf8+ Kxf8 30. Nxe6+ Kf7 31. Nxg5+ 1-0";
var board = new ChessBoard();
board = ChessBoard.LoadFromPgn(str);
foreach(var move in board.ExecutedMoves)
{
//Check 9th move of black and 11th move of white. Instead of e8g8, it is showing e8h8 for black short castling. Same way instead of White long castling it is showing e1a1 instead of e1c1. King would not move to extreme corner after castling.
}
Expected behaviour
A clear and concise description of what you expected to happen.
Black short castling = e8g8
White long castle = e1c1
same way,
Black long castling = e8c8
White short castle = e1g1
Screenshots
If applicable, add screenshots to help explain your problem.
Additional context
Add any other context about the problem here.
Describe the bug
A board with insufficient material is not being recognized as such, and its IsEndGame
is false
, etc.
To Reproduce
var board = ChessBoard.LoadFromFen("2b5/4kB2/8/8/8/8/5K2/8 b - - 0 16");
if (!board.IsIendGame)
throw new ();
Expected behaviour
Boards such as the one used in the reproduction example should be recognized as having insufficient material to proceed, and the game drawn, with board.EndGame.EndgameType == EndgameType.InsufficientMaterial
, and with its board.ToPgn()
string being given the ยฝ-ยฝ
, etc., etc.
Screenshots
N/A
Additional context
I know that you probably have a much better way to fix this in the library, but in case it is in any way useful, here is the code I added as a workaround in my application:
private static bool IsDrawnForInsufficientMaterial(this ChessBoard board)
{
// Insufficient Material isn't properly detected. Honestly, I checked. Do it ourselves.
// If there are any pawns, rooks, or queens on the board, there is sufficient material.
var fen = board.ToFen().Split()[0];
if ("PQR".Any(fen.ToUpper().Contains))
return false;
// Otherwise, it's just bishops, knights, and kings. Well, yeah, kings -- ignore them....
var remainingPieces = new string(fen.Where(static c => c is > '8' and not ('K' or 'k')).ToArray());
// Kings only or King against a single bishop or knight is drawn.
// Otherwise, except for a bishop apiece, it's sufficient.
// In B vs b, it's a draw if both bishops are on light squares or both on dark squares.
return remainingPieces.Length < 2
|| remainingPieces is "Bb" or "bB"
&& Regex.Replace(fen, "[2468]", string.Empty).Select(static (c, i) => c is 'B' or 'b' ? i : 0).Sum() % 2 is 0;
}
Describe the bug
After first move "e2" to "e4" the method "ToFen()" returns wrong value. In this case the En-Passant move is shown, but actually it should not be there since there is no black pawn that could capture. If you import this wrong Fen to Lichess for example, it fails.
To Reproduce
var cb = new ChessBoard();
cb.Move(new Move("e2", "e4");
var fen = cb.ToFen();
Expected behaviour
fen --> rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1
Actual behaviour
fen --> rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1
Describe the bug
When using Newtonsoft.Json to Deserialize a ChessBoard object, I get the following exception:
Newtonsoft.Json.JsonSerializationException: 'Error setting value to 'MoveIndex' on 'Chess.ChessBoard'.
with the Inner Exception being:
IndexOutOfRangeException: Move not found
I get this when executing this line of my code:
JsonConvert.DeserializeObject<T>(value);
where value is an instance of type T : Solver
and Solver is a class that contains this serialized field:
[JsonProperty]
private readonly Dictionary<string, MyPosition> _savedPositions = new ();
and MyPosition is a sealed record that contains this serialized field:
public ChessBoard Board { get; set; }
To Reproduce
sealed record MyPosition
{
public ChessBoard Board { get; set; } = new ();
// Actually, since the error involves MoveIndex and the Move class, it may be that
// one or more Moves may need to be made on the Board in order to get it into a
// state where the Deserialization exception happens?? I do not know, but it is
// certainly the case that one or more moves were indeed made on the Boards I
// am trying to Serialize/Deserialize.
}
class Solver
{
[JsonProperty]
private readonly Dictionary<string, MyPosition> _savedPositions = new () { ["test"] = new () };
}
...
var serialized = JsonConvert.SerializeObject(new Solver());
var deserialized = JsonConvert.DeserializeObject<Solver>(serialized);
Expected behaviour
The object should deserialize.
Screenshots
N/A
Additional context
N/A
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.