Skip to content

Commit

Permalink
feat: add tic-tac-toe React prototype (#665)
Browse files Browse the repository at this point in the history
* add prototype

* remove old folders

* update prototype and user stories

* format code

* update user stories

Co-authored-by: Jessica Wilkins  <[email protected]>

* update code

* remove extra stories

* Update fullstack-cert/react-projects/tic-tac-toe/user-stories.md

---------

Co-authored-by: Jessica Wilkins <[email protected]>
  • Loading branch information
zairahira and jdwilkin4 authored Feb 25, 2025
1 parent 88a6221 commit f57d6d7
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 0 deletions.
23 changes: 23 additions & 0 deletions fullstack-cert/react-projects/tic-tac-toe/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tic-Tac-Toe</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script data-plugins="transform-modules-umd" type="text/babel" src="index.jsx"></script>
<link rel="stylesheet" href="styles.css" />
</head>

<body>
<div id="root"></div>
<script data-plugins="transform-modules-umd" type="text/babel" data-presets="react" data-type="module">
import { Board } from './index.jsx';
ReactDOM.createRoot(document.getElementById('root')).render(<Board />);
</script>
</body>

</html>
73 changes: 73 additions & 0 deletions fullstack-cert/react-projects/tic-tac-toe/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const Square = ({ value, onClick }) => {
return (
<button className="square" onClick={onClick}>
{value}
</button>
);
};

const calculateWinner = (squares) => {
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 line of lines) {
const [a, b, c] = line;
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
};

const Board = () => {
const [squares, setSquares] = React.useState(Array(9).fill(null));
const [isXNext, setIsXNext] = React.useState(true);
const winner = calculateWinner(squares);
const isDraw = squares.every(square => square !== null) && !winner;

const handleClick = (index) => {
if (squares[index] || winner || isDraw) return;
const nextSquares = squares.slice();
nextSquares[index] = isXNext ? 'X' : 'O';
setSquares(nextSquares);
setIsXNext(!isXNext);
};

const resetGame = () => {
setSquares(Array(9).fill(null));
setIsXNext(true);
};

const renderSquare = (index) => {
return <Square value={squares[index]} onClick={() => handleClick(index)} />;
};

return (
<div className="board">
<h1>Tic-Tac-Toe</h1>
<div className="status">
{winner ? `Winner: ${winner}` : isDraw ? "It's a Draw!" : `Next Player: ${isXNext ? 'X' : 'O'}`}
</div>
<div className="board-row">
{renderSquare(0)}
{renderSquare(1)}
{renderSquare(2)}
</div>
<div className="board-row">
{renderSquare(3)}
{renderSquare(4)}
{renderSquare(5)}
</div>
<div className="board-row">
{renderSquare(6)}
{renderSquare(7)}
{renderSquare(8)}
</div>
<button className="reset-button" onClick={resetGame}>Reset Game</button>
</div>
);
};

export { Board };
48 changes: 48 additions & 0 deletions fullstack-cert/react-projects/tic-tac-toe/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
* {
font-family: "Secular One", sans-serif;
font-weight: 400;
font-style: normal;
}
.App {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

.board {
display: flex;
flex-direction: column;
align-items: center;
}

.status {
margin: 10px;
font-size: 1.2em;
}

.board-row {
display: flex;
}

.square {
width: 60px;
height: 60px;
font-size: 1.5em;
margin: 5px;
background: #fff;
border: 1px solid #999;
cursor: pointer;
border-radius: 5px;
}

.square:focus {
outline: none;
}

.reset-button {
margin-top: 20px;
padding: 10px 20px;
font-size: 1em;
cursor: pointer;
}
12 changes: 12 additions & 0 deletions fullstack-cert/react-projects/tic-tac-toe/user-stories.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
1. You should create a `Square` component that accepts two props: `value` and `onClick`.
2. Your `Square` component should return a `button` element with the class `square`.
3. The `button` element should display the `value` prop inside it.
4. The `Square` component should trigger the `onClick` function when the button is clicked.
5. You should create a `calculateWinner` function that accepts an array `squares` as a parameter and returns the winner if there is one.
6. Your `calculateWinner` function should check for winning lines using an array of indices for rows, columns, and diagonals, and return the value of the winner (`X` or `O`) if there is a match.
7. You should create and export a `Board` component.
8. Your `Board` component should use React's `useState` hook to keep track of the game state, including the `squares` array and whether it is player `X` or `O` turn.
9. Your `Board` component should call the `calculateWinner` function to check if there's a winner after each move.
10. Your `Board` component should render a status message that shows the current player or the winner, or indicate if there is a draw.
11. Your `Board` component should render a grid of `Square` components for the 3x3 Tic-Tac-Toe board.
12. You should create a `resetGame` function that resets the game state to its initial values when triggered.

0 comments on commit f57d6d7

Please sign in to comment.