diff --git a/src/components/Board.tsx b/src/components/Board.tsx index 3989a44..cefaa4c 100644 --- a/src/components/Board.tsx +++ b/src/components/Board.tsx @@ -5,18 +5,12 @@ interface BoardProps { onSquareClick: (index: number) => void; } -export default function Board(props: BoardProps) { +export default function Board({ squares, onSquareClick }: BoardProps) { return (
- props.onSquareClick(0)} /> - props.onSquareClick(1)} /> - props.onSquareClick(2)} /> - props.onSquareClick(3)} /> - props.onSquareClick(4)} /> - props.onSquareClick(5)} /> - props.onSquareClick(6)} /> - props.onSquareClick(7)} /> - props.onSquareClick(8)} /> + {squares.map((sqr, index) => ( + onSquareClick(index)} /> + ))}
); } diff --git a/src/components/Game.tsx b/src/components/Game.tsx index 80be18b..29de6d5 100644 --- a/src/components/Game.tsx +++ b/src/components/Game.tsx @@ -1,24 +1,105 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import Board from "./Board"; export default function Game() { - const [squares, setSquares] = useState<(string | null)[]>( - Array(9).fill(null) - ); - const [isXNext, setIsXNext] = useState(true); - - function handleSquareClick(index: number) { - // Temporary: no gameplay logic yet - console.log("Clicked square:", index); + const [squares, setSquares] = useState<(string | null)[]>( + Array(9).fill(null) + ); + + const timeLimit = 10; + + const [isXNext, setIsXNext] = useState(true); + const [winner, setWinner] = useState(null); + const [time, setTime] = useState(timeLimit); + const [timeRunning, setTimeRunning] = useState(false); + + useEffect(() => { + if (!timeRunning || winner) return; + + const timerId = window.setInterval(() => { + setTime((currTime) => Math.max(currTime - 1, 0)); + }, 1000); + + return () => window.clearInterval(timerId); + }, [timeRunning, winner]); + + useEffect(() => { + if (!timeRunning || winner) return; + + if (time === 0) { + setIsXNext((currPlayer) => !currPlayer); + setTime(timeLimit); + } + }, [time, timeRunning, winner, timeLimit]); + + function handleGameRestart() { + setSquares(Array(9).fill(null)); + setIsXNext(true); + setWinner(null); + setTime(timeLimit); + setTimeRunning(false); + } + + function handleSquareClick(index: number) { + if (squares[index] !== null || winner) return; + + if (!timeRunning) setTimeRunning(true); + + const nextSquares = [...squares]; + nextSquares[index] = isXNext ? "X" : "O"; + setSquares(nextSquares); + + const winningPlayer = calculateWinner(nextSquares); + if (winningPlayer) return; + + setIsXNext((p) => !p); + setTime(timeLimit); + } + + function calculateWinner(boardArr: (string | null)[]) { + const 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], + ] as const; + + for (const [a, b, c] of lines) { + if ( + boardArr[a] && + boardArr[a] === boardArr[b] && + boardArr[a] === boardArr[c] + ) { + setWinner(boardArr[a]); + setTimeRunning(false); + return boardArr[a]; + } } + return null; + } + + return ( +
+

Asaf's Basic React Tic Tac Toe

+ +

Play timer: {time}

- return ( -
-

Tic Tac Toe

+ +

{winner == null ? null : "Winner: " + winner}

- -

Next Player: {isXNext ? "X" : "O"}

+

+ {winner == null + ? isXNext + ? "Next Player: X" + : "Next Player: O" + : null} +

-
- ); + +
+ ); } diff --git a/src/components/Square.tsx b/src/components/Square.tsx index 3e9f11a..dfe8976 100644 --- a/src/components/Square.tsx +++ b/src/components/Square.tsx @@ -1,12 +1,14 @@ interface SquareProps { - value: string | null; - onClick: () => void; + value: string | null; + onClickFunction: () => void; } -export default function Square(props: SquareProps) { - return ( - - ); -} +export const Square = ({ value, onClickFunction }: SquareProps) => { + return ( + + ); +}; + +export default Square;