From c791116bf90129214084825c74cd32f06cdb2b92 Mon Sep 17 00:00:00 2001 From: Elad Abutbul Date: Mon, 15 Dec 2025 09:48:45 +0200 Subject: [PATCH 1/3] elad-abutbul --- src/components/Board.tsx | 14 +++----- src/components/Game.tsx | 67 +++++++++++++++++++++++++++++---------- src/components/Square.tsx | 6 ++-- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/components/Board.tsx b/src/components/Board.tsx index 3989a44..2ae50d5 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((square, index) => { + return onSquareClick(index)} />; + })}
); } diff --git a/src/components/Game.tsx b/src/components/Game.tsx index 80be18b..961a3ba 100644 --- a/src/components/Game.tsx +++ b/src/components/Game.tsx @@ -1,24 +1,57 @@ -import { useState } from "react"; +import { useEffect, useState } 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); + const [squares, setSquares] = useState<(string | null)[]>( + Array(9).fill(null) + ); + const [isXNext, setIsXNext] = useState(true); + const [timer, setTimer] = useState(10); + const [stop, setStop] = useState(false); + + function handleSquareClick(index: number) { + if (stop) return; + if (squares[index]) return; + const nextSquares = [...squares]; + nextSquares[index] = isXNext ? "X" : "O"; + setSquares(nextSquares); + setIsXNext((prev) => !prev); + setTimer(10); + } + function handleReset() { + setIsXNext(true); + setSquares(Array(9).fill(null)); + setTimer(10); + setStop(false); + } - function handleSquareClick(index: number) { - // Temporary: no gameplay logic yet - console.log("Clicked square:", index); + useEffect(() => { + if (!stop) { + const timerId = setTimeout(() => { + if (timer == 0) { + setIsXNext((prev) => !prev); + setTimer(10); + } else { + setTimer((time) => time - 1); + } + }, 1000); + return () => clearTimeout(timerId); } + }, [timer, stop]); - return ( -
-

Tic Tac Toe

- - -

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

- -
- ); + function handleStop() { + setStop((stop) => !stop); + } + return ( +
+

Tic Tac Toe

+

+ time for player {isXNext ? "X" : "O"}- {timer} sec +

+ +

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

+ + +
+ ); } diff --git a/src/components/Square.tsx b/src/components/Square.tsx index 3e9f11a..a227a59 100644 --- a/src/components/Square.tsx +++ b/src/components/Square.tsx @@ -3,10 +3,10 @@ interface SquareProps { onClick: () => void; } -export default function Square(props: SquareProps) { +export default function Square({value,onClick}: SquareProps) { return ( - ); } From f017c97d4add70898680c1543c3b19274a86286a Mon Sep 17 00:00:00 2001 From: Elad Abutbul Date: Mon, 15 Dec 2025 10:09:35 +0200 Subject: [PATCH 2/3] winner is function is created --- src/components/Game.tsx | 81 ++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/src/components/Game.tsx b/src/components/Game.tsx index 961a3ba..f4e7c14 100644 --- a/src/components/Game.tsx +++ b/src/components/Game.tsx @@ -8,7 +8,42 @@ export default function Game() { const [isXNext, setIsXNext] = useState(true); const [timer, setTimer] = useState(10); const [stop, setStop] = useState(false); - + + function calculateWinner(): "X" | "O" | 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], + ]; + + for (let i = 0; i < lines.length; i++) { + const a = lines[i][0]; + const b = lines[i][1]; + const c = lines[i][2]; + + if ( + squares[a] !== null && + squares[a] === squares[b] && + squares[a] === squares[c] + ) { + return squares[a] as "X" | "O"; + } + } + + return null; + } + + const winner = calculateWinner(); + + useEffect(() => { + if (winner) setStop(true); + }, [winner]); + function handleSquareClick(index: number) { if (stop) return; if (squares[index]) return; @@ -18,6 +53,7 @@ export default function Game() { setIsXNext((prev) => !prev); setTimer(10); } + function handleReset() { setIsXNext(true); setSquares(Array(9).fill(null)); @@ -26,18 +62,18 @@ export default function Game() { } useEffect(() => { - if (!stop) { - const timerId = setTimeout(() => { - if (timer == 0) { - setIsXNext((prev) => !prev); - setTimer(10); - } else { - setTimer((time) => time - 1); - } - }, 1000); - return () => clearTimeout(timerId); - } - }, [timer, stop]); + if (stop || winner) return; + const timerId = setTimeout(() => { + if (timer === 0) { + setIsXNext((prev) => !prev); + setTimer(10); + } else { + setTimer((prev) => prev - 1); + } + }, 1000); + + return () => clearTimeout(timerId); + }, [timer, stop, winner]); function handleStop() { setStop((stop) => !stop); @@ -45,13 +81,22 @@ export default function Game() { return (

Tic Tac Toe

-

- time for player {isXNext ? "X" : "O"}- {timer} sec -

+ {winner &&

Winner: {winner}

} + {winner ? ( + "" + ) : ( +

+ time for player {isXNext ? "X" : "O"}- {timer} sec +

+ )} -

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

+ {winner ? "" :

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

} - + {winner ? ( + "" + ) : ( + + )}
); } From c9629c8e7464748d7a40ca960736f17e7ae30ddc Mon Sep 17 00:00:00 2001 From: Elad Abutbul Date: Fri, 19 Dec 2025 10:36:05 +0200 Subject: [PATCH 3/3] Clean up Game logic: constants, timer with interval, and winner calculation --- src/components/Board.tsx | 2 +- src/components/Game.tsx | 93 ++++++++++++++-------------------- src/constants/GameConstants.ts | 19 +++++++ 3 files changed, 57 insertions(+), 57 deletions(-) create mode 100644 src/constants/GameConstants.ts diff --git a/src/components/Board.tsx b/src/components/Board.tsx index 2ae50d5..66a22b8 100644 --- a/src/components/Board.tsx +++ b/src/components/Board.tsx @@ -9,7 +9,7 @@ export default function Board({ squares, onSquareClick }: BoardProps) { return (
{squares.map((square, index) => { - return onSquareClick(index)} />; + return onSquareClick(index)} />; })}
); diff --git a/src/components/Game.tsx b/src/components/Game.tsx index f4e7c14..3e1ff40 100644 --- a/src/components/Game.tsx +++ b/src/components/Game.tsx @@ -1,79 +1,62 @@ import { useEffect, useState } from "react"; import Board from "./Board"; +import { GAME_CONSTANTS } from "../constants/GameConstants"; export default function Game() { - const [squares, setSquares] = useState<(string | null)[]>( - Array(9).fill(null) + const [squares, setSquares] = useState<("X" | "O" | null)[]>( + Array(GAME_CONSTANTS.BOARD_SIZE).fill(null) ); const [isXNext, setIsXNext] = useState(true); - const [timer, setTimer] = useState(10); + const [timer, setTimer] = useState(GAME_CONSTANTS.TURN_DURATION_SECONDS); const [stop, setStop] = useState(false); - function calculateWinner(): "X" | "O" | 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], - ]; + const pauseButtonLabel = stop ? GAME_CONSTANTS.RESUME : GAME_CONSTANTS.STOP; - for (let i = 0; i < lines.length; i++) { - const a = lines[i][0]; - const b = lines[i][1]; - const c = lines[i][2]; + function calculateWinner(): "X" | "O" | null { + for (const [a, b, c] of GAME_CONSTANTS.WINNING_LINES) { + const value = squares[a]; - if ( - squares[a] !== null && - squares[a] === squares[b] && - squares[a] === squares[c] - ) { - return squares[a] as "X" | "O"; + if (value && value === squares[b] && value === squares[c]) { + return value; } } - return null; } const winner = calculateWinner(); - useEffect(() => { - if (winner) setStop(true); - }, [winner]); - function handleSquareClick(index: number) { - if (stop) return; + if (stop || winner) return; if (squares[index]) return; const nextSquares = [...squares]; - nextSquares[index] = isXNext ? "X" : "O"; + nextSquares[index] = isXNext ? GAME_CONSTANTS.X : GAME_CONSTANTS.O; setSquares(nextSquares); setIsXNext((prev) => !prev); - setTimer(10); + setTimer(GAME_CONSTANTS.TURN_DURATION_SECONDS); } function handleReset() { setIsXNext(true); - setSquares(Array(9).fill(null)); - setTimer(10); + setSquares(Array(GAME_CONSTANTS.BOARD_SIZE).fill(null)); + setTimer(GAME_CONSTANTS.TURN_DURATION_SECONDS); setStop(false); } useEffect(() => { if (stop || winner) return; - const timerId = setTimeout(() => { - if (timer === 0) { - setIsXNext((prev) => !prev); - setTimer(10); - } else { - setTimer((prev) => prev - 1); - } - }, 1000); - return () => clearTimeout(timerId); - }, [timer, stop, winner]); + const id = setInterval(() => { + setTimer((prev) => { + if (prev === 0) { + setIsXNext((p) => !p); + return GAME_CONSTANTS.TURN_DURATION_SECONDS; + } + return prev - 1; + }); + }, GAME_CONSTANTS.TIMER_INTERVAL_MS); + + return () => clearInterval(id); + }, [stop, winner]); function handleStop() { setStop((stop) => !stop); @@ -81,22 +64,20 @@ export default function Game() { return (

Tic Tac Toe

- {winner &&

Winner: {winner}

} - {winner ? ( - "" - ) : ( -

- time for player {isXNext ? "X" : "O"}- {timer} sec -

- )} - {winner ? "" :

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

} - {winner ? ( - "" +

Winner: {winner}

) : ( - + <> +

+ time for player {isXNext ? GAME_CONSTANTS.X : GAME_CONSTANTS.O} : {timer} sec +

+

Next Player: {isXNext ? GAME_CONSTANTS.X : GAME_CONSTANTS.O}

+ + )} + +
); } diff --git a/src/constants/GameConstants.ts b/src/constants/GameConstants.ts new file mode 100644 index 0000000..f1a0831 --- /dev/null +++ b/src/constants/GameConstants.ts @@ -0,0 +1,19 @@ +export const GAME_CONSTANTS = { + RESUME: "resume", + STOP: "stop", + X: "X", + O: "O", + BOARD_SIZE: 9 as number, + TURN_DURATION_SECONDS: 10 as number, + TIMER_INTERVAL_MS: 1000 as number, + 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], + ], +} as const;