xxxxxxxxxx
624
// Uno Game Simulation in p5.js
let deck = [];
let discardPile = [];
let players = [[], [], [], [],]; // Four players' hands
let currentPlayer = 0;
let direction = 1; // 1 for clockwise, -1 for counter-clockwise
let lastMoveTime = 0;
let moveInterval = 200; // Moves per second
let cardWidth = 60;
let cardHeight = 90;
let animatingCards = [];
let gameStarted = false;
let wins = [0, 0, 0, 0]; // Player win counts
let barGraphCanvas;
let barGraphWidth = 200; // Width of the bar graph
let handSizeHistory = [[], [], [], []]; // Tracks hand sizes over time
let maxHandSize = 20; // Maximum hand size for scaling the graphs
// Added variables for positioning
let centerX, centerY;
let deckX, deckY, discardX, discardY;
let playerDistance = 200; // Distance from center to player's hand position
let graphHeight = 100; // Height of the hand size graphs at the bottom
function preload() {
font = loadFont('IMPACT.TTF'); // Load your font
fallBackFont= 'sans-serif'
}
function setup() {
// Main game canvas (Increased height to accommodate hand size graphs)
createCanvas(1000, 800);
// Define the center of the playing area (excluding graphs)
centerX = (width - barGraphWidth) / 2;
centerY = (height - graphHeight) / 2;
// Position the deck and discard pile symmetrically
deckX = centerX - cardWidth - 50; // Deck to the left of center
deckY = centerY - cardHeight / 2;
discardX = centerX + 50; // Discard pile to the right of center
discardY = centerY - cardHeight / 2;
initGame();
// Bar graph canvas
barGraphCanvas = createGraphics(barGraphWidth, 700);
}
function draw() {
background(0, 78, 0); // Green background
// Draw the deck
if (deck.length > 0) {
drawCardBack(deckX, deckY);
}
// Display the discard pile
if (discardPile.length > 0) {
let card = discardPile[discardPile.length - 1];
drawCard(card, discardX, discardY);
}
// Display players' hands
for (let i = 0; i < 4; i++) {
drawPlayerHand(i);
}
// Display animations
for (let i = animatingCards.length - 1; i >= 0; i--) {
let anim = animatingCards[i];
anim.update();
anim.draw();
if (anim.isFinished()) {
anim.finish();
animatingCards.splice(i, 1);
}
}
// Handle game logic based on time and game speed
if (
gameStarted &&
millis() - lastMoveTime >= moveInterval &&
animatingCards.length === 0
) {
lastMoveTime = millis();
playTurn();
}
// Draw the bar graph
drawBarGraph();
// Draw hand size graphs
drawHandSizeGraphs();
}
function initGame() {
// Reset variables
deck = [];
discardPile = [];
players = [[], [], [], []];
currentPlayer = 0;
direction = 1;
animatingCards = [];
gameStarted = false;
lastMoveTime = millis();
// Reset hand size history
handSizeHistory = [[], [], [], []];
createDeck();
deck = shuffle(deck); // Corrected shuffle usage
// Animate dealing cards to each player
let dealInterval = 100; // Time between dealing each card
let dealTime = 0;
for (let i = 0; i < 7; i++) {
for (let p = 0; p < 4; p++) {
let card = deck.pop();
let anim = new CardAnimation(
card,
{ x: deckX, y: deckY }, // From deck position
getCardPositionInHand(p, players[p].length),
dealTime
);
anim.onFinish = function () {
players[p].push(this.card);
};
animatingCards.push(anim);
dealTime += dealInterval;
}
}
// Start the discard pile after dealing
setTimeout(() => {
let startingCard = deck.pop();
while (startingCard.type !== 'number') {
deck.unshift(startingCard); // Put it back and pick another
startingCard = deck.pop();
}
discardPile.push(startingCard);
gameStarted = true; // Set gameStarted to true after initialization
}, dealTime + 500);
}
function createDeck() {
deck = [];
let colors = ['red', 'yellow', 'green', 'blue'];
let values = [Array(10).keys()].concat([Array(10).keys()].slice(1));
let specialCards = ['skip', 'reverse', 'draw two'];
// Number cards
for (let color of colors) {
for (let value of values) {
deck.push({ color, value: value.toString(), type: 'number' });
}
// Special cards
for (let sc of specialCards) {
deck.push({ color, value: sc, type: sc });
deck.push({ color, value: sc, type: sc });
}
}
// Wild cards
for (let i = 0; i < 4; i++) {
deck.push({ color: 'purple', value: 'wild', type: 'wild' });
deck.push({ color: 'purple', value: 'wild draw four', type: 'wild draw four' });
}
}
function playTurn() {
let playerHand = players[currentPlayer];
let topCard = discardPile[discardPile.length - 1];
let playableCards = playerHand.filter((card) => canPlayCard(card, topCard));
if (playableCards.length > 0) {
// Play the first playable card
let cardToPlay = playableCards[0];
let cardIndex = playerHand.indexOf(cardToPlay);
playerHand.splice(cardIndex, 1);
// Animate the card being played
let anim = new CardAnimation(
cardToPlay,
getCardPositionInHand(currentPlayer, cardIndex),
{ x: discardX, y: discardY }
);
anim.onFinish = function () {
discardPile.push(this.card);
};
animatingCards.push(anim);
// Handle special cards
handleSpecialCard(cardToPlay);
// Update hand size history
updateHandSizeHistory();
// Check if player has won
if (playerHand.length === 0) {
wins[currentPlayer]++;
displayWinner(currentPlayer);
return;
}
// Move to next player
currentPlayer = (currentPlayer + direction + 4) % 4;
} else {
// Draw a card
if (deck.length === 0) {
// Reshuffle discard pile into deck
let lastCard = discardPile.pop();
deck = shuffle(discardPile);
discardPile = [lastCard];
}
let drawnCard = deck.pop();
// Animate the card being drawn
let animDraw = new CardAnimation(
drawnCard,
{ x: deckX, y: deckY },
getCardPositionInHand(currentPlayer, playerHand.length)
);
animDraw.onFinish = function () {
players[currentPlayer].push(this.card);
// After drawing, check if the drawn card can be played
if (canPlayCard(drawnCard, topCard)) {
// Play the drawn card
players[currentPlayer].pop(); // Remove from hand
let animPlay = new CardAnimation(
drawnCard,
getCardPositionInHand(currentPlayer, playerHand.length),
{ x: discardX, y: discardY }
);
animPlay.onFinish = function () {
discardPile.push(this.card);
};
animatingCards.push(animPlay);
// Handle special cards
handleSpecialCard(drawnCard);
// Update hand size history
updateHandSizeHistory();
// Check if player has won
if (playerHand.length === 0) {
wins[currentPlayer]++;
displayWinner(currentPlayer);
return;
}
} else {
// Update hand size history
updateHandSizeHistory();
}
// Move to next player after animations
currentPlayer = (currentPlayer + direction + 4) % 4;
};
animatingCards.push(animDraw);
}
}
function canPlayCard(card, topCard) {
return (
card.color === topCard.color ||
card.value === topCard.value ||
card.color === 'purple' // Wild cards are now purple
);
}
function handleSpecialCard(card) {
if (card.type === 'skip') {
currentPlayer = (currentPlayer + direction + 4) % 4;
} else if (card.type === 'reverse') {
direction *= -1;
} else if (card.type === 'draw two') {
let nextPlayer = (currentPlayer + direction + 4) % 4;
drawCards(nextPlayer, 2);
} else if (card.type === 'wild draw four') {
let nextPlayer = (currentPlayer + direction + 4) % 4;
drawCards(nextPlayer, 4);
// Randomly select a color
card.color = ['red', 'yellow', 'green', 'blue'][floor(random(4))];
} else if (card.type === 'wild') {
// Randomly select a color
card.color = ['red', 'yellow', 'green', 'blue'][floor(random(4))];
}
}
function drawCards(playerIndex, count) {
for (let i = 0; i < count; i++) {
if (deck.length === 0) {
// Reshuffle discard pile into deck
let lastCard = discardPile.pop();
deck = shuffle(discardPile);
discardPile = [lastCard];
}
let drawnCard = deck.pop();
// Animate the card being drawn
let anim = new CardAnimation(
drawnCard,
{ x: deckX, y: deckY },
getCardPositionInHand(playerIndex, players[playerIndex].length + i)
);
anim.onFinish = function () {
players[playerIndex].push(this.card);
// Update hand size history
updateHandSizeHistory();
};
animatingCards.push(anim);
}
}
function getPlayerPosition(playerIndex) {
let positions = [
{ x: centerX, y: centerY + playerDistance }, // Bottom (Player 1)
{ x: centerX + playerDistance, y: centerY }, // Right (Player 2)
{ x: centerX, y: centerY - playerDistance }, // Top (Player 3)
{ x: centerX - playerDistance, y: centerY }, // Left (Player 4)
];
return positions[playerIndex];
}
function getCardPositionInHand(playerIndex, cardIndex) {
let basePos = getPlayerPosition(playerIndex);
let offset = 20; // Space between cards
let maxCardsInRow = 20;
let numCards = players[playerIndex].length;
if (playerIndex === 0) {
// Bottom player
let startX = basePos.x - (min(numCards, maxCardsInRow) * offset) / 2;
let x = startX + cardIndex * offset;
let y = basePos.y;
return { x, y };
} else if (playerIndex === 2) {
// Top player
let startX = basePos.x - (min(numCards, maxCardsInRow) * offset) / 2;
let x = startX + cardIndex * offset;
let y = basePos.y - cardHeight;
return { x, y };
} else if (playerIndex === 1) {
// Right player
let startY = basePos.y - (min(numCards, maxCardsInRow) * offset) / 2;
let x = basePos.x;
let y = startY + cardIndex * offset;
return { x, y };
} else if (playerIndex === 3) {
// Left player
let startY = basePos.y - (min(numCards, maxCardsInRow) * offset) / 2;
let x = basePos.x - cardWidth;
let y = startY + cardIndex * offset;
return { x, y };
}
}
function drawPlayerHand(playerIndex) {
let playerHand = players[playerIndex];
for (let i = 0; i < playerHand.length; i++) {
let card = playerHand[i];
let pos = getCardPositionInHand(playerIndex, i);
// Check if this card is being animated
let animating = animatingCards.some((anim) => anim.card === card);
if (!animating) {
if (playerIndex === 0 || playerIndex === 2) {
// Players at top and bottom
drawCard(card, pos.x, pos.y);
} else {
// Players at left and right
push();
translate(pos.x + cardWidth / 2, pos.y + cardHeight / 2);
rotate(HALF_PI);
drawCard(card, -cardWidth / 2, -cardHeight / 2);
pop();
}
}
}
// Add label for the player next to their hand
let labelPos = getPlayerPosition(playerIndex);
fill(0, 120, 0);
noStroke();
textAlign(CENTER, CENTER);
textSize(16);
textFont(font);
if (playerIndex === 0) {
// Bottom player
text(`Player ${playerIndex + 1}`, labelPos.x, labelPos.y + cardHeight / 2 + 70);
} else if (playerIndex === 2) {
// Top player
text(`Player ${playerIndex + 1}`, labelPos.x, labelPos.y - cardHeight / 2 - 70);
} else if (playerIndex === 1) {
// Right player
text(`Player ${playerIndex + 1}`, labelPos.x + cardHeight / 2 + 70, labelPos.y);
} else if (playerIndex === 3) {
// Left player
text(`Player ${playerIndex + 1}`, labelPos.x - cardHeight / 2 - 70, labelPos.y);
}
}
function drawCard(card, x, y) {
// Draw card rectangle
stroke(0);
fill(getColor(card.color));
rect(x, y, cardWidth, cardHeight, 5);
textFont(fallBackFont);
// Draw card value in the center
fill(255);
textAlign(CENTER, CENTER);
textSize(16);
noStroke();
textStyle(BOLD);
let displayValue = getCardSymbol(card);
text(displayValue, x + cardWidth / 2, y + cardHeight / 2);
// Draw small labels in corners
textSize(12);
noStroke();
textAlign(LEFT, TOP);
text(displayValue, x + 4, y + 4);
textAlign(RIGHT, BOTTOM);
text(displayValue, x + cardWidth - 3, y + cardHeight - 3);
}
function drawCardBack(x, y) {
// Draw card back
stroke(0);
fill(100);
rect(x, y, cardWidth, cardHeight, 10);
fill(255);
textAlign(CENTER, CENTER);
textSize(15);
text('DECK', x + cardWidth / 2, y + cardHeight / 2);
}
function getCardSymbol(card) {
if (card.type === 'number') {
return card.value;
} else if (card.type === 'skip') {
return '⦸';
} else if (card.type === 'reverse') {
return '↺';
} else if (card.type === 'draw two') {
return '+2';
} else if (card.type === 'wild') {
return 'W';
} else if (card.type === 'wild draw four') {
return 'W+4';
}
}
function getColor(colorName) {
switch (colorName) {
case 'red':
return color(215, 38, 0);
case 'yellow':
return color(236, 212, 7);
case 'green':
return color(55, 151, 17);
case 'blue':
return color(9, 86, 191);
case 'purple':
return color(150, 0, 255); // Special cards are purple
default:
return color(255);
}
}
function displayWinner(winnerIndex) {
gameStarted = false;
fill(255);
textSize(32);
textAlign(CENTER, CENTER);
text(
`Player ${winnerIndex + 1} wins!`,
width / 2,
height / 2 + 50
);
// Restart game after a delay
setTimeout(() => {
initGame();
}, 500);
}
// Card Animation Class
class CardAnimation {
constructor(card, startPos, endPos, delay = 0) {
this.card = card;
this.startPos = startPos;
this.endPos = endPos;
this.startTime = millis() + delay;
this.duration = 100; // Animation duration in milliseconds
this.progress = 0;
this.onFinish = null;
this.finished = false;
}
update() {
let currentTime = millis();
if (currentTime < this.startTime) {
this.progress = 0;
} else {
let elapsed = currentTime - this.startTime;
this.progress = constrain(elapsed / this.duration, 0, 1);
}
}
draw() {
if (this.progress > 0) {
let x = lerp(this.startPos.x, this.endPos.x, this.progress);
let y = lerp(this.startPos.y, this.endPos.y, this.progress);
drawCard(this.card, x, y);
}
}
isFinished() {
return this.progress >= 1 && !this.finished;
}
finish() {
if (this.onFinish) {
this.onFinish();
}
this.finished = true;
}
}
function drawBarGraph() {
barGraphCanvas.background(0, 78, 0);
barGraphCanvas.fill(255);
barGraphCanvas.textFont(font);
barGraphCanvas.textSize(16);
barGraphCanvas.textAlign(CENTER, BOTTOM);
barGraphCanvas.text('Wins', barGraphCanvas.width / 2, 30);
let maxWins = max(wins);
let barWidth = barGraphCanvas.width / 5;
for (let i = 0; i < 4; i++) {
let barHeight = map(wins[i], 0, maxWins || 1, 0, 550);
barGraphCanvas.fill(255, 255 - i * 90, i * 90);
barGraphCanvas.rect(
(i + 1) * barWidth - barWidth / 2,
barGraphCanvas.height - 50 - barHeight,
barWidth * 0.8,
barHeight
);
barGraphCanvas.fill(255);
barGraphCanvas.text(
`P${i + 1}`,
(i + 1) * barWidth,
barGraphCanvas.height - 30
);
barGraphCanvas.text(
wins[i],
(i + 1) * barWidth,
barGraphCanvas.height - 60 - barHeight
);
}
// Draw the bar graph at the right edge
image(barGraphCanvas, width - barGraphWidth, 0);
}
function updateHandSizeHistory() {
for (let i = 0; i < 4; i++) {
handSizeHistory[i].push(players[i].length);
// Limit history to a certain length to prevent memory issues
if (handSizeHistory[i].length > 100) {
handSizeHistory[i].shift();
}
}
}
function drawHandSizeGraphs() {
let graphHeight = 100;
let graphWidth = width / 4;
for (let i = 0; i < 4; i++) {
let x = i * graphWidth;
let y = height - graphHeight;
noFill();
stroke(170);
rect(790,0, 210, 700);
stroke(255);
rect(x, y, graphWidth, graphHeight);
stroke(255, 255 - i * 90, i * 90);
beginShape();
let history = handSizeHistory[i];
for (let j = 0; j < history.length; j++) {
let hx = map(j, 0, history.length - 1, x, x + graphWidth);
let hy = map(history[j], 0, maxHandSize, y + graphHeight, y);
vertex(hx, hy);
}
endShape();
// Draw player label
fill(255);
noStroke();
textAlign(CENTER, TOP);
textSize(12);
textFont(font);
text(`Player ${i + 1}`, x + graphWidth / 2, y - -5);
}
}