test: Add JSON test cases and integration tests for chess moves

```
Add comprehensive test cases for chess scenarios and validate JSON parsing in integration tests

- Add `integration_test.go` to handle JSON test cases, parse data, and validate chess move generation against expected results.
- Add `checkmates.json` with test cases for various checkmate scenarios, including mirrors and edge cases.
- Add `famous.json` detailing positions and test sequences from historical chess games for validation.
- Add `standard.json` to test and verify early-game chess positions and standard move transitions.
- Add `promotions.json` with detailed cases for pawn promotions, including edge scenarios and forced moves.
- Add additional JSON files (`castling.json`, `taxing.json`, `pawns.json`, `stalemates.json`) with test data for specific chess rule validations and edge cases like castling, stalemates, and en passant captures.
```

[testdata/integration_test.go]
- Adds a new test file to validate JSON-defined chess scenarios against board states and expected moves.
- Defines structs for handling test data, including descriptions, starting positions, and expected outcomes.
- Implements a test function to read JSON files in the directory, parse them, and execute tests on the data.
- Introduces helper functions to parse JSON data and iterate through test cases.
- Adds logic to compare expected moves from JSON with generated moves from the chess board state.
- Includes error handling for file reading, JSON unmarshalling, and position validation from the chess board.
[testdata/checkmates.json]
- Added a new JSON file containing test cases for checkmate scenarios.
- Each test case includes a FEN description and an expected output.
  - Descriptions and tests cover specific checkmate types: ladder mate, smothered mate, en passant situations, and minimal-piece mates.
- Added transposed positions for each checkmate scenario to validate mirrored setups.
[testdata/famous.json]
- Added new JSON file containing chess positions and test cases.
- Included a "description" field specifying the context of the chess problems.
- Each test case specifies a starting position (given by a FEN string) and a list of moves along with their corresponding resulting positions (also as FEN strings).
- Added detailed test cases for two famous chess games: "The Game of the Century" and "The Gold Coins Game".
  - For "The Game of the Century", provided multiple legal move sequences and their outcomes from a specific mid-game position.
  - For "The Gold Coins Game", included potential moves and their resulting positions leading to the iconic "Gold Coin move".
- Enriched some moves with descriptive comments for historical or tactical significance.
- Added various chess scenarios to thoroughly test a move generator's capability to handle diverse situations.
[testdata/standard.json]
- Added a new JSON file containing test cases for specific chess board positions.
- The JSON includes:
  - Definitions of starting positions using FEN strings.
  - Descriptions of the starting positions, such as "Standard starting position" and "Ruy Lopez opening".
  - Expected potential moves with resulting FEN states after each move. These moves explore various legal possibilities from the starting positions.
- The file contains test cases primarily for validating early-game configurations and various move transitions in chess.
[testdata/promotions.json]
- Added a new file containing JSON data describing chess test cases focused on pawn promotions.
- Included multiple scenarios covering basic promotion, transposed positions, and edge cases such as forced promotion to escape check or mate.
- Detailed expected moves and resulting board configurations in FEN format for each scenario.
- Descriptions accompanied each test case to clarify the situations being tested, like promoting via capture, forced promotions, or specific checks on the king.
- Covered scenarios where multiple promotion options exist, including promotions resulting in check or checkmate.
[testdata/castling.json]
- Added a new JSON file for castling test cases.
- Created test cases for all possible castling scenarios, including:
  - Normal castling for both sides.
  - Castling when prohibited due to an attacking piece (rook, bishop, knight, queen, king, and pawn).
  - Castling into or through check.
  - Castling when the king or rooks have moved.
- Implemented edge cases, including en passant and pawn-promotion interference with castling rights.
- Included transposed positions to test symmetry and equivalence in board states.
- Supported scenarios where attacks on rooks do not affect castling ability.
- Verified that castling remains allowed despite irrelevant pawn attacks or unoccupied intermediate squares.
[testdata/taxing.json]
- Added a large JSON file containing test cases and expected results for chess positions.
- Introduced different scenarios to analyze edge cases:
  - Positions with pieces in maximal quantity for a specific type (e.g., maximum knights, maximum queens, maximum bishops).
  - Transposed positions to verify symmetric behavior.
  - Special rules scenarios like prohibited en passant captures due to check.
- Organized data with a `description` for each scenario, `start` positions in FEN format, and corresponding `expected` moves with their resulting positions.
- Ensured diverse test coverage, including:
  - Positions designed to stress test move generation.
  - Positions covering intricate rules interactions like pawn structure scenarios (en passant).
  - Positions with multiple complexity levels (low-mobility vs high-mobility positions).
[testdata/pawns.json]
- Added a new JSON file defining test cases involving pawn positions in chess.
- The test cases cover various scenarios with en passant possibilities, moves without en passant, and full pawn setups.
- Each test scenario details the starting board position (FEN), a description, and expected moves along with the resulting FEN after each move.
- Includes mirrored or transposed versions of positions to test symmetry and edge cases.
- Scenarios test both legal and illegal moves, focusing on edge cases like restricted en passant and pawn movement logic.
- Ensures test coverage for complex pawn interactions, including rows with dense pawn positions and potential captures.
- Scenarios also evaluate king movements in relation to pawns.
- Emphasizes positions where rule-specific mechanics, like en passant, are critical to validate correctness.
[testdata/stalemates.json]
- Added a new JSON test data file for stalemate cases in chess.
- Included multiple test cases with starting positions (FEN notation) and expected results.
- Each test case represents a specific stalemate scenario or its transpose (mirrored version).
- Some descriptions provide details about positions, such as the shortest stalemate possible.
- Empty arrays are used for expected outputs, implying no further data is expected from these stalemate setups.
This commit is contained in:
MSWS
2025-03-22 17:46:26 -07:00
parent 9bb1cadac7
commit 1177bfa6bf
9 changed files with 11681 additions and 0 deletions

5194
testdata/castling.json vendored Normal file

File diff suppressed because it is too large Load Diff

64
testdata/checkmates.json vendored Normal file
View File

@@ -0,0 +1,64 @@
{
"testCases": [
{
"start": {
"fen": "1R3k2/2R5/8/8/8/1K6/8/8 b - - 0 1",
"description": "Black has been ladder-mated."
},
"expected": []
},
{
"description": "Transpose of 1R3k2/2R5/8/8/8/1K6/8/8 b - - 0 1",
"start": {
"fen": "8/8/1k6/8/8/8/2r5/1r3K2 w - - 0 1",
"description": "White has been ladder-mated."
},
"expected": []
},
{
"start": {
"fen": "8/6N1/3R4/6k1/5Pp1/1K2P3/8/4B1R1 b - f3 0 1",
"description": "Black can't capture en passant so is mated."
},
"expected": []
},
{
"description": "Transpose of 8/6N1/3R4/6k1/5Pp1/1K2P3/8/4B1R1 b - f3 0 1",
"start": {
"fen": "4b1r1/8/1k2p3/5pP1/6K1/3r4/6n1/8 w - f6 0 1",
"description": "White can't capture en passant so is mated."
},
"expected": []
},
{
"start": {
"fen": "kr6/ppN5/8/8/8/8/2K5/8 b - - 0 1",
"description": "Black has been smother mated."
},
"expected": []
},
{
"description": "Transpose of kr6/ppN5/8/8/8/8/2K5/8 b - - 0 1",
"start": {
"fen": "8/2k5/8/8/8/8/PPn5/KR6 w - - 0 1",
"description": "White has been smother mated."
},
"expected": []
},
{
"start": {
"fen": "k1K5/p1N5/8/8/8/8/8/8 b - - 0 1",
"description": "A little-known mate with minimal pieces."
},
"expected": []
},
{
"description": "Transpose of k1K5/p1N5/8/8/8/8/8/8 b - - 0 1",
"start": {
"fen": "8/8/8/8/8/8/P1n5/K1k5 w - - 0 1",
"description": "A little-known mate with minimal pieces."
},
"expected": []
}
]
}

390
testdata/famous.json vendored Normal file
View File

@@ -0,0 +1,390 @@
{
"description": "Some positions involving famous moves, that you wouldn't want your move generator to miss!",
"testCases": [
{
"start": {
"description": "From 'The Game of the Century' (Bobby Fischer v. Donald Byrne).",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P4PPP/3R1K1R w - - 4 18"
},
"expected": [
{
"move": "Rc1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P4PPP/2R2K1R b - - 5 18"
},
{
"move": "Rb1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P4PPP/1R3K1R b - - 5 18"
},
{
"move": "Ra1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P4PPP/R4K1R b - - 5 18"
},
{
"move": "Re1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P4PPP/4RK1R b - - 5 18"
},
{
"move": "Rd2",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P2R1PPP/5K1R b - - 5 18"
},
{
"move": "Rd3",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1nR1N2/P4PPP/5K1R b - - 5 18"
},
{
"move": "Ke1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P4PPP/3RK2R b - - 5 18"
},
{
"move": "Kg1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P4PPP/3R2KR b - - 5 18"
},
{
"move": "Rg1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P4PPP/3R1KR1 b - - 5 18"
},
{
"move": "g3",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2NP1/P4P1P/3R1K1R b - - 0 18"
},
{
"move": "g4",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP2P1/Q1n2N2/P4P1P/3R1K1R b - g3 0 18"
},
{
"move": "h3",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N1P/P4PP1/3R1K1R b - - 0 18"
},
{
"move": "h4",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP3P/Q1n2N2/P4PP1/3R1K1R b - h3 0 18"
},
{
"move": "Qb2",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/2n2N2/PQ3PPP/3R1K1R b - - 5 18"
},
{
"move": "Qc1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/2n2N2/P4PPP/2QR1K1R b - - 5 18"
},
{
"move": "Qb3",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/1Qn2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Qxc3",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/2Q2N2/P4PPP/3R1K1R b - - 0 18"
},
{
"move": "Qa4",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/Q1BP4/2n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Qa5",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/Q1B5/2BP4/2n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Qa6",
"fen": "r3r1k1/pp3pbp/Qqp1b1p1/2B5/2BP4/2n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Qxa7",
"fen": "r3r1k1/Qp3pbp/1qp1b1p1/2B5/2BP4/2n2N2/P4PPP/3R1K1R b - - 0 18"
},
{
"move": "Qb4",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/1QBP4/2n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Ne1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n5/P4PPP/3RNK1R b - - 5 18"
},
{
"move": "Ng1",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n5/P4PPP/3R1KNR b - - 5 18"
},
{
"move": "Nd2",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n5/P2N1PPP/3R1K1R b - - 5 18"
},
{
"move": "Nh4",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP3N/Q1n5/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Ne5",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B1N3/2BP4/Q1n5/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Ng5",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B3N1/2BP4/Q1n5/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Bb3",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/3P4/QBn2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Bd3",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/3P4/Q1nB1N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Be2",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2B5/3P4/Q1n2N2/P3BPPP/3R1K1R b - - 5 18"
},
{
"move": "Bb5",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/1BB5/3P4/Q1n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Ba6",
"fen": "r3r1k1/pp3pbp/Bqp1b1p1/2B5/3P4/Q1n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Bd5",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2BB4/3P4/Q1n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Bxe6",
"fen": "r3r1k1/pp3pbp/1qp1B1p1/2B5/3P4/Q1n2N2/P4PPP/3R1K1R b - - 0 18"
},
{
"move": "d5",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/2BP4/2B5/Q1n2N2/P4PPP/3R1K1R b - - 0 18"
},
{
"move": "Bb4",
"fen": "r3r1k1/pp3pbp/1qp1b1p1/8/1BBP4/Q1n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Bxb6",
"fen": "r3r1k1/pp3pbp/1Bp1b1p1/8/2BP4/Q1n2N2/P4PPP/3R1K1R b - - 0 18"
},
{
"move": "Bd6",
"fen": "r3r1k1/pp3pbp/1qpBb1p1/8/2BP4/Q1n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Be7",
"fen": "r3r1k1/pp2Bpbp/1qp1b1p1/8/2BP4/Q1n2N2/P4PPP/3R1K1R b - - 5 18"
},
{
"move": "Bf8",
"fen": "r3rBk1/pp3pbp/1qp1b1p1/8/2BP4/Q1n2N2/P4PPP/3R1K1R b - - 5 18"
}
]
},
{
"start": {
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 b - - 0 1",
"description": "The Gold Coins Game. Position before the Gold Coins move!"
},
"expected": [
{
"move": "Qb2",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/7r/PqP2PPP/5RK1 w - - 1 2"
},
{
"move": "Qa1",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/7r/P1P2PPP/q4RK1 w - - 1 2"
},
{
"move": "Qxc2",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/7r/P1q2PPP/5RK1 w - - 0 2"
},
{
"move": "Qd2",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/7r/P1Pq1PPP/5RK1 w - - 1 2"
},
{
"move": "Qe1",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/7r/P1P2PPP/4qRK1 w - - 1 2"
},
{
"move": "Qb3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/1q5r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Qa3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/q6r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Qd3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/3q3r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Qe3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/4q2r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Qf3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/5q1r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Qg3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/6qr/P1P2PPP/5RK1 w - - 1 2",
"description": "The Gold Coins move!"
},
{
"move": "Qb4",
"fen": "5rk1/pp4pp/4p3/2R3Q1/1q1n4/7r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Qa5",
"fen": "5rk1/pp4pp/4p3/q1R3Q1/3n4/7r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Qc4",
"fen": "5rk1/pp4pp/4p3/2R3Q1/2qn4/7r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Qxc5",
"fen": "5rk1/pp4pp/4p3/2q3Q1/3n4/7r/P1P2PPP/5RK1 w - - 0 2"
},
{
"move": "Rxh2",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/2q5/P1P2PPr/5RK1 w - - 0 2"
},
{
"move": "Rg3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/2q3r1/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rhf3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/2q2r2/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Re3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/2q1r3/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rd3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n4/2qr4/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rh4",
"fen": "5rk1/pp4pp/4p3/2R3Q1/3n3r/2q5/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rh5",
"fen": "5rk1/pp4pp/4p3/2R3Qr/3n4/2q5/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rh6",
"fen": "5rk1/pp4pp/4p2r/2R3Q1/3n4/2q5/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Nxc2",
"fen": "5rk1/pp4pp/4p3/2R3Q1/8/2q4r/P1n2PPP/5RK1 w - - 0 2"
},
{
"move": "Ne2+",
"fen": "5rk1/pp4pp/4p3/2R3Q1/8/2q4r/P1P1nPPP/5RK1 w - - 1 2"
},
{
"move": "Nb3",
"fen": "5rk1/pp4pp/4p3/2R3Q1/8/1nq4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Nf3+",
"fen": "5rk1/pp4pp/4p3/2R3Q1/8/2q2n1r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Nb5",
"fen": "5rk1/pp4pp/4p3/1nR3Q1/8/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Nf5",
"fen": "5rk1/pp4pp/4p3/2R2nQ1/8/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Nc6",
"fen": "5rk1/pp4pp/2n1p3/2R3Q1/8/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "e5",
"fen": "5rk1/pp4pp/8/2R1p1Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 0 2"
},
{
"move": "a6",
"fen": "5rk1/1p4pp/p3p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 0 2"
},
{
"move": "a5",
"fen": "5rk1/1p4pp/4p3/p1R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - a6 0 2"
},
{
"move": "b6",
"fen": "5rk1/p5pp/1p2p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 0 2"
},
{
"move": "b5",
"fen": "5rk1/p5pp/4p3/1pR3Q1/3n4/2q4r/P1P2PPP/5RK1 w - b6 0 2"
},
{
"move": "g6",
"fen": "5rk1/pp5p/4p1p1/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 0 2"
},
{
"move": "h6",
"fen": "5rk1/pp4p1/4p2p/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 0 2"
},
{
"move": "h5",
"fen": "5rk1/pp4p1/4p3/2R3Qp/3n4/2q4r/P1P2PPP/5RK1 w - h6 0 2"
},
{
"move": "Rf7",
"fen": "6k1/pp3rpp/4p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rf6",
"fen": "6k1/pp4pp/4pr2/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rf5",
"fen": "6k1/pp4pp/4p3/2R2rQ1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rf4",
"fen": "6k1/pp4pp/4p3/2R3Q1/3n1r2/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rff3",
"fen": "6k1/pp4pp/4p3/2R3Q1/3n4/2q2r1r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rxf2",
"fen": "6k1/pp4pp/4p3/2R3Q1/3n4/2q4r/P1P2rPP/5RK1 w - - 0 2"
},
{
"move": "Re8",
"fen": "4r1k1/pp4pp/4p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rd8",
"fen": "3r2k1/pp4pp/4p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rc8",
"fen": "2r3k1/pp4pp/4p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Rb8",
"fen": "1r4k1/pp4pp/4p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Ra8",
"fen": "r5k1/pp4pp/4p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Kf7",
"fen": "5r2/pp3kpp/4p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
},
{
"move": "Kh8",
"fen": "5r1k/pp4pp/4p3/2R3Q1/3n4/2q4r/P1P2PPP/5RK1 w - - 1 2"
}
]
}
]
}

95
testdata/integration_test.go vendored Normal file
View File

@@ -0,0 +1,95 @@
package testdata
import (
"encoding/json"
"io"
"os"
"strings"
"testing"
"github.com/msws/chess/board"
)
type TestData struct {
Description string `json:"description"`
Tests []TestCases `json:"testCases"`
}
type TestCases struct {
Start TestStart `json:"start"`
Expected []TestExpectation `json:"expected"`
}
type TestStart struct {
Fen string `json:"fen"`
Description string `json:"description"`
}
type TestExpectation struct {
Move string `json:"move"`
Fen string `json:"fen"`
}
func TestJSON(t *testing.T) {
data, err := os.ReadDir(".")
if err != nil {
t.Fatal(err)
}
for _, entry := range data {
t.Run(entry.Name(), func(t *testing.T) {
jsonData, err := os.Open(entry.Name())
if err != nil {
t.Error(err)
return
}
defer jsonData.Close()
byteData, err := io.ReadAll(jsonData)
if err != nil {
t.Error(err)
return
}
testJson(t, byteData)
})
}
}
func testJson(t *testing.T, bytes []byte) {
var data TestData
json.Unmarshal(bytes, &data)
t.Log(data.Description)
testData(t, data)
}
func testData(t *testing.T, data TestData) {
for _, test := range data.Tests {
t.Log(test.Start.Description)
t.Run(strings.ReplaceAll(test.Start.Fen, "/", "."), func(t *testing.T) {
testCase(t, test.Start, test.Expected)
})
}
}
func testCase(t *testing.T, start TestStart, expected []TestExpectation) {
t.Run("MoveCount", func(t *testing.T) {
testMoveCount(t, start, expected)
})
}
func testMoveCount(t *testing.T, start TestStart, expected []TestExpectation) {
board, err := board.FromFEN(start.Fen)
if err != nil {
t.Error(err)
return
}
moves := board.GetMoves()
if len(moves) != len(expected) {
t.Errorf("expected %d moves, got %d", len(expected), len(moves))
}
}

305
testdata/pawns.json vendored Normal file
View File

@@ -0,0 +1,305 @@
{
"description": "Test cases involving pawns.",
"testCases": [
{
"start": {
"fen": "7k/8/8/8/pPp5/8/8/7K b - b3 0 1",
"description": "Two possible en passant capture possibilities."
},
"expected": [
{
"move": "a3",
"fen": "7k/8/8/8/1Pp5/p7/8/7K w - - 0 2"
},
{
"move": "axb3",
"fen": "7k/8/8/8/2p5/1p6/8/7K w - - 0 2"
},
{
"move": "c3",
"fen": "7k/8/8/8/pP6/2p5/8/7K w - - 0 2"
},
{
"move": "cxb3",
"fen": "7k/8/8/8/p7/1p6/8/7K w - - 0 2"
},
{
"move": "Kg7",
"fen": "8/6k1/8/8/pPp5/8/8/7K w - - 1 2"
},
{
"move": "Kh7",
"fen": "8/7k/8/8/pPp5/8/8/7K w - - 1 2"
},
{
"move": "Kg8",
"fen": "6k1/8/8/8/pPp5/8/8/7K w - - 1 2"
}
]
},
{
"description": "Transpose of 7k/8/8/8/pPp5/8/8/7K b - b3 0 1",
"start": {
"fen": "7k/8/8/PpP5/8/8/8/7K w - b6 0 1",
"description": "Two possible en passant capture possibilities."
},
"expected": [
{
"move": "Kg1",
"fen": "7k/8/8/PpP5/8/8/8/6K1 b - - 1 1"
},
{
"move": "Kg2",
"fen": "7k/8/8/PpP5/8/8/6K1/8 b - - 1 1"
},
{
"move": "Kh2",
"fen": "7k/8/8/PpP5/8/8/7K/8 b - - 1 1"
},
{
"move": "a6",
"fen": "7k/8/P7/1pP5/8/8/8/7K b - - 0 1"
},
{
"move": "axb6",
"fen": "7k/8/1P6/2P5/8/8/8/7K b - - 0 1"
},
{
"move": "c6",
"fen": "7k/8/2P5/Pp6/8/8/8/7K b - - 0 1"
},
{
"move": "cxb6",
"fen": "7k/8/1P6/P7/8/8/8/7K b - - 0 1"
}
]
},
{
"start": {
"fen": "7k/8/8/8/pPp5/8/8/7K b - - 0 1",
"description": "Same position but without the en passant capture possibilities."
},
"expected": [
{
"move": "a3",
"fen": "7k/8/8/8/1Pp5/p7/8/7K w - - 0 2"
},
{
"move": "c3",
"fen": "7k/8/8/8/pP6/2p5/8/7K w - - 0 2"
},
{
"move": "Kg7",
"fen": "8/6k1/8/8/pPp5/8/8/7K w - - 1 2"
},
{
"move": "Kh7",
"fen": "8/7k/8/8/pPp5/8/8/7K w - - 1 2"
},
{
"move": "Kg8",
"fen": "6k1/8/8/8/pPp5/8/8/7K w - - 1 2"
}
]
},
{
"description": "Transpose of 7k/8/8/8/pPp5/8/8/7K b - - 0 1",
"start": {
"fen": "7k/8/8/PpP5/8/8/8/7K w - - 0 1",
"description": "Same position but without the en passant capture possibilities."
},
"expected": [
{
"move": "Kg1",
"fen": "7k/8/8/PpP5/8/8/8/6K1 b - - 1 1"
},
{
"move": "Kg2",
"fen": "7k/8/8/PpP5/8/8/6K1/8 b - - 1 1"
},
{
"move": "Kh2",
"fen": "7k/8/8/PpP5/8/8/7K/8 b - - 1 1"
},
{
"move": "a6",
"fen": "7k/8/P7/1pP5/8/8/8/7K b - - 0 1"
},
{
"move": "c6",
"fen": "7k/8/2P5/Pp6/8/8/8/7K b - - 0 1"
}
]
},
{
"start": {
"fen": "8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/8/5K2/8 w - - 0 1",
"description": "Full set of pawns but no moves."
},
"expected": [
{
"move": "Ke1",
"fen": "8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/8/8/4K3 b - - 1 1"
},
{
"move": "Kf1",
"fen": "8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/8/8/5K2 b - - 1 1"
},
{
"move": "Kg1",
"fen": "8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/8/8/6K1 b - - 1 1"
},
{
"move": "Ke2",
"fen": "8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/8/4K3/8 b - - 1 1"
},
{
"move": "Kg2",
"fen": "8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/8/6K1/8 b - - 1 1"
},
{
"move": "Ke3",
"fen": "8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/4K3/8/8 b - - 1 1"
},
{
"move": "Kf3",
"fen": "8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/5K2/8/8 b - - 1 1"
},
{
"move": "Kg3",
"fen": "8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/6K1/8/8 b - - 1 1"
}
]
},
{
"description": "Transpose of 8/4k3/1p1p1p1p/pPpPpPpP/P1P1P1P1/8/5K2/8 w - - 0 1",
"start": {
"fen": "8/5k2/8/p1p1p1p1/PpPpPpPp/1P1P1P1P/4K3/8 b - - 0 1",
"description": "Full set of pawns but no moves."
},
"expected": [
{
"move": "Ke6",
"fen": "8/8/4k3/p1p1p1p1/PpPpPpPp/1P1P1P1P/4K3/8 w - - 1 2"
},
{
"move": "Kf6",
"fen": "8/8/5k2/p1p1p1p1/PpPpPpPp/1P1P1P1P/4K3/8 w - - 1 2"
},
{
"move": "Kg6",
"fen": "8/8/6k1/p1p1p1p1/PpPpPpPp/1P1P1P1P/4K3/8 w - - 1 2"
},
{
"move": "Ke7",
"fen": "8/4k3/8/p1p1p1p1/PpPpPpPp/1P1P1P1P/4K3/8 w - - 1 2"
},
{
"move": "Kg7",
"fen": "8/6k1/8/p1p1p1p1/PpPpPpPp/1P1P1P1P/4K3/8 w - - 1 2"
},
{
"move": "Ke8",
"fen": "4k3/8/8/p1p1p1p1/PpPpPpPp/1P1P1P1P/4K3/8 w - - 1 2"
},
{
"move": "Kf8",
"fen": "5k2/8/8/p1p1p1p1/PpPpPpPp/1P1P1P1P/4K3/8 w - - 1 2"
},
{
"move": "Kg8",
"fen": "6k1/8/8/p1p1p1p1/PpPpPpPp/1P1P1P1P/4K3/8 w - - 1 2"
}
]
},
{
"start": {
"fen": "8/8/4k3/8/2pPp3/8/B7/7K b - d3 0 1",
"description": "Black has two en passant possibilities, but only one is legal."
},
"expected": [
{
"move": "e3",
"fen": "8/8/4k3/8/2pP4/4p3/B7/7K w - - 0 2"
},
{
"move": "exd3",
"fen": "8/8/4k3/8/2p5/3p4/B7/7K w - - 0 2"
},
{
"move": "Kd5",
"fen": "8/8/8/3k4/2pPp3/8/B7/7K w - - 1 2"
},
{
"move": "Kf5",
"fen": "8/8/8/5k2/2pPp3/8/B7/7K w - - 1 2"
},
{
"move": "Kd6",
"fen": "8/8/3k4/8/2pPp3/8/B7/7K w - - 1 2"
},
{
"move": "Kf6",
"fen": "8/8/5k2/8/2pPp3/8/B7/7K w - - 1 2"
},
{
"move": "Kd7",
"fen": "8/3k4/8/8/2pPp3/8/B7/7K w - - 1 2"
},
{
"move": "Ke7",
"fen": "8/4k3/8/8/2pPp3/8/B7/7K w - - 1 2"
},
{
"move": "Kf7",
"fen": "8/5k2/8/8/2pPp3/8/B7/7K w - - 1 2"
}
]
},
{
"description": "Transpose of 8/8/4k3/8/2pPp3/8/B7/7K b - d3 0 1",
"start": {
"fen": "7k/b7/8/2PpP3/8/4K3/8/8 w - d6 0 1",
"description": "White has two en passant possibilities, but only one is legal."
},
"expected": [
{
"move": "Kd2",
"fen": "7k/b7/8/2PpP3/8/8/3K4/8 b - - 1 1"
},
{
"move": "Ke2",
"fen": "7k/b7/8/2PpP3/8/8/4K3/8 b - - 1 1"
},
{
"move": "Kf2",
"fen": "7k/b7/8/2PpP3/8/8/5K2/8 b - - 1 1"
},
{
"move": "Kd3",
"fen": "7k/b7/8/2PpP3/8/3K4/8/8 b - - 1 1"
},
{
"move": "Kf3",
"fen": "7k/b7/8/2PpP3/8/5K2/8/8 b - - 1 1"
},
{
"move": "Kd4",
"fen": "7k/b7/8/2PpP3/3K4/8/8/8 b - - 1 1"
},
{
"move": "Kf4",
"fen": "7k/b7/8/2PpP3/5K2/8/8/8 b - - 1 1"
},
{
"move": "e6",
"fen": "7k/b7/4P3/2Pp4/8/4K3/8/8 b - - 0 1"
},
{
"move": "exd6",
"fen": "7k/b7/3P4/2P5/8/4K3/8/8 b - - 0 1"
}
]
}
]
}

1317
testdata/promotions.json vendored Normal file

File diff suppressed because it is too large Load Diff

87
testdata/stalemates.json vendored Normal file
View File

@@ -0,0 +1,87 @@
{
"testCases": [
{
"start": {
"fen": "k7/1R6/2K5/8/8/8/8/8 b - - 0 1"
},
"expected": []
},
{
"description": "Transpose of k7/1R6/2K5/8/8/8/8/8 b - - 0 1",
"start": {
"fen": "8/8/8/8/8/2k5/1r6/K7 w - - 0 1"
},
"expected": []
},
{
"start": {
"fen": "k7/8/2N5/8/8/2K5/1R6/8 b - - 0 1"
},
"expected": []
},
{
"description": "Transpose of k7/8/2N5/8/8/2K5/1R6/8 b - - 0 1",
"start": {
"fen": "8/1r6/2k5/8/8/2n5/8/K7 w - - 0 1"
},
"expected": []
},
{
"start": {
"fen": "k7/2Q5/8/8/8/2K5/8/8 b - - 0 1"
},
"expected": []
},
{
"description": "Transpose of k7/2Q5/8/8/8/2K5/8/8 b - - 0 1",
"start": {
"fen": "8/8/2k5/8/8/8/2q5/K7 w - - 0 1"
},
"expected": []
},
{
"start": {
"fen": "8/8/5R2/4k1P1/3R4/2K5/8/8 b - - 0 1",
"description": ""
},
"expected": []
},
{
"description": "Transpose of 8/8/5R2/4k1P1/3R4/2K5/8/8 b - - 0 1",
"start": {
"fen": "8/8/2k5/3r4/4K1p1/5r2/8/8 w - - 0 1",
"description": ""
},
"expected": []
},
{
"start": {
"fen": "5bnr/4p1pq/4Qpkr/7p/7P/4P3/PPPP1PP1/RNB1KBNR b KQ - 2 10",
"description": "The shortest stalemate possible from the opening position."
},
"expected": []
},
{
"description": "Transpose of 5bnr/4p1pq/4Qpkr/7p/7P/4P3/PPPP1PP1/RNB1KBNR b KQ - 2 10",
"start": {
"fen": "rnb1kbnr/pppp1pp1/4p3/7p/7P/4qPKR/4P1PQ/5BNR w kq - 2 10",
"description": "The shortest stalemate possible from the opening position."
},
"expected": []
},
{
"start": {
"fen": "8/8/R7/4k3/4Pp2/2P2P2/7B/1K6 b - e3 0 1",
"description": ""
},
"expected": []
},
{
"description": "Transpose of 8/8/R7/4k3/4Pp2/2P2P2/7B/1K6 b - e3 0 1",
"start": {
"fen": "1k6/7b/2p2p2/4pP2/4K3/r7/8/8 w - e6 0 1"
},
"expected": []
}
]
}

221
testdata/standard.json vendored Normal file
View File

@@ -0,0 +1,221 @@
{
"description": "Some common positions.",
"testCases": [
{
"start": {
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"description": "Standard starting position."
},
"expected": [
{
"move": "Na3",
"fen": "rnbqkbnr/pppppppp/8/8/8/N7/PPPPPPPP/R1BQKBNR b KQkq - 1 1"
},
{
"move": "Nc3",
"fen": "rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq - 1 1"
},
{
"move": "Nf3",
"fen": "rnbqkbnr/pppppppp/8/8/8/5N2/PPPPPPPP/RNBQKB1R b KQkq - 1 1"
},
{
"move": "Nh3",
"fen": "rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1"
},
{
"move": "a3",
"fen": "rnbqkbnr/pppppppp/8/8/8/P7/1PPPPPPP/RNBQKBNR b KQkq - 0 1"
},
{
"move": "a4",
"fen": "rnbqkbnr/pppppppp/8/8/P7/8/1PPPPPPP/RNBQKBNR b KQkq a3 0 1"
},
{
"move": "b3",
"fen": "rnbqkbnr/pppppppp/8/8/8/1P6/P1PPPPPP/RNBQKBNR b KQkq - 0 1"
},
{
"move": "b4",
"fen": "rnbqkbnr/pppppppp/8/8/1P6/8/P1PPPPPP/RNBQKBNR b KQkq b3 0 1"
},
{
"move": "c3",
"fen": "rnbqkbnr/pppppppp/8/8/8/2P5/PP1PPPPP/RNBQKBNR b KQkq - 0 1"
},
{
"move": "c4",
"fen": "rnbqkbnr/pppppppp/8/8/2P5/8/PP1PPPPP/RNBQKBNR b KQkq c3 0 1"
},
{
"move": "d3",
"fen": "rnbqkbnr/pppppppp/8/8/8/3P4/PPP1PPPP/RNBQKBNR b KQkq - 0 1"
},
{
"move": "d4",
"fen": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq d3 0 1"
},
{
"move": "e3",
"fen": "rnbqkbnr/pppppppp/8/8/8/4P3/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
},
{
"move": "e4",
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
},
{
"move": "f3",
"fen": "rnbqkbnr/pppppppp/8/8/8/5P2/PPPPP1PP/RNBQKBNR b KQkq - 0 1"
},
{
"move": "f4",
"fen": "rnbqkbnr/pppppppp/8/8/5P2/8/PPPPP1PP/RNBQKBNR b KQkq f3 0 1"
},
{
"move": "g3",
"fen": "rnbqkbnr/pppppppp/8/8/8/6P1/PPPPPP1P/RNBQKBNR b KQkq - 0 1"
},
{
"move": "g4",
"fen": "rnbqkbnr/pppppppp/8/8/6P1/8/PPPPPP1P/RNBQKBNR b KQkq g3 0 1"
},
{
"move": "h3",
"fen": "rnbqkbnr/pppppppp/8/8/8/7P/PPPPPPP1/RNBQKBNR b KQkq - 0 1"
},
{
"move": "h4",
"fen": "rnbqkbnr/pppppppp/8/8/7P/8/PPPPPPP1/RNBQKBNR b KQkq h3 0 1"
}
]
},
{
"start": {
"fen": "r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R b - - 11 7",
"description": "Ruy Lopez opening."
},
"expected": [
{
"move": "Nb4",
"fen": "r1bqkbnr/pppp1ppp/8/1B2p3/1n2P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Nd4",
"fen": "r1bqkbnr/pppp1ppp/8/1B2p3/3nP3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Na5",
"fen": "r1bqkbnr/pppp1ppp/8/nB2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Nce7",
"fen": "r1bqkbnr/ppppnppp/8/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Nb8",
"fen": "rnbqkbnr/pppp1ppp/8/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "a6",
"fen": "r1bqkbnr/1ppp1ppp/p1n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 0 8"
},
{
"move": "a5",
"fen": "r1bqkbnr/1ppp1ppp/2n5/pB2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - a6 0 8"
},
{
"move": "b6",
"fen": "r1bqkbnr/p1pp1ppp/1pn5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 0 8"
},
{
"move": "d6",
"fen": "r1bqkbnr/ppp2ppp/2np4/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 0 8"
},
{
"move": "d5",
"fen": "r1bqkbnr/ppp2ppp/2n5/1B1pp3/4P3/5N2/PPPP1PPP/RNBQK2R w - d6 0 8"
},
{
"move": "f6",
"fen": "r1bqkbnr/pppp2pp/2n2p2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 0 8"
},
{
"move": "f5",
"fen": "r1bqkbnr/pppp2pp/2n5/1B2pp2/4P3/5N2/PPPP1PPP/RNBQK2R w - f6 0 8"
},
{
"move": "g6",
"fen": "r1bqkbnr/pppp1p1p/2n3p1/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 0 8"
},
{
"move": "g5",
"fen": "r1bqkbnr/pppp1p1p/2n5/1B2p1p1/4P3/5N2/PPPP1PPP/RNBQK2R w - g6 0 8"
},
{
"move": "h6",
"fen": "r1bqkbnr/pppp1pp1/2n4p/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 0 8"
},
{
"move": "h5",
"fen": "r1bqkbnr/pppp1pp1/2n5/1B2p2p/4P3/5N2/PPPP1PPP/RNBQK2R w - h6 0 8"
},
{
"move": "Rb8",
"fen": "1rbqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Qe7",
"fen": "r1b1kbnr/ppppqppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Qf6",
"fen": "r1b1kbnr/pppp1ppp/2n2q2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Qg5",
"fen": "r1b1kbnr/pppp1ppp/2n5/1B2p1q1/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Qh4",
"fen": "r1b1kbnr/pppp1ppp/2n5/1B2p3/4P2q/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Ke7",
"fen": "r1bq1bnr/ppppkppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Be7",
"fen": "r1bqk1nr/ppppbppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Bd6",
"fen": "r1bqk1nr/pppp1ppp/2nb4/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Bc5",
"fen": "r1bqk1nr/pppp1ppp/2n5/1Bb1p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Bb4",
"fen": "r1bqk1nr/pppp1ppp/2n5/1B2p3/1b2P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Ba3",
"fen": "r1bqk1nr/pppp1ppp/2n5/1B2p3/4P3/b4N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Nf6",
"fen": "r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Nh6",
"fen": "r1bqkb1r/pppp1ppp/2n4n/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
},
{
"move": "Nge7",
"fen": "r1bqkb1r/ppppnppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w - - 12 8"
}
]
}
]
}

4008
testdata/taxing.json vendored Normal file

File diff suppressed because it is too large Load Diff