import React, { useState, useEffect } from 'react';
import './Tic-Tac-Toe.css';
const initialState = {
message: '',
playerSide: '',
playerToken: '',
computerSide: '',
computerToken: '',
gameOver: true,
board: [
['', '', ''],
['', '', ''],
['', '', ''],
],
winOptions: [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
],
};
export default function TicTacToe() {
const [state, setState] = useState(initialState);
useEffect(() => {
const gameStatus = checkGameOver();
if (gameStatus) {
handleGameOver(gameStatus);
}
//eslint-disable-next-line
}, [state.board])
const playerChoice = (value) => {
let computer = '';
value === 'X' ? (computer = 'O') : (computer = 'X');
setState({
...state,
playerSide: value,
computerSide: computer,
gameOver: false,
message: `You are ${value}'s, Joshua is ${computer}'s`,
});
};
const checkGameOver = () => {
for (const winOption of state.winOptions) {
const [a, b, c] = winOption.map(
(index) => state.board[Math.floor(index / 3)][index % 3]
);
if (a && a === b && a === c) {
return a === state.playerSide ? 'player' : 'computer';
}
}
if (state.board.every((row) => row.every((cell) => cell !== ''))) {
return 'draw';
}
return null;
};
const playerMove = (e) => {
const id = e.target.id;
const newBoard = state.board.map((row) => [...row]);
newBoard[Math.floor(id / 3)][id % 3] = state.playerSide;
setState({
...state,
board: newBoard,
playerToken: id,
});
setTimeout(() => compTurn(newBoard), 1000);
};
const compTurn = (currentBoard) => {
const newBoard = currentBoard.map((row) => [...row]);
const emptyCells = [];
newBoard.forEach((row, rowIndex) =>
row.forEach((cell, colIndex) => {
if (cell === '') {
emptyCells.push({ rowIndex, colIndex });
}
})
);
if (emptyCells.length === 0) {
return;
}
const compMove = Math.floor(Math.random() * emptyCells.length);
newBoard[emptyCells[compMove].rowIndex][emptyCells[compMove].colIndex] =
state.computerSide;
setState({
...state,
board: newBoard,
});
const gameStatus = checkGameOver();
if (gameStatus) {
handleGameOver(gameStatus);
}
};
const handleGameOver = (gameStatus) => {
if (gameStatus === 'player') {
setState({
...initialState,
message: 'Congratulations, you won!',
});
} else if (gameStatus === 'computer') {
setState({
...initialState,
message: 'Sorry, Joshua won. Better luck next time!',
});
} else if (gameStatus === 'draw') {
setState({
...initialState,
message: 'It\'s a draw! Play again?',
});
}
};
const handleResign = () => {
setState({
...initialState,
message: 'You have lost because you gave up. Choose a side to play again.',
});
};
return (
<>
<div id="game">
<h4 className='announcement'>{state.message}</h4>
{!state.playerSide ?
<div id='tic-tac-toe-board'>
<div className='player-choice'>
<h4>Choose a Side</h4>
<div className="side-choice">
<h2 onClick={() => playerChoice('X')}>X</h2>
<h2 onClick={() => playerChoice('O')}>O</h2>
</div>
</div>
</div> :
<div id="tic-tac-toe-board">
{state.board.flatMap((grid) => grid)
.map((square, idx) => {
return square === state.playerSide
? <div className='player-square' id={idx} key={idx} >{square}</div>
: square === state.computerSide
? <div className='computer-square' id={idx} key={idx} >{square}</div>
: <div className='square' id={idx} key={idx} onClick={playerMove}>{square}</div>
})
}
</div>}
<div className="actions">
{!state.gameOver &&
<button id="give-up" disabled={state.gameOver === true ? true : false} onClick={handleResign}>Resign</button>
}
</div>
</div>
</>
);
}