Add pebble game interactive

Add a new interactive game where users place pebbles on an integer line from 1 to 35. The rules are:
- A pebble can only be placed on position `i` if position `i-1` has a pebble.
- Position 1 can always have a pebble placed on it.
- Users start with `n` pebbles (chosen via a slider from 2 to 5).
- The goal is to find the largest integer position for which a pebble can be placed.
This commit is contained in:
gpt-engineer-app[bot] 2025-08-21 10:45:38 +00:00
parent 927db0fcd1
commit 8917e1a66f
3 changed files with 255 additions and 0 deletions

View file

@ -10,6 +10,7 @@ import NimGame from '@/components/NimGame';
import AssistedNimGame from '@/components/AssistedNimGame';
import GoldCoinGame from '@/components/GoldCoinGame';
import GuessingGame from '@/components/GuessingGame';
import PebblePlacementGame from '@/components/PebblePlacementGame';
interface Game {
id: string;
@ -20,6 +21,13 @@ interface Game {
}
const games: Game[] = [
{
id: 'pebble-placement-game',
title: 'Pebble Placement Strategy Game',
description: 'Test your strategic thinking! Place pebbles following specific rules to reach the furthest position possible.',
tags: ['strategy', 'logic', 'game', 'placement', 'optimization', 'puzzle'],
component: PebblePlacementGame
},
{
id: 'guessing-game',
title: 'Number Guessing Game',

View file

@ -7,6 +7,7 @@ import Layout from '@/components/Layout';
import BinarySearchTrick from '@/components/BinarySearchTrick';
import TernarySearchTrick from '@/components/TernarySearchTrick';
import GuessingGame from '@/components/GuessingGame';
import PebblePlacementGame from '@/components/PebblePlacementGame';
interface Interactive {
id: string;
@ -38,6 +39,13 @@ const interactives: Interactive[] = [
tags: ['ternary', 'magic', 'numbers', 'trick', 'data-structures', 'search'],
component: TernarySearchTrick
},
{
id: 'pebble-placement-game',
title: 'Pebble Placement Strategy Game',
description: 'Test your strategic thinking! Place pebbles following specific rules to reach the furthest position possible.',
tags: ['strategy', 'logic', 'game', 'placement', 'optimization', 'puzzle'],
component: PebblePlacementGame
},
];
const InteractiveGallery = () => {
const [searchTerm, setSearchTerm] = useState('');

View file

@ -0,0 +1,239 @@
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 and predecessor has pebble (or position 1)
if (!placedPebbles.has(position)) return false;
if (position === 1) return true;
return placedPebbles.has(position - 1);
};
const handlePositionClick = (position: number) => {
if (!gameStarted) return;
const newPlacedPebbles = new Set(placedPebbles);
if (placedPebbles.has(position)) {
// Try to remove pebble
if (canRemovePebble(position)) {
// Check if removing this pebble would make any later pebbles invalid
const pebblesAfter = Array.from(placedPebbles).filter(p => p > position);
let canRemove = true;
for (const laterPebble of pebblesAfter) {
if (laterPebble === position + 1) {
// Direct successor would become invalid
canRemove = false;
break;
}
}
if (canRemove) {
newPlacedPebbles.delete(position);
setPlacedPebbles(newPlacedPebbles);
setPebblesInHand(pebblesInHand + 1);
}
}
} else {
// Try to place pebble
if (pebblesInHand > 0 && canPlacePebble(position)) {
newPlacedPebbles.add(position);
setPlacedPebbles(newPlacedPebbles);
setPebblesInHand(pebblesInHand - 1);
}
}
};
const getPositionState = (position: number): 'placed' | 'available' | 'blocked' => {
if (placedPebbles.has(position)) return 'placed';
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':
return `${baseClasses} bg-primary text-primary-foreground border-primary hover:bg-primary/90`;
case 'available':
return `${baseClasses} bg-secondary text-secondary-foreground border-secondary hover:bg-secondary/80 hover:scale-110`;
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={5}
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> 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-35</h3>
{/* Rows of 7 positions each */}
{[0, 1, 2, 3, 4].map((row) => (
<div key={row} className="flex items-center gap-2">
<span className="text-sm text-muted-foreground w-8">
{row * 7 + 1}-{Math.min((row + 1) * 7, 35)}:
</span>
<div className="flex gap-2">
{Array.from({ length: 7 }, (_, i) => {
const position = row * 7 + i + 1;
if (position > 35) return null;
return (
<div
key={position}
className={getPositionStyle(position)}
onClick={() => handlePositionClick(position)}
title={
placedPebbles.has(position)
? `Pebble placed on ${position}. Click to remove.`
: canPlacePebble(position) && pebblesInHand > 0
? `Click to place pebble on ${position}`
: `Cannot place pebble on ${position}`
}
>
{position}
</div>
);
})}
</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-muted-foreground">
Your furthest position: <span className="font-semibold text-foreground">{furthestReached}</span>
</p>
<p className="text-sm text-muted-foreground mt-2">
Can you rearrange your pebbles to reach even further?
</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;