Skip to content

Instantly share code, notes, and snippets.

@vis97c
Last active August 4, 2023 05:30
Show Gist options
  • Save vis97c/85c24ca2e97877b8e34bc377f41f3854 to your computer and use it in GitHub Desktop.
Save vis97c/85c24ca2e97877b8e34bc377f41f3854 to your computer and use it in GitHub Desktop.
React tic tac toe
import React, { useState, useMemo } from 'react';
import ReactDOM from 'react-dom';
const rowStyle = {
display: 'flex'
}
const squareStyle = {
'width':'60px',
'height':'60px',
'backgroundColor': '#ddd',
'margin': '4px',
'display': 'flex',
'justifyContent': 'center',
'alignItems': 'center',
'fontSize': '20px',
'color': 'white',
'cursor': 'pointer',
'border': 'none',
}
const boardStyle = {
'backgroundColor': '#eee',
'width': '208px',
'alignItems': 'center',
'justifyContent': 'center',
'display': 'flex',
'flexDirection': 'column',
'border': '3px #eee solid'
}
const containerStyle = {
'display': 'flex',
'alignItems': 'center',
'flexDirection': 'column'
}
const instructionsStyle = {
'marginTop': '5px',
'marginBottom': '5px',
'fontWeight': 'bold',
'fontSize': '16px',
}
const buttonStyle = {
'marginTop': '15px',
'marginBottom': '16px',
'width': '80px',
'height': '40px',
'backgroundColor': '#8acaca',
'color': 'white',
'fontSize': '16px',
'cursor': 'pointer',
}
function Square(props) {
return (
<button
className="square"
style={squareStyle}
onClick={props.onClick}
>
{ props.move }
</button>
);
}
function Board() {
const [nextMove, setNextMove] = useState(true)
const [movements, setMovements] = useState(Array(9).fill(''))
const nextMoveValue = useMemo(() => nextMove ? "X" : "O", [nextMove])
const movementRows = useMemo(()=>{
// larger board would require a loop
return [
movements.slice(0,3),
movements.slice(3,6),
movements.slice(6)
]
}, [movements])
const movementColumns = useMemo(() => {
// larger board would require a loop
return [
movementRows.map(row=>row[0]),
movementRows.map(row=>row[1]),
movementRows.map(row=>row[2])
]
}, [movementRows])
const winner = useMemo(()=>{
// validate rows
const rowWin = movementRows.find(row => row[0] && new Set(row).size === 1)
if (rowWin) return rowWin[0]
// validate columns
const columnWin = movementColumns.find(column => column[0] && new Set(column).size === 1)
if (columnWin) return columnWin[0]
// validate diagonals
const diagonal1 = [
movementRows[0][0],
movementRows[1][1],
movementRows[2][2]
]
if(diagonal1[0] && new Set(diagonal1).size === 1) return diagonal1[0]
const diagonal2 = [
movementRows[0][2],
movementRows[1][1],
movementRows[2][0]
]
if(diagonal2[0] && new Set(diagonal2).size === 1) return diagonal2[0]
return "None"
},[movementRows])
function makeMove(squareIndex){
if(movements[squareIndex] || winner !== "None") return
const newMovements = [...movements]
newMovements[squareIndex] = nextMoveValue
setMovements(newMovements)
setNextMove(!nextMove)
}
function reset(){
setMovements(Array(9).fill(''))
setNextMove(true)
}
return (
<div style={containerStyle} className="gameBoard">
<div id="statusArea" className="status" style={instructionsStyle}>Next player: <span>{nextMoveValue}</span></div>
<div id="winnerArea" className="winner" style={instructionsStyle}>Winner: <span>{winner}</span></div>
<button style={buttonStyle} onClick={reset}>Reset</button>
<div style={boardStyle}>
{ movementRows.map((row, rowIndex) => (
<div className="board-row" style={rowStyle} key={`row-${rowIndex}`}>
{row.map((move, moveIndex) => (
<Square key={`move-${moveIndex}`} move={move} onClick={() => makeMove((rowIndex*3) + moveIndex)} />
))}
</div>
))}
</div>
</div>
);
}
function Game() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
</div>
);
}
ReactDOM.render(
<Game />,
document.getElementById('root')
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment