pyircbot/pyircbot/modules/DogeDice.py

333 lines
13 KiB
Python
Raw Normal View History

2014-01-07 10:22:21 -08:00
#!/usr/bin/env python
2014-10-02 18:14:42 -07:00
"""
.. module:: DogeDice
2015-11-01 18:03:11 -08:00
:synopsis: Module to provide a game for gambling Dogecoin
2014-10-02 18:14:42 -07:00
.. moduleauthor:: Dave Pedu <dave@davepedu.com>
"""
2017-01-01 16:27:58 -08:00
from pyircbot.modulebase import ModuleBase, ModuleHook
2014-01-07 10:22:21 -08:00
import random
import math
from threading import Timer
2017-01-01 14:59:01 -08:00
2014-01-07 10:22:21 -08:00
class DogeDice(ModuleBase):
2015-11-01 18:03:11 -08:00
def __init__(self, bot, moduleName):
ModuleBase.__init__(self, bot, moduleName);
2017-01-01 14:59:01 -08:00
self.hooks = [ModuleHook("PRIVMSG", self.gotMsg)]
2015-11-01 18:03:11 -08:00
# Load attribute storage
self.attr = self.bot.getBestModuleForService("attributes")
# Load doge RPC
self.doge = self.bot.getBestModuleForService("dogerpc")
# Dict of #channel -> game object
self.games = {}
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def gotMsg(self, args, prefix, trailing):
prefixObj = self.bot.decodePrefix(prefix)
# Ignore messages from users not logged in
loggedinfrom = self.attr.getKey(prefixObj.nick, "loggedinfrom")
2017-01-01 14:59:01 -08:00
if loggedinfrom is None:
2015-11-01 18:03:11 -08:00
# Send them a hint?
return
elif prefixObj.hostname == loggedinfrom:
if args[0][0] == "#":
# create a blank game obj if there isn't one (and whitelisted ? )
2017-01-01 14:59:01 -08:00
if not args[0] in self.games and (not self.config["channelWhitelistOn"] or
(self.config["channelWhitelistOn"] and args[0][1:] in self.config["channelWhitelist"])):
self.games[args[0]] = gameObj(self, args[0])
2015-11-01 18:03:11 -08:00
# Channel message
self.games[args[0]].gotMsg(args, prefix, trailing)
else:
# Private message
2017-01-01 14:59:01 -08:00
# self.games[args[0].gotPrivMsg(args, prefix, trailing)
2015-11-01 18:03:11 -08:00
pass
else:
# Ignore potential spoofing
pass
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def removeGame(self, channel):
del self.games[channel]
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def ondisable(self):
self.log.info("DogeDice: Unload requested, ending games...")
2017-01-01 14:59:01 -08:00
while len(self.games) > 0:
2015-11-01 18:03:11 -08:00
first = list(self.games.keys())[0]
self.games[first].gameover()
2014-01-07 10:22:21 -08:00
2017-01-01 14:59:01 -08:00
2014-01-07 10:22:21 -08:00
class gameObj:
2015-11-01 18:03:11 -08:00
def __init__(self, master, channel):
self.master = master
self.channel = channel
# Game state
# 0 = waiting for players
# - advertise self?
# - players must be registered and have enough doge for current bet
# 1 = enough players, countdown
# - Last warning to pull out
# 2 = locked in / game setup
# - Move doge from player's wallets to table wallet. kick players if they can't afford
# 3 = start of a round
# - Each player's turn to roll
# 4 = determine winner, move doge
# - if > 10 doge, house fee?
self.step = 0
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# Bet amount
self.bet = 0.0
# players list
self.players = []
# min players
self.minPlayers = 2
# max players
self.maxPlayers = 4
# Lobby countdown timer
self.startCountdownTimer = None
# pre-result timer
self.endgameResultTimer = None
2017-01-01 14:59:01 -08:00
# in-game timeout
2015-11-01 18:03:11 -08:00
self.playTimeout = None
# Wallet for this game
self.walletName = None
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def getPlayer(self, nick):
for player in self.players:
if player.nick == nick:
return player
return None
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def gotPrivMsg(self, args, prefix, trailing):
prefix = self.master.bot.decodePrefix(prefix)
pass
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def gotMsg(self, args, prefix, trailing):
prefix = self.master.bot.decodePrefix(prefix)
if self.step == 0 or self.step == 1:
# Join game
cmd = self.master.bot.messageHasCommand(".join", trailing)
if cmd:
2017-01-01 14:59:01 -08:00
if len(self.players) - 1 < self.maxPlayers:
if self.getPlayer(prefix.nick) is None:
2015-11-01 18:03:11 -08:00
userWallet = self.master.attr.getKey(prefix.nick, "dogeaccountname")
2017-01-01 14:59:01 -08:00
if userWallet is None:
2015-11-01 18:03:11 -08:00
self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick))
return
balance = self.master.doge.getBal(userWallet)
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# check if the room is 'opened' already:
2017-01-01 14:59:01 -08:00
if not self.players:
2015-11-01 18:03:11 -08:00
# require an amount
2017-01-01 14:59:01 -08:00
if len(cmd.args) == 1:
2015-11-01 18:03:11 -08:00
# Check if they have enough coins
try:
bet = float(cmd.args[0])
except:
return
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
if bet < self.master.config["minBet"]:
2017-01-01 14:59:01 -08:00
self.master.bot.act_PRIVMSG(self.channel, "%s: Minimum bet is %s DOGE!" %
(prefix.nick, self.master.config["minBet"]))
2015-11-01 18:03:11 -08:00
return
2017-01-01 14:59:01 -08:00
if balance >= bet:
2015-11-01 18:03:11 -08:00
newPlayer = playerObj(self, prefix.nick)
newPlayer.dogeWalletName = userWallet
self.players.append(newPlayer)
self.bet = bet
self.master.bot.act_PRIVMSG(self.channel, "%s: You have joined!" % (prefix.nick))
else:
2017-01-01 14:59:01 -08:00
self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" %
(prefix.nick))
2015-11-01 18:03:11 -08:00
else:
2017-01-01 14:59:01 -08:00
self.master.bot.act_PRIVMSG(self.channel, "%s: You need to specify a bet amount: "
".join 10" % (prefix.nick))
2015-11-01 18:03:11 -08:00
else:
# no amount required
2017-01-01 14:59:01 -08:00
if balance >= self.bet:
2015-11-01 18:03:11 -08:00
newPlayer = playerObj(self, prefix.nick)
newPlayer.dogeWalletName = userWallet
self.players.append(newPlayer)
self.master.bot.act_PRIVMSG(self.channel, "%s: You have joined!" % (prefix.nick))
2017-01-01 14:59:01 -08:00
if self.canStart() and self.startCountdownTimer is None:
2015-11-01 18:03:11 -08:00
self.initStartCountdown()
2017-01-01 14:59:01 -08:00
self.master.bot.act_PRIVMSG(self.channel, "The game will start in %s seconds! "
"Bet is %s DOGE each!" %
(self.master.config["lobbyIdleSeconds"], self.bet))
2015-11-01 18:03:11 -08:00
else:
2017-01-01 14:59:01 -08:00
self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" %
(prefix.nick))
2015-11-01 18:03:11 -08:00
else:
2017-01-01 14:59:01 -08:00
self.master.bot.act_PRIVMSG(self.channel, "%s: you're already in the game. Quit with .leave" %
(prefix.nick))
2015-11-01 18:03:11 -08:00
else:
2017-01-01 14:59:01 -08:00
self.master.bot.act_PRIVMSG(self.channel, "%s: the game is full (%s/%)! Cannot join." %
(prefix.nick, len(self.players), self.maxPlayers))
2015-11-01 18:03:11 -08:00
# Leave game
cmd = self.master.bot.messageHasCommand(".leave", trailing)
if cmd:
2017-01-01 14:59:01 -08:00
if self.getPlayer(prefix.nick) is None:
2015-11-01 18:03:11 -08:00
self.master.bot.act_PRIVMSG(self.channel, "%s: You're not in the game." % (prefix.nick))
else:
self.removePlayer(prefix.nick)
self.master.bot.act_PRIVMSG(self.channel, "%s: You have left the game!" % (prefix.nick))
if not self.canStart() and self.startCountdownTimer:
self.clearTimer(self.startCountdownTimer)
self.startCountdownTimer = None
2017-01-01 14:59:01 -08:00
self.master.bot.act_PRIVMSG(self.channel, "Game canceled.")
2015-11-01 18:03:11 -08:00
self.step = 0
elif self.step == 2:
pass
elif self.step == 3:
# Ignore cmds from people outside the game
player = self.getPlayer(prefix.nick)
if not player:
return
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# handle a .roll
cmd = self.master.bot.messageHasCommand(".roll", trailing)
if cmd and not player.hasRolled:
2017-01-01 14:59:01 -08:00
roll1 = random.randint(1, 6)
roll2 = random.randint(1, 6)
2015-11-01 18:03:11 -08:00
self.master.bot.act_PRIVMSG(self.channel, "%s rolls %s and %s!" % (prefix.nick, roll1, roll2))
player.hasRolled = True
2017-01-01 14:59:01 -08:00
player.rollValue = roll1 + roll2
2015-11-01 18:03:11 -08:00
# Check if all players have rolled
for player in self.players:
if not player.hasRolled:
return
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# start endgame timer
self.step = 4
self.endgameResultTimer = Timer(2, self.endgameResults)
self.endgameResultTimer.start()
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
elif self.step == 4:
pass
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def clearTimer(self, timer):
if timer:
timer.cancel()
timer = None
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def removePlayer(self, playerNick):
pos = -1
for i in range(0, len(self.players)):
if self.players[i].nick == playerNick:
pos = i
break
if pos >= 0:
self.players.pop(pos)
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def canStart(self):
# Return true if the step is 'lobby' mode and player count is OK
2017-01-01 14:59:01 -08:00
return self.step == 0 and len(self.players) >= self.minPlayers
2015-11-01 18:03:11 -08:00
def initStartCountdown(self):
# Start the game-start countdown
self.startCountdownTimer = Timer(self.master.config["lobbyIdleSeconds"], self.lobbyCountdownDone)
self.startCountdownTimer.start()
self.step = 1
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def lobbyCountdownDone(self):
self.step = 2
self.master.bot.act_PRIVMSG(self.channel, "Collecting DOGE and starting game.. Type .roll !")
# Make a wallet for this game
2017-01-01 14:59:01 -08:00
self.walletName = "DogeDice-" + self.channel
2015-11-01 18:03:11 -08:00
# Generate an address to 'create' a wallet
self.master.doge.getAcctAddr(self.walletName)
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# Verify and move funds from each player
for player in self.players:
playerBalance = self.master.doge.getAcctBal(player.dogeWalletName)
if playerBalance < self.bet:
self.master.bot.act_PRIVMSG(self.channel, "%s was dropped from the game!")
self.removePlayer(player.nick)
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
if len(self.players) <= 1:
self.master.bot.act_PRIVMSG(self.channel, "1 or players left - game over!")
self.resetGame()
return
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# Take doges
for player in self.players:
self.master.doge.move(player.dogeWalletName, self.walletName, self.bet)
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# Pre-game setup (nothing !)
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# Accept game commands
self.step = 3
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# Start play timeout
self.playTimeout = Timer(30, self.gamePlayTimeoutExpired)
self.playTimeout.start()
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def gamePlayTimeoutExpired(self):
# Time out - return doges
self.master.bot.act_PRIVMSG(self.channel, "Time expired! Returning all doges.")
2015-11-01 21:58:20 -08:00
# TODO: Option beteen this and user surrenders their coins on timeout
2015-11-01 18:03:11 -08:00
if self.step == 3:
# In game step. Refund doges
for player in self.players:
self.master.doge.move(self.walletName, player.dogeWalletName, self.bet)
self.resetGame()
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def endgameResults(self):
maxRollNames = []
maxRollValue = 0
for player in self.players:
if player.rollValue > maxRollValue:
maxRollNames = []
maxRollNames.append(player.nick)
maxRollValue = player.rollValue
if player.rollValue == maxRollValue:
2017-01-01 14:59:01 -08:00
if player.nick not in maxRollNames:
2015-11-01 18:03:11 -08:00
maxRollNames.append(player.nick)
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
pot = self.master.doge.getAcctBal(self.walletName)
2017-01-01 14:59:01 -08:00
DOGEeachDec = pot / len(maxRollNames)
DOGEeach = math.floor(DOGEeachDec * 100000000) / 100000000
if len(maxRollNames) == 1:
self.master.bot.act_PRIVMSG(self.channel, "We have a winner - %s! Winnings are: %s DOGE" %
(maxRollNames[0], DOGEeach))
2015-11-01 18:03:11 -08:00
else:
2017-01-01 14:59:01 -08:00
self.master.bot.act_PRIVMSG(self.channel, "We have a tie between %s - The take is %s DOGE each" %
(' and '.join(maxRollNames), DOGEeach))
2015-11-01 18:03:11 -08:00
# Pay out
for nick in maxRollNames:
player = self.getPlayer(nick)
self.master.doge.move(self.walletName, player.dogeWalletName, DOGEeach)
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
# the end!
self.resetGame()
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def resetGame(self):
self.clearTimer(self.startCountdownTimer)
self.startCountdownTimer = None
self.clearTimer(self.endgameResultTimer)
self.endgameResultTimer = None
self.clearTimer(self.playTimeout)
self.playTimeout = None
self.master.removeGame(self.channel)
2017-01-01 14:59:01 -08:00
2015-11-01 18:03:11 -08:00
def gameover(self):
self.gamePlayTimeoutExpired()
2017-01-01 14:59:01 -08:00
2014-01-07 10:22:21 -08:00
class playerObj:
2015-11-01 18:03:11 -08:00
def __init__(self, game, nick):
self.game = game
self.nick = nick
# Save the player's wallet name
self.dogeWalletName = None
# Set to true after they roll
self.hasRolled = False
# Sum of their dice
self.rollValue = None
2017-01-01 14:59:01 -08:00