-
-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added playble tic tac toe game in Board game section #401
Changes from all commits
0d358c2
e5b2664
15700a6
5b94853
1581088
0b3bb78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,86 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import React, { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
function TicTacToe() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
const [board, setBoard] = useState(Array(9).fill(null)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
const [isXNext, setIsXNext] = useState(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
const winner = calculateWinner(board); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider enhancing type safety and accessibility While the basic setup is correct, consider these improvements:
Example implementation of a custom hook: function useGameState() {
const [board, setBoard] = useState(Array(9).fill(null));
const [isXNext, setIsXNext] = useState(true);
const winner = calculateWinner(board);
// ... game logic here
return { board, isXNext, winner, handleClick, resetGame };
} |
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
const handleClick = (index) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
if (board[index] || winner) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
const newBoard = board.slice(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
newBoard[index] = isXNext ? 'X' : 'O'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
setBoard(newBoard); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsXNext(!isXNext); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+8
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add input validation and state update safety The handleClick function should validate the input index and use the functional update form of setState to handle potential race conditions. const handleClick = (index) => {
+ if (typeof index !== 'number' || index < 0 || index > 8) return;
if (board[index] || winner) return;
- const newBoard = board.slice();
- newBoard[index] = isXNext ? 'X' : 'O';
- setBoard(newBoard);
+ setBoard(currentBoard => {
+ if (currentBoard[index]) return currentBoard;
+ const newBoard = currentBoard.slice();
+ newBoard[index] = isXNext ? 'X' : 'O';
+ return newBoard;
+ });
setIsXNext(!isXNext);
}; 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
const resetGame = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
setBoard(Array(9).fill(null)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsXNext(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex flex-col items-center my-20"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
<h1 className="text-6xl font-bold tracking-tighter text-center mb-5"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Tic Tac Toe | ||||||||||||||||||||||||||||||||||||||||||||||||||||
</h1> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="grid grid-cols-3 gap-2 w-64"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
{board.map((value, index) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
<button | ||||||||||||||||||||||||||||||||||||||||||||||||||||
key={index} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={() => handleClick(index)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
aria-label={`Cell ${index + 1}, ${value || 'empty'}`} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
tabIndex={0} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
disabled={board[index] || winner} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
className="w-20 h-20 bg-amber-200 flex items-center justify-center text-3xl font-semibold text-black-800 border-2 border-amber-500 ${!board[index] && !winner ? 'hover:bg-amber-300' : 'cursor-not-allowed opacity-75'} transition-all duration-200 rounded-md" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix template string syntax in className The className contains an improperly formatted template string that won't work as expected. - className="w-20 h-20 bg-amber-200 flex items-center justify-center text-3xl font-semibold text-black-800 border-2 border-amber-500 ${!board[index] && !winner ? 'hover:bg-amber-300' : 'cursor-not-allowed opacity-75'} transition-all duration-200 rounded-md"
+ className={`w-20 h-20 bg-amber-200 flex items-center justify-center text-3xl font-semibold text-black-800 border-2 border-amber-500 ${!board[index] && !winner ? 'hover:bg-amber-300' : 'cursor-not-allowed opacity-75'} transition-all duration-200 rounded-md`} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
{value} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
</button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="mt-5 content-center "> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
{winner ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
<h2 className="text-2xl font-semibold text-green-600 text-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
{winner} Wins! | ||||||||||||||||||||||||||||||||||||||||||||||||||||
</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
) : board.every((cell) => cell) ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
<h2 className="text-2xl font-semibold text-red-600 text-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
It's a Draw! | ||||||||||||||||||||||||||||||||||||||||||||||||||||
</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
<h2 className="text-xl font-semibold text-gray-700 text-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Next Player: {isXNext ? 'X' : 'O'} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
<button | ||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={resetGame} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
className="ml-1 mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 hover:text-white transition-all duration-200" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Reset Game | ||||||||||||||||||||||||||||||||||||||||||||||||||||
</button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+54
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add keyboard shortcut for game reset Consider adding a keyboard shortcut (e.g., 'R' key) for resetting the game to improve user experience. + useEffect(() => {
+ const handleKeyPress = (e) => {
+ if (e.key.toLowerCase() === 'r') {
+ resetGame();
+ }
+ };
+ window.addEventListener('keydown', handleKeyPress);
+ return () => window.removeEventListener('keydown', handleKeyPress);
+ }, []);
<button
onClick={resetGame}
+ title="Press 'R' to reset game"
className="ml-1 mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 hover:text-white transition-all duration-200"
>
Reset Game
</button> 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
function calculateWinner(board) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
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, b, c] = lines[i]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
if (board[a] && board[a] === board[b] && board[a] === board[c]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return board[a]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+65
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Optimize winner calculation with memoization The winner calculation could be optimized to prevent unnecessary recalculations. Consider using useMemo: + import React, { useState, useMemo } from 'react';
function TicTacToe() {
- const winner = calculateWinner(board);
+ const winner = useMemo(() => calculateWinner(board), [board]);
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
export default TicTacToe; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix indentation using spaces instead of tabs.
The indentation in these sections uses hard tabs. For consistency with the rest of the file and to follow markdown best practices, replace tabs with spaces.
Apply this change to fix the indentation:
Also applies to: 286-289