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

PGN Parser #3

Merged
merged 45 commits into from Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
b800b30
create class and types
nav-28 Dec 31, 2022
26fce71
add makePgn function
nav-28 Jan 1, 2023
fcff4b1
add test and fix errors
nav-28 Jan 1, 2023
54e8e4a
fix errors
nav-28 Jan 1, 2023
3a4a13a
format code and change class construstors
nav-28 Jan 1, 2023
9e487e2
start pgnparser and remove child nodes
nav-28 Jan 1, 2023
93302f3
format code and add regex function
nav-28 Jan 1, 2023
6419a27
add parse header test and fix error
nav-28 Jan 1, 2023
26f6b3b
fix regex change PgnError back to Error
nav-28 Jan 1, 2023
0b18e16
finished test parse pgn
nav-28 Jan 2, 2023
c79bead
Add ChildNode to reduce the null safety checks
nav-28 Jan 2, 2023
ffc572e
add immutability to Game class
nav-28 Jan 2, 2023
c268150
add startingPosition functions
nav-28 Jan 2, 2023
328ee6c
add new variant model
nav-28 Jan 2, 2023
d07e97d
Merge remote-tracking branch 'upstream/main' into pgn
nav-28 Jan 2, 2023
d89dd6e
follow linter rules
nav-28 Jan 3, 2023
fab48c9
Make mutable class private
nav-28 Jan 3, 2023
1d4afe0
add comment functions
nav-28 Jan 4, 2023
5006143
add tests for comment functions and fix errors
nav-28 Jan 4, 2023
d01b73e
change classes to be immutable
nav-28 Jan 4, 2023
b36640e
add walk and transform methods
nav-28 Jan 4, 2023
09faed7
add cloneable to position classes
nav-28 Jan 4, 2023
4bafbae
fix errors
nav-28 Jan 5, 2023
32bb976
remove cloneable class
nav-28 Jan 5, 2023
bec11b7
fix tests
nav-28 Jan 5, 2023
a2b42f6
fix error in header parser
nav-28 Jan 6, 2023
57731d5
Add null checks
nav-28 Jan 9, 2023
cc58fd0
add benchmarks
nav-28 Jan 10, 2023
6c5c806
add copyWith to make class immutable
nav-28 Jan 10, 2023
86e70ea
add immutable tag
nav-28 Jan 10, 2023
db593ea
prefer const with const constructor
nav-28 Jan 10, 2023
149081c
const constructor
nav-28 Jan 10, 2023
648db32
make requested changes
nav-28 Jan 11, 2023
1cde634
Make requested changes 2
nav-28 Jan 11, 2023
404a675
moved position functions to Position class
nav-28 Jan 11, 2023
5dcb234
make changes
nav-28 Jan 13, 2023
1a7a4bf
rename benchmark file
nav-28 Jan 13, 2023
1413f8f
Change error in PgnParser
nav-28 Jan 13, 2023
64d4591
Update transform and add tests
nav-28 Jan 13, 2023
85e13f5
Simplify functions
nav-28 Jan 13, 2023
6ae14a1
Add tests for invalid Pgn
nav-28 Jan 13, 2023
0b81fe4
remove errors in parse and update doc comments
nav-28 Jan 14, 2023
d0ed359
Merge remote-tracking branch 'upstream/main' into pgn
nav-28 Jan 18, 2023
d3fd81c
Change position functions
nav-28 Jan 18, 2023
0b69060
Update transform function and its test
nav-28 Jan 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 58 additions & 0 deletions benchmark/dartchess_benchmark.dart
@@ -0,0 +1,58 @@
import 'package:benchmark/benchmark.dart';
import 'package:dartchess/dartchess.dart';
import 'dart:io';

void main() {
benchmark('parsePgn - kasparov-deep-blue', () {
final String data =
File('./data/kasparov-deep-blue-1997.pgn').readAsStringSync();

parseMultiGamePgn(data);
}, iterations: 1);

benchmark('perft', () {
perft(Chess.initial, 4);
}, iterations: 1);

benchmark('valid fen moves', () {
const fen = 'rn1qkb1r/pbp2ppp/1p2p3/3n4/8/2N2NP1/PP1PPPBP/R1BQ1RK1 b kq -';
final pos = Chess.fromSetup(Setup.parseFen(fen));
assert(pos.legalMoves.length == 20);
}, iterations: 1);

benchmark('play moves', () {
const fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
final pos = Chess.fromSetup(Setup.parseFen(fen));
pos.play(const NormalMove(from: 12, to: 28));
}, iterations: 1);

final pgn = [
'[Event "Rated Bullet game"]',
'[Site "https://lichess.org/P5VLFjXI"]',
'[Date "2022.08.23"]',
'[White "mutdpro"]',
'[Black "DrNykterstein"]',
'[Result "0-1"]',
'[UTCDate "2022.08.23"]',
'[UTCTime "18:14:25"]',
'[WhiteElo "3201"]',
'[BlackElo "3317"]',
'[WhiteRatingDiff "-4"]',
'[BlackRatingDiff "+4"]',
'[WhiteTitle "GM"]',
'[BlackTitle "GM"]',
'[Variant "Standard"]',
'[TimeControl "60+0"]',
'[ECO "A46"]',
'[Opening "Indian Defense: Spielmann-Indian"]',
'[Termination "Normal"]',
'[Annotator "lichess.org"]',
'\n',
'1. d4 Nf6 2. Nf3 c5 { A46 Indian Defense: Spielmann-Indian } 3. c3 d5 4. Bf4 Qb6 5. Qc2 cxd4 6. cxd4 Nc6 7. e3 g6?! { (-0.32 → 0.49) Inaccuracy. Bf5 was best. } (7... Bf5 8. Qb3 Qxb3 9. axb3 Nh5 10. Bg3 Bd7 11. Nc3 e6 12. Bb5) 8. Nc3 Bf5 9. Qb3 Qxb3 10. axb3 a6 11. Bb5?! { (0.99 → 0.31) Inaccuracy. Ne5 was best. } (11. Ne5) 11... Bd7 12. Ne5 Rc8 13. Nxd7 Kxd7 14. Be2?! { (0.48 → -0.43) Inaccuracy. Bxc6+ was best. } (14. Bxc6+ Rxc6) 14... e6 15. Kd2?! { (-0.42 → -1.25) Inaccuracy. g3 was best. } (15. g3 Bd6) 15... Bb4?! { (-1.25 → -0.32) Inaccuracy. Ne4+ was best. } (15... Ne4+ 16. Nxe4 dxe4 17. Rhc1 Bb4+ 18. Kd1 Na5 19. Rcb1 Nxb3 20. Ra4 a5 21. Be5 Rhg8 22. Bf6) 16. Bd3 Ne4+ 17. Bxe4 dxe4 18. Ke2 f5 19. d5? { (0.26 → -0.87) Mistake. Na4 was best. } (19. Na4) 19... Ne7 20. Rhd1 Nxd5 21. Nxd5 Rc2+ 22. Kf1 exd5 23. Rxd5+ Ke6 24. Rad1 Rhc8 25. Re5+ Kf6 26. g4 Rc1 27. g5+ Kf7 28. Rxc1?? { (-0.81 → -4.55) Blunder. Rd5 was best. } (28. Rd5) 28... Rxc1+ 29. Kg2 Rc5? { (-5.48 → -2.89) Mistake. Rd1 was best. } (29... Rd1) 30. Rxc5 Bxc5 31. Be5 Ke6 32. Bc3 Bd6 33. h4 f4 34. Kh3 Kf5 35. Bd4?? { (-1.40 → -4.51) Blunder. b4 was best. } (35. b4) 35... b5 36. Bb6 Be5 37. Bc5 a5 38. Bb6 a4 39. bxa4 bxa4 40. Bd4 fxe3 41. fxe3 Bxd4 42. exd4 Ke6 { White resigns. } 0-1'
].join('\n');

final game = PgnGame.parsePgn(pgn);
benchmark('makePgn', () {
game.makePgn();
}, iterations: 1);
}
8 changes: 8 additions & 0 deletions data/headers-and-moves-on-the-same-line.pgn
@@ -0,0 +1,8 @@
[Variant "Antichess"] 1.e3 e6 2.b4 Bxb4 3.Qg4 { Header on the same line as moves }

[Variant "Antichess"]
1.e3 e6 2.b4 Bxb4 3.Qg4 { Header followed by a separate line containing a sequence of moves}

[Variant "Antichess"]

1.e3 e6 2.b4 Bxb4 3.Qg4 { Header followed by a blank line and a line containing a sequence of moves }
135 changes: 135 additions & 0 deletions data/kasparov-deep-blue-1997.pgn
@@ -0,0 +1,135 @@
[Event "IBM Man-Machine, New York USA"]
[Site "01"]
[Date "1997.??.??"]
[EventDate "?"]
[Round "?"]
[Result "1-0"]
[White "Garry Kasparov"]
[Black "Deep Blue (Computer)"]
[ECO "A06"]
[WhiteElo "?"]
[BlackElo "?"]
[PlyCount "89"]

1.Nf3 d5 2.g3 Bg4 3.b3 Nd7 4.Bb2 e6 5.Bg2 Ngf6 6.O-O c6 7.d3
Bd6 8.Nbd2 O-O 9.h3 Bh5 10.e3 h6 11.Qe1 Qa5 12.a3 Bc7 13.Nh4
g5 14.Nhf3 e5 15.e4 Rfe8 16.Nh2 Qb6 17.Qc1 a5 18.Re1 Bd6
19.Ndf1 dxe4 20.dxe4 Bc5 21.Ne3 Rad8 22.Nhf1 g4 23.hxg4 Nxg4
24.f3 Nxe3 25.Nxe3 Be7 26.Kh1 Bg5 27.Re2 a4 28.b4 f5 29.exf5
e4 30.f4 Bxe2 31.fxg5 Ne5 32.g6 Bf3 33.Bc3 Qb5 34.Qf1 Qxf1+
35.Rxf1 h5 36.Kg1 Kf8 37.Bh3 b5 38.Kf2 Kg7 39.g4 Kh6 40.Rg1
hxg4 41.Bxg4 Bxg4 42.Nxg4+ Nxg4+ 43.Rxg4 Rd5 44.f6 Rd1 45.g7
1-0

[Event "IBM Man-Machine, New York USA"]
[Site "02"]
[Date "1997.??.??"]
[EventDate "?"]
[Round "?"]
[Result "1-0"]
[White "Deep Blue (Computer)"]
[Black "Garry Kasparov"]
[ECO "C93"]
[WhiteElo "?"]
[BlackElo "?"]
[PlyCount "89"]

1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Ba4 Nf6 5.O-O Be7 6.Re1 b5 7.Bb3
d6 8.c3 O-O 9.h3 h6 10.d4 Re8 11.Nbd2 Bf8 12.Nf1 Bd7 13.Ng3
Na5 14.Bc2 c5 15.b3 Nc6 16.d5 Ne7 17.Be3 Ng6 18.Qd2 Nh7 19.a4
Nh4 20.Nxh4 Qxh4 21.Qe2 Qd8 22.b4 Qc7 23.Rec1 c4 24.Ra3 Rec8
25.Rca1 Qd8 26.f4 Nf6 27.fxe5 dxe5 28.Qf1 Ne8 29.Qf2 Nd6
30.Bb6 Qe8 31.R3a2 Be7 32.Bc5 Bf8 33.Nf5 Bxf5 34.exf5 f6
35.Bxd6 Bxd6 36.axb5 axb5 37.Be4 Rxa2 38.Qxa2 Qd7 39.Qa7 Rc7
40.Qb6 Rb7 41.Ra8+ Kf7 42.Qa6 Qc7 43.Qc6 Qb6+ 44.Kf1 Rb8
45.Ra6 1-0

[Event "IBM Man-Machine, New York USA"]
[Site "03"]
[Date "1997.??.??"]
[EventDate "?"]
[Round "?"]
[Result "1/2-1/2"]
[White "Garry Kasparov"]
[Black "Deep Blue (Computer)"]
[ECO "A00"]
[WhiteElo "?"]
[BlackElo "?"]
[PlyCount "95"]

1.d3 e5 2.Nf3 Nc6 3.c4 Nf6 4.a3 d6 5.Nc3 Be7 6.g3 O-O 7.Bg2
Be6 8.O-O Qd7 9.Ng5 Bf5 10.e4 Bg4 11.f3 Bh5 12.Nh3 Nd4 13.Nf2
h6 14.Be3 c5 15.b4 b6 16.Rb1 Kh8 17.Rb2 a6 18.bxc5 bxc5 19.Bh3
Qc7 20.Bg4 Bg6 21.f4 exf4 22.gxf4 Qa5 23.Bd2 Qxa3 24.Ra2 Qb3
25.f5 Qxd1 26.Bxd1 Bh7 27.Nh3 Rfb8 28.Nf4 Bd8 29.Nfd5 Nc6
30.Bf4 Ne5 31.Ba4 Nxd5 32.Nxd5 a5 33.Bb5 Ra7 34.Kg2 g5
35.Bxe5+ dxe5 36.f6 Bg6 37.h4 gxh4 38.Kh3 Kg8 39.Kxh4 Kh7
40.Kg4 Bc7 41.Nxc7 Rxc7 42.Rxa5 Rd8 43.Rf3 Kh8 44.Kh4 Kg8
45.Ra3 Kh8 46.Ra6 Kh7 47.Ra3 Kh8 48.Ra6 1/2-1/2

[Event "IBM Man-Machine, New York USA"]
[Site "04"]
[Date "1997.??.??"]
[EventDate "?"]
[Round "?"]
[Result "1/2-1/2"]
[White "Deep Blue (Computer)"]
[Black "Garry Kasparov"]
[ECO "B10"]
[WhiteElo "?"]
[BlackElo "?"]
[PlyCount "111"]

1.e4 c6 2.d4 d6 3.Nf3 Nf6 4.Nc3 Bg4 5.h3 Bh5 6.Bd3 e6 7.Qe2 d5
8.Bg5 Be7 9.e5 Nfd7 10.Bxe7 Qxe7 11.g4 Bg6 12.Bxg6 hxg6 13.h4
Na6 14.O-O-O O-O-O 15.Rdg1 Nc7 16.Kb1 f6 17.exf6 Qxf6 18.Rg3
Rde8 19.Re1 Rhf8 20.Nd1 e5 21.dxe5 Qf4 22.a3 Ne6 23.Nc3 Ndc5
24.b4 Nd7 25.Qd3 Qf7 26.b5 Ndc5 27.Qe3 Qf4 28.bxc6 bxc6 29.Rd1
Kc7 30.Ka1 Qxe3 31.fxe3 Rf7 32.Rh3 Ref8 33.Nd4 Rf2 34.Rb1 Rg2
35.Nce2 Rxg4 36.Nxe6+ Nxe6 37.Nd4 Nxd4 38.exd4 Rxd4 39.Rg1 Rc4
40.Rxg6 Rxc2 41.Rxg7+ Kb6 42.Rb3+ Kc5 43.Rxa7 Rf1+ 44.Rb1 Rff2
45.Rb4 Rc1+ 46.Rb1 Rcc2 47.Rb4 Rc1+ 48.Rb1 Rxb1+ 49.Kxb1 Re2
50.Re7 Rh2 51.Rh7 Kc4 52.Rc7 c5 53.e6 Rxh4 54.e7 Re4 55.a4 Kb3
56.Kc1 1/2-1/2

[Event "IBM Man-Machine, New York USA"]
[Site "05"]
[Date "1997.??.??"]
[EventDate "?"]
[Round "?"]
[Result "1/2-1/2"]
[White "Garry Kasparov"]
[Black "Deep Blue (Computer)"]
[ECO "A07"]
[WhiteElo "?"]
[BlackElo "?"]
[PlyCount "98"]

1.Nf3 d5 2.g3 Bg4 3.Bg2 Nd7 4.h3 Bxf3 5.Bxf3 c6 6.d3 e6 7.e4
Ne5 8.Bg2 dxe4 9.Bxe4 Nf6 10.Bg2 Bb4+ 11.Nd2 h5 12.Qe2 Qc7
13.c3 Be7 14.d4 Ng6 15.h4 e5 16.Nf3 exd4 17.Nxd4 O-O-O 18.Bg5
Ng4 19.O-O-O Rhe8 20.Qc2 Kb8 21.Kb1 Bxg5 22.hxg5 N6e5 23.Rhe1
c5 24.Nf3 Rxd1+ 25.Rxd1 Nc4 26.Qa4 Rd8 27.Re1 Nb6 28.Qc2 Qd6
29.c4 Qg6 30.Qxg6 fxg6 31.b3 Nxf2 32.Re6 Kc7 33.Rxg6 Rd7
34.Nh4 Nc8 35.Bd5 Nd6 36.Re6 Nb5 37.cxb5 Rxd5 38.Rg6 Rd7
39.Nf5 Ne4 40.Nxg7 Rd1+ 41.Kc2 Rd2+ 42.Kc1 Rxa2 43.Nxh5 Nd2
44.Nf4 Nxb3+ 45.Kb1 Rd2 46.Re6 c4 47.Re3 Kb6 48.g6 Kxb5 49.g7
Kb4 1/2-1/2

[Event "IBM Man-Machine, New York USA"]
[Site "06"]
[Date "1997.??.??"]
[EventDate "?"]
[Round "?"]
[Result "1-0"]
[White "Deep Blue (Computer)"]
[Black "Garry Kasparov"]
[ECO "B17"]
[WhiteElo "?"]
[BlackElo "?"]
[PlyCount "37"]

1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nd7 5.Ng5 Ngf6 6.Bd3 e6
7.N1f3 h6 8.Nxe6 Qe7 9.O-O fxe6 10.Bg6+ Kd8 11.Bf4 b5 12.a4
Bb7 13.Re1 Nd5 14.Bg3 Kc8 15.axb5 cxb5 16.Qd3 Bc6 17.Bf5 exf5
18.Rxe7 Bxe7 19.c4 1-0
11 changes: 11 additions & 0 deletions data/leading-whitespace.pgn
@@ -0,0 +1,11 @@
[Variant "Standard"]

1. e4 e5 2. Nf3 Nc6 3. Bb5 { Leading whitespace, header followed, blank line, move sequence }

[Variant "Standard"] 1. e4 e5 2. Nf3 Nc6 3. Bb5 { leading whitespace, header, move sequence }

[Variant "Standard"]
1. e4 e5 2. Nf3 Nc6 3. Bb5 { leading whitespace, header, new line, move sequence }


1. e4 e5 2. Nf3 Nc6 3. Bb5 { leading whitespace, no header, move sequence }
1 change: 1 addition & 0 deletions data/pathological-headers.pgn
@@ -0,0 +1 @@
[A "b\""] [B "b\""] [C "A]]"] [D "A]]["] [E "\"A]][\""] [F "\"A]][\"\\"] [G "\"]"]
1 change: 1 addition & 0 deletions lib/dartchess.dart
Expand Up @@ -12,3 +12,4 @@ export 'src/board.dart';
export 'src/setup.dart';
export 'src/position.dart';
export 'src/debug.dart';
export 'src/pgn.dart';
95 changes: 95 additions & 0 deletions lib/src/models.dart
Expand Up @@ -262,3 +262,98 @@ class FenError implements Exception {
final String message;
const FenError(this.message);
}

/// Represents the variants of chess
enum Variant {
chess,
antichess,
kingofthehill,
threecheck,
atomic,
horde,
racingKings,
crazyhouse;

String? get string {
switch (this) {
case Variant.chess:
return null;
case Variant.antichess:
return 'antichess';
case Variant.kingofthehill:
return 'King of the Hill';
case Variant.threecheck:
return '3check';
case Variant.atomic:
return 'Atomic';
case Variant.horde:
return 'Horde';
case Variant.racingKings:
return 'Racing Kings';
case Variant.crazyhouse:
return 'Crazyhouse';
}
}

/// Parse a string for a variant if exist or return null
static Variant? fromPgn(String variant) {
switch (variant.toLowerCase()) {
case 'chess':
case 'chess960':
case 'chess 960':
case 'standard':
case 'from position':
case 'classical':
case 'normal':
case 'fischerandom': // Cute Chess
case 'fischerrandom':
case 'fischer random':
case 'wild/0':
case 'wild/1':
case 'wild/2':
case 'wild/3':
case 'wild/4':
case 'wild/5':
case 'wild/6':
case 'wild/7':
case 'wild/8':
case 'wild/8a':
return Variant.chess;
case 'crazyhouse':
case 'crazy house':
case 'house':
case 'zh':
return Variant.crazyhouse;
case 'king of the hill':
case 'koth':
case 'kingofthehill':
return Variant.kingofthehill;
case 'three-check':
case 'three check':
case 'threecheck':
case 'three check chess':
case '3-check':
case '3 check':
case '3check':
return Variant.threecheck;
case 'antichess':
case 'anti chess':
case 'anti':
return Variant.antichess;
case 'atomic':
case 'atom':
case 'atomic chess':
return Variant.atomic;
case 'horde':
case 'horde chess':
return Variant.horde;
case 'racing kings':
case 'racingkings':
case 'racing':
case 'race':
return Variant.racingKings;
default:
return null;
}
}
}