12

I'm building a chess-related application using nodejs. I've been trying to use chess.js as much as I can but I think I've hit a roadblock in terms of functionality. Before extending that functionality, I wanted to make sure that there wasn't another tool that can do what I need.

I'm looking for a way to convert a PGN string into a list of FEN moves. I was hoping to use load_pgn() in chess.js to load the moves into the object and then loop over each move and invoke the fen() function to output the current FEN. However, chess.js doesn't seem to have a way to walk through the moves in a game. Unless I'm missing something.

I'd rather not have to get into parsing strings, but will if I have to. Any suggestions?

Solution:

also see efirvida's answer below for a solution

Something like this (untested) seems to work. The function accepts a Chess object created with chess.js that already has a PGN loaded into it.

function getMovesAsFENs(chessObj) {
    var moves = chessObj.history();
    var newGame = new Chess();
    var fens = [];
    for (var i = 0; i < moves.length; i++) {
      newGame.move(moves[i]);
      fens.push(newGame.fen());
    }
    return fens;
}
manlio
  • 18,345
  • 14
  • 76
  • 126

4 Answers4

8

Take a look to the github page .load_pgn link

var chess = new Chess();
pgn = ['[Event "Casual Game"]',
       '[Site "Berlin GER"]',
       '[Date "1852.??.??"]',
       '[EventDate "?"]',
       '[Round "?"]',
       '[Result "1-0"]',
       '[White "Adolf Anderssen"]',
       '[Black "Jean Dufresne"]',
       '[ECO "C52"]',
       '[WhiteElo "?"]',
       '[BlackElo "?"]',
       '[PlyCount "47"]',
       '',
       '1.e4 e5 2.Nf3 Nc6 3.Bc4 Bc5 4.b4 Bxb4 5.c3 Ba5 6.d4 exd4 7.O-O',
       'd3 8.Qb3 Qf6 9.e5 Qg6 10.Re1 Nge7 11.Ba3 b5 12.Qxb5 Rb8 13.Qa4',
       'Bb6 14.Nbd2 Bb7 15.Ne4 Qf5 16.Bxd3 Qh5 17.Nf6+ gxf6 18.exf6',
       'Rg8 19.Rad1 Qxf3 20.Rxe7+ Nxe7 21.Qxd7+ Kxd7 22.Bf5+ Ke8',
       '23.Bd7+ Kf8 24.Bxe7# 1-0'];

chess.load_pgn(pgn.join('\n'));
// -> true

chess.fen()
// -> 1r3kr1/pbpBBp1p/1b3P2/8/8/2P2q2/P4PPP/3R2K1 b - - 0 24

something like

moves = chess.history();
var chess1 = new Chess();
for (move in moves){
    chess1.move(move);
    fen = chess1.fen()
}
ggorlen
  • 44,755
  • 7
  • 76
  • 106
efirvida
  • 4,592
  • 3
  • 42
  • 68
  • Yeah, I read the API. The issue is that once you load the PGN, there isn't a function to get all FEN positions for all moves. You can only get for the current move. –  Sep 21 '15 at 00:01
  • with the `.history()` you can get all the moves then iterates over them from the binning having the fen position for each move – efirvida Sep 21 '15 at 00:06
  • but if you make a move after load the PGN in the same instance of Chess I think didn't work because invalid move, I thinks i better with new chess instances, maybe I´m wrong and works fine, but I don't think so – efirvida Sep 21 '15 at 00:25
  • You're totally right. I just tested it and you need to create a new board to play the moves on. I'll revert my edit –  Sep 21 '15 at 01:31
5

(Not really an answer; just a comment that needs extra formatting.)

Your getMovesAsFENs function might also be written as:

function getMovesAsFENs(chessObj) {
    return chessObj.history().map(function(move) {
        chessObj.move(move);
        return chessObj.fen();
    });
}

Perhaps it doesn't matter to you, but this appeals to my sense of neatness.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
1

Here is an end to end answer with some ES6 sugar added in:

const Chess = require('chess.js').Chess;
const chess1 = new Chess();
const chess2 = new Chess();
const startPos = chess2.fen();
const pgn = `1.e4 c5 0-1`;

chess1.load_pgn(pgn);
let fens = chess1.history().map(move => {
  chess2.move(move);
  return chess2.fen();
});

//the above technique will not capture the fen of the starting position.  therefore:
fens = [startPos, ...fens];

//double checking everything
fens.forEach(fen => console.log(fen));
KaeyangTheG
  • 391
  • 2
  • 7
0

"However, chess.js doesn't seem to have a way to walk through the moves in a game. Unless I'm missing something.".

You are right (as I have read the entire library multiple times by now). And everything that needs to see back into the history is basically undoing and then redoing the moves, without some sort of real navigation integrated (it is an interesting choice to have it solved this way, with the pros of it being lighting fast for some tasks, but with the cons of being a real pain for other seemingly easier tasks like the one you need).

Disclaimer (I wrote the following tool), I have been creating a tool (isepic-chess.js) for the past 5+ years, something similar to chess.js and I think it's slowly getting there... The library stores the moves history in an object with information like (fen, from_square, to_square, san, etc.), and also have some kind of "cursor" with the move index and some move-navigation helpers.

So with isepic-chess.js you can just call the board method board.fenHistoryExport() to get the FEN list after you parse the PGN game:

var example_pgn = `[Event "m1 London"]
[Site "?"]
[Date "1861.07.??"]
[Round "9"]
[White "Kolisch, Ignatz"]
[Black "Anderssen, Adolf"]
[Result "0-1"]
[Annotator "JvR"]
[SetUp "1"]
[FEN "5r1k/pp4pp/3r3q/8/3PpP1P/1P2NbP1/PB1Q3K/R7 b - - 0 30"]
[PlyCount "13"]
[EventDate "1861.??.??"]

30... Rxf4 $1 {Anderssen starts fireworks.} 31. Qe1 (31.gxf4 $2 Qxh4+ 32.Kg1
Rg6+) 31... Rg6 (31...Rxh4+ $1 32.gxh4 Rg6 $1) 32. Bc1 (32.Ng2 $1) 32... Rxh4+
$1 33. gxh4 Qf4+ 34. Kh3 Bg2+ $1 35. Nxg2 Qf3+ 36. Kh2 Qxg2# { Anderssen won
the match by this mate (+4, =2, -3).} 0-1`;

var board = Ic.initBoard({
  pgn : example_pgn
});

console.log(board.fenHistoryExport());

There is a more complete node.js example in the README.md with the const {Ic} = require("isepic-chess"); import thingy to have it running in node.js.

ajax333221
  • 11,436
  • 16
  • 61
  • 95