-
-
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 2 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 |
---|---|---|
|
@@ -22,6 +22,7 @@ import bg from '../../assets/Boardgames/bg.jpg'; | |
import { LazyLoadImage } from 'react-lazy-load-image-component'; | ||
import 'react-lazy-load-image-component/src/effects/blur.css'; | ||
import MainHOC from '../MainHOC'; | ||
import { Link } from 'react-router-dom'; | ||
export default MainHOC(Boardgame); | ||
function Boardgame() { | ||
const [selectedBoard, setSelectedBoard] = useState(null); | ||
|
@@ -396,7 +397,7 @@ function Boardgame() { | |
onClick={() => handleInstantPlay(board)} | ||
className="px-4 py-2 text-white bg-blue-500 rounded-lg opacity-0 transition-opacity duration-700 delay-300 group-hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-blue-500" | ||
> | ||
Instant Play | ||
<Link to="/TicTacToe">Instant Play</Link> | ||
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. Conditionally render the Instant Play button. Currently, the "Instant Play" button appears for all board games, but based on the PR objectives, only Tic Tac Toe should be playable. This might confuse users who expect other games to be playable as well. Consider conditionally rendering the button only for Tic Tac Toe: <div className="flex space-x-2 mt-4">
<button
onClick={() => handleOpenInstructions(board)}
className="px-4 py-2 text-white bg-green-500 rounded-lg opacity-0 transition-opacity duration-700 delay-300 group-hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
See Instructions
</button>
+ {board.title === "Tic-Tac-Toe" && (
<Link
to="/TicTacToe"
className="inline-block px-4 py-2 text-white bg-blue-500 rounded-lg opacity-0 transition-opacity duration-700 delay-300 group-hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
Instant Play
</Link>
+ )}
</div>
Fix button accessibility and remove unused handler. The current implementation has several issues:
Consider this implementation instead: -<button
- onClick={() => handleInstantPlay(board)}
- className="px-4 py-2 text-white bg-blue-500 rounded-lg opacity-0 transition-opacity duration-700 delay-300 group-hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
->
- <Link to="/TicTacToe">Instant Play</Link>
-</button>
+<Link
+ to="/TicTacToe"
+ className="inline-block px-4 py-2 text-white bg-blue-500 rounded-lg opacity-0 transition-opacity duration-700 delay-300 group-hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
+>
+ Instant Play
+</Link>
|
||
</button> | ||
</div> | ||
</div> | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,79 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
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-3xl font-bold 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)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
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 hover:bg-amber-300 transition-all duration-200 rounded-md" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
{value} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
</button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
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. Improve accessibility and keyboard navigation The game board buttons need accessibility enhancements:
Apply these improvements: <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 hover:bg-amber-300
+ 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
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
</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> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
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 Enhance game status with score tracking Consider adding a score tracking feature to make the game more engaging across multiple rounds. Add score tracking: + const [scores, setScores] = useState({ X: 0, O: 0 });
const resetGame = () => {
setBoard(Array(9).fill(null));
setIsXNext(true);
};
+ const updateScore = (winner) => {
+ if (winner) {
+ setScores(prev => ({
+ ...prev,
+ [winner]: prev[winner] + 1
+ }));
+ }
+ };
+ // Add score display in the UI
+ <div className="mt-3 text-lg">
+ <span className="mr-4">X: {scores.X}</span>
+ <span>O: {scores.O}</span>
+ </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; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ import VerifyOtp from '../components/Pages/VerifyOtp'; | |
import EmailVerify from '../components/Pages/EmailVerify'; | ||
import Membership from '../components/Membership'; | ||
import HelpAndSupport from '../components/Pages/HelpAndSupport'; | ||
import TicTacToe from '../components/Pages/Games/TicTacToe'; | ||
const router = createBrowserRouter( | ||
createRoutesFromElements( | ||
<Route path="/" element={<App />}> | ||
|
@@ -41,6 +42,7 @@ const router = createBrowserRouter( | |
<Route path="/email-verify" element={<EmailVerify />} /> | ||
<Route path="/membership" element={<Membership />} /> | ||
<Route path="/help" element={<HelpAndSupport />} /> | ||
<Route path="/TicTacToe" element={<TicTacToe />} /> | ||
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 route naming consistency and organization. The current implementation has a few areas for improvement:
Consider this refactoring approach: <Route path="/boardgame" element={<Boardgame />} />
+ <Route path="/boardgame/tic-tac-toe" element={<TicTacToe />} />
<Route path="/events" element={<Event />} />
...
- <Route path="/TicTacToe" element={<TicTacToe />} /> Note: If you implement this change, remember to update the corresponding Link component in Boardgame.jsx to use the new path.
|
||
|
||
</Route> | ||
) | ||
|
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