Skip to content

Instantly share code, notes, and snippets.

@bufordeeds
Created May 17, 2025 01:32
Show Gist options
  • Save bufordeeds/11eda11ea1865779c36303d0f4ad0e4d to your computer and use it in GitHub Desktop.
Save bufordeeds/11eda11ea1865779c36303d0f4ad0e4d to your computer and use it in GitHub Desktop.
Skull Digital
import React, { useState } from 'react';
const SkullGame = () => {
// Game state
const [gamePhase, setGamePhase] = useState('placement'); // placement, bidding, flipping, result
const [currentPlayer, setCurrentPlayer] = useState(0);
const [players, setPlayers] = useState([
{ id: 0, name: 'You', cards: 4, stack: [], points: 0, isHuman: true },
{ id: 1, name: 'Alice', cards: 4, stack: [], points: 0, isHuman: false },
{ id: 2, name: 'Bob', cards: 4, stack: [], points: 0, isHuman: false },
{ id: 3, name: 'Charlie', cards: 4, stack: [], points: 0, isHuman: false }
]);
const [currentBid, setCurrentBid] = useState(0);
const [highestBidder, setHighestBidder] = useState(null);
const [selectedCard, setSelectedCard] = useState(null);
const [cardsFlipped, setCardsFlipped] = useState(0);
const [message, setMessage] = useState("Place your first card");
// Player's hand (3 roses, 1 skull)
const [playerHand, setPlayerHand] = useState([
{ id: 1, type: 'rose', selected: false },
{ id: 2, type: 'rose', selected: false },
{ id: 3, type: 'rose', selected: false },
{ id: 4, type: 'skull', selected: false }
]);
// Functions
const selectCard = (cardId) => {
if (gamePhase !== 'placement' || currentPlayer !== 0) return;
const newHand = playerHand.map(card => ({
...card,
selected: card.id === cardId
}));
setPlayerHand(newHand);
setSelectedCard(cardId);
};
const placeCard = () => {
if (!selectedCard || gamePhase !== 'placement' || currentPlayer !== 0) return;
// Find the selected card
const card = playerHand.find(card => card.id === selectedCard);
// Update player's stack
const newPlayers = [...players];
newPlayers[0].stack.push({ type: card.type, faceUp: false });
// Remove card from hand
const newHand = playerHand.filter(card => card.id !== selectedCard);
setPlayerHand(newHand);
setSelectedCard(null);
// Move to next player or phase
if (newPlayers.every(player => player.stack.length > 0)) {
setGamePhase('bidding');
setMessage("Bidding phase: Make your bid");
} else {
setCurrentPlayer((currentPlayer + 1) % players.length);
setMessage(`${players[(currentPlayer + 1) % players.length].name}'s turn to place a card`);
}
setPlayers(newPlayers);
};
const makeBid = (bid) => {
if (gamePhase !== 'bidding' || currentPlayer !== 0) return;
setCurrentBid(bid);
setHighestBidder(0);
setCurrentPlayer(1);
setMessage(`You bid ${bid}. ${players[1].name}'s turn to bid`);
};
const pass = () => {
if (gamePhase !== 'bidding' || currentPlayer !== 0) return;
setMessage(`You passed. ${players[1].name}'s turn to bid`);
setCurrentPlayer(1);
};
const startFlipping = () => {
if (gamePhase !== 'bidding' || highestBidder !== 0) return;
setGamePhase('flipping');
setMessage("Start flipping cards from your stack first");
};
// Simulated AI moves for demo purposes
React.useEffect(() => {
if (!players[currentPlayer]?.isHuman && currentPlayer !== 0) {
const timer = setTimeout(() => {
// Simple AI behavior
if (gamePhase === 'placement') {
// AI places a card
const newPlayers = [...players];
// 20% chance of placing a skull
const cardType = Math.random() < 0.2 ? 'skull' : 'rose';
newPlayers[currentPlayer].stack.push({ type: cardType, faceUp: false });
setPlayers(newPlayers);
if (newPlayers.every(player => player.stack.length > 0)) {
setGamePhase('bidding');
setMessage("Bidding phase: Make your bid");
setCurrentPlayer(0);
} else {
setCurrentPlayer((currentPlayer + 1) % players.length);
setMessage(`${players[(currentPlayer + 1) % players.length].name}'s turn to place a card`);
}
} else if (gamePhase === 'bidding') {
// AI bids or passes
if (Math.random() < 0.6 && currentBid < 8) {
// AI makes a bid
const newBid = currentBid + 1;
setCurrentBid(newBid);
setHighestBidder(currentPlayer);
setMessage(`${players[currentPlayer].name} bids ${newBid}`);
} else {
// AI passes
setMessage(`${players[currentPlayer].name} passes`);
}
const nextPlayer = (currentPlayer + 1) % players.length;
setCurrentPlayer(nextPlayer);
// If we've gone all the way around, highest bidder starts flipping
if (nextPlayer === highestBidder) {
setGamePhase('flipping');
if (highestBidder === 0) {
setMessage("Start flipping cards from your stack first");
} else {
setMessage(`${players[highestBidder].name} starts flipping cards`);
// Simulate AI flipping for demo
setTimeout(() => {
setGamePhase('result');
// 50/50 success/failure for demo
if (Math.random() < 0.5) {
setMessage(`${players[highestBidder].name} succeeded and gained a point!`);
const newPlayers = [...players];
newPlayers[highestBidder].points += 1;
setPlayers(newPlayers);
} else {
setMessage(`${players[highestBidder].name} flipped a skull and lost a card!`);
const newPlayers = [...players];
newPlayers[highestBidder].cards -= 1;
setPlayers(newPlayers);
}
// Reset for next round
setTimeout(() => {
resetGame();
}, 3000);
}, 2000);
}
}
}
}, 1500);
return () => clearTimeout(timer);
}
}, [currentPlayer, gamePhase, players, currentBid, highestBidder]);
const resetGame = () => {
// Reset for next round - simplified for demo
setGamePhase('placement');
setCurrentPlayer(0);
setCurrentBid(0);
setHighestBidder(null);
setCardsFlipped(0);
setMessage("Place your first card");
// Reset stacks but keep points and remaining cards
const newPlayers = players.map(player => ({
...player,
stack: []
}));
setPlayers(newPlayers);
// Give player new cards
setPlayerHand([
{ id: 1, type: 'rose', selected: false },
{ id: 2, type: 'rose', selected: false },
{ id: 3, type: 'rose', selected: false },
{ id: 4, type: 'skull', selected: false }
]);
};
return (
<div className="flex flex-col items-center justify-between h-screen bg-gray-900 text-white p-4">
{/* Game header */}
<div className="w-full text-center mb-2">
<h1 className="text-2xl font-bold">Skull</h1>
<p className="text-sm text-gray-300">Round 1</p>
</div>
{/* Message area */}
<div className="bg-gray-800 rounded-lg p-3 w-full text-center mb-2">
<p>{message}</p>
</div>
{/* AI Players */}
<div className="w-full flex justify-between mb-6">
{players.slice(1).map(player => (
<div key={player.id} className={`text-center p-2 ${currentPlayer === player.id ? 'bg-gray-700 rounded-lg' : ''}`}>
<p className="text-sm">{player.name}</p>
<div className="flex justify-center gap-1 my-1">
{Array(player.points).fill(0).map((_, i) => (
<div key={i} className="w-3 h-3 bg-yellow-500 rounded-full"></div>
))}
</div>
<div className="relative h-16 w-12 mx-auto">
{player.stack.map((card, index) => (
<div
key={index}
className="absolute h-16 w-12 border rounded-lg"
style={{
backgroundColor: '#444',
transform: `translateY(${-index * 3}px)`,
zIndex: index
}}
></div>
))}
</div>
<p className="text-xs mt-1">Cards: {player.cards}</p>
</div>
))}
</div>
{/* Center play area */}
<div className="flex-grow w-full flex flex-col items-center justify-center">
{gamePhase === 'bidding' && currentPlayer === 0 && (
<div className="flex flex-col items-center gap-4">
<p className="text-lg">Current bid: {currentBid}</p>
<div className="flex gap-2">
{[currentBid + 1, currentBid + 2, currentBid + 3].map(bid => (
<button
key={bid}
className="bg-blue-600 px-4 py-2 rounded-lg"
onClick={() => makeBid(bid)}
>
Bid {bid}
</button>
))}
</div>
<button
className="bg-red-600 px-6 py-2 rounded-lg mt-2"
onClick={pass}
>
Pass
</button>
</div>
)}
{gamePhase === 'flipping' && highestBidder === 0 && (
<div className="text-center">
<p className="mb-4">Flip {currentBid} cards without finding a skull</p>
<p className="text-lg">Cards flipped: {cardsFlipped} / {currentBid}</p>
<div className="mt-4">
<button
className="bg-purple-600 px-6 py-2 rounded-lg"
onClick={() => {
// Simple demo - 30% chance of finding a skull
if (Math.random() < 0.3) {
setMessage("You flipped a skull and lost!");
setGamePhase('result');
const newPlayers = [...players];
newPlayers[0].cards -= 1;
setPlayers(newPlayers);
setTimeout(resetGame, 3000);
} else {
const newFlipped = cardsFlipped + 1;
setCardsFlipped(newFlipped);
if (newFlipped >= currentBid) {
setMessage("Success! You gained a point!");
setGamePhase('result');
const newPlayers = [...players];
newPlayers[0].points += 1;
setPlayers(newPlayers);
setTimeout(resetGame, 3000);
}
}
}}
>
Flip Next Card
</button>
</div>
</div>
)}
</div>
{/* Player's area */}
<div className="w-full">
<div className="flex justify-center gap-1 mb-1">
{Array(players[0].points).fill(0).map((_, i) => (
<div key={i} className="w-3 h-3 bg-yellow-500 rounded-full"></div>
))}
</div>
<div className="relative h-20 mx-auto w-16">
{players[0].stack.map((card, index) => (
<div
key={index}
className="absolute h-20 w-16 border rounded-lg"
style={{
backgroundColor: '#444',
transform: `translateY(${-index * 4}px)`,
zIndex: index
}}
></div>
))}
</div>
{/* Player's hand */}
{gamePhase === 'placement' && currentPlayer === 0 && (
<>
<div className="flex justify-center gap-3 mt-4 mb-2">
{playerHand.map(card => (
<div
key={card.id}
className={`h-28 w-20 rounded-lg flex items-center justify-center cursor-pointer transition-transform ${
card.selected ? 'border-4 border-yellow-400 transform -translate-y-2' : 'border border-gray-600'
}`}
style={{ backgroundColor: card.type === 'rose' ? '#722ed1' : '#d4380d' }}
onClick={() => selectCard(card.id)}
>
<div className="text-center">
{card.type === 'rose' ? (
<div className="text-2xl">🌹</div>
) : (
<div className="text-2xl">💀</div>
)}
</div>
</div>
))}
</div>
<div className="flex justify-center mt-2">
<button
className={`px-6 py-3 rounded-lg ${selectedCard ? 'bg-green-600' : 'bg-gray-600'}`}
disabled={!selectedCard}
onClick={placeCard}
>
Place Card
</button>
</div>
</>
)}
</div>
</div>
);
};
export default SkullGame;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment