add second turn

This commit is contained in:
ddorn 2020-04-26 11:12:20 +02:00
parent a6b2f0d5d9
commit 0cba843902
1 changed files with 103 additions and 50 deletions

View File

@ -6,7 +6,7 @@ import traceback
from collections import defaultdict from collections import defaultdict
from operator import attrgetter from operator import attrgetter
from time import sleep from time import sleep
from typing import Dict from typing import Dict, Type
import discord import discord
from discord.ext import commands from discord.ext import commands
@ -31,6 +31,8 @@ with open("problems") as f:
PROBLEMS = f.read().splitlines() PROBLEMS = f.read().splitlines()
MAX_REFUSE = len(PROBLEMS) - 5 MAX_REFUSE = len(PROBLEMS) - 5
ROUND_NAMES = ["premier tour", "deuxième tour"]
class TfjmError(Exception): class TfjmError(Exception):
def __init__(self, msg): def __init__(self, msg):
@ -44,12 +46,12 @@ class Team:
def __init__(self, ctx, name): def __init__(self, ctx, name):
self.name = name self.name = name
self.role = get(ctx.guild.roles, name=name) self.role = get(ctx.guild.roles, name=name)
self.tirage_order = None self.tirage_order = [None, None]
self.passage_order = None self.passage_order = [None, None]
self.accepted_problem = None self.accepted_problems = [None, None]
self.drawn_problem = None # Waiting to be accepted or refused self.drawn_problem = None # Waiting to be accepted or refused
self.rejected = set() self.rejected = [set(), set()]
@property @property
def mention(self): def mention(self):
@ -62,7 +64,7 @@ class Tirage:
self.channel = channel self.channel = channel
self.teams = [Team(ctx, team) for team in teams] self.teams = [Team(ctx, team) for team in teams]
self.phase = TirageOrderPhase(self) self.phase = TirageOrderPhase(self, round=0)
def team_for(self, author): def team_for(self, author):
for team in self.teams: for team in self.teams:
@ -89,21 +91,34 @@ class Tirage:
async def update_phase(self, ctx): async def update_phase(self, ctx):
if self.phase.finished(): if self.phase.finished():
self.phase = await self.phase.next(ctx) next_class = await self.phase.next(ctx)
if self.phase is None: if next_class is None:
await ctx.send( await ctx.send(
"Le tirage est fini ! Bonne chance à tous pour la suite !" "Le tirage est fini ! Bonne chance à tous pour la suite !"
) )
del tirages[self.channel] del tirages[self.channel]
else: else:
# Continue on the same round.
# If a Phase wants to change the round
# it needs to change its own round.
self.phase = next_class(self, self.phase.round)
await self.phase.start(ctx) await self.phase.start(ctx)
class Phase: class Phase:
NEXT = None NEXT = None
def __init__(self, tirage): def __init__(self, tirage, round=0):
"""
A Phase of the tirage.
:param tirage: Backreference to the tirage
:param round: round number, 0 for the first round and 1 for the second
"""
assert round in (0, 1)
self.round = round
self.tirage: Tirage = tirage self.tirage: Tirage = tirage
async def fais_pas_chier(self, ctx): async def fais_pas_chier(self, ctx):
@ -140,24 +155,22 @@ class Phase:
async def start(self, ctx): async def start(self, ctx):
pass pass
async def next(self, ctx: Context) -> "Phase": async def next(self, ctx: Context) -> "Type[Phase]":
if self.NEXT is None: return self.NEXT
return None
return self.NEXT(self.tirage)
class OrderPhase(Phase): class OrderPhase(Phase):
def __init__(self, tirage, name, order_name, reverse=False): def __init__(self, tirage, round, name, order_name, reverse=False):
super().__init__(tirage) super().__init__(tirage, round)
self.name = name self.name = name
self.reverse = reverse self.reverse = reverse
self.order_name = order_name self.order_name = order_name
def order_for(self, team): def order_for(self, team):
return getattr(team, self.order_name) return getattr(team, self.order_name)[self.round]
def set_order_for(self, team, order): def set_order_for(self, team, order):
setattr(team, self.order_name, order) getattr(team, self.order_name)[self.round] = order
async def dice(self, ctx, author, dice): async def dice(self, ctx, author, dice):
team = self.team_for(author) team = self.team_for(author)
@ -171,22 +184,20 @@ class OrderPhase(Phase):
def finished(self) -> bool: def finished(self) -> bool:
return all(self.order_for(team) is not None for team in self.teams) return all(self.order_for(team) is not None for team in self.teams)
async def next(self, ctx) -> "Phase": async def next(self, ctx) -> "Type[Phase]":
orders = [self.order_for(team) for team in self.teams] orders = [self.order_for(team) for team in self.teams]
if len(set(orders)) == len(orders): if len(set(orders)) == len(orders):
# All dice are different: good # All dice are different: good
self.teams.sort(key=self.order_for, reverse=self.reverse) self.teams.sort(key=self.order_for, reverse=self.reverse)
await ctx.send( await ctx.send(
f"L'ordre {self.name} pour ce tour est donc :\n" f"L'ordre {self.name} pour ce 9 est donc :\n"
" - " " - "
+ "\n - ".join( + "\n - ".join(
f"{team.role.mention} ({self.order_for(team)})" f"{team.role.mention} ({self.order_for(team)})"
for team in self.teams for team in self.teams
) )
) )
if self.NEXT is not None: return self.NEXT
return self.NEXT(self.tirage)
return None
else: else:
# Find dice that are the same # Find dice that are the same
count = defaultdict(list) count = defaultdict(list)
@ -206,14 +217,29 @@ class OrderPhase(Phase):
) )
for team in re_do: for team in re_do:
self.set_order_for(team, None) self.set_order_for(team, None)
return self # We need to do this phase again.
return self.__class__
class ShowPhase(Phase):
"""Phase where the bot resumes all that happened."""
NEXT = None
class TiragePhase(Phase): class TiragePhase(Phase):
"""The phase where captains accept or refuse random problems.""" """The phase where captains accept or refuse random problems."""
def __init__(self, tirage): NEXT = ShowPhase
super().__init__(tirage)
def __init__(self, tirage, round=0):
"""
The main phase of the Tirage.
:param tirage: Backreference to the tirage
:param round: round number, 0 for the first round and 1 for the second
"""
super().__init__(tirage, round)
self.turn = 0 self.turn = 0
@property @property
@ -221,7 +247,7 @@ class TiragePhase(Phase):
return self.teams[self.turn] return self.teams[self.turn]
def available(self, problem): def available(self, problem):
return all(team.accepted_problem != problem for team in self.teams) return all(team.accepted_problems[self.round] != problem for team in self.teams)
async def choose_problem(self, ctx: Context, author, problem): async def choose_problem(self, ctx: Context, author, problem):
team = self.current_team team = self.current_team
@ -232,7 +258,9 @@ class TiragePhase(Phase):
) )
return return
assert team.accepted_problem is None, "Choosing pb for a team that has a pb..." assert (
team.accepted_problems[self.round] is None
), "Choosing pb for a team that has a pb..."
if team.drawn_problem: if team.drawn_problem:
await ctx.send( await ctx.send(
@ -244,7 +272,13 @@ class TiragePhase(Phase):
f"Malheureusement, **{problem}** à déjà été choisi, " f"Malheureusement, **{problem}** à déjà été choisi, "
f"vous pouvez tirer un nouveau problème." f"vous pouvez tirer un nouveau problème."
) )
elif problem in team.rejected: elif problem in team.accepted_problems:
await ctx.send(
f"{team.mention} à tiré **{problem}** mais "
f"l'a déjà présenté au premier tour. "
f"Vous pouvez directement piocher un autre problème (`!rp`)."
)
elif problem in team.rejected[self.round]:
team.drawn_problem = problem team.drawn_problem = problem
await ctx.send( await ctx.send(
f"Vous avez déjà refusé **{problem}**, " f"Vous avez déjà refusé **{problem}**, "
@ -254,7 +288,7 @@ class TiragePhase(Phase):
) )
else: else:
team.drawn_problem = problem team.drawn_problem = problem
if len(team.rejected) >= MAX_REFUSE: if len(team.rejected[self.round]) >= MAX_REFUSE:
await ctx.send( await ctx.send(
f"Vous pouvez accepter ou refuser **{problem}** " f"Vous pouvez accepter ou refuser **{problem}** "
f"mais si vous choisissez de le refuser, il y " f"mais si vous choisissez de le refuser, il y "
@ -264,7 +298,7 @@ class TiragePhase(Phase):
else: else:
await ctx.send( await ctx.send(
f"Vous pouvez accepter (`!oui`) ou refuser (`!non`) **{problem}**. " f"Vous pouvez accepter (`!oui`) ou refuser (`!non`) **{problem}**. "
f"Il reste {MAX_REFUSE - len(team.rejected)} refus sans pénalité " f"Il reste {MAX_REFUSE - len(team.rejected[self.round])} refus sans pénalité "
f"pour {team.mention}." f"pour {team.mention}."
) )
@ -278,7 +312,9 @@ class TiragePhase(Phase):
) )
return return
assert team.accepted_problem is None, "Choosing pb for a team that has a pb..." assert (
team.accepted_problems[self.round] is None
), "Choosing pb for a team that has a pb..."
if not team.drawn_problem: if not team.drawn_problem:
if yes: if yes:
@ -293,20 +329,20 @@ class TiragePhase(Phase):
) )
else: else:
if yes: if yes:
team.accepted_problem = team.drawn_problem team.accepted_problems[self.round] = team.drawn_problem
await ctx.send( await ctx.send(
f"L'équipe {team.mention} a accepté " f"L'équipe {team.mention} a accepté "
f"**{team.accepted_problem}** ! Les autres équipes " f"**{team.accepted_problems[self.round]}** ! Les autres équipes "
f"ne peuvent plus l'accepter." f"ne peuvent plus l'accepter."
) )
else: else:
if team.drawn_problem in team.rejected: if team.drawn_problem in team.rejected[self.round]:
await ctx.send( await ctx.send(
f"{team.mention} a refusé **{team.drawn_problem}** " f"{team.mention} a refusé **{team.drawn_problem}** "
f"sans pénalité." f"sans pénalité."
) )
else: else:
team.rejected.add(team.drawn_problem) team.rejected[self.round].add(team.drawn_problem)
await ctx.send(f"{team.mention} a refusé **{team.drawn_problem}**!") await ctx.send(f"{team.mention} a refusé **{team.drawn_problem}**!")
team.drawn_problem = None team.drawn_problem = None
@ -318,7 +354,7 @@ class TiragePhase(Phase):
# Find next team that needs to draw. # Find next team that needs to draw.
i = (self.turn + 1) % len(self.teams) i = (self.turn + 1) % len(self.teams)
while self.teams[i].accepted_problem: while self.teams[i].accepted_problems[self.round]:
i = (i + 1) % len(self.teams) i = (i + 1) % len(self.teams)
self.turn = i self.turn = i
@ -327,13 +363,13 @@ class TiragePhase(Phase):
) )
def finished(self) -> bool: def finished(self) -> bool:
return all(team.accepted_problem for team in self.teams) return all(team.accepted_problems[self.round] for team in self.teams)
async def start(self, ctx: Context): async def start(self, ctx: Context):
# First sort teams according to the tirage_order # First sort teams according to the tirage_order
self.teams.sort(key=attrgetter("tirage_order")) self.teams.sort(key=lambda team: team.tirage_order[self.round])
async with ctx.typing(): if self.round == 0:
sleep(0.5) sleep(0.5)
await ctx.send("Passons au tirage des problèmes !") await ctx.send("Passons au tirage des problèmes !")
sleep(0.5) sleep(0.5)
@ -354,21 +390,37 @@ class TiragePhase(Phase):
f"Un problème déjà rejeté ne compte pas deux fois." f"Un problème déjà rejeté ne compte pas deux fois."
) )
await ctx.send("Bonne chance à tous ! C'est parti...") await ctx.send("Bonne chance à tous ! C'est parti...")
sleep(1.5)
else:
# Second round
sleep(0.5)
await ctx.send(
"Il reste juste le tirage du deuxième tour. Les règles sont les mêmes qu'avant "
"à la seule différence qu'une équipe ne peut pas tirer le problème "
"sur lequel elle est passée au premier tour."
)
sleep(1.5)
await ctx.send( await ctx.send(
f"{self.current_team.mention} à toi l'honneur! " f"{self.current_team.mention} à toi l'honneur! "
f"Lance `!random-problem` quand tu veux." f"Lance `!random-problem` quand tu veux."
) )
async def next(self, ctx: Context) -> "Type[Phase]":
if self.round == 0:
await ctx.send("Nous allons passer au deuxième tour")
self.round = 1
return TirageOrderPhase
return ShowPhase
class PassageOrderPhase(OrderPhase): class PassageOrderPhase(OrderPhase):
"""The phase to determine the chicken's order.""" """The phase to determine the chicken's order."""
NEXT = TiragePhase NEXT = TiragePhase
def __init__(self, tirage): def __init__(self, tirage, round=0):
super().__init__(tirage, "de passage", "passage_order", True) super().__init__(tirage, round, "de passage", "passage_order", True)
async def start(self, ctx): async def start(self, ctx):
await ctx.send( await ctx.send(
@ -390,16 +442,17 @@ class TirageOrderPhase(OrderPhase):
NEXT = PassageOrderPhase NEXT = PassageOrderPhase
def __init__(self, tirage): def __init__(self, tirage, round=0):
super().__init__(tirage, "des tirages", "tirage_order", False) super().__init__(tirage, round, "des tirages", "tirage_order", False)
async def start(self, ctx): async def start(self, ctx):
captain = get(ctx.guild.roles, name=CAPTAIN_ROLE) captain = get(ctx.guild.roles, name=CAPTAIN_ROLE)
sleep(0.5) # The bot is more human if it doesn't type at the speed of light sleep(0.5) # The bot is more human if it doesn't type at the speed of light
await ctx.send( await ctx.send(
"Nous allons d'abord tirer au sort l'ordre de tirage des problèmes, " "Nous allons d'abord tirer au sort l'ordre de tirage des problèmes "
"puis l'ordre de passage lors du tour." f"pour le {ROUND_NAMES[self.round]}, "
"puis l'ordre de passage lors de ce tour."
) )
sleep(0.5) sleep(0.5)
await ctx.send( await ctx.send(
@ -454,8 +507,8 @@ async def draw_skip(ctx, *teams):
tirage.phase = TiragePhase(tirage) tirage.phase = TiragePhase(tirage)
for i, team in enumerate(tirage.teams): for i, team in enumerate(tirage.teams):
team.tirage_order = i team.tirage_order = [i, None]
team.passage_order = i team.passage_order = [i, None]
await ctx.send(f"Skipping to {tirage.phase.__class__.__name__}.") await ctx.send(f"Skipping to {tirage.phase.__class__.__name__}.")
await tirage.phase.start(ctx) await tirage.phase.start(ctx)