Make a JavaScript Chess Board
Tags: algorithms, chess, react, javascript
Published: 2023-04-20 23:17:32

Creating a chess board in JavaScript is straightforward once you have knowledge of the Forsyth–Edwards Notation (FEN), a string of characters carrying information about the position of a chess board. For instance, the FEN of the starting position is as follows:
```
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
```
[Forsyth-Edwards Notation (FEN)](https://www.chess.com/terms/fen-chess)

To draw the board, you only need the string of characters at the beginning of the FEN. To begin, you can create a function that turns this string into an array of length 64. Here is an example of how to do this in JavaScript:

```javascript
function fenToArray(fen) {
    const piecesString = fen.split(' ')[0].replace(/\//g, '');
    const piecesArrayNums = piecesString.split('');
    const piecesArray = piecesArrayNums.map(piece => {
        if (parseInt(piece)) {
            return [...Array(parseInt(piece)).fill(null)];
        };
        return piece;
      }).flat();
    return piecesArray;
}
```

Additionally, you will need images of pieces to render on the board, as well as two variables for the color of the light and dark squares. Here is an example of how to set up these objects and variables:

```javascript
const lightSquare = '#c4c4c4';
const darkSquare = selectedColor;

const piecePngObj = {
  'b': blackBishop,
  'n': blackKnight,
  'k': blackKing,
  'p': blackPawn,
  'q': blackQueen,
  'r': blackRook,
  'B': whiteBishop,
  'N': whiteKnight,
  'K': whiteKing,
  'P': whitePawn,
  'Q': whiteQueen,
  'R': whiteRook, 
}
```
Here, the values of the piecePngObj object reference the path to the 12 piece images.

Finally, you can create the board itself. Here is an example of how to do this in React with Material UI, though the logic will be similar in other implementations:

First, create the board of 64 squares.

```javascript
function Board() {

    const fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
    const fenArray = fenToArray(fen);

    const squares = [];

    // Create board squares and pieces
    for (let i = 0; i < 64; i++) {

        const row = Math.floor(i / 8);
        const color = (row % 2 === 0 && i % 2 === 0) || 
                      (row % 2 === 1 && i % 2 === 1) 
                      ? lightSquare : darkSquare;

        squares.push(
            <Box 
              key={i}
              bgcolor={color}
              sx={{
                width: "100%",
                height: "100%",
                backgroundColor: color,
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
              }}
            >
            </Box>
          );
    };

    return (
        <Box
          className='board-container'
          sx={{
            height: length,
            width: length,
            display: 'grid',
            gridTemplateColumns: 'repeat(8, 1fr)',
            gridTemplateRows: 'repeat(8, 1fr)',
            position: 'relative',
          }}
        >
          {squares}
        </Box>
    );
}
```

To determine the color of the i-th square, it is necessary to consider if the row is odd or even; the first color in each row alternates, and the colors also alternate through each row. We store the 64 squares in a variable called `squares`, which is then returned wrapped in a Material UI `<Box/>` component. To make the 64 squares render in an 8x8 grid, use the `gridTemplateColumns` and `gridTemplateRows` CSS properties. For non-React and non-MUI implementations, this part may look different.

To add the piece images to the board, use the `fenArray` variable defined earlier.

```javascript
import Draggable from 'react-draggable';

function Board() {

    const fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
    const fenArray = fenToArray(fen);

    const squares = [];

    // Create board squares and pieces
    for (let i = 0; i < 64; i++) {

        const row = Math.floor(i / 8);
        const color = (row % 2 === 0 && i % 2 === 0) || 
                      (row % 2 === 1 && i % 2 === 1) 
                      ? lightSquare : darkSquare;

        const pieceImg = (
            <Box 
              draggable='false'
              component='img'
              src={piecePngObj[`${fenArray[i]}`]}
              alt=''
              sx={{ maxWidth: "100%", maxHeight: "100%" }}
          />
        );

        squares.push(
            <Box 
              key={i}
              bgcolor={color}
              sx={{
                width: "100%",
                height: "100%",
                backgroundColor: color,
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
              }}
            >
              {staticBoard ? pieceImg : 
                <Draggable>
                  {pieceImg}
                </Draggable>
              }
            </Box>
          );
    };

    return (
        <Box
          className='board-container'
          sx={{
            height: length,
            width: length,
            display: 'grid',
            gridTemplateColumns: 'repeat(8, 1fr)',
            gridTemplateRows: 'repeat(8, 1fr)',
            position: 'relative',
          }}
        >
          {squares}
        </Box>
    );
}
```

The `pieceImg` variable is an `<img/>` component that displays the corresponding image for each non-empty square, or an empty square if the square is empty. The image source is determined by referencing the `piecePngObj` object containing the links to the various piece images.

If using React, it's possible to make the pieces draggable using the `react-draggable` npm package. However, additional work is required to ensure that the pieces behave correctly within the board, such as staying inside the board boundaries and snapping back to their original positions upon release.

Full code:
```javascript
import Draggable from 'react-draggable';
import Box from '@mui/material/Box';

const lightSquare = '#c4c4c4';
const darkSquare = selectedColor;

const piecePngObj = {
  'b': blackBishop,
  'n': blackKnight,
  'k': blackKing,
  'p': blackPawn,
  'q': blackQueen,
  'r': blackRook,
  'B': whiteBishop,
  'N': whiteKnight,
  'K': whiteKing,
  'P': whitePawn,
  'Q': whiteQueen,
  'R': whiteRook, 
}

function fenToArray(fen) {
    const piecesString = fen.split(' ')[0].replace(/\//g, '');
    const piecesArrayNums = piecesString.split('');
    const piecesArray = piecesArrayNums.map(piece => {
        if (parseInt(piece)) {
            return [...Array(parseInt(piece)).fill(null)];
        };
        return piece;
      }).flat();
    return piecesArray;
}

function Board() {

    const fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
    const fenArray = fenToArray(fen);

    const squares = [];

    // Create board squares and pieces
    for (let i = 0; i < 64; i++) {

        const row = Math.floor(i / 8);
        const color = (row % 2 === 0 && i % 2 === 0) || 
                      (row % 2 === 1 && i % 2 === 1) 
                      ? lightSquare : darkSquare;

        const pieceImg = (
            <Box 
              draggable='false'
              component='img'
              src={piecePngObj[`${fenArray[i]}`]}
              alt=''
              sx={{ maxWidth: "100%", maxHeight: "100%" }}
          />
        );

        squares.push(
            <Box 
              key={i}
              bgcolor={color}
              sx={{
                width: "100%",
                height: "100%",
                backgroundColor: color,
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
              }}
            >
              {staticBoard ? pieceImg : 
                <Draggable
                  bounds='.board-container'
                  onStart={onStart}
                  onStop={() => onStop(i)}
                  onDrag={(e, ui) => handleDrag(e, ui, i)}
                  position={positions[i]}
                >
                  {pieceImg}
                </Draggable>
              }
            </Box>
          );
    };

    return (
        <Box
          className='board-container'
          sx={{
            height: length,
            width: length,
            display: 'grid',
            gridTemplateColumns: 'repeat(8, 1fr)',
            gridTemplateRows: 'repeat(8, 1fr)',
            position: 'relative',
          }}
        >
          {squares}
        </Box>
    );
}

export default Board;
```