DEV Community

Kyle Schneider
Kyle Schneider

Posted on

Make a JavaScript Chess Board

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
Enter fullscreen mode Exit fullscreen mode

Forsyth-Edwards Notation (FEN)

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:

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;
}
Enter fullscreen mode Exit fullscreen mode

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:

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, 
}
Enter fullscreen mode Exit fullscreen mode

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.

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>
    );
}
Enter fullscreen mode Exit fullscreen mode

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.

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>
    );
}
Enter fullscreen mode Exit fullscreen mode

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:

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;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)