Reading Portable Game Notation (pgn)
The pgn format is widely used to notate the plays in a game of chess. It seems natural to use pgn files to drive the movement of pieces in the visualization since adopting this standard would permit us to visualize any game. While pgn is designed to be human-readable and easily read by computers, there are tradeoffs in the data representation that require some extra work to translate the notation of a given move into an action to drive the visualization.
Interpreting the pgn format
A move described in a pgn
file is written in standard algebraic notation (SAN) and contains implicit meaning:
e.g.
e4
implies to move a white or black pawn to thee4
square. In the opening moves of the game, only white’s king pawn can make this move.Nf3
implies to move a white or black knight to thef3
square.
Within the .pgn
file, the moves are given as pairs. Since white always begins the game we can read a move as follows:
1. e4 c5
means that white opens by moving their king pawn to e4 and black responds by moving their queen’s bishop pawn to c5.
Obtaining and parsing
For our datasource, we Chess Microbase for its 1000 publicly available games for download in the .pgn format. This project starts with Brown vs. Quinteros
Parsing the entire .pgn
is beyond the scope of this project and there are already many excellent solutions available. We choose Aditya DS’s excellent Chess PGN Parser for its ability to parse a .pgn
into .json
. (the format used for this project)
Translating Implicit Moves
All moves are stored in the moves
array.
const moves = ["e4", "c5", "Nf3","Kf3"]
We deduce white’s turn or black’s turn by the index of the move:
if (index % 2 == 0) {
// white's move
}
While the goal is to let d3
use a .pgn
game file and drive the placement and movement of pieces within the DOM, we don’t have enough information for parsing a move like Nf3
.
- Which of the four knights is moving to “f3”?
- What is the starting square?
While trying to avoid tracking the entire board state, we use make use of a utility function that returns the possible moves for each knight on the board.
// get the moves for the knight on square g1
const potentialMoves = knightMoves("g1")
// returns ["e2","f3","h3"]
Iterate through the potential moves for each knight and determine whether or not it contains “f3”. In this case, white’s king knight moves from “g1” to “f3”.
The same logic was used for determining which bishop or rook should move.
Commit 2b859d