Search code examples
javascriptchess

chess.js - unable to make a move using the permissive parser


I can only make moves using the SAN and object notations. The permissive parser doesn't work. I tried using the { permissive: true } option but it doesn't seem to do anything. Did I miss something?

const game = new Chess();
game.move('e2-e4');
game.fen();

Returns rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1.

const game = new Chess();
game.move('e4'); // Or game.move({ from: 'e2', to: 'e4' });
game.fen();

Returns rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1. And by the way, 'e4' looks more permissive than 'e2-e4' to me...


Solution

  • Short answer: In a browser context, add the sloppy option:

    game.move('e2-e4', { sloppy: true })
    

    Why

    At the time of writing, the latest version of chess.js is v0.13.4 and there is (still) a difference between the npm module and the browser version.

    Node.js

    When using your code in Node.js, the first snippet performs the move:

    import { Chess } from 'chess.js'
    
    const game = new Chess();
    game.move('e2-e4');
    console.log(game.fen());
    

    This has the expected, correct output:

    rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1
    

    Browser

    In a browser, we can reproduce the issue when including this module script:

    <script type="module">
        import { Chess } from 'https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.13.4/chess.min.js'
        
        const game = new Chess();
        console.log(game.move('e2-e4')); // null!
        console.log(game.fen());
    </script> 

    The Difference

    The difference can be seen in the source code:

    In the source code on GitHub we find this function definition (heading):

      move(
        move: string | { from: string; to: string; promotion?: string },
        { strict = false }: { strict?: boolean } = {}
      ) {
     {
        /*
         * The move function can be called with in the following parameters:
         *
         * .move('Nxb7')       <- argument is a case-sensitive SAN string
         *
         * .move({ from: 'h7', <- argument is a move object
         *         to :'h8',
         *         promotion: 'q' })
         *
         *
         * An optional strict argument may be supplied to tell chess.js to
         * strictly follow the SAN specification.
         */
    

    But in the source code provided by CNDjs (for browsers) we find:

    move: function (move, options) {
          /* The move function can be called with in the following parameters:
           *
           * .move('Nxb7')      <- where 'move' is a case-sensitive SAN string
           *
           * .move({ from: 'h7', <- where the 'move' is a move object (additional
           *         to :'h8',      fields are ignored)
           *         promotion: 'q',
           *      })
           */
    
          // allow the user to specify the sloppy move parser to work around over
          // disambiguation bugs in Fritz and Chessbase
          var sloppy =
            typeof options !== 'undefined' && 'sloppy' in options
              ? options.sloppy
              : false
    

    Note that the first one foresees a strict option, while the second provides a sloppy option. The default is the opposite.

    The node.js module evolved over time, changing the default for this option from strict to sloppy, thereby replacing the sloppy option with the strict option. See some of the stale branches of the GitHub project which still have the previous behaviour.

    Solution

    So in a browser context you should provide the sloppy option:

    <script type="module">
        import { Chess } from 'https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.13.4/chess.min.js'
        
        const options = { sloppy: true };
        const game = new Chess();
        console.log(game.move('e2-e4', options));
        console.log(game.fen());
    </script>