✨ add second turn
This commit is contained in:
parent
a6b2f0d5d9
commit
0cba843902
|
@ -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(
|
await ctx.send(
|
||||||
f"{self.current_team.mention} à toi l'honneur! "
|
"Il reste juste le tirage du deuxième tour. Les règles sont les mêmes qu'avant "
|
||||||
f"Lance `!random-problem` quand tu veux."
|
"à 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(
|
||||||
|
f"{self.current_team.mention} à toi l'honneur! "
|
||||||
|
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)
|
||||||
|
|
Loading…
Reference in New Issue