“I am participating in the nuggets Community Game Creativity Submission Contest. For details, please see: Game Creativity Submission Contest.”

Code repository, go by and click a Star ✨

Draw a checkerboard

Composition of a chessboard

Let’s start with A chess board. The board is made up of 8 x 8 alternating black and white squares. The axis is a-H from left to right at the lower left corner of the white square, and 1-8 from bottom to top.

In code

When I first conceived the checkerboard, I planned to draw an SVG checkerboard and used a two-dimensional array to describe the checkerboard, with 0 representing the white grid and 1 representing the black grid.

export const grid = [
    [0.1.0.1.0.1.0.1],
    [1.0.1.0.1.0.1.0],
    [0.1.0.1.0.1.0.1],
    [1.0.1.0.1.0.1.0],
    [0.1.0.1.0.1.0.1],
    [1.0.1.0.1.0.1.0],
    [0.1.0.1.0.1.0.1],
    [1.0.1.0.1.0.1.0]].Copy the code

So this is what it looks like when you write it in code

<svg width={8 * gridSize} height={8 * gridSize} stroke={borderColor}>
  <g
     width={gridSize}
     height={gridSize}
     key={` ${rowIndex}-The ${colIndex} `} >
    <rect
      x={colIndex * gridSize}
      y={rowIndex * gridSize}
      width={gridSize}
      height={gridSize}
      style={{ fill: col= = =1 ? blackGrid : whiteGrid}} / >
  </g>.64A grid < / SVG >Copy the code

In SVG, line breaking is easy, just control the x and y values. The first grid of the first row is [0, 0], the second grid is [0, 50], the second grid is [50, 0], the second grid is [50, 50], and so on. ColIndex * gridSize, rowIndex * gridSize.

The resulting SVG board looks like this

<svg width={8 * gridSize} height={8 * gridSize} stroke={borderColor}>
  {grid.map((row, rowIndex) = > {
    return row.map((col, colIndex) = > {
      return <g
        width={gridSize}
        height={gridSize}
        key={` ${rowIndex}-The ${colIndex} `} >
        <rect
          x={colIndex * gridSize}
          y={rowIndex * gridSize}
          width={gridSize}
          height={gridSize}
          style={{ fill: col= = =1 ? blackGrid : whiteGrid}} / >
      </g>
    })
  })}
</svg>
Copy the code

The second option

Since this is ultimately going to be a playable version, it’s still necessary to write divs. The easiest way to do this is in Flex: row-wrap; This property is easy to implement.

.board {
    display: flex;
    flex-flow: row wrap;
}
.cell {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    flex-grow: 1;
    flex-shrink: 0;
    width: 12.5%;
    height: 12.5%;
    cursor: pointer;
}
Copy the code

Again, draw the grid through two loops, and let the layout do the rest, without calculating the coordinates.

<div 
  className='board'
  style={{
    width: 8 * gridSize,
    height: 8 * gridSize,
    border: `1px solid ${borderColor}`,
  }}>
  {chessboard.board().map((row: object[], rowIndex: number) = > {
    return row.map((item: any, colIndex: number) = > {
      return <Cell
               key={getGridAxis({ row: rowIndex.col: colIndex })}
               gridAxis={getGridAxis({ row: rowIndex.col: colIndex })}
               item={item}
               rowIndex={rowIndex}
               colIndex={colIndex}
             />
    })
  })}
</div>
Copy the code

Change the theme of the board

In the above code, I did not hardcode the colors directly into the CSS file, because I prepared two themes, the default theme, and the wood theme, in consideration of changing themes.

export const defaultTheme = {
    borderColor: 'lightgrey'.whiteGrid: '#fff'.blackGrid: 'lightgrey'.whitePieceColor: '#2b2b2b'.blackPieceColor: '#2b2b2b'.gridSize: 50.fontSize: 40,}export const woodenTheme = {
    borderColor: '#AD9278'.whiteGrid: '#D1BF9D'.blackGrid: '#AD9278'.whitePieceColor: '#2b2b2b'.blackPieceColor: '#2b2b2b'.gridSize: 50.fontSize: 40,}Copy the code

The React Context API makes it easy to do this. First we create a Context object.

interface ThemeProps {
    borderColor: string;
    whiteGrid: string;
    blackGrid: string;
    whitePieceColor: string;
    blackPieceColor: string;
    gridSize: number;
    fontSize: number;
}
interface ThemeContextProps {
    theme: ThemeProps;
    selectTheme: (t: string) = > void;
}
export const ThemeContext = createContext<ThemeContextProps>({} as ThemeContextProps);
Copy the code

Then we need to wrap a Provider package in the outermost layer of our App

const ThemeContenxtProvider: FC = ({ children }) = > {
    const [theme, setTheme] = useState<ThemeProps>(defaultTheme);
    return <ThemeContext.Provider
        value={{
            theme.selectTheme: (t: string) = > {
                switch (t) {
                    case 'wooden':
                        setTheme(woodenTheme);
                        break;
                    default:
                        setTheme(defaultTheme);
                        break;
                }
            },
        }}
    >
        {children}
    </ThemeContext.Provider>
};
export default ThemeContenxtProvider;
function App() {
    return (
      <ThemeContenxtProvider>
        <div className="container-md">
          <div className="row">
            <div className="sm-4 col main-container">
              <h3 className=' '>Simple Chess Board</h3>
              <p className=' '>Made by banana with ❤️</p>
              <ChessBoard />
              <ThemeSelect />
            </div>
          </div>
        </div>
      </ThemeContenxtProvider>
    );
}
export default App;
Copy the code

The reason for wrapping ThemeContenxtProvider one more layer is that it is possible to use Hooks directly in the Provider to do data initialization instead of exposing ctx. Provider. This requires the data initialization logic to be maintained in the outer component.

Details can be found in the React Hooks document