Coder Social home page Coder Social logo

shaack / cm-pgn Goto Github PK

View Code? Open in Web Editor NEW
26.0 2.0 18.0 179 KB

Parse and create PGNs (Portable Game Notation for chess games)

License: MIT License

JavaScript 90.75% Shell 0.17% HTML 1.07% PEG.js 8.00%
chess pgn parser javascript es6 chessmail released

cm-pgn's Introduction

cm-pgn

Parser for PGNs (Portable Game Notation)

This is as ES6 Module for parsing and rendering of PGNs (Portable Game Notation).

The API is similar to history() of chess.js, but this module supports variations, nags and comments in the pgn.

I used the grammar file from PgnViewerJS of mliebelt to create the parser.

Install

npm install cm-pgn

Usage

Use the Pgn class as JS Module:

<script type="module">
    import {Pgn} from "./PATH/TO/cm-pgn/src/Pgn.js"
    // parse pgn
    const pgn = new Pgn(`[Site "Berlin"]
[Date "1989.07.02"]
[White "Haack, Stefan"]
[Black "Maier, Karsten"]

1. e4 e5 (e6) 2. Nf3 $1 {Great move!} Nc6 *`)
</script>

Pgn constructor

constructor(pgnString = "", props = {})

if you set { sloppy: true } in props, some non-standard move notations will be accepted. See also .move(move, options) from chess.js.

Data structure

The pgn has a pgn.header and a pgn.history.

pgn.header

The header holds the PGN header elements in the key value object tags.

pgn.header.tags = {
    Site: "Berlin",
    Date: "1989.07.02",
    White: "Haack, Stefan",
    Black: "Maier, Karsten"
}

pgn.history

The moves are stored in an array. Every element of that array has the following structure

pgn.history.moves[i] = {
    color: "w", // the moving color
    fen: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1", // the fen after that move
    flags: "b", // the flags, like described below
    from: "e2", // the square from
    next: {color: "b", from: "e7", to: "e6", flags: "n", piece: "p", /*…*/}, // a pointer to the next move 
    piece: "p", // the piece type 
    ply: 1, // the ply number
    previous: undefined, // a pointer to the previous move
    san: "e4", // the move in SAN notation
    to: "e4", // the square to
    uci: "e2e4", // the move in UCI notation
    variation: (4) [{/*…*/}, {/*…*/}, {/*…*/}, {/*…*/}], // a pointer to the begin of the current variation
    variations: [] // all variations starting with that move
}

pgn.history.moves[i].flags

  • 'n' - a non-capture
  • 'b' - a pawn push of two squares
  • 'e' - an en passant capture
  • 'c' - a standard capture
  • 'p' - a promotion
  • 'k' - kingside castling
  • 'q' - queenside castling

pgn.history.moves[i].piece

  • 'p' - pawn
  • 'n' - knight
  • 'b' - bishop
  • 'r' - root
  • 'q' - queen
  • 'k' - king

Examples

const history = pgn.history
assert.equal(4, history.moves.length)
assert.equal(history.moves[0].san, "e4")
assert.equal(history.moves[1].variations.length, 1)
assert.equal(history.moves[1].variations[0][0].san, "e6")
assert.equal(history.moves[2].nag, "$1")
assert.equal(history.moves[2].commentAfter, "Great move!")
assert.equal(history.moves[2].fen, "rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2")
assert.equal(history.moves[3].from, "b8")
assert.equal(history.moves[3].to, "c6")
assert.equal(history.moves[3].uci, "b8c6")
assert.equal(history.moves[3].san, "Nc6")
assert.equal(history.moves[3].previous.san, "Nf3")
assert.equal(history.moves[3].previous.next.san, "Nc6")

Development

This module uses PEG.js for parser generation. The parser (pgnParser.js) in src/cm-pgn/parser/ is generated from the grammar file src/grammar/pgn.pegjs.

To recreate the parser after modification of src/grammar/pgn.pegjs, run bin/generate-parser.sh.

Testing

Run the unit tests

External Links

cm-pgn's People

Contributors

atlndao avatar donkawechico avatar goodvibs avatar hahalosah avatar shaack avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

cm-pgn's Issues

How to run unit tests?

I've converted this package to Typescript in my fork, but I'm not sure how to run the unit tests to check it. The npm test script seems to suggest that I should just open test/index.html, but when I do that, I see errors like:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at file:///Users/jackstenglein/Documents/projects/cm-pgn/test/PgnList.test.js. (Reason: CORS request not http).

Also, if I can verify that the tests still pass, would you be interested in a PR for the Typescript conversion?

Parsing fails on PGN with multiple comments after final move

Thank you again for this stellar package. It seems PGNs with multiple comments after the final move cannot be parsed. The following PGN throws a syntax error on parse:

[Result "*"]

1. d4 Nf6 { comment 1 } { comment 2 } *

As far as I can tell, the PGN is according to the specification. The following parses properly:

[Result "*"]

1. d4 Nf6 { comment 1 } { comment 2 } 2. Bg5 *

If you're interested, I'd be happy to look into the grammar and write a PR.

Documentation

It would be nice to have some basic installation/usage documentation, including information how do you use it in the browser.

Allow rendering of PGNs with variations

The method pgn.toString() should render a valid PGN.

For this it is necessary to implement the toString()method for Header and History.

Therefore Header and History should remain as Objects and store the related data (not only export the parseHeader/parseHistory function).

The History-Object should keep the history-data, the Header-Object should keep the header tags.

Then it is possible to create "toString()" functions for the Header and the History Object and also to use these Objects independent of the Pgn Object. (Header renders the header and History renders the history on toString().

Result: Pgn.js renders the pgn on toString() as
header.toString() + "\n" + history.toString()

The first move of a variation over-writes the previous move's "next" pointer to point to the alternate line rather than its own line

Overview

A bit tricky to describe so I've written a unit test to make it a bit clearer:

    it('should create history with correct next for main line moves when variations present', () => {
        const pgn = new Pgn(`[SetUp "1"]

            1. e4 e5 2. Nc3 (2. Nf3 Bc5) 2... Nc6 *`)

        const ply2_main_line = pgn.history.moves[1];
        const ply3_main_line = pgn.history.moves[2];
        const ply3_variation_line = pgn.history.moves[2].variations[0][0];

        assert.equal(ply2_main_line.next, ply3_variation_line); // <--- This is currently passing (and shouldn't, I think)
        assert.equal(ply2_main_line.next, ply3_main_line); // <--- This should pass but is failing
    })

Expectation

Each move in a move.variation array should have its next (and previous) pointers be consistent with what is actually previous and next in its variation array.

For example. If someMove.variation = [move1, move2, move3, move4], then move1.next should be move2, move2.next should be move3, and so on regardless of whether a given move in that line is a branching point.

Actual

If a move has a variations array of arrays, then the first move of one of those variations over-writes the previous line's last move to have its "next" pointer point to the alternate line rather than its own line.

Suggested Fix

I have a PR I've pushed (#21) that fixes the issue by changing traverse to check whether a given move already has a previous or next pointer before setting it.

Uncaught peg$SyntaxError

Hello, I'm getting this error message :

Uncaught peg$SyntaxError {message: "Expected " ", "*", "0-1", "0:1", "1-0", "1/2-1/2",…], [a-h], end of input, or integer but "[" found.", expected: Array(44), found: "[", location: {…}, name: "SyntaxError", …}

Code:

<script type="module">
  import {Pgn} from "/Pgn.js";
  const pgnvv = new Pgn(PGN);
console.log(pgnvv.history());
</script>

PGN:

[Event "?"]
[Site "?"]
[Date "????.??.??"]
[Round "?"]
[White "?"]
[Black "?"]
[Result "*"]
[FEN "r3kb1r/pbq2ppp/2p2n2/1pn1pP2/4P1P1/2NB1Q2/PPPBN2P/R3K2R w KQkq - 0 1"]
[Setup "1"]

1. a4 { [%csl Rh1,Ra8,Gf6,Gc5,Gc3,Gb5,Yb4,Yb7,Yc6,Ra1,Rg2,Rf3,Re4,Rd5][%cal
Gf6e4,Gc5e4,Gc3d1,Gd1f2,Rb7e4,Re4h1,Rg4g5,Rf6d7]}  ( 1. g5)1... b4 2. Nd1 Nfxe4
3. Bxe4 Nxe4 4. Qxe4 c5 5. Qc4 Bxh1 6. Bxb4 Be7 7. Ne3 O-O 8. O-O-O Bf3 9. Nd5
Qb7 10. f6 Bxd5 11. Qxd5 Qxd5 12. Rxd5 gxf6 13. Bxc5 Rfd8 14. Rxd8+ Bxd8 15. b4
h5 16. h3 hxg4 17. hxg4 Kg7 18. Ng3 Bb6 19. Be7 Kg6 20. c4 Bf2 21. Nf5 Kg5 22.
Nd6 Kxg4 23. c5 Kf3 24. c6 e4 25. c7 Bd4 26. Nb5 Be5 27. Bd6 e3 28. Bxe5 fxe5
29. Nd6 Kf2  *

When I strip all the headers manually I get :

IllegalMoveException {fen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", notation: "g5", toString: ƒ}

But g5 is a variation, not an illegal move.

Add Multiple variations to history fails with invalid move error

Hi

if you try to add more than 3 variations to a move it will fail with the invalid move error.

should add a variant and render it => fail
Error: invalid move

these moves are valid I tested them on lichess.

from the History test I tried to change the history test like this

    it("should add a variant and render it", () => {
    const history = new History();
    const ply1 = history.addMove("e4");
    history.addMove("e6");
    history.addMove("d3");
    history.addMove("d5");
    history.addMove("Nd2");

    history.addMove("e5", ply1);
    history.addMove("f3", ply1);
    history.addMove("e6", ply1);
    history.addMove("Ne2", ply1);
    assert.equals(history.moves[1].variations.length, 4);
  });

if I manage to fix this issue I will post the solution here.
also, I can send a pull request for render variations.

IllegalMoveException on legal move

Hi, I'm working on building my own PGN viewer and this package looks like it would be really helpful. I am getting an IllegalMoveException on what seems to be a legal move, however.

Here is the PGN:

[Event "?"]
[Site "?"]
[Date "?"]
[Round "?"]
[White "?"]
[Black "?"]
[Result "*"]
[ECO "C25"]
[Annotator "?"]
[PlyCount "25"]
[EventDate "?"]
[SourceDate "?"]

1. e4 e5 2. Nc3 Nc6 3. f4 (3.
Bc4 Nf6 (3... Bc5 4. Qg4
Qf6 (4... g6 5. Qf3 Nf6 6. Nge2 d6 7. d3 Bg4 8. Qg3 {[%cal Rc1g5]}) 5. Nd5
Qxf2+ 6. Kd1 Kf8 7. Nh3 Qd4 8. d3 d6 9. Qf3 Bxh3 10. Rf1 Be6 11. c3) 4. d3 Na5
(4... Bb4 5. Nge2 d5 (5... O-O 6. O-O d6 (6... h6 7.
a3 Be7 8. f4) 7. Bg5) 6. exd5 Nxd5 7. O-O
Be6 8. a3 Bxc3 9. bxc3 O-O 10. a4) (4... Bc5 5. f4 d6 6. Nf3 Ng4 (6... Bg4
7. Na4 O-O 8. Nxc5 dxc5 9. O-O Nd4 10. c3 Nxf3+ 11. gxf3
Bh3 12. Rf2 exf4 13. Bxf4) (6... O-O 7. Na4 Bg4 8. Nxc5 dxc5 9. O-O Qd6 10. Qd2
Bxf3 11. gxf3 Rad8 12. Kh1 Nh5 13. fxe5 Nxe5 14. Qg5 Nxc4 15. Qxh5 Ne5 16. f4)
7. Ng5 O-O 8. f5 Nf2 9.
Qh5) 5. Qf3 Nxc4 6. dxc4 Bc5 (6... d6 7. h3 Be6 8. b3 Be7 9. Nge2 O-O 10. O-O)
7. Be3 Bxe3 8. Qxe3 O-O 9. h3 d6 10. b3 Be6 11. Nge2) 3... exf4 (3... d6 4. Nf3 Bg4 5. Bb5
a6 6. Bxc6+ bxc6 7. h3 Bxf3 8. Qxf3) (3... Nf6 4. fxe5 Nxe5 5. d4) (3... Bc5 4.
Nf3 d6 5. Bb5 Nge7 6. Na4) 4. Nf3 g5 (4... d6 5.
d4 g5 6. d5 Ne5 7. Bb5+ Bd7 8. Bxd7+ Nxd7 9. h4 g4 10. Nd4 Qf6 11. Ncb5) 5. h4 
(5. d4 g4 6. Bc4 gxf3 7. O-O 
(7. Qxf3 Qh4+ 8. g3 Nxd4 9. Qf2 Qf6 10. Bxf4 Bb4 11. e5 Qc6
12. O-O-O Bxc3 13. Bxf7+ Kxf7 14. bxc3 Nf3 15. Rhf1) 7... fxg2 (7... Nxd4 8. Bxf4 Bc5 9. Kh1 d6 10. b4 Bb6
11. Nd5 fxg2+ 12. Kxg2 Ne6 13. Qf3) 8. Rxf4) (5. g3 g4 6. Nh4 f3 7. d4 d6
8. Be3 Be7 9. Qd2 Bxh4 10. gxh4 Qxh4+ 11. Bf2 Qh6 12. Qxh6 Nxh6 13. Nd5 Kd7 14. Kd2) 5... g4 6. Ng5 h6 7. Nxf7 Kxf7 8.
d4 d5 (8... f3 9. Bc4+ Kg7 10. gxf3 Be7 11. Be3) 9.
Bxf4 Nf6 10. Nxd5 (10. exd5
Nxd5 (10... Bd6 11. Bxd6 Qxd6 12. dxc6) 11. Bc4 Be6 12. O-O) 10... Nxd5 11. Bc4 Be6 12. exd5 Bxd5 13. O-O *

The error I get is this:
IllegalMoveException: r1bqk2r/pppp1ppp/2n2n2/4p3/1bB1P3/2NP4/PPP2PPP/R1BQK1NR w KQkq - 1 5 => Nge2

I tried running the PGN through the Chess Tempo PGN viewer and did not have an issue. Unless there is something I'm missing I don't believe any Nge2 moves in this are illegal.

Let me know if any other info would be helpful! Thanks!

Failed to resolve import

Hi Stefan, I updated cm-pgn to 2.3.4 from 2.3.1 and I'm getting the following error:

Failed to resolve import "../../lib/chess.mjs/Chess.js" from "node_modules/cm-pgn/src/cm-pgn/History.js?v=e7e4b3e6". Does the file exist?

This looks like an internal reference error within cm-pgn.

Any thoughts on what's going on?

Having just gone through upgrading cm-chessboard I think this is related to this path structure /cm-pgn/src/cm-pgn/ in the above as I see you have corrected a similar this structure in cm-chessboard.

thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.