Search This Blog

A Barely Adequate Guide to Displaying Chess Games with JavaScript

I've done a few of these Barely Adequate Guides in the past, the idea being that I want to explore how to do something with code that I've never done before, and I'll figure it out while writing the post so that we can all see my thought process as I go about solving the problem of getting something new up and running. This time I want to bring together two things I love doing in my free time: programming and chess. I wouldn't say I'm an especially good chess player—I'm quite mediocre actually—but I love the game, and I love learning it and reading books about it.

In order to bring some chess discussion to this blog, I want to be able to display some games, and in order to do that, we're going to go hunting for some JavaScript libraries. But first, we should lay out some goals for this mini project. It's always good to have some achievable goals in mind when solving a problem so we'll know when we're making progress. Here are our goals:
  1. Display a chess board, preferably with pieces in their starting position.
  2. Display an arbitrary (but legal) position.
  3. Display a game with predefined moves and move controls, maybe also displaying a move list.

Displaying a Chess Board


Okay, that's probably enough to get started. I imagine integrating a chess engine so that the reader can play against the computer would be overkill, and it involves a bunch of issues that I'd rather not think about for this post. To figure out how to display a chess board, let's head over to every programmer's favorite tool, Google. After searching for "javascript chess," Google came up with some very promising leads. The first result was for chessboardjs.com, which looks like it will do nicely for #1, and scanning through the rest of the first page results, it looks like we will probably need the chess.js library as well.

After downloading the chessboard.js library, I extracted it into my github.io repo and pushed it to GitHub so I could access it from my blog. Next, I wanted to try it out by adding the following HTML and JavaScript to this page:
<script src="https://koblenski.github.io/javascript/chessboardjs-0.3.0/js/chessboard-0.3.0.min.js" type="text/javascript"></script>
<link href="https://koblenski.github.io/javascript/chessboardjs-0.3.0/css/chessboard-0.3.0.min.css" rel="stylesheet"></link>
<div id="board1" style="margin: 0 auto;width: 400px"></div>
<script type="text/javascript">var board = ChessBoard('board1', 'start');</script>
The first line loads the chessboard.js library, the second line loads the CSS for the chess board, the third line sets up the space where the board will be, and the last line creates the chess board. It's very similar to the chessboard.js website's simple example of a basic chess board, and here is the resulting board, complete with pieces in their starting position!



Huh, that didn't work so well. It looks like the pieces are missing their pictures. If I look in the Chrome console, I see a bunch of 404 errors for the piece PNGs:

GET https://sam-koblenski.blogspot.com/b/img/chesspieces/wikipedia/wP.png 404 ()

Of course! The piece pictures aren't on my blog site in that location, they're on my github.io site where all of the other chessboard.js stuff is. Maybe I can point the ChessBoard() constructor at the correct location for the piece pictures. After a bit of looking around on chessboardjs.com, I find something promising in the pieceTheme configuration, and I give this code a try:
<div id="board2" style="margin: 0 auto; width: 400px;">
</div>
<script type="text/javascript">
$(function() {
  var cfg = {
    pieceTheme: 'https://koblenski.github.io/javascript/chessboardjs-0.3.0/img/chesspieces/wikipedia/{piece}.png',
    position: 'start'
  };
  var board = ChessBoard('board2', cfg);
});
</script>
I'm wrapping the JavaScript in a jQuery document ready function so that I can create multiple boards without the variables colliding. Plus, it's just good practice to wait for the DOM to finish being built before running scripts. Now the board looks like this:


Still not great. The pieces are now visible, but they're too big and the background should be transparent so the squares are visible behind the pieces. Here, I got stuck for a couple hours. I couldn't figure out where I went wrong, and I spent time verifying that I was using chessboard.js correctly. I created a similar setup at jsfiddle.net, and it worked perfectly fine. Then, I spun up a quick rails app that served up the same HTML and JavaScript, and that worked perfectly fine, too. I was struggling mightily when I finally took a look at this post's preview in Chrome Developer Tools and found that there was a Google Blogger CSS class that was adding padding, a background color, and a border to all images that were under the hierarchy of the post-body class.

Once I saw that it was a CSS thing, the rest was easy. I edited the CSS in Chrome until I got the piece images looking right, and then I had to decide how I was going to correct the CSS in the post. I could either edit the CSS in chessboard-0.3.0.min.css since I had my own version of it, or I could change the CSS in my blog to style the images that appear on a chess board differently than the other images in my blog posts. I'm resistant to customizing open source libraries because that opens up maintenance issues, so I decided to change the CSS in my Blogger template. This operation is quite easy to do. From the main view of your blog:
  1. In the left menu, click Theme.
  2. Under “Live on blog," click Customize.
  3. In the left menu, click Advanced > Add CSS.
  4. Add your code.
  5. On the top right, click Apply to blog.
The code I added was this:
.chessboard img {
  padding: 0px;
  background: transparent;
  border: 0px;
}
Now I just have to add the chessboard class to each chess board <div>, and the pieces should appear correctly:


Nice! now we're making progress. With that excursion over, we can move on.

Displaying a Non-starting Position


After getting over that initial hump, this task should be easy. Looking at the chessboard.js documentation, we see that the board configuration position parameter takes a FEN string to describe the position. FEN stands for Forsyth-Edwards Notation, and it's a compact way to exactly specify a chess board position. The notation is pretty simple, with letters for the different pieces, digits for the number of adjacent blank squares, and '/' for the end of a row. The details aren't terribly important for us now because you can easily find FEN generators online. You can even get FEN strings out of chessboard.js if you set up a board to move pieces and then display the FEN string it generates.

Here is the example code to generate a Ruy Lopez opening position:
<div class="chessboard" id="board4" style="margin: 0 auto; width: 400px;">
</div>
<script type="text/javascript">
$(function() {
  var cfg = {
    pieceTheme: 'https://koblenski.github.io/javascript/chessboardjs-0.3.0/img/chesspieces/wikipedia/{piece}.png',
    position: 'r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R'
  };
  var board = ChessBoard('board4', cfg);
});
</script>
And here's what the board looks like:


That was simple. It will be easy to generate FEN strings for positions and show boards of those positions in future blog posts with this setup. Let's move on.

Stepping Through a Game


This goal is likely to be a bit harder. There's an example of how to move pieces in the chessboard.js documentation, but that's not enough to play through a predefined game, stepping moves forward and backward. It's time to bring chess.js into the picture. This library adds a few things we want, like moving pieces while maintaining a history of moves, undoing moves, and parsing PGN strings. PGN stands for Portable Game Notation, and it's a standard way of specifying an entire game of chess, including meta data about who played the game, when it was played, and the chess event where it took place.

It turns out that even with these features, we still need to do some bookkeeping of our own because chess.js doesn't have a concept of stepping back and forth through the moves in a game. We'll start by adding chess.js to the page (it's available from CDNJS), and sketching out how we want to use it and integrate it with our chessboard.js UI:
<script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.2/chess.js"></script>
<div class="chessboard" id="board5" style="margin: 0 auto; width: 400px;">
</div>
<div style="margin: 5px auto; width: 400px;">
<input type="button" id="startPositionBtn5" value="|<" />
<input type="button" id="prevBtn5" value="<" />
<input type="button" id="nextBtn5" value=">" />
<input type="button" id="endPositionBtn5" value=">|" />
<p><span id="pgn5"></span></p>
</div>
<script type="text/javascript">
$(function() {
  var cfg = {
    pieceTheme: 'https://koblenski.github.io/javascript/chessboardjs-0.3.0/img/chesspieces/wikipedia/{piece}.png',
    position: 'start'
  };
  var board = ChessBoard('board5', cfg);
  var game = new Chess();

  // 1. Load a PGN into the game
  // 2. Get the full move history
  // 3. If Next button clicked, move forward one
  // 4. If Prev button clicked, move backward one
  // 5. If Start button clicked, go to start position
  // 6. If End button clicked, go to end position
});
</script>
I added four buttons underneath the board that will allow moves to be played forward and backward and to go to the beginning and end of the game, and there's a <span> underneath the buttons to hold the move list for the game that's being displayed.

The chess.js game object was added, and now we need to fill out the steps listed in the comments. Each of them is pretty straightforward on its own. Let's start at the top with loading a PGN string into the game:
  // 1. Load a PGN into the game
  var pgn = '1.e4 e5 2.Nf3 Nf6 3.Nc3 d5 4.exd5 Nxd5 5.Bc4 Nf4 6.O-O e4 7.Re1 Kd7 8.Rxe4 Qg5 9.Nxg5 f6 10.Qg4+ Ne6 11.Qxe6+ Kd8 12.Qe8#  1-0';
  game.load_pgn(pgn);
  $('#pgn5').html(pgn);
I got the PGN string from a game I played on Chess.com, but they're pretty easy to get from almost any computer chess program or website. The string can also contain the meta info about the game, but it's not necessary for this example. Then the string is loaded into the game object, and I also put the string in the HTML for the move list so that it will be visible to the reader. Next, let's get the full move history of the game as an array from the game object so we can keep track of which move we're on and feed it back into the game:
  // 2. Get the full move history
  var history = game.history();
  game.reset();
  var i = 0;
After pulling out the move history, we want to reset the game back to the starting position because that's what's going to be displayed first on the board. We also declare an index variable that will keep track of which move we're displaying. Next, let's start moving:
  // 3. If Next button clicked, move forward one
  $('#nextBtn5').on('click', function() {
    game.move(history[i]);
    board.position(game.fen());
    i += 1;
    if (i > history.length) {
      i = history.length;
    }
  });
This code adds a click handler to the button with the '>' symbol on it. The handler adds the current move to the game, updates the board with the current game position, and increments the move index, making sure not to go past the end of the move array. It may look like there's a bug if the next button is clicked after we've reached the end of the array, but chess.js behaves nicely when given garbage moves so it's not a problem. We also want to make sure the index stays in sync with the game when we introduce moving backward, as we'll soon see with this code:
  // 4. If Prev button clicked, move backward one
  $('#prevBtn5').on('click', function() {
    game.undo();
    board.position(game.fen());
    i -= 1;
    if (i < 0) {
      i = 0;
    }
  });
Adding the previous move handler is nearly the same as the next move handler, but we're decrementing instead of incrementing. Also, we can't update the game with move() because we're taking away moves, so we need to use undo(). Now you should be able to see what happens if we restricted the index to the last move of the move array in the next move handler. If we did that, then going to the end of the game, then backing up at least one move would leave the index at the move before the move that should be passed to the game when the next button is clicked. That could potentially mess up the game quite a bit, so we let the index go one past the end of the array instead. The last couple click handlers are simple:
  // 5. If Start button clicked, go to start position
  $('#startPositionBtn5').on('click', function() {
    game.reset();
    board.start();
    i = 0;
  });

  // 6. If End button clicked, go to end position
  $('#endPositionBtn5').on('click', function() {
    game.load_pgn(pgn);
    board.position(game.fen());
    i = history.length;
  });
Clicking the start button resets the game, the board, and the move index back to the starting position. Clicking the end button puts the states of everything in the opposite extreme. The easiest way to set the game to the end is to reload the PGN, as opposed to looping through all of the moves in the history list. That should be all of the features. Here's what our chess game looks like:



Pretty slick! I like how that turned out. It might be nice to style the move list a bit more and highlight which move is currently being displayed. Other features that would be cool to add would be allowing the reader to make different moves to explore the position, adding stockfish.js to analyze the strength of the position, and highlighting threats or suggested moves. Those features seem like they would take more time to implement and get right, and at some point it might be easier to copy the displayed PGN into a real chess program to do better analysis. What we made here is quite adequate for an inline display to assist in talking about a chess game, it looks great, and it didn't take long to put together. It's amazing what you can do with a couple open source JavaScript libraries.

3 comments:

  1. Thanks for this post! I was doing a similar thing integrating the chessboardjs (and chess.js) libraries into a site running on WordPress. I had the same issues as you did with the pieces not showing correctly or not showing up at all due to the 404 you mentioned. This helped me from any further headscratching. :)

    ReplyDelete
  2. you helped me to understand a few things that I was confused about. All clear now, big thanks!

    ReplyDelete
  3. Thanks; something like this should be added to the chess.js readme.

    ReplyDelete