React hooksで⚪︎×ゲーム

by wintyo

HTML

<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.6/index.min.js" integrity="sha512-PMVw2HzV4gXtPe+BpvOQRQe9HtKcU4jA4N9ilRq+paTcz/ERKCt4d9hqXlslfsKOydknA4Y4JsiQexm/1W8zIA==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.7.2/prop-types.js" integrity="sha512-Ttnt6nUh/1oV+4T/KWkG/ORvlIEOZohMCT/GIzelxTceoIreR2A1r6zmgFnEvi7GgstoDblmpMO/Gi9CXKTqGQ==" crossorigin="anonymous"></script>

SCSS

.game {
  display: flex;
  
  &__item {
    & + & {
      margin-left: 20px;
    }
  }
}

.board {
  border: solid 1px #000;
  width: 200px;
  
  &__line {
    display: flex;
    
    & + & {
      border-top: solid 1px #000;
    }
  }
}

.board-item {
  position: relative;
  flex: 1 1 0;
  
  &.-clickable {
    cursor: pointer; 
  }
  
  & + & {
    border-left: solid 1px #000;
  }
  
  &::before {
    display: block;
    padding-top: 100%;
    content: '';
  }
  
  &__content {
    position: absolute;
    font-size: 50px;
    line-height: 1;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
}

React

const { useState } = React;

const SIZE = 3;
const PLAYERS = ['X', '◯'];

/**
 * 最初の盤面データを生成する
 * @param size - 縦横それぞれの方向の個数
 * @return 盤面データ
 */
const createInitialBoardData = (size) => {
  return Array(size).fill().map(() => Array(size).fill(null));
};

/**
 * 勝者の判定をする
 * @param boardData - 盤面データ
 * @return 勝者(nullの場合はまだ未決定)
 */
const judgeWinner = (boardData) => {
  // 横ラインの勝者判定
  for (let i = 0; i < boardData.length; i++) {
  	const putPlayer = boardData[i][0];
    // 始めが誰も置いていない場合は調べても無駄なのでスキップ
    if (putPlayer == null) {
      continue;
    }
    const isThePlayerWon = boardData[i]
      .every((player) => player === putPlayer);
    if (isThePlayerWon) {
      return putPlayer;
    }
  }
  
  // 縦ラインの勝者判定
  for (let j = 0; j < boardData[0].length; j++) {
  	const putPlayer = boardData[0][j];
    // 始めが誰も置いていない場合は調べても無駄なのでスキップ
    if (putPlayer == null) {
      continue;
    }
    const isThePlayerWon = _.times(boardData[0].length)
      .map((i) => boardData[i][j])
      .every((player) => player === putPlayer);
    if (isThePlayerWon) {
      return putPlayer;
    }
  }
  
  // 右下ラインの勝者判定
  {
    const putPlayer = boardData[0][0];
    if (putPlayer != null) {
      const isThePlayerWon = _.times(boardData.length)
        .map((i) => boardData[i][i])
        .every((player) => player === putPlayer);
      if (isThePlayerWon) {
        return putPlayer;
      }
    }
  }
  
  // 右上ラインの勝者判定
  {
    const putPlayer = boardData[boardData.length - 1][0];
    if (putPlayer != null) {
      const isThePlayerWon = _.times(boardData.length)
        .map((i) => boardData[boardData.length - 1 - i][i])
        .every((player) => player === putPlayer);
      if (isThePlayerWon) {
        return putPlayer;
      }
    }
  }
  
  // 誰も見つけられなかったらnullを返す
  return null;
};

/**
 * 盤面のコンポーネント
 */
const Board = (props) => {
  return (
  	<div className="board">
      {props.table.map((row, i) => (
      	<div key={i} className="board__line">
      	  {row.map((col, j) => (
  ...