diff --git a/src/App.tsx b/src/App.tsx index 60f17e7..0339c8c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -31,6 +31,7 @@ import NorthcottsGamePage from "./pages/NorthcottsGamePage"; import KnightsPuzzlePage from "./pages/KnightsPuzzlePage"; import NimGamePage from "./pages/NimGamePage"; import AssistedNimGamePage from "./pages/AssistedNimGamePage"; +import GoldCoinGamePage from "./pages/GoldCoinGamePage"; 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"; @@ -88,6 +89,7 @@ const App = () => ( } /> } /> } /> + } /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} } /> diff --git a/src/components/GamesGallery.tsx b/src/components/GamesGallery.tsx index 2aae6df..83d68d2 100644 --- a/src/components/GamesGallery.tsx +++ b/src/components/GamesGallery.tsx @@ -8,6 +8,7 @@ import GameOfSim from '@/components/GameOfSim'; import NorthcottsGame from '@/components/NorthcottsGame'; import NimGame from '@/components/NimGame'; import AssistedNimGame from '@/components/AssistedNimGame'; +import GoldCoinGame from '@/components/GoldCoinGame'; interface Game { id: string; @@ -18,6 +19,13 @@ interface Game { } const games: Game[] = [ + { + id: 'gold-coin', + title: 'The Gold Coin Game', + description: 'A strategic two-player game where you move coins to the left or take the leftmost coin. The player who takes the gold coin wins!', + tags: ['strategy', 'two-player', 'logic', 'game-theory', 'movement', 'coins'], + component: GoldCoinGame + }, { id: 'assisted-nim', title: 'Assisted Game of Nim', diff --git a/src/components/GoldCoinGame.tsx b/src/components/GoldCoinGame.tsx new file mode 100644 index 0000000..3650542 --- /dev/null +++ b/src/components/GoldCoinGame.tsx @@ -0,0 +1,368 @@ +import React, { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { Badge } from '@/components/ui/badge'; +import { ArrowLeft, ArrowRight, Crown, Circle } from 'lucide-react'; +import SocialShare from '@/components/SocialShare'; + +type Player = 0 | 1; +type CellState = 'empty' | 'penny' | 'gold' | 'gold-prev'; + +interface GameState { + track: CellState[]; + currentPlayer: Player; + winner: Player | null; + gameStarted: boolean; + selectedCoin: number | null; + moveHistory: string[]; +} + +const GoldCoinGame: React.FC = () => { + const [gameState, setGameState] = useState({ + track: ['empty', 'penny', 'empty', 'penny', 'empty', 'penny', 'empty', 'gold', 'empty', 'empty', 'empty'], + currentPlayer: 0, + winner: null, + gameStarted: false, + selectedCoin: null, + moveHistory: [] + }); + + const [showRules, setShowRules] = useState(false); + const [showGameOver, setShowGameOver] = useState(false); + + const playerNames = ['Player 1', 'Player 2']; + const playerColors = ['#3b82f6', '#ef4444']; + + const initializeGame = () => { + setGameState({ + track: ['empty', 'penny', 'empty', 'penny', 'empty', 'penny', 'empty', 'gold', 'empty', 'empty', 'empty'], + currentPlayer: 0, + winner: null, + gameStarted: true, + selectedCoin: null, + moveHistory: [] + }); + }; + + const resetGame = () => { + setGameState({ + track: ['empty', 'penny', 'empty', 'penny', 'empty', 'penny', 'empty', 'gold', 'empty', 'empty', 'empty'], + currentPlayer: 0, + winner: null, + gameStarted: false, + selectedCoin: null, + moveHistory: [] + }); + setShowGameOver(false); + }; + + const canMoveCoin = (fromIndex: number, toIndex: number): boolean => { + if (fromIndex <= toIndex) return false; // Can only move left + if (toIndex < 0) return false; // Can't move off the track + + // Check if there are any coins in the way + for (let i = toIndex + 1; i < fromIndex; i++) { + if (gameState.track[i] !== 'empty') { + return false; + } + } + + return true; + }; + + const canTakeCoin = (index: number): boolean => { + // Can only take the leftmost coin + for (let i = 0; i < index; i++) { + if (gameState.track[i] !== 'empty') { + return false; + } + } + return gameState.track[index] !== 'empty'; + }; + + const handleCellClick = (index: number) => { + if (gameState.winner !== null) return; + + const cellState = gameState.track[index]; + + // If a coin is selected, try to move it + if (gameState.selectedCoin !== null) { + if (canMoveCoin(gameState.selectedCoin, index)) { + const newTrack = [...gameState.track]; + newTrack[index] = newTrack[gameState.selectedCoin]; + newTrack[gameState.selectedCoin] = 'empty'; + + const moveDescription = `Player ${gameState.currentPlayer + 1} moved ${newTrack[index] === 'gold' ? 'gold coin' : 'penny'} from position ${gameState.selectedCoin + 1} to position ${index + 1}`; + + setGameState(prev => ({ + ...prev, + track: newTrack, + currentPlayer: ((prev.currentPlayer + 1) % 2) as Player, + selectedCoin: null, + moveHistory: [...prev.moveHistory, moveDescription] + })); + } + return; + } + + // Select a coin if it's not empty + if (cellState !== 'empty') { + setGameState(prev => ({ + ...prev, + selectedCoin: index + })); + } + }; + + const handleTakeCoin = (index: number) => { + if (gameState.winner !== null) return; + if (!canTakeCoin(index)) return; + + const cellState = gameState.track[index]; + const newTrack = [...gameState.track]; + newTrack[index] = 'empty'; + + const moveDescription = `Player ${gameState.currentPlayer + 1} took ${cellState === 'gold' ? 'gold coin' : 'penny'} from position ${index + 1}`; + + // Check if this is a winning move + const isWinningMove = cellState === 'gold'; + + setGameState(prev => ({ + ...prev, + track: newTrack, + currentPlayer: isWinningMove ? prev.currentPlayer : ((prev.currentPlayer + 1) % 2) as Player, + winner: isWinningMove ? prev.currentPlayer : null, + selectedCoin: null, + moveHistory: [...prev.moveHistory, moveDescription] + })); + + if (isWinningMove) { + setShowGameOver(true); + } + }; + + const renderCell = (cellState: CellState, index: number) => { + const isSelected = gameState.selectedCoin === index; + const canTake = canTakeCoin(index); + const isClickable = cellState !== 'empty' || gameState.selectedCoin !== null; + + return ( + handleCellClick(index)} + > + {cellState === 'gold' && ( + + + + )} + {cellState === 'penny' && ( + + + + )} + {cellState === 'gold-prev' && ( + + + + )} + + {index + 1} + + + ); + }; + + const renderTakeButton = (index: number) => { + const canTake = canTakeCoin(index); + const cellState = gameState.track[index]; + + if (!canTake || cellState === 'empty') return null; + + return ( + handleTakeCoin(index)} + > + Take {cellState === 'gold' ? 'Gold' : 'Penny'} + + ); + }; + + return ( + + + + + + The Gold Coin Game + + + A two-player strategy game. Move coins to the left or take the leftmost coin. + The player who takes the gold coin wins! + + + + {/* Game Controls */} + + {!gameState.gameStarted ? ( + + Start Game + + ) : ( + + Reset Game + + )} + setShowRules(true)} variant="outline"> + Rules + + + + {/* Game Status */} + {gameState.gameStarted && ( + + + Current Player: + + {playerNames[gameState.currentPlayer]} + + + {gameState.selectedCoin !== null && ( + + Selected coin at position {gameState.selectedCoin + 1}. Click a position to move it. + + )} + + )} + + {/* Game Board */} + + + + {gameState.track.map((cellState, index) => ( + + {renderCell(cellState, index)} + {renderTakeButton(index)} + + ))} + + + + {/* Instructions */} + + {gameState.selectedCoin !== null ? ( + Click a position to move the selected coin, or click "Take" to collect a coin + ) : ( + Click on a coin to select it, then click a position to move it left, or click "Take" to collect the leftmost coin + )} + + + + {/* Move History */} + {gameState.moveHistory.length > 0 && ( + + Move History + + {gameState.moveHistory.map((move, index) => ( + + {move} + + ))} + + + )} + + + + {/* Rules Dialog */} + + + + The Gold Coin Game Rules + + + + Objective + Be the first player to acquire the gold coin and win the game! + + + + Setup + The game starts with coins placed on a track. The gold coin is positioned farthest to the right. + + + + On Your Turn + You have two options: + + Move a coin: Move any coin to the left by one or more spaces. A coin cannot jump over another coin. + Take a coin: Take the leftmost coin on the track into your possession. + + + + + Winning + The player who successfully takes the gold coin is declared the winner! + + + + Strategy Tips + + Try to control the position of the gold coin + Use pennies to block your opponent's moves + Plan several moves ahead + Don't always take the first available coin + + + + + + + {/* Game Over Dialog */} + + + + Game Over! + + + + + + {playerNames[gameState.winner!]} wins! + + + + Congratulations! {playerNames[gameState.winner!]} successfully acquired the gold coin. + + + Play Again + + + + + + + + ); +}; + +export default GoldCoinGame; \ No newline at end of file diff --git a/src/components/InteractiveIndex.tsx b/src/components/InteractiveIndex.tsx index d3e38da..97325bb 100644 --- a/src/components/InteractiveIndex.tsx +++ b/src/components/InteractiveIndex.tsx @@ -91,6 +91,15 @@ const allInteractives: Interactive[] = [ theme: 'Puzzles', dateAdded: '2024-07-21' }, + { + id: 'gold-coin', + title: 'The Gold Coin Game', + description: 'A strategic two-player game where you move coins to the left or take the leftmost coin. The player who takes the gold coin wins!', + tags: ['strategy', 'two-player', 'logic', 'game-theory', 'movement', 'coins'], + path: '/games/gold-coin', + theme: 'Games', + dateAdded: '2024-12-19' + }, { id: 'assisted-nim', title: 'Assisted Game of Nim', diff --git a/src/pages/GoldCoinGamePage.tsx b/src/pages/GoldCoinGamePage.tsx new file mode 100644 index 0000000..d838ae9 --- /dev/null +++ b/src/pages/GoldCoinGamePage.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import GoldCoinGame from '@/components/GoldCoinGame'; + +const GoldCoinGamePage = () => { + return ( + + + + ); +}; + +export default GoldCoinGamePage; \ No newline at end of file
Click a position to move the selected coin, or click "Take" to collect a coin
Click on a coin to select it, then click a position to move it left, or click "Take" to collect the leftmost coin
Be the first player to acquire the gold coin and win the game!
The game starts with coins placed on a track. The gold coin is positioned farthest to the right.
You have two options:
The player who successfully takes the gold coin is declared the winner!
+ Congratulations! {playerNames[gameState.winner!]} successfully acquired the gold coin. +