working new tirage

This commit is contained in:
ddorn 2020-05-06 19:04:36 +02:00
parent dfc8176084
commit a95c20c5b5
2 changed files with 61 additions and 191 deletions

View File

@ -3,12 +3,14 @@ import random
import sys
import traceback
from functools import wraps
from pathlib import Path
from pprint import pprint
from io import StringIO
from typing import Type, Union, Dict, List
import discord
import yaml
from src.constants import *
@ -70,7 +72,9 @@ class Team:
# """
class Poule:
class Poule(yaml.YAMLObject):
yaml_tag = "Poule"
def __init__(self, poule, rnd):
self.poule = poule
self.rnd = rnd
@ -79,15 +83,17 @@ class Poule:
return f"{self.poule}{self.rnd + 1}"
class BaseTirage:
class BaseTirage(yaml.YAMLObject):
yaml_tag = "Tirage"
def __init__(self, *teams: discord.Role, fmt=(3, 3)):
assert sum(fmt) == len(teams)
assert sum(fmt) == len(teams), "Different number of teams and format"
self.teams: Dict[str, Team] = {t.name: Team(t) for t in teams}
self.format = fmt
self.queue = asyncio.Queue()
self.poules: Dict[Poule, List[str]] = {}
"""A mapping between the poule name and the list of teams in this poule."""
"""A mapping between the poule and the list of teams in this poule."""
async def event(self, event: Event):
event.set()
@ -153,8 +159,9 @@ class BaseTirage:
while None in dices.values():
event = await self.next(int)
# TODO: avoid KeyError
if dices[event.team] is None:
if event.team not in dices:
await self.warn_wrong_team(None, event.team)
elif dices[event.team] is None:
dices[event.team] = event.value
await self.info_dice(event.team, event.value)
else:

View File

@ -17,20 +17,16 @@ from discord.ext import commands
from discord.ext.commands import group, Cog, Context
from discord.utils import get
from src.base_tirage import BaseTirage, Event
from src.base_tirage import BaseTirage, Event, Poule
from src.constants import *
from src.core import CustomBot
from src.errors import TfjmError, UnwantedCommand
__all__ = ["Tirage", "TirageCog"]
__all__ = ["TirageCog"]
from src.utils import send_and_bin, french_join
def in_passage_order(teams, round=0):
return sorted(teams, key=lambda team: team.passage_order[round] or 0, reverse=True)
Record = namedtuple("Record", ["name", "pb", "penalite"])
@ -79,6 +75,36 @@ class DiscordTirage(BaseTirage):
self.ctx = ctx
self.captain_mention = get(ctx.guild.roles, name=Role.CAPTAIN).mention
ts = self.load_all()
self.id = 1 + (max(ts) if ts else 0)
self.save()
@staticmethod
def load_all():
if not File.TIRAGES.exists():
return {}
with open(File.TIRAGES) as f:
tirages = yaml.load(f)
return tirages
def save(self):
ts = self.load_all()
ctx = self.ctx
queue = self.queue
self.ctx = None
self.queue = None
ts[self.id] = self
File.TIRAGES.touch()
with open(File.TIRAGES, "w") as f:
yaml.dump(ts, f)
self.ctx = ctx
self.queue = queue
def team_for(self, author):
for team in self.teams:
if get(author.roles, name=team):
@ -256,8 +282,7 @@ class DiscordTirage(BaseTirage):
| {1.name} | Opp | Déf | Rap |
+-----+---------+---------+---------+
| {2.name} | Rap | Opp | Déf |
+-----+---------+---------+---------+
```"""
+-----+---------+---------+---------+```"""
else:
table = """```
+-----+---------+---------+---------+---------+
@ -271,8 +296,7 @@ class DiscordTirage(BaseTirage):
| {2.name} | Rap | Opp | Déf | |
+-----+---------+---------+---------+---------+
| {3.name} | | Rap | Opp | Déf |
+-----+---------+---------+---------+---------+
```"""
+-----+---------+---------+---------+---------+```"""
embed = discord.Embed(
title=f"Résumé du tirage entre {french_join([t.name for t in teams])}",
@ -293,11 +317,13 @@ class DiscordTirage(BaseTirage):
)
embed.set_footer(
text="Ce tirage peut être affiché à tout moment avec `!draw show XXX`"
text=f"Ce tirage peut être affiché à tout moment avec `!draw show {self.id}`"
)
await self.ctx.send(embed=embed)
self.save()
@safe
@send_all
async def info_start(self):
@ -327,7 +353,6 @@ class DiscordTirage(BaseTirage):
"Vous pouvez désormais trouver les problèmes que vous devrez opposer ou rapporter "
"sur la page de votre équipe."
)
# TODO: Save it
# TODO: make them available with the api
@safe
@ -380,170 +405,10 @@ class DiscordTirage(BaseTirage):
msg += "!"
await self.ctx.send(msg)
class TiragePhase:
"""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):
team = self.current_team
if self.team_for(author) != team:
raise UnwantedCommand(
f"C'est à {team.name} 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)`."
)
# Choose an *available* problem
problems = [
p for p in PROBLEMS if self.available(p) and not p in team.accepted_problems
]
problem = random.choice(problems)
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:
pass
else:
if yes:
team.accepted_problems[self.round] = team.drawn_problem
else:
team.rejected[self.round].add(team.drawn_problem)
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
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
async def show(self, ctx):
self.ctx = ctx
for poule in self.poules:
await self.annonce_poule(poule)
class TirageCog(Cog, name="Tirages"):
@ -715,6 +580,7 @@ class TirageCog(Cog, name="Tirages"):
# Here all data should be valid
self.tirages[channel_id] = DiscordTirage(ctx, *teams, fmt=fmt)
await self.tirages[channel_id].run()
if self.tirages[channel_id]:
@ -762,13 +628,12 @@ class TirageCog(Cog, name="Tirages"):
# await tirage.update_phase(ctx)
def get_tirages(self) -> Dict[int, BaseTirage]:
if not File.TIRAGES.exists():
return {}
return DiscordTirage.load_all()
with open(File.TIRAGES) as f:
tirages = yaml.load(f)
return tirages
def save_tirages(self, tirages):
File.TIRAGES.touch()
with open(File.TIRAGES, "w") as f:
yaml.dump(tirages, f)
@draw_group.command(name="show")
async def show_cmd(self, ctx: Context, tirage_id: str = "all"):
@ -780,7 +645,6 @@ class TirageCog(Cog, name="Tirages"):
`!draw show 42` - Affiche le tirage n°42
"""
return
tirages = self.get_tirages()
if not tirages:
@ -793,8 +657,7 @@ class TirageCog(Cog, name="Tirages"):
"Vous pouvez en consulter un en particulier avec `!draw show ID`."
)
msg = "\n".join(
f"`{key}`: {', '.join(team.name for team in tirage.teams)}"
for key, tirage in tirages.items()
f"`{key}`: {', '.join(tirage.teams)}" for key, tirage in tirages.items()
)
await ctx.send(msg)
else: