// plugins/hilo.js - Hi-Lo Casino game plugin const fs = require('fs'); const path = require('path'); module.exports = { init(bot) { console.log('Hi-Lo Casino plugin initialized'); this.bot = bot; // Persistent player statistics (saved to file) this.playerStats = new Map(); // nick -> { totalWins, totalLosses, totalGames, biggestWin, winStreak, bestStreak, name } // Active games storage - nick -> { firstCard, bet, challenger } this.activeGames = new Map(); // Set up score file path this.scoresFile = path.join(__dirname, 'hilo_scores.json'); // Load existing scores this.loadScores(); }, cleanup(bot) { console.log('Hi-Lo Casino plugin cleaned up'); // Save scores before cleanup this.saveScores(); // Clear active games this.activeGames.clear(); }, // Load scores from file loadScores() { try { if (fs.existsSync(this.scoresFile)) { const data = fs.readFileSync(this.scoresFile, 'utf8'); const scoresObject = JSON.parse(data); // Convert back to Map this.playerStats = new Map(Object.entries(scoresObject)); console.log(`🃏 Loaded ${this.playerStats.size} Hi-Lo player stats from ${this.scoresFile}`); // Log top 3 players if any exist if (this.playerStats.size > 0) { const topPlayers = Array.from(this.playerStats.values()) .sort((a, b) => b.totalWins - a.totalWins) .slice(0, 3); console.log('🏆 Top Hi-Lo players:', topPlayers.map(p => `${p.name}(${p.totalWins})`).join(', ')); } } else { console.log(`🃏 No existing Hi-Lo scores file found, starting fresh`); } } catch (error) { console.error(`❌ Error loading Hi-Lo scores:`, error); this.playerStats = new Map(); // Reset to empty if load fails } }, // Save scores to file saveScores() { try { // Convert Map to plain object for JSON serialization const scoresObject = Object.fromEntries(this.playerStats); const data = JSON.stringify(scoresObject, null, 2); fs.writeFileSync(this.scoresFile, data, 'utf8'); console.log(`💾 Saved ${this.playerStats.size} Hi-Lo player stats to ${this.scoresFile}`); } catch (error) { console.error(`❌ Error saving Hi-Lo scores:`, error); } }, // Update a player's persistent stats and save to file updatePlayerStats(nick, updates) { // Ensure playerStats Map exists if (!this.playerStats) { this.playerStats = new Map(); } if (!this.playerStats.has(nick)) { this.playerStats.set(nick, { totalWins: 0, totalLosses: 0, totalGames: 0, biggestWin: 0, winStreak: 0, bestStreak: 0, name: nick }); } const player = this.playerStats.get(nick); Object.assign(player, updates); // Save to file after each update this.saveScores(); }, // Generate random card (1-100) generateCard() { return Math.floor(Math.random() * 100) + 1; }, // Get card display with emoji getCardDisplay(card) { let emoji = '🃏'; if (card <= 25) emoji = '🟢'; // Low (1-25) else if (card <= 50) emoji = '🟡'; // Medium-Low (26-50) else if (card <= 75) emoji = '🟠'; // Medium-High (51-75) else emoji = '🔴'; // High (76-100) return `${emoji}${card}`; }, commands: [ { name: 'hilo', description: 'Start a Hi-Lo game or guess higher/lower', execute: function(context, bot) { const plugin = module.exports; const target = context.replyTo; const from = context.nick; const args = context.args; // Check if replying to an existing game if (plugin.activeGames.has(from)) { bot.say(target, `${from}: You already have an active Hi-Lo game! Use !higuess or !loguess`); return; } // Check if someone challenged this player let challengerGame = null; for (const [challenger, game] of plugin.activeGames.entries()) { if (game.challenger === from) { challengerGame = { challenger, game }; break; } } if (challengerGame) { bot.say(target, `${from}: ${challengerGame.challenger} is waiting for your !higuess or !loguess response!`); return; } // Parse command: !hilo if (args.length < 2) { bot.say(target, `Usage: !hilo - Example: !hilo alice 10`); return; } const opponent = args[0]; const bet = parseInt(args[1]); if (isNaN(bet) || bet < 1 || bet > 100) { bot.say(target, `${from}: Bet must be between 1 and 100`); return; } if (opponent === from) { bot.say(target, `${from}: You can't challenge yourself!`); return; } // Initialize players if new if (!plugin.playerStats.has(from)) { plugin.updatePlayerStats(from, { totalWins: 0, totalLosses: 0, totalGames: 0, biggestWin: 0, winStreak: 0, bestStreak: 0, name: from }); } // Generate first card const firstCard = plugin.generateCard(); // Store the game plugin.activeGames.set(from, { firstCard: firstCard, bet: bet, challenger: opponent }); const cardDisplay = plugin.getCardDisplay(firstCard); bot.say(target, `🃏 ${from} challenges ${opponent} to Hi-Lo! First card: ${cardDisplay} | Bet: ${bet} pts | ${opponent}: !higuess or !loguess?`); } }, { name: 'higuess', description: 'Guess the next card will be higher', execute: function(context, bot) { const plugin = module.exports; const target = context.replyTo; const from = context.nick; plugin.processGuess(target, from, 'higher', bot); } }, { name: 'loguess', description: 'Guess the next card will be lower', execute: function(context, bot) { const plugin = module.exports; const target = context.replyTo; const from = context.nick; plugin.processGuess(target, from, 'lower', bot); } }, { name: 'hilostats', description: 'Show your Hi-Lo statistics', execute: function(context, bot) { const plugin = module.exports; const target = context.replyTo; const from = context.nick; if (!plugin.playerStats.has(from)) { bot.say(target, `${from}: You haven't played Hi-Lo yet! Use !hilo to start.`); return; } const player = plugin.playerStats.get(from); const winRate = player.totalGames > 0 ? ((player.totalWins / player.totalGames) * 100).toFixed(1) : 0; bot.say(target, `🃏 ${from}: ${player.totalWins}W/${player.totalLosses}L (${winRate}%) | Best: ${player.biggestWin} | Streak: ${player.winStreak} (best: ${player.bestStreak}) | Games: ${player.totalGames}`); } }, { name: 'tophilo', description: 'Show top Hi-Lo players', execute: function(context, bot) { const plugin = module.exports; const target = context.replyTo; if (plugin.playerStats.size === 0) { bot.say(target, 'No Hi-Lo scores recorded yet! Use !hilo to start playing.'); return; } // Get top players by different metrics const byWins = Array.from(plugin.playerStats.values()) .sort((a, b) => b.totalWins - a.totalWins) .slice(0, 3); const byStreak = Array.from(plugin.playerStats.values()) .sort((a, b) => b.bestStreak - a.bestStreak) .slice(0, 1); let output = '🏆 Top Hi-Lo: '; byWins.forEach((player, i) => { const trophy = i === 0 ? '🥇' : i === 1 ? '🥈' : '🥉'; const winRate = player.totalGames > 0 ? ((player.totalWins / player.totalGames) * 100).toFixed(0) : 0; output += `${trophy}${player.name}(${player.totalWins}W/${winRate}%) `; }); if (byStreak[0] && byStreak[0].bestStreak > 0) { output += `| 🔥 Best Streak: ${byStreak[0].name}(${byStreak[0].bestStreak})`; } bot.say(target, output); } }, { name: 'resethilo', description: 'Reset all Hi-Lo scores (admin command)', execute: function(context, bot) { const plugin = module.exports; const target = context.replyTo; const from = context.nick; // Simple admin check const adminNicks = ['admin', 'owner', 'cancerbot', 'megasconed']; if (!adminNicks.includes(from)) { bot.say(target, `${from}: Access denied - admin only command`); return; } const playerCount = plugin.playerStats.size; plugin.playerStats.clear(); plugin.activeGames.clear(); plugin.saveScores(); bot.say(target, `🗑️ ${from}: Reset ${playerCount} Hi-Lo player stats`); } } ], // Process the guess (shared logic) processGuess(target, from, guess, bot) { // Find the game where this player is the challenger let gameData = null; let challenger = null; for (const [chalName, game] of this.activeGames.entries()) { if (game.challenger === from) { gameData = game; challenger = chalName; break; } } if (!gameData) { bot.say(target, `${from}: No Hi-Lo challenge found for you!`); return; } // Initialize challenger if new if (!this.playerStats.has(from)) { this.updatePlayerStats(from, { totalWins: 0, totalLosses: 0, totalGames: 0, biggestWin: 0, winStreak: 0, bestStreak: 0, name: from }); } // Generate second card const secondCard = this.generateCard(); const firstCard = gameData.firstCard; const bet = gameData.bet; // Determine if guess was correct let isCorrect = false; if (guess === 'higher' && secondCard > firstCard) isCorrect = true; if (guess === 'lower' && secondCard < firstCard) isCorrect = true; if (secondCard === firstCard) { // Tie - push/draw bot.say(target, `🃏 ${from}: ${this.getCardDisplay(firstCard)}→${this.getCardDisplay(secondCard)} | 🤝 TIE! No winner`); this.activeGames.delete(challenger); return; } // Update stats for both players const challengerPlayer = this.playerStats.get(challenger); const guesserPlayer = this.playerStats.get(from); let challengerUpdates = { totalGames: challengerPlayer.totalGames + 1 }; let guesserUpdates = { totalGames: guesserPlayer.totalGames + 1 }; const firstDisplay = this.getCardDisplay(firstCard); const secondDisplay = this.getCardDisplay(secondCard); const direction = guess === 'higher' ? 'HI' : 'LO'; if (isCorrect) { // Guesser wins guesserUpdates.totalWins = guesserPlayer.totalWins + 1; guesserUpdates.winStreak = guesserPlayer.winStreak + 1; challengerUpdates.totalLosses = challengerPlayer.totalLosses + 1; challengerUpdates.winStreak = 0; if (guesserUpdates.winStreak > guesserPlayer.bestStreak) { guesserUpdates.bestStreak = guesserUpdates.winStreak; } if (bet > guesserPlayer.biggestWin) { guesserUpdates.biggestWin = bet; } bot.say(target, `🃏 ${from}: ${firstDisplay}→${secondDisplay} ${direction} | ✅ CORRECT! +${bet} pts | Streak: ${guesserUpdates.winStreak}`); } else { // Challenger wins challengerUpdates.totalWins = challengerPlayer.totalWins + 1; challengerUpdates.winStreak = challengerPlayer.winStreak + 1; guesserUpdates.totalLosses = guesserPlayer.totalLosses + 1; guesserUpdates.winStreak = 0; if (challengerUpdates.winStreak > challengerPlayer.bestStreak) { challengerUpdates.bestStreak = challengerUpdates.winStreak; } if (bet > challengerPlayer.biggestWin) { challengerUpdates.biggestWin = bet; } bot.say(target, `🃏 ${from}: ${firstDisplay}→${secondDisplay} ${direction} | ❌ WRONG! ${challenger} wins ${bet} pts`); } // Update both players this.updatePlayerStats(challenger, challengerUpdates); this.updatePlayerStats(from, guesserUpdates); // Remove the game this.activeGames.delete(challenger); } };