Skip to content

Instantly share code, notes, and snippets.

@mithi
Last active October 3, 2020 16:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mithi/c7ce503eeaee2f07721b44000becf5d4 to your computer and use it in GitHub Desktop.
Save mithi/c7ce503eeaee2f07721b44000becf5d4 to your computer and use it in GitHub Desktop.
/*
0 | 1 | 2
----------
3 | 4 | 5
----------
6 | 7 | 8
*/
function Board({board, onClick}) {
const renderSquare = i => (
<button
className="square"
key={`square${i}`}
onClick={() => onClick(i)}
>
{board[i]}
</button>
)
const renderRow = array => (
<div className="board-row">{array.map(i => renderSquare(i))}</div>
)
return (
<div>
{renderRow([0, 1, 2])}
{renderRow([3, 4, 5])}
{renderRow([6, 7, 8])}
</div>
)
}
function TicTacToe() {
const [history, setHistory] = useLocalStorageState('tic-tac-toe:history', [
Array(9).fill(null),
])
const [currentStep, setCurrentStep] = useLocalStorageState(
'tic-tac-toe:step',
0,
)
const board = history[currentStep]
const winner = solveWinner(board)
const boardFull = !board.some(x => x === null)
const currentPlayer = solveCurrentPlayer(board)
const status = solveStatus(winner, boardFull, currentPlayer)
const updateBoard = squareId => {
if (board[squareId] || winner) {
return
}
const newBoard = [...board]
newBoard[squareId] = currentPlayer
const newHistory = [...history.slice(0, currentStep + 1), newBoard]
setHistory(newHistory)
setCurrentStep(currentStep + 1)
}
const restart = () => {
setCurrentStep(0)
setHistory([history[0]])
}
const historyButtons = history.map((_, step) => {
const label = step ? `Go to move #${step}` : 'Go to game start'
const isCurrentStep = step === currentStep
return (
<li key={`${step}-history-button`}>
<button
disabled={isCurrentStep}
onClick={() => setCurrentStep(step)}
>
{label} {isCurrentStep ? '(current)' : null}
</button>
</li>
)
})
const restartButton = (
<button className="restart" onClick={restart}>
...RESTART...
</button>
)
return (
<div className="game">
<div className="game-board">
<div className="status">{status}</div>
<Board {...{board, onClick: updateBoard}} />
{restartButton}
<ol>{historyButtons}</ol>
</div>
</div>
)
}
function solveStatus(winner, boardFull, currentPlayer) {
if (winner) {
return `${winner} Won.`
}
if (boardFull) {
return 'Nobody won.'
}
return `turn: ${currentPlayer}`
}
function solveCurrentPlayer(board) {
const xCount = board.filter(r => r === 'X').length
const oCount = board.filter(r => r === 'O').length
return oCount === xCount ? 'X' : 'O'
}
const WINNING_LINES = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
]
function solveWinner(board) {
for (const line of WINNING_LINES) {
const [a, b, c] = line
const [w, x, y] = [board[a], board[b], board[c]]
const lineFormed = w && w === x && w === y
if (lineFormed) {
return w
}
}
return null
}
function App() {
return <TicTacToe />
}
export default App
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment