doppelganger + refactored code
This commit is contained in:
parent
42275c7ab5
commit
e0029118f9
|
@ -21,7 +21,7 @@ bot.remove_command('help')
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
await bot.change_presence(status=discord.Status.idle, activity=discord.Game('One Night Ultimate Werewolf'))
|
await bot.change_presence(status=discord.Status.online, activity=discord.Game('One Night Ultimate Werewolf'))
|
||||||
print('We have logged in as {0.user}'.format(bot))
|
print('We have logged in as {0.user}'.format(bot))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from random import shuffle
|
from random import shuffle
|
||||||
import asyncio
|
import asyncio
|
||||||
import discord
|
import discord
|
||||||
from werewolf_roles import Role, Werewolf, Mason
|
from werewolf_roles import Role, Doppelganger, Werewolf, Minion, Mason, Seer, Robber, Troublemaker, Drunk, Insomniac, Tanner, Hunter, No_role
|
||||||
from werewolf_players import Player, No_player
|
from werewolf_players import Player, No_player
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,13 +37,14 @@ class Game:
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.role = dict()
|
self.role = dict()
|
||||||
for r in Role.__subclasses__():
|
for r in Role.__subclasses__():
|
||||||
if r not in [Werewolf, Mason]:
|
if r not in [Werewolf, Mason, No_role]:
|
||||||
r(self)
|
r(self)
|
||||||
self.role[Werewolf] = []
|
self.role[Werewolf] = []
|
||||||
self.role[Mason] = []
|
self.role[Mason] = []
|
||||||
self.voting_list = self.player_list + [No_player()]
|
self.voting_list = self.player_list + [No_player()]
|
||||||
for c in self.voting_list:
|
for c in self.voting_list:
|
||||||
c.tally = 0
|
c.tally = 0
|
||||||
|
c.won = c.dead = False
|
||||||
|
|
||||||
def distribute_roles(self):
|
def distribute_roles(self):
|
||||||
shuffle(self.role_list)
|
shuffle(self.role_list)
|
||||||
|
@ -58,11 +59,17 @@ class Game:
|
||||||
await self.for_all_player(lambda p: p.send_info(f"Your role: **{p.night_role.name()}**"))
|
await self.for_all_player(lambda p: p.send_info(f"Your role: **{p.night_role.name()}**"))
|
||||||
|
|
||||||
async def night_phases(self):
|
async def night_phases(self):
|
||||||
await asyncio.gather(*[r.phase1() for r in self.active_role])
|
await asyncio.gather(*[self.role[r].query() for r in [Doppelganger, Seer, Robber, Troublemaker, Drunk]]) # slow
|
||||||
await asyncio.gather(*[r.phase2() for r in self.active_role])
|
await self.role[Doppelganger].send_copy_info()
|
||||||
await asyncio.gather(*[r.phase3() for r in self.active_role])
|
await self.role[Doppelganger].simulate() # slow
|
||||||
await asyncio.gather(*[r.phase4() for r in self.active_role])
|
await asyncio.gather(*[w.phase() for w in self.role[Werewolf]]) # slow
|
||||||
await asyncio.gather(*[r.phase5() for r in self.active_role])
|
await asyncio.gather(*[w.send_info() for w in [self.role[Minion]] + self.role[Mason] + [self.role[Seer]]])
|
||||||
|
await self.role[Robber].simulate()
|
||||||
|
await self.role[Robber].send_info()
|
||||||
|
await self.role[Troublemaker].simulate()
|
||||||
|
await self.role[Drunk].simulate()
|
||||||
|
await self.role[Insomniac].send_info()
|
||||||
|
await self.role[Doppelganger].insomniac()
|
||||||
|
|
||||||
async def start_day(self):
|
async def start_day(self):
|
||||||
await self.send("The day has started")
|
await self.send("The day has started")
|
||||||
|
@ -73,7 +80,7 @@ class Game:
|
||||||
# replace with dm: await self.receive('$vote')
|
# replace with dm: await self.receive('$vote')
|
||||||
await self.send("Vote in DM")
|
await self.send("Vote in DM")
|
||||||
|
|
||||||
await self.for_all_player(lambda p: p.cast_vote(self.voting_list))
|
await self.for_all_player(lambda p: p.cast_vote("Who do you want to kill?", self.voting_list))
|
||||||
|
|
||||||
def tally(self):
|
def tally(self):
|
||||||
for p in self.player_list:
|
for p in self.player_list:
|
||||||
|
@ -84,18 +91,18 @@ class Game:
|
||||||
dead = [p for p in self.player_list if p.tally >= maxi]
|
dead = [p for p in self.player_list if p.tally >= maxi]
|
||||||
for d in dead:
|
for d in dead:
|
||||||
d.dead = True
|
d.dead = True
|
||||||
if d.day_role.is_Hunter:
|
if d.day_role.is_role(Hunter):
|
||||||
dead.append(d.vote)
|
dead.append(d.vote)
|
||||||
return dead
|
return dead
|
||||||
|
|
||||||
def who_won(self, dead):
|
def who_won(self, dead):
|
||||||
|
|
||||||
no_dead = (len(dead) == 0)
|
no_dead = (len(dead) == 0)
|
||||||
tanner_dead = any(d.day_role.is_Tanner for d in dead)
|
tanner_dead = any(d.day_role.is_role(Tanner) for d in dead)
|
||||||
werewolf_dead = any(d.day_role.is_Werewolf for d in dead)
|
werewolf_dead = any(d.day_role.is_role(Werewolf) for d in dead)
|
||||||
werewolf_in_game = any(p.day_role.is_Werewolf for p in self.player_list)
|
werewolf_in_game = any(p.day_role.is_role(Werewolf) for p in self.player_list)
|
||||||
minion_dead = any(d.day_role.is_Minion for d in dead)
|
minion_dead = any(d.day_role.is_role(Minion) for d in dead)
|
||||||
minion_in_game = any(p.day_role.is_Minion for p in self.player_list)
|
minion_in_game = any(p.day_role.is_role(Minion) for p in self.player_list)
|
||||||
|
|
||||||
werewolf_won = False
|
werewolf_won = False
|
||||||
village_won = False
|
village_won = False
|
||||||
|
@ -128,9 +135,9 @@ class Game:
|
||||||
werewolf_won = True
|
werewolf_won = True
|
||||||
|
|
||||||
for p in self.player_list:
|
for p in self.player_list:
|
||||||
if p.day_role.is_Werewolf or p.day_role.is_Minion:
|
if p.day_role.is_role(Werewolf) or p.day_role.is_role(Minion):
|
||||||
p.won = werewolf_won
|
p.won = werewolf_won
|
||||||
elif p.day_role.is_Tanner:
|
elif p.day_role.is_role(Tanner):
|
||||||
p.won = p.dead
|
p.won = p.dead
|
||||||
else:
|
else:
|
||||||
p.won = village_won
|
p.won = village_won
|
||||||
|
@ -144,14 +151,14 @@ class Game:
|
||||||
if village_won:
|
if village_won:
|
||||||
winnning = ["Village won!"]
|
winnning = ["Village won!"]
|
||||||
if tanner_won:
|
if tanner_won:
|
||||||
winnning.append(f"{sum(1 for d in dead if d.day_role.is_Tanner)} tanner won")
|
winnning.append(f"{sum(1 for d in dead if d.day_role.is_role(Tanner))} tanner won")
|
||||||
|
|
||||||
embed = discord.Embed(title=' and '.join(winnning), color=0x00ffff)
|
embed = discord.Embed(title=' and '.join(winnning), color=0x00ffff)
|
||||||
for p in self.player_list:
|
for p in self.player_list:
|
||||||
won_emoji = ":trophy:" if p.won else "frowning2"
|
won_emoji = ":trophy:" if p.won else ":frowning2:"
|
||||||
dead_emoji = ":skull:" if p.dead else ":no_mouth:"
|
dead_emoji = ":skull:" if p.dead else ":no_mouth:"
|
||||||
embed.add_field(name="", value=f"{won_emoji} {dead_emoji} {p.tally}:ballot_box: {str(p)} :sunrise_over_mountains:{str(p.day_role)} (:full_moon:{str(p.night_role)}) :point_right:{str(p.vote)}", inline=False)
|
embed.add_field(name=str(p), value=f"{won_emoji} {dead_emoji} {p.tally}:ballot_box: role: {str(p.day_role)} (was: {str(p.night_role)}) :point_right: {str(p.vote)}", inline=False)
|
||||||
embed.add_field(name="Middle cards", value=', '.join(str(r) for r in self.middle_card))
|
embed.add_field(name="Middle cards", value=', '.join(r.name() for r in self.middle_card), inline=False)
|
||||||
|
|
||||||
await self.channel.send(embed=embed)
|
await self.channel.send(embed=embed)
|
||||||
|
|
||||||
|
|
|
@ -45,24 +45,26 @@ class Player:
|
||||||
text = f"{question}\n" + f"{'='*len(question)}\n\n" + '\n'.join(f"[{str(i)}]({str(options[i])})" for i in range(len(options)))
|
text = f"{question}\n" + f"{'='*len(question)}\n\n" + '\n'.join(f"[{str(i)}]({str(options[i])})" for i in range(len(options)))
|
||||||
await self.dm.send(f"```md\n{text}```")
|
await self.dm.send(f"```md\n{text}```")
|
||||||
|
|
||||||
async def check_num(self, choice, N):
|
def check_num(self, choice, N):
|
||||||
if not choice.isdigit():
|
if not choice.isdigit():
|
||||||
await self.send_wrong(f"Your choice {choice} is not a number")
|
raise ValueError(f"Your choice {choice} is not a number")
|
||||||
return False
|
|
||||||
if not 0 <= int(choice) < N:
|
if not 0 <= int(choice) < N:
|
||||||
await self.send_wrong(f"Your choice {choice} is not in range 0 - {N-1}")
|
raise ValueError(f"Your choice {choice} is not in range 0 - {N-1}")
|
||||||
return False
|
|
||||||
|
|
||||||
async def receive_choice(self, options, n_ans=1):
|
async def receive_choice(self, options, n_ans=1):
|
||||||
while True:
|
while True:
|
||||||
def check(choice):
|
def check(choice):
|
||||||
return choice.channel == self.dm
|
return choice.channel == self.dm and choice.author == self.member
|
||||||
choice = (await self.game.bot.wait_for('message', timeout=30.0, check=check)).content.split()
|
choice = (await self.game.bot.wait_for('message', timeout=30.0, check=check)).content.split()
|
||||||
|
|
||||||
if not len(choice) == n_ans:
|
if not len(choice) == n_ans:
|
||||||
await self.send_wrong(f"Please give {n_ans} numbers not {len(choice)}")
|
await self.send_wrong(f"Please give {n_ans} numbers not {len(choice)}")
|
||||||
continue
|
continue
|
||||||
if not all(self.check_num(c, len(options)) for c in choice):
|
try:
|
||||||
|
for c in choice:
|
||||||
|
self.check_num(c, len(options))
|
||||||
|
except ValueError as error:
|
||||||
|
await self.send_wrong(str(error))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
await self.send_confirmation(f"Received: {', '.join(choice)}")
|
await self.send_confirmation(f"Received: {', '.join(choice)}")
|
||||||
|
@ -70,7 +72,7 @@ class Player:
|
||||||
|
|
||||||
async def get_choice(self, question, options):
|
async def get_choice(self, question, options):
|
||||||
await self.ask_choice(question, options)
|
await self.ask_choice(question, options)
|
||||||
return await self.receive_choice(options)[0]
|
return (await self.receive_choice(options))[0]
|
||||||
|
|
||||||
async def get_double_choice(self, question, options):
|
async def get_double_choice(self, question, options):
|
||||||
await self.ask_choice(question, options)
|
await self.ask_choice(question, options)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import functools
|
||||||
from werewolf_players import No_player
|
from werewolf_players import No_player
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,13 +8,15 @@ class Role:
|
||||||
self.player = player
|
self.player = player
|
||||||
self.player.setRole(self)
|
self.player.setRole(self)
|
||||||
self.add_yourself()
|
self.add_yourself()
|
||||||
self.is_Hunter = self.is_Tanner = self.is_Werewolf = self.is_Minion = False
|
|
||||||
|
|
||||||
def add_yourself(self):
|
def add_yourself(self):
|
||||||
self.game.role[type(self)] = self
|
self.game.role[type(self)] = self
|
||||||
|
|
||||||
async def send_role_list(self, cls):
|
async def send_role_list(self, cls):
|
||||||
await self.player.send_info(f"{cls}: {', '.join(str(p) for p in self.game.role[cls])}")
|
await self.player.send_info(f"{cls.name()}: {', '.join(r.player.name() for r in self.game.role[cls])}")
|
||||||
|
|
||||||
|
def is_role(self, cls):
|
||||||
|
return isinstance(self, cls)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def match(message):
|
def match(message):
|
||||||
|
@ -22,6 +25,14 @@ class Role:
|
||||||
return role_class
|
return role_class
|
||||||
raise ValueError(f"Invalid role: {message}")
|
raise ValueError(f"Invalid role: {message}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def no_player(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
async def wrapper(self, *args, **kwargs):
|
||||||
|
if not isinstance(self.player, No_player):
|
||||||
|
return await func(self, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def name(cls):
|
def name(cls):
|
||||||
return cls.__name__.casefold()
|
return cls.__name__.casefold()
|
||||||
|
@ -31,17 +42,45 @@ class Role:
|
||||||
|
|
||||||
|
|
||||||
class Doppelganger(Role):
|
class Doppelganger(Role):
|
||||||
pass
|
@Role.no_player
|
||||||
|
async def query(self):
|
||||||
|
self.choice = await self.player.get_choice("Which player role do you want to copy?", self.player.other())
|
||||||
|
|
||||||
|
@Role.no_player
|
||||||
|
async def send_copy_info(self):
|
||||||
|
self.copy_role = type(self.player.other()[self.choice].day_role)
|
||||||
|
await self.send_info(f"You copied: {self.copy_role}")
|
||||||
|
|
||||||
|
@Role.no_player
|
||||||
|
async def simulate(self):
|
||||||
|
if self.copy_role in [Werewolf, Mason]:
|
||||||
|
self.copy_role.add_yourself(self)
|
||||||
|
if self.copy_role == Werewolf:
|
||||||
|
await self.copy_role.phase(self)
|
||||||
|
if self.copy_role in [Mason, Minion]:
|
||||||
|
await self.copy_role.send_info(self)
|
||||||
|
|
||||||
|
if self.copy_role in [Seer, Robber, Troublemaker, Drunk]:
|
||||||
|
await self.copy_role.query(self)
|
||||||
|
if self.copy_role in [Robber, Troublemaker, Drunk]:
|
||||||
|
self.copy_role.simulate(self)
|
||||||
|
if self.copy_role in [Seer, Robber]:
|
||||||
|
await self.copy_role.send_info(self)
|
||||||
|
|
||||||
|
@Role.no_player
|
||||||
|
async def insomniac(self):
|
||||||
|
if self.copy_role == Insomniac:
|
||||||
|
self.copy_role.send_info(self)
|
||||||
|
|
||||||
|
def is_role(self, cls):
|
||||||
|
return self.copy_role == cls
|
||||||
|
|
||||||
|
|
||||||
class Werewolf(Role):
|
class Werewolf(Role):
|
||||||
def __init__(self, game, player=No_player()):
|
|
||||||
super().__init__(game, player)
|
|
||||||
self.is_Werewolf = True
|
|
||||||
|
|
||||||
def add_yourself(self):
|
def add_yourself(self):
|
||||||
self.game.role[Werewolf].append(self)
|
self.game.role[Werewolf].append(self)
|
||||||
|
|
||||||
|
@Role.no_player
|
||||||
async def phase(self):
|
async def phase(self):
|
||||||
if len(self.game.role[Werewolf]) >= 2:
|
if len(self.game.role[Werewolf]) >= 2:
|
||||||
await self.send_role_list(Werewolf)
|
await self.send_role_list(Werewolf)
|
||||||
|
@ -53,12 +92,9 @@ class Werewolf(Role):
|
||||||
|
|
||||||
|
|
||||||
class Minion(Role):
|
class Minion(Role):
|
||||||
def __init__(self, game, player=No_player()):
|
@Role.no_player
|
||||||
super().__init__(game, player)
|
|
||||||
self.is_Minion = True
|
|
||||||
|
|
||||||
async def send_info(self):
|
async def send_info(self):
|
||||||
if len(self.game.werewolf_list) == 0:
|
if len(self.game.role[Werewolf]) == 0:
|
||||||
await self.player.send_info("There were no werewolves, so you need to kill a villager!")
|
await self.player.send_info("There were no werewolves, so you need to kill a villager!")
|
||||||
else:
|
else:
|
||||||
await self.send_role_list(Werewolf)
|
await self.send_role_list(Werewolf)
|
||||||
|
@ -68,57 +104,61 @@ class Mason(Role):
|
||||||
def add_yourself(self):
|
def add_yourself(self):
|
||||||
self.game.role[Mason].append(self)
|
self.game.role[Mason].append(self)
|
||||||
|
|
||||||
|
@Role.no_player
|
||||||
async def send_info(self):
|
async def send_info(self):
|
||||||
await self.send_role_list(Mason)
|
await self.send_role_list(Mason)
|
||||||
|
|
||||||
|
|
||||||
class Seer(Role):
|
class Seer(Role):
|
||||||
|
@Role.no_player
|
||||||
async def query(self):
|
async def query(self):
|
||||||
self.choice = await self.player.get_choice("Which 1 player card or 2 middle cards do you want to look at?", self.player.other() + ["left & middle", "middle & right", "left & right"])
|
self.choice = await self.player.get_choice("Which 1 player card or 2 middle cards do you want to look at?", self.player.other() + ["left & middle", "middle & right", "left & right"])
|
||||||
|
|
||||||
|
@Role.no_player
|
||||||
async def send_info(self):
|
async def send_info(self):
|
||||||
if self.choice < len(self.player.other()):
|
if self.choice < len(self.player.other()):
|
||||||
await self.player.send_info(self.player.other()[self.choice].night_role)
|
await self.player.send_info(self.player.other()[self.choice].night_role)
|
||||||
else:
|
else:
|
||||||
self.choice -= len(self.player.other())
|
a, b = [(0, 1), (1, 2), (0, 2)][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_info(f"{self.game.middle_card[a]} {self.game.middle_card[b]}")
|
await self.player.send_info(f"{self.game.middle_card[a]} {self.game.middle_card[b]}")
|
||||||
|
|
||||||
|
|
||||||
class Robber(Role):
|
class Robber(Role):
|
||||||
|
@Role.no_player
|
||||||
async def query(self):
|
async def query(self):
|
||||||
self.choice = await self.player.get_choice("Which player do you want to rob?", self.player.other())
|
self.choice = await self.player.get_choice("Which player do you want to rob?", self.player.other())
|
||||||
|
|
||||||
async def simulate(self):
|
@Role.no_player
|
||||||
|
def simulate(self):
|
||||||
self.player.swap(self.player.other()[self.choice])
|
self.player.swap(self.player.other()[self.choice])
|
||||||
|
|
||||||
|
@Role.no_player
|
||||||
async def send_info(self):
|
async def send_info(self):
|
||||||
await self.player.send_info(f"You robbed: {self.player.day_role}")
|
await self.player.send_info(f"You robbed: {self.player.day_role}")
|
||||||
|
|
||||||
|
|
||||||
class Troublemaker(Role):
|
class Troublemaker(Role):
|
||||||
|
@Role.no_player
|
||||||
async def query(self):
|
async def query(self):
|
||||||
self.A, self.B = await self.player.get_double_choice("Who do you want to exchange? (send two numbers)", self.player.other())
|
self.A, self.B = await self.player.get_double_choice("Who do you want to exchange? (send two numbers)", self.player.other())
|
||||||
|
|
||||||
async def simulate(self):
|
@Role.no_player
|
||||||
|
def simulate(self):
|
||||||
self.player.other()[self.A].swap(self.player.other()[self.B])
|
self.player.other()[self.A].swap(self.player.other()[self.B])
|
||||||
|
|
||||||
|
|
||||||
class Drunk(Role):
|
class Drunk(Role):
|
||||||
|
@Role.no_player
|
||||||
async def query(self):
|
async def query(self):
|
||||||
self.choice = await self.player.get_choice("Which card from the middle do you want to take?", ["left", "middle", "right"])
|
self.choice = await self.player.get_choice("Which card from the middle do you want to take?", ["left", "middle", "right"])
|
||||||
|
|
||||||
async def simulate(self):
|
@Role.no_player
|
||||||
|
def simulate(self):
|
||||||
self.player.day_role, self.game.middle_card[self.choice] = self.game.middle_card[self.choice], self.player.day_role # swap
|
self.player.day_role, self.game.middle_card[self.choice] = self.game.middle_card[self.choice], self.player.day_role # swap
|
||||||
|
|
||||||
|
|
||||||
class Insomniac(Role):
|
class Insomniac(Role):
|
||||||
|
@Role.no_player
|
||||||
async def send_info(self):
|
async def send_info(self):
|
||||||
await self.player.send_info(f"You are now: {self.player.day_role}")
|
await self.player.send_info(f"You are now: {self.player.day_role}")
|
||||||
|
|
||||||
|
@ -128,15 +168,11 @@ class Villager(Role):
|
||||||
|
|
||||||
|
|
||||||
class Tanner(Role):
|
class Tanner(Role):
|
||||||
def __init__(self, game, player=No_player()):
|
pass
|
||||||
super().__init__(game, player)
|
|
||||||
self.is_Tanner = True
|
|
||||||
|
|
||||||
|
|
||||||
class Hunter(Role):
|
class Hunter(Role):
|
||||||
def __init__(self, game, player=No_player()):
|
pass
|
||||||
super().__init__(game, player)
|
|
||||||
self.is_Hunter = True
|
|
||||||
|
|
||||||
|
|
||||||
class No_role(Role):
|
class No_role(Role):
|
||||||
|
|
Loading…
Reference in New Issue