interactives/src/components/PebblePlacementGame.tsx
gpt-engineer-app[bot] 0c19a83999 Change grey text to white
Updated the text color in the "All pebbles placed!" message from grey to white in `src/components/PebblePlacementGame.tsx`.
Here are the files that were edited by the AI in that message:
-edited src/components/PebblePlacementGame.tsx
2025-08-21 10:56:28 +00:00

236 lines
No EOL
9 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Slider } from '@/components/ui/slider';
import { Badge } from '@/components/ui/badge';
import { RotateCcw, Trophy } from 'lucide-react';
import SocialShare from '@/components/SocialShare';
interface PebblePlacementGameProps {
showSocialShare?: boolean;
}
const PebblePlacementGame: React.FC<PebblePlacementGameProps> = ({ showSocialShare = false }) => {
const [numPebbles, setNumPebbles] = useState<number>(3);
const [pebblesInHand, setPebblesInHand] = useState<number>(3);
const [placedPebbles, setPlacedPebbles] = useState<Set<number>>(new Set());
const [furthestReached, setFurthestReached] = useState<number>(0);
const [gameStarted, setGameStarted] = useState<boolean>(false);
// Reset game when number of pebbles changes
useEffect(() => {
resetGame();
}, [numPebbles]);
// Update furthest reached whenever pebbles are placed
useEffect(() => {
if (placedPebbles.size > 0) {
const maxPosition = Math.max(...Array.from(placedPebbles));
if (maxPosition > furthestReached) {
setFurthestReached(maxPosition);
}
}
}, [placedPebbles, furthestReached]);
const resetGame = () => {
setPebblesInHand(numPebbles);
setPlacedPebbles(new Set());
setFurthestReached(0);
setGameStarted(false);
};
const startGame = () => {
setGameStarted(true);
};
const canPlacePebble = (position: number): boolean => {
// Can always place on position 1
if (position === 1) return true;
// For other positions, predecessor must have a pebble
return placedPebbles.has(position - 1);
};
const canRemovePebble = (position: number): boolean => {
// Can only remove if pebble is placed
if (!placedPebbles.has(position)) return false;
// Position 1 is always removable
if (position === 1) return true;
// For other positions, predecessor must have pebble
return placedPebbles.has(position - 1);
};
const hasAnyRemovablePebble = (): boolean => {
return Array.from(placedPebbles).some(pos => canRemovePebble(pos));
};
const handlePositionClick = (position: number) => {
if (!gameStarted) return;
if (placedPebbles.has(position)) {
// Try to remove pebble
if (canRemovePebble(position)) {
const newPlacedPebbles = new Set(placedPebbles);
newPlacedPebbles.delete(position);
setPlacedPebbles(newPlacedPebbles);
setPebblesInHand(pebblesInHand + 1);
}
} else {
// Try to place pebble
if (pebblesInHand > 0 && canPlacePebble(position)) {
const newPlacedPebbles = new Set(placedPebbles);
newPlacedPebbles.add(position);
setPlacedPebbles(newPlacedPebbles);
setPebblesInHand(pebblesInHand - 1);
}
}
};
const getPositionState = (position: number): 'placed-removable' | 'placed-fixed' | 'available' | 'blocked' => {
if (placedPebbles.has(position)) {
return canRemovePebble(position) ? 'placed-removable' : 'placed-fixed';
}
if (canPlacePebble(position) && pebblesInHand > 0) return 'available';
return 'blocked';
};
const getPositionStyle = (position: number): string => {
const state = getPositionState(position);
const baseClasses = "w-8 h-8 border-2 rounded-full flex items-center justify-center text-sm font-medium cursor-pointer transition-all duration-200";
switch (state) {
case 'placed-removable':
return `${baseClasses} bg-green-100 text-green-800 border-green-200 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-300 dark:border-green-700`;
case 'placed-fixed':
return `${baseClasses} bg-red-100 text-red-800 border-red-200 cursor-not-allowed dark:bg-red-900/30 dark:text-red-300 dark:border-red-700`;
case 'available':
return `${baseClasses} bg-secondary text-secondary-foreground border-primary border-2 hover:bg-secondary/80 hover:scale-110 shadow-sm`;
case 'blocked':
return `${baseClasses} bg-muted text-muted-foreground border-muted cursor-not-allowed opacity-50`;
}
};
return (
<div className="max-w-6xl mx-auto p-6 space-y-6">
<Card>
<CardHeader className="text-center">
<CardTitle className="text-2xl font-bold">Pebble Placement Strategy Game</CardTitle>
<CardDescription className="text-lg">
Place your pebbles strategically to reach the furthest position possible!
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Game Setup */}
<div className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">Number of Pebbles: {numPebbles}</label>
<Slider
value={[numPebbles]}
onValueChange={(value) => setNumPebbles(value[0])}
min={2}
max={6}
step={1}
className="w-full max-w-xs"
disabled={gameStarted}
/>
</div>
{!gameStarted ? (
<Button onClick={startGame} size="lg">
Start Game
</Button>
) : (
<div className="flex items-center gap-4">
<Button onClick={resetGame} variant="outline" size="sm">
<RotateCcw className="w-4 h-4 mr-2" />
Reset Game
</Button>
<Badge variant="secondary">
Pebbles in hand: {pebblesInHand}
</Badge>
{furthestReached > 0 && (
<Badge variant="default" className="bg-green-600">
<Trophy className="w-3 h-3 mr-1" />
Furthest: {furthestReached}
</Badge>
)}
</div>
)}
</div>
{/* Rules */}
<div className="bg-muted p-4 rounded-lg space-y-2">
<h3 className="font-semibold text-sm">Rules:</h3>
<ul className="text-sm space-y-1 text-muted-foreground">
<li> You can always place a pebble on position 1</li>
<li> You can place a pebble on position i only if position (i-1) has a pebble</li>
<li> You can remove a pebble from position i only if position (i-1) has a pebble</li>
<li> The pebble on position 1 is always removable</li>
<li> Goal: Place a pebble on the largest possible position</li>
</ul>
</div>
{/* Game Board */}
{gameStarted && (
<div className="space-y-4">
<h3 className="text-lg font-semibold">Positions 1-100</h3>
{/* All positions in a flowing line */}
<div className="flex flex-wrap gap-2">
{Array.from({ length: 100 }, (_, i) => {
const position = i + 1;
return (
<div
key={position}
className={getPositionStyle(position)}
onClick={() => handlePositionClick(position)}
title={
placedPebbles.has(position)
? canRemovePebble(position)
? `Pebble placed on ${position}. Click to remove.`
: `Pebble on ${position} cannot be removed (no predecessor pebble)`
: canPlacePebble(position) && pebblesInHand > 0
? `Click to place pebble on ${position}`
: `Cannot place pebble on ${position}`
}
>
{position}
</div>
);
})}
</div>
</div>
)}
{/* Game Status */}
{gameStarted && pebblesInHand === 0 && (
<div className="text-center p-4 bg-accent rounded-lg">
<h3 className="text-lg font-semibold">All pebbles placed!</h3>
<p className="text-accent-foreground">
Your furthest position: <span className="font-semibold text-accent-foreground">{furthestReached}</span>
</p>
{hasAnyRemovablePebble() ? (
<p className="text-sm text-accent-foreground mt-2">
You can still rearrange your pebbles to try reaching further!
</p>
) : (
<p className="text-sm text-accent-foreground mt-2">
No more moves possible - great job!
</p>
)}
</div>
)}
</CardContent>
</Card>
{showSocialShare && (
<SocialShare
title="Pebble Placement Strategy Game"
description="Test your strategic thinking with this pebble placement puzzle!"
/>
)}
</div>
);
};
export default PebblePlacementGame;