import React, { useState, useEffect } from 'react'; import { Shuffle, RotateCcw, HelpCircle, X, Info, Trophy, Clock } from 'lucide-react'; export default function SolitairePlus() { // Game state const [deck, setDeck] = useState([]); const [tableau, setTableau] = useState([[], [], [], []]); const [foundation, setFoundation] = useState([[], [], [], []]); const [hand, setHand] = useState([]); const [score, setScore] = useState(0); const [moves, setMoves] = useState(0); const [gameOver, setGameOver] = useState(false); const [showRules, setShowRules] = useState(false); const [selectedCard, setSelectedCard] = useState(null); const [selectedSource, setSelectedSource] = useState(null); const [gameWon, setGameWon] = useState(false); const [showInfo, setShowInfo] = useState(true); const [gameTime, setGameTime] = useState(0); const [gameActive, setGameActive] = useState(false); // Initialize game useEffect(() => { newGame(); }, []); // Game timer useEffect(() => { let interval; if (gameActive && !gameOver) { interval = setInterval(() => { setGameTime(prevTime => prevTime + 1); }, 1000); } return () => clearInterval(interval); }, [gameActive, gameOver]); // Check for win condition useEffect(() => { if (foundation.every(pile => pile.length === 13)) { setGameWon(true); setGameOver(true); setGameActive(false); } }, [foundation]); const formatTime = (seconds) => { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }; const newGame = () => { // Create and shuffle deck const suits = ['hearts', 'diamonds', 'clubs', 'spades']; const values = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']; const newDeck = []; for (const suit of suits) { for (let i = 0; i < values.length; i++) { newDeck.push({ suit, value: values[i], numValue: i + 1, color: suit === 'hearts' || suit === 'diamonds' ? 'red' : 'black', id: `${values[i]}-${suit}` }); } } // Fisher-Yates shuffle for (let i = newDeck.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [newDeck[i], newDeck[j]] = [newDeck[j], newDeck[i]]; } // Set up initial tableau const newTableau = [[], [], [], []]; for (let i = 0; i < 4; i++) { for (let j = 0; j < 4; j++) { newTableau[i].push(newDeck.pop()); } } setDeck(newDeck); setTableau(newTableau); setFoundation([[], [], [], []]); setHand([]); setScore(0); setMoves(0); setGameOver(false); setGameWon(false); setSelectedCard(null); setSelectedSource(null); setGameTime(0); setGameActive(true); }; const drawCard = () => { if (deck.length === 0 || gameOver) return; const newHand = [...hand]; const newDeck = [...deck]; newHand.push(newDeck.pop()); setHand(newHand); setDeck(newDeck); setMoves(moves + 1); }; const selectCard = (card, source, index, pileIndex = null) => { if (gameOver) return; // If a card is already selected, try to move it if (selectedCard) { if (source === selectedSource && index === selectedCard.index && pileIndex === selectedCard.pileIndex) { // Deselect if clicking the same card setSelectedCard(null); setSelectedSource(null); return; } // Try to move the selected card to the new location moveCard(selectedSource, selectedCard.index, selectedCard.pileIndex, source, index, pileIndex); setSelectedCard(null); setSelectedSource(null); } else { // Select the card setSelectedCard({ card, index, pileIndex }); setSelectedSource(source); } }; const moveCard = (fromSource, fromIndex, fromPileIndex, toSource, toIndex, toPileIndex) => { // Handle different move scenarios if (fromSource === 'hand' && toSource === 'foundation') { moveFromHandToFoundation(fromIndex, toPileIndex); } else if (fromSource === 'tableau' && toSource === 'foundation') { moveFromTableauToFoundation(fromPileIndex, fromIndex, toPileIndex); } else if (fromSource === 'tableau' && toSource === 'tableau') { moveFromTableauToTableau(fromPileIndex, fromIndex, toPileIndex); } else if (fromSource === 'hand' && toSource === 'tableau') { moveFromHandToTableau(fromIndex, toPileIndex); } }; const moveFromHandToFoundation = (handIndex, foundationIndex) => { const card = hand[handIndex]; const foundationPile = foundation[foundationIndex]; // Check if valid move (A on empty or same suit and one higher value) if ( (foundationPile.length === 0 && card.value === 'A') || (foundationPile.length > 0 && foundationPile[foundationPile.length - 1].suit === card.suit && foundationPile[foundationPile.length - 1].numValue === card.numValue - 1) ) { const newHand = [...hand]; const newFoundation = [...foundation]; // Remove from hand newHand.splice(handIndex, 1); // Add to foundation newFoundation[foundationIndex] = [...foundationPile, card]; setHand(newHand); setFoundation(newFoundation); setScore(score + 10); setMoves(moves + 1); } }; const moveFromTableauToFoundation = (tableauIndex, cardIndex, foundationIndex) => { const tableauPile = tableau[tableauIndex]; if (cardIndex !== tableauPile.length - 1) return; // Only top card can be moved const card = tableauPile[cardIndex]; const foundationPile = foundation[foundationIndex]; // Check if valid move if ( (foundationPile.length === 0 && card.value === 'A') || (foundationPile.length > 0 && foundationPile[foundationPile.length - 1].suit === card.suit && foundationPile[foundationPile.length - 1].numValue === card.numValue - 1) ) { const newTableau = [...tableau]; const newFoundation = [...foundation]; // Remove from tableau newTableau[tableauIndex] = tableauPile.slice(0, -1); // Add to foundation newFoundation[foundationIndex] = [...foundationPile, card]; setTableau(newTableau); setFoundation(newFoundation); setScore(score + 10); setMoves(moves + 1); } }; const moveFromTableauToTableau = (fromPileIndex, cardIndex, toPileIndex) => { const fromPile = tableau[fromPileIndex]; const toPile = tableau[toPileIndex]; if (cardIndex !== fromPile.length - 1) return; // Only top card can be moved if (fromPileIndex === toPileIndex) return; // Can't move to same pile const card = fromPile[cardIndex]; // Check if valid move (K on empty or different color and one lower value) if ( (toPile.length === 0 && card.value === 'K') || (toPile.length > 0 && toPile[toPile.length - 1].color !== card.color && toPile[toPile.length - 1].numValue === card.numValue + 1) ) { const newTableau = [...tableau]; // Remove from source pile newTableau[fromPileIndex] = fromPile.slice(0, -1); // Add to destination pile newTableau[toPileIndex] = [...toPile, card]; setTableau(newTableau); setScore(score + 5); setMoves(moves + 1); } }; const moveFromHandToTableau = (handIndex, tableauIndex) => { const card = hand[handIndex]; const tableauPile = tableau[tableauIndex]; // Check if valid move if ( (tableauPile.length === 0 && card.value === 'K') || (tableauPile.length > 0 && tableauPile[tableauPile.length - 1].color !== card.color && tableauPile[tableauPile.length - 1].numValue === card.numValue + 1) ) { const newHand = [...hand]; const newTableau = [...tableau]; // Remove from hand newHand.splice(handIndex, 1); // Add to tableau newTableau[tableauIndex] = [...tableauPile, card]; setHand(newHand); setTableau(newTableau); setScore(score + 5); setMoves(moves + 1); } }; const renderCard = (card, source, index, pileIndex = null) => { if (!card) return null; const isSelected = selectedCard && selectedSource === source && selectedCard.index === index && selectedCard.pileIndex === pileIndex; const suitSymbol = { 'hearts': '♥', 'diamonds': '♦', 'clubs': '♣', 'spades': '♠' }; const suitColor = card.color === 'red' ? 'text-red-600' : 'text-gray-900'; return (
{gameWon ? 'Congratulations! You completed the game.' : 'No more possible moves.'}
Objective: Move all cards to the foundation piles, sorted by suit from Ace to King.