feat: add standalone Nim game page, route, and container layout; remove global header/footer from /games/nim

This commit is contained in:
Neeldhara Misra 2025-07-22 02:40:47 +05:30
parent bfa6e66450
commit 3705152d34
5 changed files with 69 additions and 8 deletions

View file

@ -29,6 +29,7 @@ import TernarySearchTrickPage from "./pages/TernarySearchTrickPage";
import TernarySearchTrickGreenScreen from "./pages/TernarySearchTrickGreenScreen";
import NorthcottsGamePage from "./pages/NorthcottsGamePage";
import KnightsPuzzlePage from "./pages/KnightsPuzzlePage";
import NimGamePage from "./pages/NimGamePage";
import Foundations from "./pages/theme-pages/discrete-math/Foundations";
import Proofs from "./pages/theme-pages/discrete-math/Proofs";
import Counting from "./pages/theme-pages/discrete-math/Counting";
@ -84,6 +85,7 @@ const App = () => (
<Route path="/ternary-search-trick/:mode" element={<TernarySearchTrickGreenScreen />} />
<Route path="/northcotts-game" element={<NorthcottsGamePage />} />
<Route path="/knights-puzzle" element={<KnightsPuzzlePage />} />
<Route path="/games/nim" element={<NimGamePage />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />

View file

@ -90,6 +90,15 @@ const allInteractives: Interactive[] = [
path: '/knights-puzzle',
theme: 'Puzzles',
dateAdded: '2024-07-21'
},
{
id: 'nim',
title: 'Game of Nim',
description: 'A classic two-player game of strategy. Take turns removing stones from heaps. The player to take the last stone wins!',
tags: ['strategy', 'two-player', 'math', 'logic', 'game-theory'],
path: '/games/nim',
theme: 'Games',
dateAdded: '2024-07-21'
}
];

View file

@ -41,6 +41,8 @@ const NimGame: React.FC = () => {
const initialMaxHeapSizeRef = useRef<number>(0);
// Store the pixel width for the entire heaps group container (label + stones)
const [heapsGroupWidth, setHeapsGroupWidth] = useState<string>('0px');
// Store the initial heaps for reset
const initialHeapsRef = useRef<number[]>([]);
// When a new game starts, record the initial heap sizes and max size and compute the group width
React.useEffect(() => {
@ -58,6 +60,14 @@ const NimGame: React.FC = () => {
// eslint-disable-next-line
}, [mode, heaps.length]);
// When a new game starts, record the initial heaps
React.useEffect(() => {
if (mode !== null && heaps.length > 0) {
initialHeapsRef.current = [...heaps];
}
// eslint-disable-next-line
}, [mode, heaps.length]);
// Start a new game
const startGame = (selectedMode: Mode) => {
setMode(selectedMode);
@ -103,6 +113,21 @@ const NimGame: React.FC = () => {
setCurrentPlayer(0);
};
// Reset the game to the initial heaps
const handleResetGame = () => {
setHeaps([...initialHeapsRef.current]);
setWinner(null);
setCurrentPlayer(0);
};
// Start a new game (go back to mode selection)
const handleNewGame = () => {
setMode(null);
setHeaps([]);
setWinner(null);
setCurrentPlayer(0);
};
// User heap count slider change
const handleHeapCountChange = (val: number) => {
setUserHeapCount(val);
@ -166,15 +191,21 @@ const NimGame: React.FC = () => {
{mode !== null && (
<>
{/* Current Turn Indicator */}
<div className="text-center">
{/* Current Turn Indicator and Reset Button */}
<div className="text-center flex flex-col items-center gap-2">
{winner === null ? (
<div className="space-y-2">
<p className="text-sm text-muted-foreground">Current Turn:</p>
<span className={`inline-block text-lg px-4 py-2 rounded font-semibold ${currentPlayer === 0 ? 'bg-sky-200 text-sky-800' : 'bg-red-200 text-red-800'}`}>
Player {currentPlayer + 1}
</span>
</div>
<>
<div>
<p className="text-sm text-muted-foreground">Current Turn:</p>
<span className={`inline-block text-lg px-4 py-2 rounded font-semibold ${currentPlayer === 0 ? 'bg-sky-200 text-sky-800' : 'bg-red-200 text-red-800'}`}>
Player {currentPlayer + 1}
</span>
</div>
<div className="flex gap-2 mt-2">
<Button variant="outline" size="sm" onClick={handleResetGame}>Reset Game</Button>
<Button variant="outline" size="sm" onClick={handleNewGame}>New Game</Button>
</div>
</>
) : (
<div className="space-y-2">
<span className="inline-block text-lg px-4 py-2 rounded font-semibold bg-green-200 text-green-800">Winner: Player {winner + 1}!</span>

View file

@ -62,6 +62,13 @@ const allInteractives = [{
tags: ['chess', 'knights', 'puzzle'],
path: '/knights-puzzle',
theme: 'Puzzles'
}, {
id: 'nim',
title: 'Game of Nim',
description: 'A classic two-player game of strategy. Take turns removing stones from heaps. The player to take the last stone wins!',
tags: ['strategy', 'two-player', 'math', 'logic'],
path: '/games/nim',
theme: 'Games'
}];
// Featured interactives (3 fixed ones)

12
src/pages/NimGamePage.tsx Normal file
View file

@ -0,0 +1,12 @@
import React from 'react';
import NimGame from '@/components/NimGame';
const NimGamePage = () => {
return (
<div className="container mx-auto px-4 py-8">
<NimGame />
</div>
);
};
export default NimGamePage;