276 lines
11 KiB
Python
Executable File
276 lines
11 KiB
Python
Executable File
#!/usr/bin/env python
|
|
"""
|
|
.. module::CardsAgainstHumanity
|
|
:synopsis: Cards against Humanity, in IRC. Cards against IRC?
|
|
.. moduleauthor:: Nick Krichevsky <nick@ollien.com>
|
|
|
|
"""
|
|
from pyircbot.modulebase import ModuleBase, ModuleHook
|
|
from threading import Timer
|
|
from random import choice
|
|
|
|
|
|
class CardsAgainstHumanity(ModuleBase):
|
|
def __init__(self, bot, moduleName):
|
|
# init the base module
|
|
ModuleBase.__init__(self, bot, moduleName)
|
|
self.hooks = [ModuleHook("PRIVMSG", self.scramble)]
|
|
|
|
# Dictionary
|
|
self.whitesFile = open(self.getFilePath("answers.txt"), 'r')
|
|
self.blacksFile = open(self.getFilePath("questions.txt"), 'r')
|
|
self.whites = [line.rstrip() for line in self.whitesFile]
|
|
self.blacks = [line.rstrip() for line in self.blacksFile]
|
|
self.currentBlack = ""
|
|
self.whitesFile.close()
|
|
self.blacksFile.close()
|
|
self.log.info("CAH: Loaded." + str(len(self.whites)) + " White Cards " + str(len(self.blacks)) + " Black Cards")
|
|
# Per channel games
|
|
self.games = {}
|
|
|
|
def scramble(self, args, prefix, trailing):
|
|
channel = args[0]
|
|
if channel[0] == "#":
|
|
if channel not in self.games:
|
|
self.games[channel] = cardsGame(self, channel, self.whites, self.blacks)
|
|
self.games[channel].stuff(args, prefix, trailing)
|
|
|
|
def ondisable(self):
|
|
self.log.info("CAH: Unload requested, ending games...")
|
|
# for game in self.games:
|
|
# self.games[game].gameover()
|
|
|
|
|
|
class cardsGame:
|
|
def __init__(self, master, channel, whites, blacks):
|
|
self.master = master
|
|
self.channel = channel
|
|
# Running?
|
|
self.running = False
|
|
# Current word
|
|
# self.message = 'xmopxshell has downs'
|
|
self.players = {}
|
|
self.timers = {}
|
|
self.whites = whites
|
|
self.blacks = blacks
|
|
self.lastCzar = -1
|
|
self.czar = ""
|
|
self.started = False
|
|
self.active = False
|
|
self.allowPick = 0
|
|
self.choices = {}
|
|
self.czarTimer = None
|
|
|
|
def stuff(self, args, prefix, trailing):
|
|
prefix = self.master.bot.decodePrefix(prefix)
|
|
sender = prefix.nick
|
|
if self.master.bot.messageHasCommand(".joinGame", trailing):
|
|
self.join(sender)
|
|
elif self.master.bot.messageHasCommand(".ready", trailing):
|
|
result = self.markReady(sender)
|
|
if result:
|
|
self.started = True
|
|
self.master.bot.act_PRIVMSG(self.channel, "All players are ready!")
|
|
for player in self.players:
|
|
self.master.bot.act_PRIVMSG(player, "ITS TIME TO D-D-D-D-D-DUEL!")
|
|
self.players[player] = []
|
|
for player in self.players:
|
|
self.deal(player)
|
|
self.sendCards(player)
|
|
self.active = True
|
|
self.makeTurn()
|
|
elif self.master.bot.messageHasCommand(".pick", trailing):
|
|
if self.active:
|
|
if sender != self.czar:
|
|
print(sender, self.czar)
|
|
print(sender != self.czar)
|
|
if self.allowPick > 0:
|
|
if sender in self.players:
|
|
cards = trailing.split(' ')[1:]
|
|
if len(cards) == self.allowPick:
|
|
if self.checkBounds(cards):
|
|
if sender not in self.choices:
|
|
cardChoices = [self.players[sender][int(index)] for index in cards]
|
|
print(cardChoices)
|
|
self.choices[sender] = cardChoices
|
|
self.removeAndReplenishCards(sender, cardChoices)
|
|
self.sendCards(sender)
|
|
del self.choices[sender]
|
|
if sender in self.timers:
|
|
self.timers[sender].cancel()
|
|
if self.allDrawn():
|
|
self.readChoices()
|
|
self.master.bot.act_PRIVMSG(self.channel,
|
|
self.czar + "! Please choose the winner!")
|
|
self.czarTimer = Timer(180, self.kick, (self.czar, "taking too long to pick a "
|
|
"choice. The next turn iwll be made."))
|
|
self.makeTurn()
|
|
|
|
else:
|
|
self.master.bot.act_PRIVMSG(self.channel, sender + ", you picked a card that was "
|
|
"out of the range. Please don't do that.")
|
|
else:
|
|
self.master.bot.act_PRIVMSG(self.channel, sender + ", you picked " + str(len(cards)) +
|
|
" cards. You were supposed to pick " + str(self.allowPick))
|
|
elif self.master.bot.messageHasCommand(".choose", trailing):
|
|
if sender == self.czar:
|
|
choice = trailing.split()[1:]
|
|
if len(choice) == 1:
|
|
if self.checkChoiceBounds(int(choice[0])):
|
|
self.master.bot.act_PRIVMSG(self.channel, list(self.choices.keys())[int(choice[0])] + ", you "
|
|
"won the round!")
|
|
if self.czarTimer is not None:
|
|
self.czarTimer.cancel()
|
|
self.makeTurn()
|
|
else:
|
|
self.master.bot.act_PRIVMSG(self.channel, sender + ", your choice was out of the range. Please "
|
|
"don't do that.")
|
|
else:
|
|
self.master.bot.act_PRIVMSG(self.channel, sender + ", you picked " + str(len(choice)) + " winners."
|
|
" You were only supposed to pick 1.")
|
|
elif self.master.bot.messageHasCommand('.leave', trailing):
|
|
if sender in self.players:
|
|
self.kick(sender, 'choosing to leave the game you dolt')
|
|
if sender is self.czar:
|
|
self.makeTurn()
|
|
|
|
def join(self, nick):
|
|
if not self.started:
|
|
if nick not in self.players:
|
|
self.players[nick] = False
|
|
self.master.bot.act_PRIVMSG(self.channel, nick + " has joined the game! | The players currently are " +
|
|
str(self.players))
|
|
else:
|
|
print("the game has already started!")
|
|
self.master.bot.act_PRIVMSG(self.channel, "The game has already started!")
|
|
|
|
def markReady(self, nick):
|
|
if not self.started:
|
|
if nick in self.players:
|
|
self.players[nick] = True
|
|
for player in self.players:
|
|
print(player)
|
|
if not self.players[player]:
|
|
print (player + " not ready")
|
|
return False
|
|
return True
|
|
else:
|
|
self.master.bot.act_PRIVMSG(self.channel, "You are not in the game! Type .joinGame!")
|
|
else:
|
|
print("game has already started!")
|
|
self.master.bot.act_PRIVMSG(self.channel, "The game has already started!")
|
|
|
|
def deal(self, nick):
|
|
self.players[nick] = [self.pickWhite() for i in range (7)]
|
|
|
|
def pickWhite(self):
|
|
card = choice(self.whites)
|
|
self.whites.remove(card)
|
|
return card
|
|
|
|
def pickBlack(self):
|
|
card = choice(self.blacks)
|
|
self.blacks.remove(card)
|
|
return card
|
|
|
|
def sendCards(self, nick):
|
|
cards = ""
|
|
for card in self.players[nick]:
|
|
cards += str(self.players[nick].index(card)) + ". "
|
|
cards += card + " "
|
|
self.master.bot.act_PRIVMSG(nick, "Your cards are " + cards)
|
|
|
|
def readCard(self, card):
|
|
count = card.count('_')
|
|
if count == 0:
|
|
if 'haiku' in card:
|
|
count = 3
|
|
else:
|
|
count = 1
|
|
self.master.bot.act_PRIVMSG(self.channel, "The black card is \"" + card + "\" Pick " + str(count))
|
|
return count
|
|
|
|
def pickCzar(self):
|
|
index = self.lastCzar + 1
|
|
if index < len(self.players):
|
|
self.lastCzar = index
|
|
return index
|
|
else:
|
|
self.lastCzar = 0
|
|
return 0
|
|
|
|
def announceCzar(self):
|
|
self.master.bot.act_PRIVMSG(self.channel, "The Czar is " + self.czar + "!")
|
|
|
|
def checkBounds(self, cards):
|
|
for item in cards:
|
|
if int(item) > 6 or int(item) < 0:
|
|
return False
|
|
return True
|
|
|
|
def checkChoiceBounds(self, choice):
|
|
if choice < 0 or choice > len(self.choices) - 1:
|
|
return False
|
|
return True
|
|
|
|
def makeTurn(self):
|
|
self.choices.clear()
|
|
card = self.pickBlack()
|
|
self.timers.clear()
|
|
self.currentBlack = card
|
|
self.allowPick = self.readCard(card)
|
|
self.lastCzar = self.pickCzar()
|
|
self.czar = list(self.players.keys())[self.lastCzar]
|
|
print (self.lastCzar, self.czar)
|
|
for player in self.players:
|
|
if player != self.czar:
|
|
self.timers[player] = Timer(180, self.kick, (player, "taking more than 180 seconds for their turn."))
|
|
self.timers[player].start()
|
|
self.announceCzar()
|
|
|
|
def kick(self, nick, reason):
|
|
del self.players[nick]
|
|
if nick in self.timers:
|
|
self.timers[nick].cancel()
|
|
del self.timers[nick]
|
|
self.master.bot.act_PRIVMSG(self.channel, nick + " has been kicked due to " + reason)
|
|
|
|
if len(self.players) <= 1:
|
|
self.master.bot.act_PRIVMSG(self.channel, "The game is being shut down due to having <=1 players")
|
|
self.started = False
|
|
self.active = False
|
|
for timer in self.timers:
|
|
timer.cancel()
|
|
self.timers.clear()
|
|
self.players.clear()
|
|
|
|
def removeAndReplenishCards(self, nick, cards):
|
|
for card in cards:
|
|
self.players[nick].remove(card)
|
|
self.players[nick].append(self.pickWhite())
|
|
|
|
def readChoices(self):
|
|
if '_' in self.currentBlack:
|
|
for player in list(self.choices.keys()):
|
|
cardInstance = str(list(self.choices.keys()).index(player)) + ". " + self.currentBlack
|
|
cardInstance = list(cardInstance) # do this as opposed to space to preserve spaces
|
|
for plr_choice in self.choices[player]:
|
|
for char in cardInstance:
|
|
if char == '_':
|
|
print(char)
|
|
plr_choice = plr_choice.replace('.', '')
|
|
cardInstance[cardInstance.index(char)] = plr_choice
|
|
break
|
|
self.master.bot.act_PRIVMSG(self.channel, ''.join(cardInstance))
|
|
else:
|
|
for player in self.choices:
|
|
self.master.bot.act_PRIVMSG(self.channel, self.currentBlack + ' ' + ' '.join(self.choices[player]))
|
|
|
|
def allDrawn(self):
|
|
for player in self.players:
|
|
if player not in self.choices:
|
|
if player != self.czar:
|
|
return False
|
|
return True
|
|
|