// plugins/pigs.js - Pass the Pigs game plugin module.exports = { init(bot) { console.log('Pass the Pigs plugin initialized'); this.bot = bot; // Game state and scores storage this.pigScores = new Map(); // nick -> { totalScore, bestRoll, rollCount, name, eliminated } this.gameState = { active: false, players: [], currentPlayer: 0, turnScore: 0, channel: null }; }, cleanup(bot) { console.log('Pass the Pigs plugin cleaned up'); // Clear any active games on cleanup this.gameState = { active: false, players: [], currentPlayer: 0, turnScore: 0, channel: null }; }, // Pass the Pigs game simulation with increased difficulty rollPigs() { // Actual probabilities from 11,954 sample study const positions = [ { name: 'Side (no dot)', points: 0, weight: 34.9 }, { name: 'Side (dot)', points: 0, weight: 30.2 }, { name: 'Razorback', points: 5, weight: 22.4 }, { name: 'Trotter', points: 5, weight: 8.8 }, { name: 'Snouter', points: 10, weight: 3.0 }, { name: 'Leaning Jowler', points: 15, weight: 0.61 } ]; const rollPig = () => { const totalWeight = positions.reduce((sum, pos) => sum + pos.weight, 0); let random = Math.random() * totalWeight; for (const position of positions) { random -= position.weight; if (random <= 0) { return position; } } return positions[0]; // fallback }; const pig1 = rollPig(); const pig2 = rollPig(); // Check for touching conditions first (increased probability for more difficulty) if (Math.random() < 0.035) { // 3.5% chance of touching (was 1.5%) if (Math.random() < 0.25) { // 25% of touches are Piggyback return { result: 'Piggyback', points: -9999, description: '🐷📚 Piggyback! ELIMINATED!' }; } else { // 75% are Makin' Bacon/Oinker return { result: 'Makin\' Bacon', points: -999, description: '🥓 Makin\' Bacon! Lose ALL points!' }; } } // Handle side positions (Sider vs Pig Out) const pig1IsSide = pig1.name.startsWith('Side'); const pig2IsSide = pig2.name.startsWith('Side'); if (pig1IsSide && pig2IsSide) { // Both on sides - increased chance of opposite sides (Pig Out) const pig1Dot = pig1.name.includes('dot'); const pig2Dot = pig2.name.includes('dot'); // Bias toward opposite sides for more busts const oppositeRoll = Math.random(); if (oppositeRoll < 0.6) { // 60% chance of opposite sides when both are siders return { result: 'Pig Out', points: 0, description: '💥 Pig Out! Turn ends!' }; } else { return { result: 'Sider', points: 1, description: '🐷 Sider' }; } } // If only one pig is on its side, that pig scores nothing if (pig1IsSide && !pig2IsSide) { return { result: pig2.name, points: pig2.points, description: `🐷 ${pig2.name} (${pig2.points})` }; } if (pig2IsSide && !pig1IsSide) { return { result: pig1.name, points: pig1.points, description: `🐷 ${pig1.name} (${pig1.points})` }; } // Neither pig is on its side - normal scoring if (pig1.name === pig2.name) { // Double combinations - sum doubled (quadruple individual) const doublePoints = (pig1.points + pig2.points) * 2; switch (pig1.name) { case 'Razorback': return { result: 'Double Razorback', points: doublePoints, description: '🐷🐷 Double Razorback (20)' }; case 'Trotter': return { result: 'Double Trotter', points: doublePoints, description: '🐷🐷 Double Trotter (20)' }; case 'Snouter': return { result: 'Double Snouter', points: doublePoints, description: '🐷🐷 Double Snouter (40)' }; case 'Leaning Jowler': return { result: 'Double Leaning Jowler', points: doublePoints, description: '🐷🐷 Double Leaning Jowler (60)' }; } } // Mixed combination - sum of individual scores const totalPoints = pig1.points + pig2.points; return { result: 'Mixed Combo', points: totalPoints, description: `🐷 ${pig1.name} + ${pig2.name} (${totalPoints})` }; }, // Turn management for pig game nextTurn(target) { this.gameState.currentPlayer = (this.gameState.currentPlayer + 1) % 2; const nextPlayer = this.gameState.players[this.gameState.currentPlayer]; // Check if next player is eliminated const nextPlayerStats = this.pigScores.get(nextPlayer); if (nextPlayerStats.eliminated) { const winner = this.gameState.players[(this.gameState.currentPlayer + 1) % 2]; this.bot.say(target, `${nextPlayer} is eliminated! ${winner} wins!`); this.endGame(target); return; } this.bot.say(target, `🎲 ${nextPlayer}'s turn!`); }, endGame(target) { this.bot.say(target, '🏁 Game ended! Use !pigs to start new game.'); this.gameState = { active: false, players: [], currentPlayer: 0, turnScore: 0, channel: null }; }, commands: [ { name: 'pigs', description: 'Start or join a Pass the Pigs game', execute(context, bot) { const target = context.replyTo; const from = context.nick; const to = context.channel || context.replyTo; // Only allow channel play for turn-based games if (!context.channel) { bot.say(target, 'Turn-based pig games can only be played in channels!'); return; } // Game setup phase if (!this.gameState.active) { // First player joins if (this.gameState.players.length === 0) { this.gameState.players.push(from); this.gameState.channel = to; // Initialize player if (!this.pigScores.has(from)) { this.pigScores.set(from, { totalScore: 0, bestRoll: 0, rollCount: 0, name: from, eliminated: false }); } bot.say(target, `🐷 ${from} wants to start a pig game! Second player use !pigs to join`); return; } // Second player joins - start game if (this.gameState.players.length === 1 && !this.gameState.players.includes(from)) { this.gameState.players.push(from); this.gameState.active = true; this.gameState.currentPlayer = 0; this.gameState.turnScore = 0; // Initialize second player if (!this.pigScores.has(from)) { this.pigScores.set(from, { totalScore: 0, bestRoll: 0, rollCount: 0, name: from, eliminated: false }); } bot.say(target, `🎮 ${this.gameState.players[0]} vs ${this.gameState.players[1]} - ${this.gameState.players[0]} goes first!`); return; } // Handle various edge cases if (this.gameState.players.includes(from)) { bot.say(target, `${from}: You're already in the game!`); return; } else { bot.say(target, `${from}: Game is full! Wait for current game to finish.`); return; } } // Game is active - check if it's player's turn if (!this.gameState.players.includes(from)) { bot.say(target, `${from}: You're not in the current game!`); return; } const currentPlayerName = this.gameState.players[this.gameState.currentPlayer]; if (from !== currentPlayerName) { bot.say(target, `${from}: It's ${currentPlayerName}'s turn!`); return; } // Check if current player is eliminated const playerStats = this.pigScores.get(from); if (playerStats.eliminated) { bot.say(target, `${from}: You are eliminated!`); this.endGame(target); return; } // Roll the pigs! const roll = this.rollPigs(); playerStats.rollCount++; let message = ''; let endTurn = false; let endGame = false; if (roll.points === -9999) { // Piggyback - eliminate player and end game playerStats.eliminated = true; message = `${from}: ${roll.description}`; endGame = true; } else if (roll.points === -999) { // Makin' Bacon - lose all points and end turn const lostPoints = playerStats.totalScore; playerStats.totalScore = 0; this.gameState.turnScore = 0; message = `${from}: ${roll.description} Lost ${lostPoints} total!`; endTurn = true; } else if (roll.points === 0) { // Pig Out - lose turn score and end turn message = `${from}: ${roll.description} Lost ${this.gameState.turnScore} turn points!`; this.gameState.turnScore = 0; endTurn = true; } else { // Normal scoring - add to turn score this.gameState.turnScore += roll.points; // Update best roll if this is better if (roll.points > playerStats.bestRoll) { playerStats.bestRoll = roll.points; } const potentialTotal = playerStats.totalScore + this.gameState.turnScore; message = `${from}: ${roll.description} | Turn: ${this.gameState.turnScore} | Total: ${playerStats.totalScore}`; // Check potential win if (potentialTotal >= 100) { message += ` | Can win!`; } } bot.say(target, message); if (endGame) { this.endGame(target); } else if (endTurn) { this.nextTurn(target); } }.bind(this) }, { name: 'bank', description: 'Bank your turn points in Pass the Pigs', execute(context, bot) { const target = context.replyTo; const from = context.nick; if (!this.gameState.active || !this.gameState.players.includes(from)) { bot.say(target, `${from}: You're not in an active pig game!`); return; } const currentPlayerName = this.gameState.players[this.gameState.currentPlayer]; if (from !== currentPlayerName) { bot.say(target, `${from}: It's ${currentPlayerName}'s turn!`); return; } if (this.gameState.turnScore === 0) { bot.say(target, `${from}: No points to bank!`); return; } // Bank the points const playerStats = this.pigScores.get(from); playerStats.totalScore += this.gameState.turnScore; bot.say(target, `${from}: Banked ${this.gameState.turnScore} points! Total: ${playerStats.totalScore}`); // Check for win if (playerStats.totalScore >= 100) { bot.say(target, `🎉 ${from} WINS with ${playerStats.totalScore} points! 🎉`); this.endGame(target); return; } this.gameState.turnScore = 0; this.nextTurn(target); }.bind(this) }, { name: 'quitpigs', description: 'Quit the current pig game', execute(context, bot) { const target = context.replyTo; const from = context.nick; if (!this.gameState.active && this.gameState.players.length === 0) { bot.say(target, `${from}: No pig game to quit!`); return; } if (this.gameState.players.includes(from)) { bot.say(target, `${from} quit the pig game!`); this.endGame(target); } else { bot.say(target, `${from}: You're not in the current game!`); } }.bind(this) }, { name: 'toppigs', description: 'Show top pig players', execute(context, bot) { const target = context.replyTo; if (this.pigScores.size === 0) { bot.say(target, 'No pig scores recorded yet! Use !pigs to start playing.'); return; } // Convert to array and sort by total score const sortedScores = Array.from(this.pigScores.values()) .sort((a, b) => b.totalScore - a.totalScore) .slice(0, 5); // Top 5 bot.say(target, '🏆 Top Pig Players:'); sortedScores.forEach((player, index) => { const rank = index + 1; const trophy = rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : `${rank}.`; bot.say(target, `${trophy} ${player.name}: ${player.totalScore} points (best roll: ${player.bestRoll}, ${player.rollCount} rolls)`); }); }.bind(this) }, { name: 'pigstatus', description: 'Show current pig game status', execute(context, bot) { const target = context.replyTo; if (!this.gameState.active) { if (this.gameState.players.length === 1) { bot.say(target, `🐷 ${this.gameState.players[0]} is waiting for a second player! Use !pigs to join.`); } else { bot.say(target, 'No active pig game. Use !pigs to start one!'); } return; } const currentPlayer = this.gameState.players[this.gameState.currentPlayer]; const player1Stats = this.pigScores.get(this.gameState.players[0]); const player2Stats = this.pigScores.get(this.gameState.players[1]); bot.say(target, `🎮 Current Game: ${this.gameState.players[0]} (${player1Stats.totalScore}) vs ${this.gameState.players[1]} (${player2Stats.totalScore})`); bot.say(target, `🎲 ${currentPlayer}'s turn | Turn score: ${this.gameState.turnScore}`); }.bind(this) } ] };