fen.lua - This module manipulates FEN files.


 local FEN = require 'chess.fen'

 french = 'rnbqkbnr/ppp2ppp/4p3/3p4/3PP3//PPP2PPP/RNBQKNR w KQkq - 0 3'
 newfen = FEN.fenstr_move(french, 'Nc3')

 f = FEN.fenstr2tab(french)


This module manipulates chess positions in FEN notation.

It is used by pgn2eco to look for positions arising in /home/eco/ECOMast.txt and report their ECO numbers, by keeping a fenstr==>eco database of positions and their ECO numbers. pgn2eco can be used, for example, to find the ECO numbers corresponding to positions that arise in opening-repertoire books.


doc ()

This returns a multi-line string documenting the FEN notation.

fenstr2asciidiag (fenstr)

This accepts a position in FEN format and returns a multiline string which can be printed on /dev/tty

fenstr_move (fenstr, move)

This accepts a position in FEN format, and an individual move in PGN syntax, then applies the move to the position and returns the resulting FEN string.


This accepts a position in FEN format, and returns a somewhat simplified version of it. Specifically, the move-number and the fifty-move-number are ommitted, and the enpassant square is reset to '-' if there is no opposing pawn available to capture on that square.

The returned string can be used as a key to index the position in a database, for example of opening variations or endgame positions, where it matters not if the position has arisen after 9 moves or after 8 (eg: the Sveshnikov) or has arisen by 1.d4 f5 2.g3 or by 1.g3 f5 2.d4

pgn_moves (pgntext)

This accepts a game, or segment of a game, in PGN notation, and returns an array of the moves.


fenstr2tab (fenstr)

This splits a FEN string into a table of its fields, and returns the table. These table values are all strings; their keys are 'postab', 'active', 'castling', 'enpassant', 'fiftymove', 'movenum'

The key called 'postab' splits the position-string into a 2D array of the squares of the position. Both the rank index and the column index are numbers: postab[ncol][nrank]

fentab2str (fentab)

This performs the reverse conversion, returning the FEN string. During this conversion, the contents of the fentab['postab'] are converted back into position-string.


A FEN record contains six fields.
The separator between fields is a space. The fields are:

  1. Piece placement (from white's perspective). Each rank is described, starting with rank 8 and ending with rank 1; within each rank, the contents of each square are described from file "a" through file "h". White pieces are designated using upper-case letters ("PNBRQK") while black pieces use lowercase ("pnbrqk"). Empty squares are noted using digits 1 through 8 (the number of empty squares); "/" separates ranks.
  2. Active color. "w" means White moves next, "b" means Black.
  3. Castling availability. If neither side can castle, this is "-". Otherwise, this has one or more letters: "K" (White can castle kingside), "Q" (White can castle queenside), "k" (Black can castle kingside), and/or "q" (Black can castle queenside).
  4. En passant target square in algebraic notation. If there's no en passant target square, this is "-". If a pawn has just made a two-square move, this is the position "behind" the pawn. This is recorded regardless of whether there is a pawn in position to make an en passant capture.
  5. Halfmove clock: This is the number of halfmoves since the last capture or pawn advance. This is used to determine if a draw can be claimed under the fifty-move rule.
  6. Fullmove number: The number of the full move. It starts at 1, and is incremented after Black's move.


This module is available as a luarock and should be installed by

  luarocks install chess-fen

To use pgn2eco you will also need the lgdbm module:

  luarocks install lgdbm

You can see the source-code in:


 20200422 1.8 remove require 'DataDumper', just used for testing
 20200422 1.7 fix bug in posstr2postab()
 20191013 1.6 ./bin files use /usr/bin/env lua, and pgn2fen gets perldoc
 20181012 1.5 frompiece2xy() rejects candidates if pinned to the king
 20180908 1.4 fenstr_move() allows +?!
 20180901 1.3 fenstr_move() reports impossible moves better
 20180408 1.2 first released version
 20180407 1.1 add fenstr2key()
 20180315 1.0 initial prototype


Peter J Billam,