You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
11 KiB
275 lines
11 KiB
#!/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 |
|
|
|
|