diff --git a/src/__pycache__/Role.cpython-37.pyc b/src/__pycache__/Role.cpython-37.pyc new file mode 100644 index 0000000..d429ccc Binary files /dev/null and b/src/__pycache__/Role.cpython-37.pyc differ diff --git a/src/__pycache__/werewolf_game.cpython-37.pyc b/src/__pycache__/werewolf_game.cpython-37.pyc new file mode 100644 index 0000000..e8e5524 Binary files /dev/null and b/src/__pycache__/werewolf_game.cpython-37.pyc differ diff --git a/src/__pycache__/werewolf_players.cpython-37.pyc b/src/__pycache__/werewolf_players.cpython-37.pyc new file mode 100644 index 0000000..bdcf3df Binary files /dev/null and b/src/__pycache__/werewolf_players.cpython-37.pyc differ diff --git a/src/__pycache__/werewolf_roles.cpython-37.pyc b/src/__pycache__/werewolf_roles.cpython-37.pyc new file mode 100644 index 0000000..121e3a5 Binary files /dev/null and b/src/__pycache__/werewolf_roles.cpython-37.pyc differ diff --git a/src/werewolf_bot.py b/src/werewolf_bot.py new file mode 100644 index 0000000..c52ab07 --- /dev/null +++ b/src/werewolf_bot.py @@ -0,0 +1,65 @@ +import os +import discord +import asyncio +from dotenv import load_dotenv + +from werewolf_game import Game as Werewolf_Game + + +load_dotenv() +TOKEN = os.getenv('DISCORD_TOKEN') +if TOKEN is None: + print("Missing discord token!") + exit(1) + +bot = discord.Client() + +@bot.event +async def on_ready(): + print('We have logged in as {0.user}'.format(bot)) + + + +async def hello(message): + print("Hello") + await message.channel.send('Hello!:regional_indicator_a:') + + print(message.mentions) + +@bot.event +async def on_message(message): + + global running + + if message.author == bot.user: + return + + + if message.content.startswith('$hello'): + await hello(message) + return + + + if message.content.startswith('$logout'): + await bot.logout() + return + + + if message.content.startswith('$werewolf'): + + # start (only one instance running) + + if werewolf_game.running: + await message.channel.send("Sorry! A game is already running") + return + + werewolf_game.running = True + werewolf_game.set_channel(message.channel) + + + await werewolf_game.game() + + return + +werewolf_game = Werewolf_Game(bot) +bot.run(TOKEN) diff --git a/src/werewolf_game.py b/src/werewolf_game.py new file mode 100644 index 0000000..b01a931 --- /dev/null +++ b/src/werewolf_game.py @@ -0,0 +1,218 @@ +from random import shuffle +import asyncio +from werewolf_roles import Role, Doppelganger, Werewolf, Minion, Mason, Seer, Robber, Troublemaker, Drunk, Insomniac, Villiager, Tanner, Hunter, No_role +from werewolf_players import Player, No_player + +class Game: + + def __init__(self, bot): + self.running = False + self.bot = bot + self.player_list = [] + self.role_list = Role.role_set + + + def set_channel(self, channel): + self.channel = channel + + + async def send(self, message): + await self.channel.send(message) + + + async def receive(self, command): + def check(msg): + return msg.channel == self.channel and msg.content.startswith(command) + + return await self.bot.wait_for('message', check = check) + + def setup(self): + self.werewolf_list = [] + self.mason_list = [] + + + async def set_players(self): + + await self.send("Who is playing?") + msg = await self.receive('$players') + + # use info from last round otherwise + if not msg.content.startswith('$players last'): + self.player_list = [await Player.make(mem, self) for mem in msg.mentions] + + # check conditions + if not 0 <= len(self.player_list) <= 10: + raise ValueError("Invalid number of players: " + str(len(self.player_list))) + + # send confirmation + await self.send("Players: " + ", ".join(p.name() for p in self.player_list)) + + + async def set_roles(self): + await self.send("With which roles do you want to play?") + msg = await self.receive('$roles') + + # use info from last round otherwise + if not msg.content.startswith('$roles last'): + tmp_role = [Role.match(r, self) for r in msg.content.split()[1:]] + + # invalid input + if None in tmp_role: + raise ValueError("Invalid list of roles: " + str(tmp_role)) + + self.role_list = tmp_role + + # check condition + if not len(self.role_list) == (len(self.player_list) + 3): + raise ValueError("Invalid number of roles: " + str(len(self.role_list)) + " with " + str(len(self.player_list)) + " players") + + # send confirmation + await self.send("Roles: " + ", ".join(r.name() for r in self.role_list)) + + + def distribute_roles(self): + shuffle(self.role_list) + for i in range(len(self.player_list)): + self.player_list[i].setRole(self.role_list[i]) + self.role_list[i].setPlayer(self.player_list[i]) + + self.middle_card = self.role_list[-3:] + self.active_role = sorted(self.role_list[:-3], key = lambda x: x.order) #necessary? + + + async def start_night(self): + await asyncio.gather( *[p.send("The night has begun") for p in self.player_list] ) + + async def send_role(self): + await asyncio.gather( *[p.send("Your role: " + p.night_role.name()) for p in self.player_list] ) + + async def night_phases(self): + await asyncio.gather( *[r.phase1() for r in self.active_role] ) + await asyncio.gather( *[r.phase2() for r in self.active_role] ) + await asyncio.gather( *[r.phase3() for r in self.active_role] ) + await asyncio.gather( *[r.phase4() for r in self.active_role] ) + await asyncio.gather( *[r.phase5() for r in self.active_role] ) + + async def start_day(self): + await self.send("The day has started") + + async def vote(self, options): + + # vote + await self.receive('$vote') + await self.send("Vote in DM") + + await asyncio.gather( *[p.cast_vote(options) for p in self.player_list] ) + + await self.send("Votes\n\n" + '\n'.join(str(p) + " :arrow_right: " + str(p.vote) for p in self.player_list)) + + def tally(self, options): + for o in options: + o.tally = 0 + + for p in self.player_list: + p.vote.tally += 1 + + def who_dead(self, options): + + maxi = max(o.tally for o in options) + dead = [p for p in self.player_list if p.tally >= maxi] + for d in dead: + d.dead = True + if isinstance(d.day_role.copy, Hunter): + dead.append(d.vote) + + return dead + + def who_won(self, dead): + + no_dead = (len(dead) == 0) + tanner_dead = any(isinstance(d.day_role.copy, Tanner) for d in dead) + werewolf_dead = any(isinstance(d.day_role.copy, Werewolf) for d in dead) + werewolf_in_game = any(isinstance(p.day_role.copy, Werewolf) for p in self.player_list) + minion_dead = any(isinstance(d.day_role.copy, Minion) for d in dead) + minion_in_game = any(isinstance(p.day_role.copy, Minion) for p in self.player_list) + + werewolf_won = False + village_won = False + tanner_won = False + + # could make it shorter using boolean algebra + if no_dead: + if werewolf_in_game: + werewolf_won = True + else: + village_won = True + else: + if tanner_dead: + tanner_won = True + + if werewolf_dead: + village_won = True + + else: + if werewolf_dead: + village_won = True + else: + if minion_dead: + if werewolf_in_game: + werewolf_won = True + else: + village_won = True + else: + if minion_in_game: + werewolf_won = True + + return werewolf_won, village_won, tanner_won, dead + + async def result(self, werewolf_won, village_won, tanner_won, dead): + + if werewolf_won: + await self.send("Werewolves won!") + + if village_won: + await self.send("Village won!") + + for d in dead: + if isinstance(d.day_role.copy, Tanner): + await self.send(str(d) + " won a tanner") + + await self.send(":skull: " + ', '.join(str(d) for d in dead)) + await self.send('\n'.join(":ballot_box " + str(p.tally) + " votes for " + str(p) + " who is " + str(p.day_role) + " (was " + str(p.night_role) + ") " for p in self.player_list)) + await self.send("Middle cards: " + ', '.join(str(r) for r in self.middle_card)) + + # debug + await self.send("Success") + + def end(self): + self.running = False + + + async def game(self): + + try: + + self.setup() + await self.set_players() + await self.set_roles() + self.distribute_roles() + await self.start_night() + await self.send_role() + + await self.night_phases() + + await self.start_day() + #discussion timer + + options = self.player_list + [No_player(self)] + await self.vote(options) + self.tally(options) + await self.result(*self.who_won(self.who_dead(options))) + + except ValueError as error: + await self.send(error) + except asyncio.TimeoutError: + await self.send("Error: I got bored waiting for your input") + finally: + self.end() + await self.send("Game ended") diff --git a/src/werewolf_players.py b/src/werewolf_players.py new file mode 100644 index 0000000..9086772 --- /dev/null +++ b/src/werewolf_players.py @@ -0,0 +1,56 @@ +class Player: + + @staticmethod + async def make(member, game): + p = Player() + p.member = member + p.dm = member.dm_channel or await member.create_dm() + p.game = game + return p + + @staticmethod + def swap(player_A, player_B): + player_A.day_role, player_B.day_role = player_B.day_role, player_A.day_role + + def setRole(self, role): + self.night_role = role + self.day_role = role + + def name(self): + return self.member.name + + def __repr__(self): + return self.name() + + def other(self): + return [p for p in self.game.player_list if p != self] + + async def send(self, message): + await self.dm.send(message) + + async def ask_choice(self, options): + await self.send('\n'.join( "(" + str(i) + ") " + str(options[i]) for i in range(len(options)) )) + + async def receive_choice(self, options): + def check(choice): + return choice.channel == self.dm and choice.content.isdigit() and 0 <= int(choice.content) < len(options) + + return int((await self.game.bot.wait_for('message', timeout=30.0, check = check)).content) + + async def get_choice(self, options): + await self.ask_choice(options) + return await self.receive_choice(options) + + async def cast_vote(self, options): + self.vote = options[await self.get_choice(options)] + +class No_player(Player): + + def __init__(self): + self.day_role = No_role() + + def name(self): + return "no one" + + def __str__(self): + return self.name() diff --git a/src/werewolf_roles.py b/src/werewolf_roles.py new file mode 100644 index 0000000..8d108cd --- /dev/null +++ b/src/werewolf_roles.py @@ -0,0 +1,156 @@ +class Role: + + def __init__(self, game): + self.game = game + self.copy = self + + def setPlayer(self, player): + self.player = player + + async def phase1(self): # query stuff + doppelganger simulation + pass + + async def phase2(self): # werewolf stuff + seer info + pass + + async def phase3(self): # robber simulation & info + pass + + async def phase4(self): # troublemaker simulation + pass + + async def phase5(self): # mostly sending info + drunk simulation + pass + + @staticmethod + def match(message, game): + for role_class in Role.role_set: + if message.casefold() == role_class.name(): + return role_class(game) + + @classmethod + def name(cls): + return cls.__name__.casefold() + + def __str__(self): + return self.name() + +class Doppelganger(Role): + order = 1 + +class Werewolf(Role): + order = 2 + + def setPlayer(self, player): + super().setPlayer(player) + self.game.werewolf_list.append(player) + + async def phase2(self): + if len(self.game.werewolf_list) >= 2: + await self.player.send("Werewolves: " + str(self.game.werewolf_list)) + else: + await self.player.send("You are the only werewolf") + await self.player.send("Which card in the middle do you want to look at?") + self.choice = await self.player.get_choice(["left", "middle", "right"]) + + await self.player.send("A card in the middle is: " + self.game.middle_card[self.choice].name()) + + +class Minion(Role): + order = 3 + + async def phase2(self): + if len(self.game.werewolf_list) == 0: + await self.player.send("There were no werewolves so you became one") + else: + await self.player.send("Werewolves: " + str(self.game.werewolf_list)) + + +class Mason(Role): + order = 4 + + def setPlayer(self, player): + super().setPlayer(player) + self.game.mason_list.append(player) + + async def phase2(self): + await self.player.send("Mason " + str(self.game.mason_list)) + + +class Seer(Role): + order = 5 + + async def phase1(self): + await self.player.send("Which 1 player card or 2 middle cards do you want to look at?") + self.choice = await self.player.get_choice(self.player.other() + ["left & middle", "middle & right", "left & right"]) + + async def phase2(self): + if self.choice < len(self.player.other()): + await self.player.send(self.player.other()[self.choice].night_role) + else: + self.choice -= len(self.player.other()) + if self.choice == 0: + a, b = 0, 1 + elif self.choice == 1: + a, b = 1, 2 + else: + a, b = 0, 2 + + await self.player.send(str(self.game.middle_card[a]) + " " + str(self.game.middle_card[b])) + + +class Robber(Role): + order = 6 + + async def phase1(self): + await self.player.send("Which player do you want to rob?") + self.choice = await self.player.get_choice(self.player.other()) + + async def phase3(self): + Player.swap(self.player, self.player.other()[self.choice]) + await self.player.send("You robbed: " + str(self.player.day_role)) + +class Troublemaker(Role): + order = 7 + + async def phase1(self): + await self.player.send("Who do you want to exchange? (send two separate numbers)") + self.A = await self.player.get_choice(self.player.other()) + self.B = await self.player.get_choice(self.player.other()) + + async def phase4(self): + Player.swap(self.player.other()[self.A], self.player.other()[self.B]) + # receive conformation + await self.player.send("Received " + str(self.A) + " " + str(self.B)) + +class Drunk(Role): + order = 8 + + async def phase1(self): + await self.player.send("Which card from the middle do you want to take?") + self.choice = await self.player.get_choice(["left", "middle", "right"]) + + async def phase5(self): + self.player.day_role, self.game.middle_card[self.choice] = self.game.middle_card[self.choice], self.player.day_role + #receive conformation + await self.player.send("Received " + str(self.choice)) + +class Insomniac(Role): + order = 9 + + async def phase5(self): + await self.player.send("You are now: " + str(self.player.day_role)) + +class Villiager(Role): + order = 10 + +class Tanner(Role): + order = 11 + +class Hunter(Role): + order = 12 + +class No_role(Role): + order = 1000 + +Role.role_set = [Doppelganger, Werewolf, Minion, Mason, Seer, Robber, Troublemaker, Drunk, Insomniac, Villiager, Tanner, Hunter] \ No newline at end of file diff --git a/src/werewolve-bot-old.py b/src/werewolve-bot-old.py deleted file mode 100644 index ba07ef9..0000000 --- a/src/werewolve-bot-old.py +++ /dev/null @@ -1,381 +0,0 @@ -import os -import discord -from enum import Enum -from random import shuffle -import asyncio -from dotenv import load_dotenv - -load_dotenv() -TOKEN = os.getenv('DISCORD_TOKEN') - -class Role(Enum): - doppelganger = 1 - werewolve = 2 - minion = 3 - mason = 4 - seer = 5 - robber = 6 - troublemaker = 7 - drunk = 8 - insomniac = 9 - villiager = 10 - tanner = 11 - hunter = 12 - -class Player: - - def __init__(self, member, role): - self.member = member - self.night_role = role - self.day_role = role - self.vote = -1 - self.tally = 0 - - async def create_dm(self): - self.dm = self.member.dm_channel or await self.member.create_dm() - - async def send(self, message): - await self.dm.send(message) - - async def sendRole(self): - await self.send("Your role: " + self.night_role.name) - - async def sendPoll(self): - await self.send(Player.poll_message) - - async def sendMiddle(self): - await self.send(Player.poll_middle) - - async def receiveChoice(self): - - global bot - - def check(vote): - - return vote.channel == self.dm and vote.content.isdigit() and 0 <= int(vote.content) < Player.size - - vote = await bot.wait_for('message', timeout=30.0, check = check) - await self.send("Received: " + vote.content) - - return int(vote.content) - - -bot = discord.Client() - -@bot.event -async def on_ready(): - print('We have logged in as {0.user}'.format(bot)) - - - -running = False - -async def hello(message): - print("Hello") - await message.channel.send('Hello!:regional_indicator_a:') - - def check(vote): - return vote.content in ["a", "b"] and vote.channel == message.channel - - vote = await bot.wait_for('message', timeout=10.0, check = check) - await message.channel.send("Received! " + vote.content) - -@bot.event -async def on_message(message): - - global running - - if message.author == bot.user: - return - - - if message.content.startswith('$hello'): - await hello(message) - return - - - if message.content.startswith('$logout'): - await bot.logout() - return - - if message.content.startswith('$werewolve'): - - # start (only one instance running) - - if running: - await message.channel.send("Sorry! A game is already running") - return - - - - - """ - if len(players) < 4: - await message.channel.send("To few players!") - return - - """ - - running = True - - - # setup - - members = [mem for mem in message.channel.members if not mem.bot] - - Player.size = len(members) - - - role_set = [Role.werewolve, Role.werewolve, Role.drunk, Role.insomniac, Role.seer, Role.robber, Role.hunter] + (Player.size-4)*[Role.villiager] - shuffle(role_set) - - players = [] - - - werewolve = [] - minion = None - mason = [] - seer = None - robber = None - troublemaker = None - drunk = None - insomniac = None - villiager = [] - tanner = None - hunter = None - - for i in range(Player.size): - - players.append(Player(members[i], role_set[i])) - await players[i].create_dm() - - - if role_set[i] == Role.werewolve: - werewolve.append(players[i]) - - elif role_set[i] == Role.mason: - mason.append(players[i]) - - elif role_set[i] == Role.seer: - seer = players[i] - - elif role_set[i] == Role.robber: - robber = players[i] - - elif role_set[i] == Role.troublemaker: - troublemaker = players[i] - - elif role_set[i] == Role.drunk: - drunk = players[i] - - elif role_set[i] == Role.insomniac: - insomniac = players[i] - - elif role_set[i] == Role.villiager: - villiager.append(players[i]) - - elif role_set[i] == Role.tanner: - tanner = players[i] - - elif role_set[i] == Role.hunter: - hunter = players[i] - - middle = role_set[-3:] - - - Player.poll_middle = "(0) left\n(1) middle\n(2) right" - - Player.poll_message = "" - for i in range(Player.size): - Player.poll_message += "(" + str(i) + ") " + players[i].member.name + "\n" - - # doing phase - - - #send role info to all - send_roles = [p.sendRole() for p in players] - await asyncio.gather(*send_roles) - - - - # query stuff - - #doppelganger stuff - - async def query_seer(): - if seer is None: - return - - await seer.send("Who do you want to look at?") - await seer.sendPoll() - - async def query_robber(): - if robber is None: - return - - await robber.send("Who do you want to rob?") - await robber.sendPoll() - - async def query_troublemaker(): - if troublemaker is None: - return - - await troublemaker.send("Who do you want to exchange?") - await troublemaker.sendPoll() - await troublemaker.sendPoll() - - async def query_drunk(): - if drunk is None: - return - - await drunk.send("Which card from the middle do you want to take?") - await drunk.sendMiddle() - - await asyncio.gather(query_seer(), query_robber(), query_troublemaker(), query_drunk()) - - - - #receive and confirm! - async def receive_seer(): - if seer is not None: - return await seer.receiveChoice() - - async def receive_robber(): - if robber is not None: - return await robber.receiveChoice() - - async def receive_troublemaker(): - if troublemaker is not None: - return await troublemaker.receiveChoice() - - async def receive_drunk(): - if drunk is not None: - return await drunk.receiveChoice() - - seerChoice, robberChoice, troublemakerChoice, drunkChoice = await asyncio.gather(receive_seer(), receive_robber(), receive_troublemaker(), receive_drunk()) - - - - # simulate - - #exchange robber - if robber is not None: - robber.day_role, players[robberChoice].day_role = players[robberChoice].day_role, robber.day_role - #exchange troublemaker - if troublemaker is not None: - A = players[troublemakerChoice[0]] - B = players[troublemakerChoice[1]] - A.day_role, B.day_role = B.day_role, A.day_role - #exchange drunk - if drunk is not None: - drunk.day_role, middle[drunkChoice] = middle[drunkChoice], drunk.day_role - - - - #send werewolves identity to werewolves and minion - async def send_werewolves(): - message = "" - for w in werewolve: - message += w.member.name + " " - - message += "were werewolves" - - sender = [bad.send(message) for bad in werewolve] - if minion is not None: - sender.append(minion.send(message)) - - await asyncio.gather(*sender) - - #send mason identity to masons - async def send_masons(): - message = "" - for m in mason: - message += m.member.name + " " - - message += " were masons" - - sender = [m.send(message) for m in mason] - await asyncio.gather(*sender) - - #send info to seer - async def send_seer(): - if seer is not None: - await seer.send(players[seerChoice].member.name + " was a " + players[seerChoice].night_role.name) - - #send info to robber - async def send_robber(): - if robber is not None: - await robber.send("You stole the role: " + players[robberChoice].night_role.name) - - #send insomniac new role to insomniac - async def send_insomniac(): - if insomniac is not None: - await insomniac.send("You are now a " + insomniac.day_role.name) - - await asyncio.gather(send_werewolves(), send_masons(), send_seer(), send_robber(), send_insomniac()) - - - - # discussion - - # vote - - def check_vote(vote): - return vote.content == "$vote" and vote.channel == message.channel - - await bot.wait_for('message', check = check_vote) - - await message.channel.send("Vote in DM") - - - send_votes = [p.sendPoll() for p in players] - await asyncio.gather(*send_votes) - - receive_votes = [p.receiveChoice() for p in players] - tmp = await asyncio.gather(*receive_votes) - for i in range(Player.size): - players[i].vote = tmp[i] - - for p in players: - players[p.vote].tally += 1 - - maxi = max( [p.tally for p in players] ) - - dead = [p for p in players if p.tally >= maxi] - - if hunter in dead: - dead.append(players[hunter.vote]) - - - - # result and end - # show day-role & night role - - msg = "" - for d in dead: - msg += d.member.name + " " - msg += "are dead!" - await message.channel.send(msg) - - if any(d in werewolve for d in dead): - msg = "The village won!" - else: - msg = "The werewolves won!" - - await message.channel.send(msg) - - msg = "" - for p in players: - msg += p.member.name + ": " + p.day_role.name + " (day) " + p.night_role.name + " (night) and voted for " + players[p.vote].member.name + "\n" - - msg += middle - - await message.channel.send(msg) - - - - running = False - - - -bot.run(TOKEN) \ No newline at end of file diff --git a/src/werewolve-bot.py b/src/werewolve-bot.py deleted file mode 100644 index 55bcafd..0000000 --- a/src/werewolve-bot.py +++ /dev/null @@ -1,498 +0,0 @@ -import os -import discord -from random import shuffle -import asyncio -from dotenv import load_dotenv - -load_dotenv() -TOKEN = os.getenv('DISCORD_TOKEN') -if TOKEN is None: - print("Missing discord token!") - exit(1) - - - -class Role: - - def __init__(self, game): - self.game = game - self.copy = self - - def setPlayer(self, player): - self.player = player - - async def phase1(self): # query stuff + doppelganger simulation - pass - - async def phase2(self): # werewolf stuff + seer info - pass - - async def phase3(self): # robber simulation & info - pass - - async def phase4(self): # troublemaker simulation - pass - - async def phase5(self): # mostly sending info + drunk simulation - pass - - @staticmethod - def match(message, game): - for role_class in Role.role_set: - if message.casefold() == role_class.name(): - return role_class(game) - - @classmethod - def name(cls): - return cls.__name__.casefold() - - def __str__(self): - return self.name() - -class Doppelganger(Role): - order = 1 - -class Werewolf(Role): - order = 2 - - def setPlayer(self, player): - super().setPlayer(player) - self.game.werewolf_list.append(player) - - async def phase2(self): - if len(self.game.werewolf_list) >= 2: - await self.player.send("Werewolves: " + str(self.game.werewolf_list)) - else: - await self.player.send("You are the only werewolf") - await self.player.send("Which card in the middle do you want to look at?") - self.choice = await self.player.get_choice(["left", "middle", "right"]) - - await self.player.send("A card in the middle is: " + self.game.middle_card[self.choice].name()) - - -class Minion(Role): - order = 3 - - async def phase2(self): - if len(self.game.werewolf_list) == 0: - await self.player.send("There were no werewolves so you became one") - else: - await self.player.send("Werewolves: " + str(self.game.werewolf_list)) - - -class Mason(Role): - order = 4 - - def setPlayer(self, player): - super().setPlayer(player) - self.game.mason_list.append(player) - - async def phase2(self): - await self.player.send("Mason " + str(self.game.mason_list)) - - -class Seer(Role): - order = 5 - - async def phase1(self): - await self.player.send("Which 1 player card or 2 middle cards do you want to look at?") - self.choice = await self.player.get_choice(self.player.other() + ["left & middle", "middle & right", "left & right"]) - - async def phase2(self): - if self.choice < len(self.player.other()): - await self.player.send(self.player.other()[self.choice].night_role) - else: - self.choice -= len(self.player.other()) - if self.choice == 0: - a, b = 0, 1 - elif self.choice == 1: - a, b = 1, 2 - else: - a, b = 0, 2 - - await self.player.send(str(self.game.middle_card[a]) + " " + str(self.game.middle_card[b])) - - -class Robber(Role): - order = 6 - - async def phase1(self): - await self.player.send("Which player do you want to rob?") - self.choice = await self.player.get_choice(self.player.other()) - - async def phase3(self): - Player.swap(self.player, self.player.other()[self.choice]) - await self.player.send("You robbed: " + str(self.player.day_role)) - -class Troublemaker(Role): - order = 7 - - async def phase1(self): - await self.player.send("Who do you want to exchange? (send two separate numbers)") - self.A = await self.player.get_choice(self.player.other()) - self.B = await self.player.get_choice(self.player.other()) - - async def phase4(self): - Player.swap(self.player.other()[self.A], self.player.other()[self.B]) - # receive conformation - await self.player.send("Received " + str(self.A) + " " + str(self.B)) - -class Drunk(Role): - order = 8 - - async def phase1(self): - await self.player.send("Which card from the middle do you want to take?") - self.choice = await self.player.get_choice(["left", "middle", "right"]) - - async def phase5(self): - self.player.day_role, self.game.middle_card[self.choice] = self.game.middle_card[self.choice], self.player.day_role - #receive conformation - await self.player.send("Received " + str(self.choice)) - -class Insomniac(Role): - order = 9 - - async def phase5(self): - await self.player.send("You are now: " + str(self.player.day_role)) - -class Villiager(Role): - order = 10 - -class Tanner(Role): - order = 11 - -class Hunter(Role): - order = 12 - -class No_role(Role): - order = 1000 - - -Role.role_set = [Doppelganger, Werewolf, Minion, Mason, Seer, Robber, Troublemaker, Drunk, Insomniac, Villiager, Tanner, Hunter] - - -class Player: - - @staticmethod - async def make(member, game): - p = Player() - p.member = member - p.dm = member.dm_channel or await member.create_dm() - p.game = game - return p - - @staticmethod - def swap(player_A, player_B): - player_A.day_role, player_B.day_role = player_B.day_role, player_A.day_role - - def setRole(self, role): - self.night_role = role - self.day_role = role - - def name(self): - return self.member.name - - def __repr__(self): - return self.name() - - def other(self): - return [p for p in self.game.player_list if p != self] - - async def send(self, message): - await self.dm.send(message) - - async def ask_choice(self, options): - await self.send('\n'.join( "(" + str(i) + ") " + str(options[i]) for i in range(len(options)) )) - - async def receive_choice(self, options): - def check(choice): - return choice.channel == self.dm and choice.content.isdigit() and 0 <= int(choice.content) < len(options) - - return int((await self.game.bot.wait_for('message', timeout=30.0, check = check)).content) - - async def get_choice(self, options): - await self.ask_choice(options) - return await self.receive_choice(options) - - async def cast_vote(self, options): - self.vote = options[await self.get_choice(options)] - -class No_player(Player): - - def __init__(self): - self.day_role = No_role() - - def name(self): - return "no one" - - def __str__(self): - return self.name() - - -class one_night: - - def __init__(self, bot): - self.running = False - self.bot = bot - self.player_list = [] - self.role_list = Role.role_set - - - def set_channel(self, channel): - self.channel = channel - - - async def send(self, message): - await self.channel.send(message) - - - async def receive(self, command): - def check(msg): - return msg.channel == self.channel and msg.content.startswith(command) - - return await bot.wait_for('message', check = check) - - def setup(self): - self.werewolf_list = [] - self.mason_list = [] - - - async def set_players(self): - - await self.send("Who is playing?") - msg = await self.receive('$players') - - # use info from last round otherwise - if not msg.content.startswith('$players last'): - self.player_list = [await Player.make(mem, self) for mem in msg.mentions] - - # check conditions - if not 0 <= len(self.player_list) <= 10: - raise ValueError("Invalid number of players: " + str(len(self.player_list))) - - # send confirmation - await self.send("Players: " + ", ".join(p.name() for p in self.player_list)) - - - async def set_roles(self): - await self.send("With which roles do you want to play?") - msg = await self.receive('$roles') - - # use info from last round otherwise - if not msg.content.startswith('$roles last'): - tmp_role = [Role.match(r, self) for r in msg.content.split()[1:]] - - # invalid input - if None in tmp_role: - raise ValueError("Invalid list of roles: " + str(tmp_role)) - - self.role_list = tmp_role - - # check condition - if not len(self.role_list) == (len(self.player_list) + 3): - raise ValueError("Invalid number of roles: " + str(len(self.role_list)) + " with " + str(len(self.player_list)) + " players") - - # send confirmation - await self.send("Roles: " + ", ".join(r.name() for r in self.role_list)) - - - def distribute_roles(self): - shuffle(self.role_list) - for i in range(len(self.player_list)): - self.player_list[i].setRole(self.role_list[i]) - self.role_list[i].setPlayer(self.player_list[i]) - - self.middle_card = self.role_list[-3:] - self.active_role = sorted(self.role_list[:-3], key = lambda x: x.order) #necessary? - - - async def start_night(self): - await asyncio.gather( *[p.send("The night has begun") for p in self.player_list] ) - - async def send_role(self): - await asyncio.gather( *[p.send("Your role: " + p.night_role.name()) for p in self.player_list] ) - - async def night_phases(self): - await asyncio.gather( *[r.phase1() for r in self.active_role] ) - await asyncio.gather( *[r.phase2() for r in self.active_role] ) - await asyncio.gather( *[r.phase3() for r in self.active_role] ) - await asyncio.gather( *[r.phase4() for r in self.active_role] ) - await asyncio.gather( *[r.phase5() for r in self.active_role] ) - - async def start_day(self): - await self.send("The day has started") - - async def vote(self, options): - - # vote - await self.receive('$vote') - await self.send("Vote in DM") - - await asyncio.gather( *[p.cast_vote(options) for p in self.player_list] ) - - await self.send("Votes\n\n" + '\n'.join(str(p) + " :arrow_right: " + str(p.vote) for p in self.player_list)) - - def tally(self, options): - for o in options: - o.tally = 0 - - for p in self.player_list: - p.vote.tally += 1 - - def who_dead(self, options): - - maxi = max(o.tally for o in options) - dead = [p for p in self.player_list if p.tally >= maxi] - for d in dead: - d.dead = True - if isinstance(d.day_role.copy, Hunter): - dead.append(d.vote) - - return dead - - def who_won(self, dead): - - no_dead = (len(dead) == 0) - tanner_dead = any(isinstance(d.day_role.copy, Tanner) for d in dead) - werewolf_dead = any(isinstance(d.day_role.copy, Werewolf) for d in dead) - werewolf_in_game = any(isinstance(p.day_role.copy, Werewolf) for p in self.player_list) - minion_dead = any(isinstance(d.day_role.copy, Minion) for d in dead) - minion_in_game = any(isinstance(p.day_role.copy, Minion) for p in self.player_list) - - werewolf_won = False - village_won = False - tanner_won = False - - # could make it shorter using boolean algebra - if no_dead: - if werewolf_in_game: - werewolf_won = True - else: - village_won = True - else: - if tanner_dead: - tanner_won = True - - if werewolf_dead: - village_won = True - - else: - if werewolf_dead: - village_won = True - else: - if minion_dead: - if werewolf_in_game: - werewolf_won = True - else: - village_won = True - else: - if minion_in_game: - werewolf_won = True - - return werewolf_won, village_won, tanner_won, dead - - async def result(self, werewolf_won, village_won, tanner_won, dead): - - if werewolf_won: - await self.send("Werewolves won!") - - if village_won: - await self.send("Village won!") - - for d in dead: - if isinstance(d.day_role.copy, Tanner): - await self.send(str(d) + " won a tanner") - - await self.send(":skull: " + ', '.join(str(d) for d in dead)) - await self.send('\n'.join(":ballot_box " + str(p.tally) + " votes for " + str(p) + " who is " + str(p.day_role) + " (was " + str(p.night_role) + ") " for p in self.player_list)) - await self.send("Middle cards: " + ', '.join(str(r) for r in self.middle_card)) - - # debug - await self.send("Success") - - def end(self): - self.running = False - - - async def game(self): - - try: - - self.setup() - await self.set_players() - await self.set_roles() - self.distribute_roles() - await self.start_night() - await self.send_role() - - await self.night_phases() - - await self.start_day() - #discussion timer - - options = self.player_list + [No_role(self)] - await self.vote(options) - self.tally(options) - await self.result(*self.who_won(self.who_dead(options))) - - except ValueError as error: - await self.send(error) - except asyncio.TimeoutError: - await self.send("Error: I got bored waiting for your input") - finally: - self.end() - await self.send("Game ended") - - - -bot = discord.Client() - -@bot.event -async def on_ready(): - print('We have logged in as {0.user}'.format(bot)) - - - -async def hello(message): - print("Hello") - await message.channel.send('Hello!:regional_indicator_a:') - - print(message.mentions) - -@bot.event -async def on_message(message): - - global running - - if message.author == bot.user: - return - - - if message.content.startswith('$hello'): - await hello(message) - return - - - if message.content.startswith('$logout'): - await bot.logout() - return - - - if message.content.startswith('$werewolf'): - - # start (only one instance running) - - if werewolf_game.running: - await message.channel.send("Sorry! A game is already running") - return - - werewolf_game.running = True - werewolf_game.set_channel(message.channel) - - - await werewolf_game.game() - - return - -werewolf_game = one_night(bot) -bot.run(TOKEN)