diff --git a/package-lock.json b/package-lock.json
index 2fec462..88e255c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -57,6 +57,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -1389,6 +1390,7 @@
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -1399,6 +1401,7 @@
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -1458,6 +1461,7 @@
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.49.0",
"@typescript-eslint/types": "8.49.0",
@@ -1709,6 +1713,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1814,6 +1819,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -2035,6 +2041,7 @@
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -2721,6 +2728,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -2782,6 +2790,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
"integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -2984,6 +2993,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -3070,6 +3080,7 @@
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -3191,6 +3202,7 @@
"integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
"dev": true,
"license": "MIT",
+ "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/src/components/Board.tsx b/src/components/Board.tsx
index 3989a44..64b709b 100644
--- a/src/components/Board.tsx
+++ b/src/components/Board.tsx
@@ -2,21 +2,22 @@ import Square from "./Square";
interface BoardProps {
squares: (string | null)[];
+ isGameOver: boolean;
onSquareClick: (index: number) => void;
}
export default function Board(props: 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)} />
+ props.onSquareClick(0)} isDisabled={props.squares[0] != null || props.isGameOver} />
+ props.onSquareClick(1)} isDisabled={props.squares[1] != null || props.isGameOver}/>
+ props.onSquareClick(2)} isDisabled={props.squares[2] != null || props.isGameOver}/>
+ props.onSquareClick(3)} isDisabled={props.squares[3] != null || props.isGameOver}/>
+ props.onSquareClick(4)} isDisabled={props.squares[4] != null || props.isGameOver}/>
+ props.onSquareClick(5)} isDisabled={props.squares[5] != null || props.isGameOver}/>
+ props.onSquareClick(6)} isDisabled={props.squares[6] != null || props.isGameOver}/>
+ props.onSquareClick(7)} isDisabled={props.squares[7] != null || props.isGameOver}/>
+ props.onSquareClick(8)} isDisabled={props.squares[8] != null || props.isGameOver}/>
);
}
diff --git a/src/components/Game.tsx b/src/components/Game.tsx
index 80be18b..e0663a4 100644
--- a/src/components/Game.tsx
+++ b/src/components/Game.tsx
@@ -1,4 +1,4 @@
-import { useState } from "react";
+import {useEffect, useState } from "react";
import Board from "./Board";
export default function Game() {
@@ -6,18 +6,85 @@ export default function Game() {
Array(9).fill(null)
);
const [isXNext, setIsXNext] = useState(true);
+ const [winner, setWinner] = useState<(string | null)>(null)
+ const turnTime = 10;
+ const X = "x"
+ const O = "O";
+ const currentPlayer = isXNext? X:O
+ const[timeLeft, setTimeLeft] = useState(turnTime)
+ const winningCombinations: number[][] = [
+ [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 tide = !winner && !squares.flat().includes(null)
+ const winnerText = tide? "It's A tide": winner? `winner is ${winner}`: ""
+ const nextTurnText = `Next Player: ${isXNext ? X : O}`
+
+ useEffect(() => {
+ if(winner || tide) return
+
+ if(timeLeft <= 0) {
+ setIsXNext(prev => !prev)
+ setTimeLeft(turnTime)
+ };
+
+ const timer = setTimeout(() => {
+ setTimeLeft(timeLeft - 1)
+ }, 1000)
+
+ return () => clearTimeout(timer)
+
+ },[timeLeft])
+
+ function checkCombination(comb: number[], newSquares: (string | null)[]): Boolean {
+ return newSquares[comb[0]] !== null &&
+ newSquares[comb[1]] === newSquares[comb[0]] &&
+ newSquares[comb[2]] === newSquares[comb[1]]
+ }
+
+ function calculateWinner(newSquares: (string | null)[]): (string | null) {
+ for(const comb of winningCombinations) {
+ if(checkCombination(comb, newSquares)) {
+ return newSquares[comb[0]]
+ }
+ }
+
+ return null;
+ }
function handleSquareClick(index: number) {
- // Temporary: no gameplay logic yet
- console.log("Clicked square:", index);
+ const newSquares: (string | null)[] = [...squares]
+ newSquares[index] = currentPlayer;
+ const winner = calculateWinner(newSquares)
+ setTimeLeft(turnTime)
+ if(!winner) {
+ setIsXNext(prev => !prev)
+ }
+
+ setSquares(newSquares)
+ setWinner(winner)
+ }
+
+ function reset() {
+ setSquares(Array(9).fill(null))
+ setWinner(null)
+ setIsXNext(true)
}
return (
Tic Tac Toe
-
-
-
Next Player: {isXNext ? "X" : "O"}
+
+ {winnerText || nextTurnText}
+ {(winner || tide) && }
+ {!(winner || tide) && `time: ${timeLeft}`}
);
diff --git a/src/components/Square.tsx b/src/components/Square.tsx
index 3e9f11a..9f0e609 100644
--- a/src/components/Square.tsx
+++ b/src/components/Square.tsx
@@ -1,11 +1,12 @@
interface SquareProps {
value: string | null;
+ isDisabled: boolean;
onClick: () => void;
}
export default function Square(props: SquareProps) {
return (
-