mirror of
				https://gitlab.com/ddorn/tfjm-discord-bot.git
				synced 2025-11-04 09:02:14 +01:00 
			
		
		
		
	♻️ factor tirage logic out
This commit is contained in:
		
							
								
								
									
										518
									
								
								src/cogs/tirage_logic.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										518
									
								
								src/cogs/tirage_logic.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,518 @@
 | 
			
		||||
#!/bin/python
 | 
			
		||||
import asyncio
 | 
			
		||||
import random
 | 
			
		||||
from collections import defaultdict, namedtuple
 | 
			
		||||
from typing import Type
 | 
			
		||||
 | 
			
		||||
import discord
 | 
			
		||||
import yaml
 | 
			
		||||
from discord.ext.commands import Context
 | 
			
		||||
from discord.utils import get
 | 
			
		||||
 | 
			
		||||
from src.constants import *
 | 
			
		||||
from src.errors import TfjmError, UnwantedCommand
 | 
			
		||||
 | 
			
		||||
__all__ = ["Tirage"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def in_passage_order(teams, round=0):
 | 
			
		||||
    return sorted(teams, key=lambda team: team.passage_order[round] or 0, reverse=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Team(yaml.YAMLObject):
 | 
			
		||||
    yaml_tag = "Team"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, ctx, name):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.mention = get(ctx.guild.roles, name=name).mention
 | 
			
		||||
        self.tirage_order = [None, None]
 | 
			
		||||
        self.passage_order = [None, None]
 | 
			
		||||
 | 
			
		||||
        self.accepted_problems = [None, None]
 | 
			
		||||
        self.drawn_problem = None  # Waiting to be accepted or refused
 | 
			
		||||
        self.rejected = [set(), set()]
 | 
			
		||||
 | 
			
		||||
    def coeff(self, round):
 | 
			
		||||
        if len(self.rejected[round]) <= MAX_REFUSE:
 | 
			
		||||
            return 2
 | 
			
		||||
        else:
 | 
			
		||||
            return 2 - 0.5 * (len(self.rejected[round]) - MAX_REFUSE)
 | 
			
		||||
 | 
			
		||||
    def details(self, round):
 | 
			
		||||
        return f"""{self.mention}:
 | 
			
		||||
 - Accepté: {self.accepted_problems[round]}
 | 
			
		||||
 - Refusés: {", ".join(p[0] for p in self.rejected[round]) if self.rejected[round] else "aucun"}
 | 
			
		||||
 - Coefficient: {self.coeff(round)}
 | 
			
		||||
 - Ordre au tirage: {self.tirage_order[round]}
 | 
			
		||||
 - Ordre de passage: {self.passage_order[round]}
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Tirage(yaml.YAMLObject):
 | 
			
		||||
    yaml_tag = "Tirage"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, ctx, channel, teams):
 | 
			
		||||
        assert len(teams) in (3, 4)
 | 
			
		||||
 | 
			
		||||
        self.channel: int = channel
 | 
			
		||||
        self.teams = [Team(ctx, team) for team in teams]
 | 
			
		||||
        self.phase = TirageOrderPhase(self, round=0)
 | 
			
		||||
 | 
			
		||||
    def team_for(self, author):
 | 
			
		||||
        for team in self.teams:
 | 
			
		||||
            if get(author.roles, name=team.name):
 | 
			
		||||
                return team
 | 
			
		||||
 | 
			
		||||
        # Should theoretically not happen
 | 
			
		||||
        raise TfjmError(
 | 
			
		||||
            "Tu n'es pas dans une des équipes qui font le tirage, "
 | 
			
		||||
            "merci de ne pas intervenir."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    async def dice(self, ctx, n):
 | 
			
		||||
        if n != 100:
 | 
			
		||||
            raise UnwantedCommand(
 | 
			
		||||
                "C'est un dé à 100 faces qu'il faut tirer! (`!dice 100`)"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        await self.phase.dice(ctx, ctx.author, random.randint(1, n))
 | 
			
		||||
        await self.update_phase(ctx)
 | 
			
		||||
 | 
			
		||||
    async def choose_problem(self, ctx):
 | 
			
		||||
        await self.phase.choose_problem(ctx, ctx.author, random.choice(PROBLEMS))
 | 
			
		||||
        await self.update_phase(ctx)
 | 
			
		||||
 | 
			
		||||
    async def accept(self, ctx, yes):
 | 
			
		||||
        await self.phase.accept(ctx, ctx.author, yes)
 | 
			
		||||
        await self.update_phase(ctx)
 | 
			
		||||
 | 
			
		||||
    async def update_phase(self, ctx):
 | 
			
		||||
        if self.phase.finished():
 | 
			
		||||
            next_class = await self.phase.next(ctx)
 | 
			
		||||
 | 
			
		||||
            if next_class is None:
 | 
			
		||||
                self.phase = None
 | 
			
		||||
                await ctx.send(
 | 
			
		||||
                    "Le tirage est fini ! Bonne chance à tous pour la suite !"
 | 
			
		||||
                )
 | 
			
		||||
                await self.show(ctx)
 | 
			
		||||
                await self.end(ctx)
 | 
			
		||||
            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)
 | 
			
		||||
 | 
			
		||||
    async def end(self, ctx):
 | 
			
		||||
        if False:
 | 
			
		||||
            # Allow everyone to send messages again
 | 
			
		||||
            send = discord.PermissionOverwrite()  # reset
 | 
			
		||||
            await ctx.channel.edit(overwrites={ctx.guild.default_role: send})
 | 
			
		||||
 | 
			
		||||
        tl = []
 | 
			
		||||
        if TIRAGES_FILE.exists():
 | 
			
		||||
            with open(TIRAGES_FILE) as f:
 | 
			
		||||
                tl = list(yaml.load_all(f))
 | 
			
		||||
        else:
 | 
			
		||||
            TIRAGES_FILE.touch()
 | 
			
		||||
        tl.append(self)
 | 
			
		||||
        with open(TIRAGES_FILE, "w") as f:
 | 
			
		||||
            yaml.dump_all(tl, f)
 | 
			
		||||
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            f"A tout moment, ce rapport peut être envoyé avec `!show {len(tl) - 1}`"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    async def show(self, ctx):
 | 
			
		||||
        teams = ", ".join(team.mention for team in self.teams)
 | 
			
		||||
        msg = f"Voici un résumé du tirage entre les équipes {teams}."
 | 
			
		||||
 | 
			
		||||
        if len(self.teams) == 3:
 | 
			
		||||
            table = """```
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
    |     | Phase 1 | Phase 2 | Phase 3 |
 | 
			
		||||
    |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
    | {0.name} |   Déf   |   Rap   |   Opp   |
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
    | {1.name} |   Opp   |   Déf   |   Rap   |
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
    | {2.name} |   Rap   |   Opp   |   Déf   |
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
```"""
 | 
			
		||||
        else:
 | 
			
		||||
            table = """```
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    |     | Phase 1 | Phase 2 | Phase 3 | Phase 4 |
 | 
			
		||||
    |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |   Pb {3.pb}  |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    | {0.name} |   Déf   |         |   Rap   |   Opp   |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    | {0.name} |   Opp   |   Déf   |         |   Rap   |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    | {0.name} |   Rap   |   Opp   |   Déf   |         |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    | {0.name} |         |   Rap   |   Opp   |   Déf   |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
```"""
 | 
			
		||||
        Record = namedtuple("Record", ["name", "pb", "penalite"])
 | 
			
		||||
 | 
			
		||||
        for round in (0, 1):
 | 
			
		||||
            records = [
 | 
			
		||||
                Record(
 | 
			
		||||
                    team.name,
 | 
			
		||||
                    (team.accepted_problems[round] or "- None")[0],
 | 
			
		||||
                    f"k = {team.coeff(round)} ",
 | 
			
		||||
                )
 | 
			
		||||
                for team in in_passage_order(self.teams, round)
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
            msg += f"\n\n**{ROUND_NAMES[round].capitalize()}**:\n"
 | 
			
		||||
            msg += table.format(*records) + "\n"
 | 
			
		||||
            for team in self.teams:
 | 
			
		||||
                msg += team.details(round)
 | 
			
		||||
 | 
			
		||||
        await ctx.send(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Phase:
 | 
			
		||||
    NEXT = None
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    def team_for(self, author):
 | 
			
		||||
        return self.tirage.team_for(author)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def teams(self):
 | 
			
		||||
        return self.tirage.teams
 | 
			
		||||
 | 
			
		||||
    @teams.setter
 | 
			
		||||
    def teams(self, teams):
 | 
			
		||||
        self.tirage.teams = teams
 | 
			
		||||
 | 
			
		||||
    def captain_mention(self, ctx):
 | 
			
		||||
        return get(ctx.guild.roles, name=Role.CAPTAIN).mention
 | 
			
		||||
 | 
			
		||||
    async def dice(self, ctx: Context, author, dice):
 | 
			
		||||
        raise UnwantedCommand()
 | 
			
		||||
 | 
			
		||||
    async def choose_problem(self, ctx: Context, author, problem):
 | 
			
		||||
        raise UnwantedCommand()
 | 
			
		||||
 | 
			
		||||
    async def accept(self, ctx: Context, author, yes):
 | 
			
		||||
        raise UnwantedCommand()
 | 
			
		||||
 | 
			
		||||
    def finished(self) -> bool:
 | 
			
		||||
        return NotImplemented
 | 
			
		||||
 | 
			
		||||
    async def start(self, ctx):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    async def next(self, ctx: Context) -> "Type[Phase]":
 | 
			
		||||
        return self.NEXT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OrderPhase(Phase):
 | 
			
		||||
    def __init__(self, tirage, round, name, order_name, reverse=False):
 | 
			
		||||
        super().__init__(tirage, round)
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.reverse = reverse
 | 
			
		||||
        self.order_name = order_name
 | 
			
		||||
 | 
			
		||||
    def order_for(self, team):
 | 
			
		||||
        return getattr(team, self.order_name)[self.round]
 | 
			
		||||
 | 
			
		||||
    def set_order_for(self, team, order):
 | 
			
		||||
        getattr(team, self.order_name)[self.round] = order
 | 
			
		||||
 | 
			
		||||
    async def dice(self, ctx, author, dice):
 | 
			
		||||
        team = self.team_for(author)
 | 
			
		||||
 | 
			
		||||
        if self.order_for(team) is None:
 | 
			
		||||
            self.set_order_for(team, dice)
 | 
			
		||||
            await ctx.send(f"L'équipe {team.mention} a obtenu... **{dice}**")
 | 
			
		||||
        else:
 | 
			
		||||
            raise UnwantedCommand("tu as déjà lancé un dé !")
 | 
			
		||||
 | 
			
		||||
    def finished(self) -> bool:
 | 
			
		||||
        return all(self.order_for(team) is not None for team in self.teams)
 | 
			
		||||
 | 
			
		||||
    async def next(self, ctx) -> "Type[Phase]":
 | 
			
		||||
        orders = [self.order_for(team) for team in self.teams]
 | 
			
		||||
        if len(set(orders)) == len(orders):
 | 
			
		||||
            # All dice are different: good
 | 
			
		||||
            self.teams.sort(key=self.order_for, reverse=self.reverse)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"L'ordre {self.name} pour ce tour est donc :\n"
 | 
			
		||||
                " - "
 | 
			
		||||
                + "\n - ".join(
 | 
			
		||||
                    f"{team.mention} ({self.order_for(team)})" for team in self.teams
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            return self.NEXT
 | 
			
		||||
        else:
 | 
			
		||||
            # Find dice that are the same
 | 
			
		||||
            count = defaultdict(list)
 | 
			
		||||
            for team in self.teams:
 | 
			
		||||
                count[self.order_for(team)].append(team)
 | 
			
		||||
 | 
			
		||||
            re_do = []
 | 
			
		||||
            for teams in count.values():
 | 
			
		||||
                if len(teams) > 1:
 | 
			
		||||
                    re_do.extend(teams)
 | 
			
		||||
 | 
			
		||||
            teams_str = ", ".join(team.role.mention for team in re_do)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Les equipes {teams_str} ont fait le même résultat "
 | 
			
		||||
                "et doivent relancer un dé. "
 | 
			
		||||
                "Le nouveau lancer effacera l'ancien."
 | 
			
		||||
            )
 | 
			
		||||
            for team in re_do:
 | 
			
		||||
                self.set_order_for(team, None)
 | 
			
		||||
            # We need to do this phase again.
 | 
			
		||||
            return self.__class__
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TiragePhase(Phase):
 | 
			
		||||
    """The phase where captains accept or refuse random problems."""
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def current_team(self):
 | 
			
		||||
        return self.teams[self.turn]
 | 
			
		||||
 | 
			
		||||
    def available(self, problem):
 | 
			
		||||
        return all(team.accepted_problems[self.round] != problem for team in self.teams)
 | 
			
		||||
 | 
			
		||||
    async def choose_problem(self, ctx: Context, author, problem):
 | 
			
		||||
        team = self.current_team
 | 
			
		||||
        if self.team_for(author) != team:
 | 
			
		||||
            raise UnwantedCommand(
 | 
			
		||||
                f"C'est à {team.mention} de choisir "
 | 
			
		||||
                f"un problème, merci d'attendre :)"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        assert (
 | 
			
		||||
            team.accepted_problems[self.round] is None
 | 
			
		||||
        ), "Choosing pb for a team that has a pb..."
 | 
			
		||||
 | 
			
		||||
        if team.drawn_problem:
 | 
			
		||||
            raise UnwantedCommand(
 | 
			
		||||
                "Vous avez déjà tiré un problème, merci de l'accepter (`!yes`) "
 | 
			
		||||
                "ou de le refuser (`!no)`."
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        await ctx.send(f"{team.mention} a tiré **{problem}** !")
 | 
			
		||||
        if not self.available(problem):
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Malheureusement, **{problem}** à déjà été choisi, "
 | 
			
		||||
                f"vous pouvez tirer un nouveau problème."
 | 
			
		||||
            )
 | 
			
		||||
        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
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Vous avez déjà refusé **{problem}**, "
 | 
			
		||||
                f"vous pouvez le refuser à nouveau (`!refuse`) et "
 | 
			
		||||
                f"tirer immédiatement un nouveau problème "
 | 
			
		||||
                f"ou changer d'avis et l'accepter (`!accept`)."
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            team.drawn_problem = problem
 | 
			
		||||
            if len(team.rejected[self.round]) >= MAX_REFUSE:
 | 
			
		||||
                await ctx.send(
 | 
			
		||||
                    f"Vous pouvez accepter ou refuser **{problem}** "
 | 
			
		||||
                    f"mais si vous choisissez de le refuser, il y "
 | 
			
		||||
                    f"aura une pénalité de 0.5 sur le multiplicateur du "
 | 
			
		||||
                    f"défenseur."
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                await ctx.send(
 | 
			
		||||
                    f"Vous pouvez accepter (`!oui`) ou refuser (`!non`) **{problem}**. "
 | 
			
		||||
                    f"Il reste {MAX_REFUSE - len(team.rejected[self.round])} refus sans pénalité "
 | 
			
		||||
                    f"pour {team.mention}."
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
    async def accept(self, ctx: Context, author, yes):
 | 
			
		||||
        team = self.current_team
 | 
			
		||||
 | 
			
		||||
        if self.team_for(author) != team:
 | 
			
		||||
            raise UnwantedCommand(
 | 
			
		||||
                f"c'est à {team.mention} "
 | 
			
		||||
                f"de choisir un problème, merci d'attendre :)"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        assert (
 | 
			
		||||
            team.accepted_problems[self.round] is None
 | 
			
		||||
        ), "Choosing pb for a team that has a pb..."
 | 
			
		||||
 | 
			
		||||
        if not team.drawn_problem:
 | 
			
		||||
            if yes:
 | 
			
		||||
                raise UnwantedCommand(
 | 
			
		||||
                    "Tu es bien optimiste pour vouloir accepter un problème "
 | 
			
		||||
                    "avant de l'avoir tiré !"
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                raise UnwantedCommand(
 | 
			
		||||
                    "Halte là ! Ce serait bien de tirer un problème d'abord... "
 | 
			
		||||
                    "et peut-être qu'il te plaira :) "
 | 
			
		||||
                )
 | 
			
		||||
        else:
 | 
			
		||||
            if yes:
 | 
			
		||||
                team.accepted_problems[self.round] = team.drawn_problem
 | 
			
		||||
                await ctx.send(
 | 
			
		||||
                    f"L'équipe {team.mention} a accepté "
 | 
			
		||||
                    f"**{team.accepted_problems[self.round]}** ! Les autres équipes "
 | 
			
		||||
                    f"ne peuvent plus l'accepter."
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                msg = f"{team.mention} a refusé **{team.drawn_problem}** "
 | 
			
		||||
                if team.drawn_problem in team.rejected[self.round]:
 | 
			
		||||
                    msg += "sans pénalité."
 | 
			
		||||
                else:
 | 
			
		||||
                    msg += "!"
 | 
			
		||||
                    team.rejected[self.round].add(team.drawn_problem)
 | 
			
		||||
                await ctx.send(msg)
 | 
			
		||||
 | 
			
		||||
            team.drawn_problem = None
 | 
			
		||||
 | 
			
		||||
            # Next turn
 | 
			
		||||
            if self.finished():
 | 
			
		||||
                self.turn = None
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            # Find next team that needs to draw.
 | 
			
		||||
            i = (self.turn + 1) % len(self.teams)
 | 
			
		||||
            while self.teams[i].accepted_problems[self.round]:
 | 
			
		||||
                i = (i + 1) % len(self.teams)
 | 
			
		||||
            self.turn = i
 | 
			
		||||
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"C'est au tour de {self.current_team.mention} de choisir un problème."
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def finished(self) -> bool:
 | 
			
		||||
        return all(team.accepted_problems[self.round] for team in self.teams)
 | 
			
		||||
 | 
			
		||||
    async def start(self, ctx: Context):
 | 
			
		||||
        # First sort teams according to the tirage_order
 | 
			
		||||
        self.teams.sort(key=lambda team: team.tirage_order[self.round])
 | 
			
		||||
 | 
			
		||||
        if self.round == 0:
 | 
			
		||||
            await asyncio.sleep(0.5)
 | 
			
		||||
            await ctx.send("Passons au tirage des problèmes !")
 | 
			
		||||
            await asyncio.sleep(0.5)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Les {self.captain_mention(ctx)}s vont tirer des problèmes au "
 | 
			
		||||
                f"hasard, avec `!random-problem` ou `!rp` pour ceux qui aiment "
 | 
			
		||||
                f"les abbréviations."
 | 
			
		||||
            )
 | 
			
		||||
            await asyncio.sleep(0.5)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                "Ils pouront ensuite accepter ou refuser les problèmes avec "
 | 
			
		||||
                "`!accept` ou `!refuse`."
 | 
			
		||||
            )
 | 
			
		||||
            await asyncio.sleep(0.5)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Chaque équipe peut refuser jusqu'a {MAX_REFUSE} "
 | 
			
		||||
                f"problèmes sans pénalité (voir §13 du règlement). "
 | 
			
		||||
                f"Un problème déjà rejeté ne compte pas deux fois."
 | 
			
		||||
            )
 | 
			
		||||
            await ctx.send("Bonne chance à tous ! C'est parti...")
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            # Second round
 | 
			
		||||
            await asyncio.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."
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        await asyncio.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 None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PassageOrderPhase(OrderPhase):
 | 
			
		||||
    """The phase to determine the chicken's order."""
 | 
			
		||||
 | 
			
		||||
    NEXT = TiragePhase
 | 
			
		||||
 | 
			
		||||
    def __init__(self, tirage, round=0):
 | 
			
		||||
        super().__init__(tirage, round, "de passage", "passage_order", True)
 | 
			
		||||
 | 
			
		||||
    async def start(self, ctx):
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            "Nous allons maintenant tirer l'ordre de passage durant le tour. "
 | 
			
		||||
            "L'ordre du tour sera dans l'ordre décroissant des lancers, "
 | 
			
		||||
            "c'est-à-dire que l'équipe qui tire le plus grand nombre "
 | 
			
		||||
            "présentera en premier."
 | 
			
		||||
        )
 | 
			
		||||
        await asyncio.sleep(0.5)
 | 
			
		||||
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            f"Les {self.captain_mention(ctx)}s, vous pouvez lancer "
 | 
			
		||||
            f"à nouveau un dé 100 (`!dice 100`)"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TirageOrderPhase(OrderPhase):
 | 
			
		||||
    """Phase to determine the tirage's order."""
 | 
			
		||||
 | 
			
		||||
    NEXT = PassageOrderPhase
 | 
			
		||||
 | 
			
		||||
    def __init__(self, tirage, round=0):
 | 
			
		||||
        super().__init__(tirage, round, "des tirages", "tirage_order", False)
 | 
			
		||||
 | 
			
		||||
    async def start(self, ctx):
 | 
			
		||||
 | 
			
		||||
        await asyncio.sleep(
 | 
			
		||||
            0.5
 | 
			
		||||
        )  # The bot is more human if it doesn't type at the speed of light
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            "Nous allons d'abord tirer au sort l'ordre de tirage des problèmes "
 | 
			
		||||
            f"pour le {ROUND_NAMES[self.round]}, "
 | 
			
		||||
            "puis l'ordre de passage lors de ce tour."
 | 
			
		||||
        )
 | 
			
		||||
        await asyncio.sleep(0.5)
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            f"Les {self.captain_mention(ctx)}s, vous pouvez désormais lancer un dé 100 "
 | 
			
		||||
            "comme ceci `!dice 100`. "
 | 
			
		||||
            "L'ordre des tirages suivants sera l'ordre croissant des lancers. "
 | 
			
		||||
        )
 | 
			
		||||
@@ -1,8 +1,5 @@
 | 
			
		||||
#!/bin/python
 | 
			
		||||
import asyncio
 | 
			
		||||
import random
 | 
			
		||||
from collections import defaultdict, namedtuple
 | 
			
		||||
from typing import Type
 | 
			
		||||
 | 
			
		||||
import discord
 | 
			
		||||
import yaml
 | 
			
		||||
@@ -10,511 +7,9 @@ from discord.ext import commands
 | 
			
		||||
from discord.ext.commands import Context, group, Cog
 | 
			
		||||
from discord.utils import get
 | 
			
		||||
 | 
			
		||||
from src.cogs.tirage_logic import TiragePhase, Tirage
 | 
			
		||||
from src.constants import *
 | 
			
		||||
from src.errors import TfjmError, UnwantedCommand
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def in_passage_order(teams, round=0):
 | 
			
		||||
    return sorted(teams, key=lambda team: team.passage_order[round] or 0, reverse=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Team(yaml.YAMLObject):
 | 
			
		||||
    yaml_tag = "Team"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, ctx, name):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.mention = get(ctx.guild.roles, name=name).mention
 | 
			
		||||
        self.tirage_order = [None, None]
 | 
			
		||||
        self.passage_order = [None, None]
 | 
			
		||||
 | 
			
		||||
        self.accepted_problems = [None, None]
 | 
			
		||||
        self.drawn_problem = None  # Waiting to be accepted or refused
 | 
			
		||||
        self.rejected = [set(), set()]
 | 
			
		||||
 | 
			
		||||
    def coeff(self, round):
 | 
			
		||||
        if len(self.rejected[round]) <= MAX_REFUSE:
 | 
			
		||||
            return 2
 | 
			
		||||
        else:
 | 
			
		||||
            return 2 - 0.5 * (len(self.rejected[round]) - MAX_REFUSE)
 | 
			
		||||
 | 
			
		||||
    def details(self, round):
 | 
			
		||||
        return f"""{self.mention}:
 | 
			
		||||
 - Accepté: {self.accepted_problems[round]}
 | 
			
		||||
 - Refusés: {", ".join(p[0] for p in self.rejected[round]) if self.rejected[round] else "aucun"}
 | 
			
		||||
 - Coefficient: {self.coeff(round)}
 | 
			
		||||
 - Ordre au tirage: {self.tirage_order[round]}
 | 
			
		||||
 - Ordre de passage: {self.passage_order[round]}
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Tirage(yaml.YAMLObject):
 | 
			
		||||
    yaml_tag = "Tirage"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, ctx, channel, teams):
 | 
			
		||||
        assert len(teams) in (3, 4)
 | 
			
		||||
 | 
			
		||||
        self.channel: int = channel
 | 
			
		||||
        self.teams = [Team(ctx, team) for team in teams]
 | 
			
		||||
        self.phase = TirageOrderPhase(self, round=0)
 | 
			
		||||
 | 
			
		||||
    def team_for(self, author):
 | 
			
		||||
        for team in self.teams:
 | 
			
		||||
            if get(author.roles, name=team.name):
 | 
			
		||||
                return team
 | 
			
		||||
 | 
			
		||||
        # Should theoretically not happen
 | 
			
		||||
        raise TfjmError(
 | 
			
		||||
            "Tu n'es pas dans une des équipes qui font le tirage, "
 | 
			
		||||
            "merci de ne pas intervenir."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    async def dice(self, ctx, n) -> bool:
 | 
			
		||||
        if n != 100:
 | 
			
		||||
            raise UnwantedCommand(
 | 
			
		||||
                "C'est un dé à 100 faces qu'il faut tirer! (`!dice 100`)"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        await self.phase.dice(ctx, ctx.author, random.randint(1, n))
 | 
			
		||||
        await self.update_phase(ctx)
 | 
			
		||||
 | 
			
		||||
    async def choose_problem(self, ctx):
 | 
			
		||||
        await self.phase.choose_problem(ctx, ctx.author, random.choice(PROBLEMS))
 | 
			
		||||
        await self.update_phase(ctx)
 | 
			
		||||
 | 
			
		||||
    async def accept(self, ctx, yes):
 | 
			
		||||
        await self.phase.accept(ctx, ctx.author, yes)
 | 
			
		||||
        await self.update_phase(ctx)
 | 
			
		||||
 | 
			
		||||
    async def update_phase(self, ctx):
 | 
			
		||||
        if self.phase.finished():
 | 
			
		||||
            next_class = await self.phase.next(ctx)
 | 
			
		||||
 | 
			
		||||
            if next_class is None:
 | 
			
		||||
                self.phase = None
 | 
			
		||||
                await ctx.send(
 | 
			
		||||
                    "Le tirage est fini ! Bonne chance à tous pour la suite !"
 | 
			
		||||
                )
 | 
			
		||||
                await self.show(ctx)
 | 
			
		||||
                await self.end(ctx)
 | 
			
		||||
            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)
 | 
			
		||||
 | 
			
		||||
    async def end(self, ctx):
 | 
			
		||||
        if False:
 | 
			
		||||
            # Allow everyone to send messages again
 | 
			
		||||
            send = discord.PermissionOverwrite()  # reset
 | 
			
		||||
            await ctx.channel.edit(overwrites={ctx.guild.default_role: send})
 | 
			
		||||
 | 
			
		||||
        tl = []
 | 
			
		||||
        if TIRAGES_FILE.exists():
 | 
			
		||||
            with open(TIRAGES_FILE) as f:
 | 
			
		||||
                tl = list(yaml.load_all(f))
 | 
			
		||||
        else:
 | 
			
		||||
            TIRAGES_FILE.touch()
 | 
			
		||||
        tl.append(self)
 | 
			
		||||
        with open(TIRAGES_FILE, "w") as f:
 | 
			
		||||
            yaml.dump_all(tl, f)
 | 
			
		||||
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            f"A tout moment, ce rapport peut être envoyé avec `!show {len(tl) - 1}`"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    async def show(self, ctx):
 | 
			
		||||
        teams = ", ".join(team.mention for team in self.teams)
 | 
			
		||||
        msg = f"Voici un résumé du tirage entre les équipes {teams}."
 | 
			
		||||
 | 
			
		||||
        if len(self.teams) == 3:
 | 
			
		||||
            table = """```
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
    |     | Phase 1 | Phase 2 | Phase 3 |
 | 
			
		||||
    |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
    | {0.name} |   Déf   |   Rap   |   Opp   |
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
    | {1.name} |   Opp   |   Déf   |   Rap   |
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
    | {2.name} |   Rap   |   Opp   |   Déf   |
 | 
			
		||||
    +-----+---------+---------+---------+
 | 
			
		||||
```"""
 | 
			
		||||
        else:
 | 
			
		||||
            table = """```
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    |     | Phase 1 | Phase 2 | Phase 3 | Phase 4 |
 | 
			
		||||
    |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |   Pb {3.pb}  |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    | {0.name} |   Déf   |         |   Rap   |   Opp   |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    | {0.name} |   Opp   |   Déf   |         |   Rap   |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    | {0.name} |   Rap   |   Opp   |   Déf   |         |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
    | {0.name} |         |   Rap   |   Opp   |   Déf   |
 | 
			
		||||
    +-----+---------+---------+---------+---------+
 | 
			
		||||
```"""
 | 
			
		||||
        Record = namedtuple("Record", ["name", "pb", "penalite"])
 | 
			
		||||
 | 
			
		||||
        for round in (0, 1):
 | 
			
		||||
            records = [
 | 
			
		||||
                Record(
 | 
			
		||||
                    team.name,
 | 
			
		||||
                    (team.accepted_problems[round] or "- None")[0],
 | 
			
		||||
                    f"k = {team.coeff(round)} ",
 | 
			
		||||
                )
 | 
			
		||||
                for team in in_passage_order(self.teams, round)
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
            msg += f"\n\n**{ROUND_NAMES[round].capitalize()}**:\n"
 | 
			
		||||
            msg += table.format(*records) + "\n"
 | 
			
		||||
            for team in self.teams:
 | 
			
		||||
                msg += team.details(round)
 | 
			
		||||
 | 
			
		||||
        await ctx.send(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Phase:
 | 
			
		||||
    NEXT = None
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    def team_for(self, author):
 | 
			
		||||
        return self.tirage.team_for(author)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def teams(self):
 | 
			
		||||
        return self.tirage.teams
 | 
			
		||||
 | 
			
		||||
    @teams.setter
 | 
			
		||||
    def teams(self, teams):
 | 
			
		||||
        self.tirage.teams = teams
 | 
			
		||||
 | 
			
		||||
    def captain_mention(self, ctx):
 | 
			
		||||
        return get(ctx.guild.roles, name=Role.CAPTAIN).mention
 | 
			
		||||
 | 
			
		||||
    async def dice(self, ctx: Context, author, dice):
 | 
			
		||||
        raise UnwantedCommand()
 | 
			
		||||
 | 
			
		||||
    async def choose_problem(self, ctx: Context, author, problem):
 | 
			
		||||
        raise UnwantedCommand()
 | 
			
		||||
 | 
			
		||||
    async def accept(self, ctx: Context, author, yes):
 | 
			
		||||
        raise UnwantedCommand()
 | 
			
		||||
 | 
			
		||||
    def finished(self) -> bool:
 | 
			
		||||
        return NotImplemented
 | 
			
		||||
 | 
			
		||||
    async def start(self, ctx):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    async def next(self, ctx: Context) -> "Type[Phase]":
 | 
			
		||||
        return self.NEXT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OrderPhase(Phase):
 | 
			
		||||
    def __init__(self, tirage, round, name, order_name, reverse=False):
 | 
			
		||||
        super().__init__(tirage, round)
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.reverse = reverse
 | 
			
		||||
        self.order_name = order_name
 | 
			
		||||
 | 
			
		||||
    def order_for(self, team):
 | 
			
		||||
        return getattr(team, self.order_name)[self.round]
 | 
			
		||||
 | 
			
		||||
    def set_order_for(self, team, order):
 | 
			
		||||
        getattr(team, self.order_name)[self.round] = order
 | 
			
		||||
 | 
			
		||||
    async def dice(self, ctx, author, dice):
 | 
			
		||||
        team = self.team_for(author)
 | 
			
		||||
 | 
			
		||||
        if self.order_for(team) is None:
 | 
			
		||||
            self.set_order_for(team, dice)
 | 
			
		||||
            await ctx.send(f"L'équipe {team.mention} a obtenu... **{dice}**")
 | 
			
		||||
        else:
 | 
			
		||||
            raise UnwantedCommand("tu as déjà lancé un dé !")
 | 
			
		||||
 | 
			
		||||
    def finished(self) -> bool:
 | 
			
		||||
        return all(self.order_for(team) is not None for team in self.teams)
 | 
			
		||||
 | 
			
		||||
    async def next(self, ctx) -> "Type[Phase]":
 | 
			
		||||
        orders = [self.order_for(team) for team in self.teams]
 | 
			
		||||
        if len(set(orders)) == len(orders):
 | 
			
		||||
            # All dice are different: good
 | 
			
		||||
            self.teams.sort(key=self.order_for, reverse=self.reverse)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"L'ordre {self.name} pour ce tour est donc :\n"
 | 
			
		||||
                " - "
 | 
			
		||||
                + "\n - ".join(
 | 
			
		||||
                    f"{team.mention} ({self.order_for(team)})" for team in self.teams
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            return self.NEXT
 | 
			
		||||
        else:
 | 
			
		||||
            # Find dice that are the same
 | 
			
		||||
            count = defaultdict(list)
 | 
			
		||||
            for team in self.teams:
 | 
			
		||||
                count[self.order_for(team)].append(team)
 | 
			
		||||
 | 
			
		||||
            re_do = []
 | 
			
		||||
            for teams in count.values():
 | 
			
		||||
                if len(teams) > 1:
 | 
			
		||||
                    re_do.extend(teams)
 | 
			
		||||
 | 
			
		||||
            teams_str = ", ".join(team.role.mention for team in re_do)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Les equipes {teams_str} ont fait le même résultat "
 | 
			
		||||
                "et doivent relancer un dé. "
 | 
			
		||||
                "Le nouveau lancer effacera l'ancien."
 | 
			
		||||
            )
 | 
			
		||||
            for team in re_do:
 | 
			
		||||
                self.set_order_for(team, None)
 | 
			
		||||
            # We need to do this phase again.
 | 
			
		||||
            return self.__class__
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TiragePhase(Phase):
 | 
			
		||||
    """The phase where captains accept or refuse random problems."""
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def current_team(self):
 | 
			
		||||
        return self.teams[self.turn]
 | 
			
		||||
 | 
			
		||||
    def available(self, problem):
 | 
			
		||||
        return all(team.accepted_problems[self.round] != problem for team in self.teams)
 | 
			
		||||
 | 
			
		||||
    async def choose_problem(self, ctx: Context, author, problem):
 | 
			
		||||
        team = self.current_team
 | 
			
		||||
        if self.team_for(author) != team:
 | 
			
		||||
            raise UnwantedCommand(
 | 
			
		||||
                f"C'est à {team.mention} de choisir "
 | 
			
		||||
                f"un problème, merci d'attendre :)"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        assert (
 | 
			
		||||
            team.accepted_problems[self.round] is None
 | 
			
		||||
        ), "Choosing pb for a team that has a pb..."
 | 
			
		||||
 | 
			
		||||
        if team.drawn_problem:
 | 
			
		||||
            raise UnwantedCommand(
 | 
			
		||||
                "Vous avez déjà tiré un problème, merci de l'accepter (`!yes`) "
 | 
			
		||||
                "ou de le refuser (`!no)`."
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        await ctx.send(f"{team.mention} a tiré **{problem}** !")
 | 
			
		||||
        if not self.available(problem):
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Malheureusement, **{problem}** à déjà été choisi, "
 | 
			
		||||
                f"vous pouvez tirer un nouveau problème."
 | 
			
		||||
            )
 | 
			
		||||
        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
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Vous avez déjà refusé **{problem}**, "
 | 
			
		||||
                f"vous pouvez le refuser à nouveau (`!refuse`) et "
 | 
			
		||||
                f"tirer immédiatement un nouveau problème "
 | 
			
		||||
                f"ou changer d'avis et l'accepter (`!accept`)."
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            team.drawn_problem = problem
 | 
			
		||||
            if len(team.rejected[self.round]) >= MAX_REFUSE:
 | 
			
		||||
                await ctx.send(
 | 
			
		||||
                    f"Vous pouvez accepter ou refuser **{problem}** "
 | 
			
		||||
                    f"mais si vous choisissez de le refuser, il y "
 | 
			
		||||
                    f"aura une pénalité de 0.5 sur le multiplicateur du "
 | 
			
		||||
                    f"défenseur."
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                await ctx.send(
 | 
			
		||||
                    f"Vous pouvez accepter (`!oui`) ou refuser (`!non`) **{problem}**. "
 | 
			
		||||
                    f"Il reste {MAX_REFUSE - len(team.rejected[self.round])} refus sans pénalité "
 | 
			
		||||
                    f"pour {team.mention}."
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
    async def accept(self, ctx: Context, author, yes):
 | 
			
		||||
        team = self.current_team
 | 
			
		||||
 | 
			
		||||
        if self.team_for(author) != team:
 | 
			
		||||
            raise UnwantedCommand(
 | 
			
		||||
                f"c'est à {team.mention} "
 | 
			
		||||
                f"de choisir un problème, merci d'attendre :)"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        assert (
 | 
			
		||||
            team.accepted_problems[self.round] is None
 | 
			
		||||
        ), "Choosing pb for a team that has a pb..."
 | 
			
		||||
 | 
			
		||||
        if not team.drawn_problem:
 | 
			
		||||
            if yes:
 | 
			
		||||
                raise UnwantedCommand(
 | 
			
		||||
                    "Tu es bien optimiste pour vouloir accepter un problème "
 | 
			
		||||
                    "avant de l'avoir tiré !"
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                raise UnwantedCommand(
 | 
			
		||||
                    "Halte là ! Ce serait bien de tirer un problème d'abord... "
 | 
			
		||||
                    "et peut-être qu'il te plaira :) "
 | 
			
		||||
                )
 | 
			
		||||
        else:
 | 
			
		||||
            if yes:
 | 
			
		||||
                team.accepted_problems[self.round] = team.drawn_problem
 | 
			
		||||
                await ctx.send(
 | 
			
		||||
                    f"L'équipe {team.mention} a accepté "
 | 
			
		||||
                    f"**{team.accepted_problems[self.round]}** ! Les autres équipes "
 | 
			
		||||
                    f"ne peuvent plus l'accepter."
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                msg = f"{team.mention} a refusé **{team.drawn_problem}** "
 | 
			
		||||
                if team.drawn_problem in team.rejected[self.round]:
 | 
			
		||||
                    msg += "sans pénalité."
 | 
			
		||||
                else:
 | 
			
		||||
                    msg += "!"
 | 
			
		||||
                    team.rejected[self.round].add(team.drawn_problem)
 | 
			
		||||
                await ctx.send(msg)
 | 
			
		||||
 | 
			
		||||
            team.drawn_problem = None
 | 
			
		||||
 | 
			
		||||
            # Next turn
 | 
			
		||||
            if self.finished():
 | 
			
		||||
                self.turn = None
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            # Find next team that needs to draw.
 | 
			
		||||
            i = (self.turn + 1) % len(self.teams)
 | 
			
		||||
            while self.teams[i].accepted_problems[self.round]:
 | 
			
		||||
                i = (i + 1) % len(self.teams)
 | 
			
		||||
            self.turn = i
 | 
			
		||||
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"C'est au tour de {self.current_team.mention} de choisir un problème."
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def finished(self) -> bool:
 | 
			
		||||
        return all(team.accepted_problems[self.round] for team in self.teams)
 | 
			
		||||
 | 
			
		||||
    async def start(self, ctx: Context):
 | 
			
		||||
        # First sort teams according to the tirage_order
 | 
			
		||||
        self.teams.sort(key=lambda team: team.tirage_order[self.round])
 | 
			
		||||
 | 
			
		||||
        if self.round == 0:
 | 
			
		||||
            await asyncio.sleep(0.5)
 | 
			
		||||
            await ctx.send("Passons au tirage des problèmes !")
 | 
			
		||||
            await asyncio.sleep(0.5)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Les {self.captain_mention(ctx)}s vont tirer des problèmes au "
 | 
			
		||||
                f"hasard, avec `!random-problem` ou `!rp` pour ceux qui aiment "
 | 
			
		||||
                f"les abbréviations."
 | 
			
		||||
            )
 | 
			
		||||
            await asyncio.sleep(0.5)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                "Ils pouront ensuite accepter ou refuser les problèmes avec "
 | 
			
		||||
                "`!accept` ou `!refuse`."
 | 
			
		||||
            )
 | 
			
		||||
            await asyncio.sleep(0.5)
 | 
			
		||||
            await ctx.send(
 | 
			
		||||
                f"Chaque équipe peut refuser jusqu'a {MAX_REFUSE} "
 | 
			
		||||
                f"problèmes sans pénalité (voir §13 du règlement). "
 | 
			
		||||
                f"Un problème déjà rejeté ne compte pas deux fois."
 | 
			
		||||
            )
 | 
			
		||||
            await ctx.send("Bonne chance à tous ! C'est parti...")
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            # Second round
 | 
			
		||||
            await asyncio.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."
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        await asyncio.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 None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PassageOrderPhase(OrderPhase):
 | 
			
		||||
    """The phase to determine the chicken's order."""
 | 
			
		||||
 | 
			
		||||
    NEXT = TiragePhase
 | 
			
		||||
 | 
			
		||||
    def __init__(self, tirage, round=0):
 | 
			
		||||
        super().__init__(tirage, round, "de passage", "passage_order", True)
 | 
			
		||||
 | 
			
		||||
    async def start(self, ctx):
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            "Nous allons maintenant tirer l'ordre de passage durant le tour. "
 | 
			
		||||
            "L'ordre du tour sera dans l'ordre décroissant des lancers, "
 | 
			
		||||
            "c'est-à-dire que l'équipe qui tire le plus grand nombre "
 | 
			
		||||
            "présentera en premier."
 | 
			
		||||
        )
 | 
			
		||||
        await asyncio.sleep(0.5)
 | 
			
		||||
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            f"Les {self.captain_mention(ctx)}s, vous pouvez lancer "
 | 
			
		||||
            f"à nouveau un dé 100 (`!dice 100`)"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TirageOrderPhase(OrderPhase):
 | 
			
		||||
    """Phase to determine the tirage's order."""
 | 
			
		||||
 | 
			
		||||
    NEXT = PassageOrderPhase
 | 
			
		||||
 | 
			
		||||
    def __init__(self, tirage, round=0):
 | 
			
		||||
        super().__init__(tirage, round, "des tirages", "tirage_order", False)
 | 
			
		||||
 | 
			
		||||
    async def start(self, ctx):
 | 
			
		||||
 | 
			
		||||
        await asyncio.sleep(
 | 
			
		||||
            0.5
 | 
			
		||||
        )  # The bot is more human if it doesn't type at the speed of light
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            "Nous allons d'abord tirer au sort l'ordre de tirage des problèmes "
 | 
			
		||||
            f"pour le {ROUND_NAMES[self.round]}, "
 | 
			
		||||
            "puis l'ordre de passage lors de ce tour."
 | 
			
		||||
        )
 | 
			
		||||
        await asyncio.sleep(0.5)
 | 
			
		||||
        await ctx.send(
 | 
			
		||||
            f"Les {self.captain_mention(ctx)}s, vous pouvez désormais lancer un dé 100 "
 | 
			
		||||
            "comme ceci `!dice 100`. "
 | 
			
		||||
            "L'ordre des tirages suivants sera l'ordre croissant des lancers. "
 | 
			
		||||
        )
 | 
			
		||||
from src.errors import TfjmError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TirageCog(Cog, name="Tirages"):
 | 
			
		||||
@@ -610,9 +105,10 @@ class TirageCog(Cog, name="Tirages"):
 | 
			
		||||
        """
 | 
			
		||||
        Commence un tirage avec 3 ou 4 équipes.
 | 
			
		||||
 | 
			
		||||
        Cette commande attend trois trigrammes d'équipes, par ex:
 | 
			
		||||
        Cette commande attend des trigrames d'équipes.
 | 
			
		||||
 | 
			
		||||
            !draw start AAA BBB CCC
 | 
			
		||||
        Exemple:
 | 
			
		||||
            `!draw start AAA BBB CCC`
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        channel: discord.TextChannel = ctx.channel
 | 
			
		||||
@@ -696,11 +192,11 @@ class TirageCog(Cog, name="Tirages"):
 | 
			
		||||
    @draw_group.command(name="show")
 | 
			
		||||
    async def show_cmd(self, ctx: Context, tirage_id: str):
 | 
			
		||||
        """
 | 
			
		||||
        Affiche le résumé d'un tirage
 | 
			
		||||
        Affiche le résumé d'un tirage.
 | 
			
		||||
 | 
			
		||||
        Les ID de tirages valides sont visibles avec
 | 
			
		||||
        `!draw show all` et les details avec `!draw show 42`
 | 
			
		||||
         (si l'ID qui vous intéresse est 42).
 | 
			
		||||
        Exemples:
 | 
			
		||||
            `!draw show all` - Liste les ID possible
 | 
			
		||||
            `!draw show 42` - Affiche le tirage n°42
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        if not TIRAGES_FILE.exists():
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user