Implémentation des défis

This commit is contained in:
Emmy D'Anello 2025-03-13 23:56:25 +01:00
parent 67650bcd60
commit d25a233590
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
2 changed files with 228 additions and 92 deletions

58
bot.py
View File

@ -56,7 +56,7 @@ Couleur = Literal["rouge", "vert"]
intents = discord.Intents.default()
intents.message_content = True
PREFIX = '$'
PREFIX = '!'
bot = commands.Bot(command_prefix=PREFIX, intents=intents)
DATA_FILE = Path(__file__).parent / "data.json"
@ -69,9 +69,9 @@ else:
'cantons': {code_canton: {'capture': None, 'verrouille': False} for code_canton in CANTONS.keys()},
'defis': {
'mains': {equipe: [] for equipe in EQUIPES},
'powerups': {equipe: 0 for equipe in EQUIPES},
'bonus': {equipe: 0 for equipe in EQUIPES},
'tires_capture': [],
'tires_competition': [],
'tires_vol': [],
}
}
with DATA_FILE.open('w') as data_file:
@ -184,12 +184,12 @@ async def equipe(ctx: commands.Context, couleur: Couleur):
@bot.command()
async def defis(ctx: commands.Context, *, type_defi: Literal['capture', 'competition'] = "capture"):
async def defis(ctx: commands.Context, *, type_defi: Literal['capture', 'vol'] = "capture"):
await ctx.send(f"Liste des défis de {type_defi} :\n" + "\n".join(f"* {defi['id']} : {defi['nom']}" for defi in DEFIS[type_defi]))
@bot.command()
async def description(ctx: commands.Context, type_defi: Literal['capture', 'competition'] = "capture", id_defi: int | None = None):
async def description(ctx: commands.Context, type_defi: Literal['capture', 'vol'] = "capture", id_defi: int | None = None):
defis = DEFIS[type_defi]
embeds = []
if id_defi is not None:
@ -198,7 +198,7 @@ async def description(ctx: commands.Context, type_defi: Literal['capture', 'comp
defi = next(defi for defi in defis if defi['id'] == id_defi)
except StopIteration:
raise commands.BadArgument(f"Le défi de {type_defi}{id_defi} n'existe pas.")
embed.add_field(name=f"{defi['nom']} {defi['powerups'] * ":star:"}", value=defi['description'], inline=False)
embed.add_field(name=f"{defi['nom']} {defi['bonus'] * ":star:"}", value=defi['description'], inline=False)
embeds.append(embed)
else:
for page in range((len(defis) - 1) // 25 + 1):
@ -206,7 +206,7 @@ async def description(ctx: commands.Context, type_defi: Literal['capture', 'comp
embed = discord.Embed(title=f"Description des défis", colour=discord.Colour.gold())
embed.set_footer(text=f"Page {page + 1}/{(len(defis) - 1) // 25 + 1}")
for defi in defis_page:
embed.add_field(name=f"{defi['nom']} {defi['powerups'] * ":star:"} (n°{defi['id']})", value=defi['description'], inline=False)
embed.add_field(name=f"{defi['nom']} {defi['bonus'] * ":star:"} (n°{defi['id']})", value=defi['description'], inline=False)
embeds.append(embed)
await ctx.send(embeds=embeds)
@ -232,18 +232,18 @@ async def tirage(ctx: commands.Context, nb_defis: int = 5):
@bot.command()
async def competition(ctx: commands.Context):
defi_competition = random.choice([defi for defi in DEFIS['competition'] if defi['id'] not in data['defis']['tires_competition']])
data['defis']['tires_competition'].append(defi_competition['id'])
embed = discord.Embed(title=defi_competition['nom'], description=defi_competition['description'])
embed.set_footer(text=f"Défi de compétition n°{defi_competition['id']}")
async def vol(ctx: commands.Context):
defi_vol = random.choice([defi for defi in DEFIS['vol'] if defi['id'] not in data['defis']['tires_vol']])
data['defis']['tires_vol'].append(defi_vol['id'])
embed = discord.Embed(title=defi_vol['nom'], description=defi_vol['description'])
embed.set_footer(text=f"Défi de compétition n°{defi_vol['id']}")
with DATA_FILE.open('w') as data_file:
json.dump(data, data_file, indent=2)
await ctx.send("@everyone Un canton est attaqué ! L'équipe vainqueure de ce défi conservera son contrôle jusqu'à la fin du jeu :", embed=embed)
@bot.command()
async def remiser(ctx: commands.Context, type_defi: Literal['capture', 'competition'] = "capture", id_defi: int | None = None):
async def remiser(ctx: commands.Context, type_defi: Literal['capture', 'vol'] = "capture", id_defi: int | None = None):
defis = DEFIS[type_defi]
try:
defi = next(defi for defi in defis if defi['id'] == id_defi)
@ -284,19 +284,19 @@ async def afficher_main(ctx: commands.Context, mode: Literal['public', 'prive']
raise commands.BadArgument(f"Vous n'appartez à aucune équipe. Merci de faire `{PREFIX}equipe [{"|".join(EQUIPES)}]`.")
main = data['defis']['mains'][couleur]
nb_powerups = data['defis']['powerups'][couleur]
nb_bonus = data['defis']['bonus'][couleur]
embeds = []
colour = discord.Color.red() if couleur == "rouge" else discord.Color.green()
for id_defi in main:
defi = next(defi for defi in DEFIS['capture'] if defi['id'] == id_defi)
embed = discord.Embed(title=f"{defi['nom']} {defi['powerups'] * ":star:"}", description=defi['description'], colour=colour)
embed = discord.Embed(title=f"{defi['nom']} {defi['bonus'] * ":star:"}", description=defi['description'], colour=colour)
embed.set_footer(text=f"Défi n°{defi['id']}")
embeds.append(embed)
if mode == "public":
await ctx.send(f"Défis de l'équipe **{couleur}** :", embeds=embeds)
else:
channel_dm = await bot.create_dm(namedtuple('User', 'id')(author_id))
await channel_dm.send(f"Vous disposez de **{nb_powerups} powerup{"s" if nb_powerups >= 2 else ""} {nb_powerups * ":star:"}**.\nVos défis en main :", embeds=embeds, view=MainView(ctx, author_id, main))
await channel_dm.send(f"Vous disposez de **{nb_bonus} bonus {nb_bonus * ":star:"}**.\nVos défis en main :", embeds=embeds, view=MainView(ctx, author_id, main))
@bot.command()
@ -319,15 +319,15 @@ async def terminer(ctx: commands.Context, id_defi: int, author_id: int | None =
main.remove(id_defi)
main.append(nouveau_defi['id'])
data['defis']['tires_capture'].append(nouveau_defi['id'])
data['defis']['powerups'][equipe] += defi['powerups']
data['defis']['bonus'][equipe] += defi['bonus']
with DATA_FILE.open('w') as data_file:
json.dump(data, data_file, indent=2)
channel = channel or ctx
await channel.send(f"Défi n°{id_defi} **{defi['nom']}** terminé ! Il est retiré de votre main.")
await channel.send(f"Votre équipe gagne **{defi['powerups']} powerup{"s" if defi['powerups'] >= 2 else ""}**. Vous en possédez désormais {data['defis']['powerups'][equipe]}.")
await channel.send(f"Votre équipe gagne **{defi['bonus']} bonus**. Vous en possédez désormais {data['defis']['bonus'][equipe]}.")
colour = discord.Color.red() if equipe == "rouge" else discord.Color.green()
embed = discord.Embed(title=f"{nouveau_defi['nom']} {defi['powerups'] * ":star:"}", description=nouveau_defi['description'], colour=colour)
embed = discord.Embed(title=f"{nouveau_defi['nom']} {defi['bonus'] * ":star:"}", description=nouveau_defi['description'], colour=colour)
embed.set_footer(text=f"Défi n°{nouveau_defi['id']}")
await channel.send("**Votre nouveau défi en main :**", embed=embed)
for member_id in data['equipes'][equipe]:
@ -335,7 +335,7 @@ async def terminer(ctx: commands.Context, id_defi: int, author_id: int | None =
@bot.command()
async def powerup(ctx: commands.Context, equipe: Couleur | None = None, nouvelle_valeur: int | None = None):
async def bonus(ctx: commands.Context, equipe: Couleur | None = None, nouvelle_valeur: int | None = None):
if equipe is None:
author_id = ctx.author.id
for equipe, membres_equipe in data['equipes'].items():
@ -344,16 +344,16 @@ async def powerup(ctx: commands.Context, equipe: Couleur | None = None, nouvelle
else:
raise commands.BadArgument(f"Vous n'appartez à aucune équipe. Merci de faire `{PREFIX}equipe [{"|".join(EQUIPES)}]`.")
nb_powerups = data['defis']['powerups'][equipe]
nb_bonus = data['defis']['bonus'][equipe]
if nouvelle_valeur is None:
if nb_powerups >= 1:
data['defis']['powerups'][equipe] -= 1
await ctx.send(f"L'équipe **{equipe}** vient d'utiliser un powerup !")
if nb_bonus >= 1:
data['defis']['bonus'][equipe] -= 1
await ctx.send(f"L'équipe **{equipe}** vient d'utiliser un bonus !")
else:
await ctx.reply(f"Vous n'avez plus de powerup.", ephemeral=True)
await ctx.reply(f"Vous n'avez plus de bonus.", ephemeral=True)
else:
data['defis']['powerups'][equipe] = nouvelle_valeur
await ctx.send(f"L'équipe **{equipe}** a désormais **{nouvelle_valeur} powerup{"s" if nouvelle_valeur >= 2 else ""}**, contre {nb_powerups} auparavant.")
data['defis']['bonus'][equipe] = nouvelle_valeur
await ctx.send(f"L'équipe **{equipe}** a désormais **{nouvelle_valeur} bonus**, contre {nb_bonus} auparavant.")
with DATA_FILE.open('w') as data_file:
json.dump(data, data_file, indent=2)
@ -486,11 +486,11 @@ async def save(ctx: commands.Context):
@defis.error
@description.error
@tirage.error
@competition.error
@vol.error
@remiser.error
@afficher_main.error
@terminer.error
@powerup.error
@bonus.error
@echange.error
@melanger.error
@de.error

View File

@ -1,117 +1,253 @@
{
"capture": [
{
"id": 1,
"nom": "Défi 1",
"description": "Défi 1",
"powerups": 1
},
{
"id": 2,
"nom": "Défi 2",
"description": "Défi 2",
"powerups": 1
"nom": "Artisan⋅e chocolatièr⋅e",
"description": "À partir de chocolat local, construis un pont.\n\nLa structure doit rester en place pendant au moins 15 secondes sans s'effondrer.",
"bonus": 0
},
{
"id": 3,
"nom": "Défi 3",
"description": "Défi 3",
"powerups": 1
"nom": "Manger (chez) les riches",
"description": "Mange une spécialité locale du canton.",
"bonus": 0
},
{
"id": 4,
"nom": "Défi 4",
"description": "Défi 4",
"powerups": 0
"nom": "Non-alignement",
"description": "Devant une institution locale, adresse une liste de 20 revendications géopolitiquement neutres.",
"bonus": 0
},
{
"id": 5,
"nom": "Défi 5",
"description": "Défi 5",
"powerups": 0
"nom": "Low five",
"description": "Fais un low-five au point le plus bas d'altitude du canton.",
"bonus": 0
},
{
"id": 6,
"nom": "Défi 6",
"description": "Défi 6",
"powerups": 0
"nom": "Artiste GPS",
"description": "Trace la croix du drapeau suisse à l'aide de ton tracé GPS.\n\nLa croix doit être ressemblante sans être nécessairement parfaite.",
"bonus": 0
},
{
"id": 7,
"nom": "Défi 7",
"description": "Défi 7",
"powerups": 2
"nom": "Prendre de la hauteur",
"description": "Rends-toi sur le toit d'un bâtiment de manière légale.",
"bonus": 1
},
{
"id": 8,
"nom": "Défi 8",
"description": "Défi 8",
"powerups": 1
"nom": "Marcher sur l'eau",
"description": "Traverse un cours d'eau d'une rive à l'autre.\n\nLa distance traversée doit être d'au moins un mètre.\nIl n'est pas possible d'emprunter une structure existante pour le traverser, en particulier d'utiliser un pont.",
"bonus": 1
},
{
"id": 9,
"nom": "Défi 9",
"description": "Défi 9",
"powerups": 0
},
{
"id": 10,
"nom": "Défi 10",
"description": "Défi 10",
"powerups": 0
},
{
"id": 11,
"nom": "Défi 11",
"description": "Défi 11",
"powerups": 0
"nom": "Randonnée",
"description": "Construis une tour de cailloux à plus de 800 mètres d'altitude.",
"bonus": 1
},
{
"id": 12,
"nom": "Défi 12",
"description": "Défi 12",
"powerups": 0
"nom": "Cave à faux-mage",
"description": "Demande du fromage vegan dans une cave à fromage ou dans une fromagerie productrice.\n\nIl suffit de demander, sans attendre de résultat.",
"bonus": 1
},
{
"id": 13,
"nom": "Défi 13",
"description": "Défi 13",
"powerups": 1
"nom": "Panorama",
"description": "Photographie taon partenaire de l'autre côté d'un lac.\n\nLe lac doit être reconnu comme tel sur les cartes, et l'étendue d'eau doit être pleinement visible sur l'image.",
"bonus": 1
},
{
"id": 14,
"nom": "Défi 14",
"description": "Défi 14",
"powerups": 0
"nom": "Rageux⋅se",
"description": "Auprès de la meilleure attraction touristique (musée, monument, paysage,…) du canton, fais une critique négative en 5 points en vidéo.",
"bonus": 0
},
{
"id": 15,
"nom": "Défi 15",
"description": "Défi 15",
"powerups": 0
"nom": "Non-binaire",
"description": "Dans un lieu que l'on peut qualifier d'entre-deux (milieu d'un pont, mezzanine entre 2 étages,…), chante « The Code » de Nemo.",
"bonus": 0
},
{
"id": 16,
"nom": "Défi 16",
"description": "Défi 16",
"powerups": 2
"nom": "Démocratie",
"description": "Organiser une votation citoyenne sur un sujet au choix. La votation doit récolter au moins 10 votant·es.",
"bonus": 1
},
{
"id": 17,
"nom": "Inaction climatique suisse",
"description": "Nettoie 10 déchets jetés au sol d'un espace.",
"bonus": 0
},
{
"id": 18,
"nom": "Révolution bovine",
"description": "Parle à une vache dans un pré et tente de la syndicaliser en lui suggérant de se libérer de sa condition d'élevage.\n\nIl n'est pas nécessaire de se faire comprendre.",
"bonus": 1
},
{
"id": 19,
"nom": "Génie incivil",
"description": "Passe sous 3 ponts.",
"bonus": 0
},
{
"id": 20,
"nom": "Tourisme minimal",
"description": "Va faire du tourisme dans le village le moins peuplé du canton disposant d'une gare à moins de 1 km.",
"bonus": 1
},
{
"id": 21,
"nom": "Jeu de mots au menu",
"description": "Trouve du riz cantonais.",
"bonus": 0
},
{
"id": 22,
"nom": "Patois",
"description": "Demande un mot en romanche.",
"bonus": 1
},
{
"id": 23,
"nom": "Du Jura aux Alpes",
"description": "Transporte une pierre jurassienne dans les Alpes, ou inversement. En cas de succès, les cantons de départ et d'arrivée sont capturés, à moins que le canton de départ n'ait été capturé par l'équipe adverse entre temps, auquel cas c'est un échec.\n\nIl est possible de faire d'autres défis pendant le transport. Les points de départ et d'arrivée doivent être à au moins 500 mètres d'altitude pour le Jura et 700 mètres d'altitude pour les Alpes.",
"bonus": 2
},
{
"id": 24,
"nom": "Du Rhin au Rhône",
"description": "Touche le Rhône et le Rhin dans la même journée. En cas de succès, les cantons de départ et d'arrivée sont capturés, à moins que le canton d'origine n'ait été capturé avant l'accomplissement du défi, auquel cas c'est un échec.\n\nIl est possible de réaliser d'autres défis pendant l'accomplissement de celui-ci.",
"bonus": 2
},
{
"id": 25,
"nom": "Pas moche, juste différent",
"description": "Auprès du bâtiment le plus moche identifié selon ses propres goûts, rédige un poème qui en fait l'éloge et le traduire en français, allemand et italien.\n\nLe poème doit être rédigé manuscrit sur une feuille.",
"bonus": 0
},
{
"id": 26,
"nom": "Rançon",
"description": "Écris « Bataille4Suisse » avec des papiers découpés et ré-agencés, tel un message de rançon.",
"bonus": 0
},
{
"id": 27,
"nom": "Moyenne",
"description": "Rends-toi le plus proche possible du barycentre (point central) d'une commune qui a entre 4100 et 4300 habitant⋅es. De ce point de vue, choisir jusqu'à 10 sommets visibles et non cachés par des bâtiments, et trouver quel est le sommet d'altitude moyenne.",
"bonus": 1
},
{
"id": 28,
"nom": "Helvetica",
"description": "Trouve un texte écrit en police d'écriture Helvetica.\n\nIl peut être imprimé ou sur écran d'affichage, et doit être trouvé et ne peut pas être créé ou mis en cette police par les joueureuses ni à leur demande.",
"bonus": 1
},
{
"id": 29,
"nom": "Terre en vue",
"description": "Positionne-toi à au moins 10 km de tout lac.",
"bonus": 1
},
{
"id": 30,
"nom": "Vue de haut",
"description": "Rends-toi à la gare ferroviaire accessible la plus haute du canton.",
"bonus": 1
},
{
"id": 31,
"nom": "Pi Day",
"description": "Rends-toi dans une commune dont l'un de ses noms officiels contient la lettre P, et dessiner un cercle le plus ressemblant possible.",
"bonus": 1
},
{
"id": 32,
"nom": "Quelle heure est-il ?",
"description": "Trouve une montre ou une horloge à cadran qui indique une heure entre 10h10 et 10h15.\n\nIl n'est pas autorisé de régler l'heure ni de demander à la régler, et il ne peut pas s'agir d'une montre qu'on possède ou portée par un·e passant·e.",
"bonus": 0
},
{
"id": 33,
"nom": "Vers l'infini et au-delà",
"description": "Photographie un sommet à au moins 2000 mètres d'altitude.\n\nUne photo où le sommet est caché par les nuages ou par la nuit est acceptée.",
"bonus": 1
},
{
"id": 34,
"nom": "Traversée",
"description": "Traverse un canton B depuis un canton A vers un canton C.\n\nLe canton B est capturé.",
"bonus": 1
},
{
"id": 35,
"nom": "Promotion culturelle",
"description": "Prends un flyer dans un lieu culturel, et en fais-en la promotion dans un lieu culturel similaire d'un autre canton.\n\nEn cas de succès du défi, les deux cantons sont capturés.",
"bonus": 1
}
],
"competition": [
{
"id": 1,
"nom": "Compétition 1",
"description": "Compétition 1"
"nom": "Au sommet",
"description": "Photographie le plus de sommets en 15 minutes."
},
{
"id": 2,
"nom": "Compétition 2",
"description": "Compétition 2"
"nom": "Chocolat :chocolate_bar:",
"description": "Photographie le plus de chocolats différents (marque/chocolatièr⋅e et nom) en 15 minutes."
},
{
"id": 3,
"nom": "Compétition 3",
"description": "Compétition 3"
"nom": "Fromage :cheese:",
"description": "Photographie le plus de fromages différents en 15 minutes."
},
{
"id": 4,
"nom": "Vélo :bike:",
"description": "Photographie le plus de vélos différents en 15 minutes."
},
{
"id": 5,
"nom": "Mot aléatoire",
"description": "Fais dire à un⋅e passant⋅e le mot de ton choix le plus rapidement possible, sans donner le mot ni sa définition exacte."
},
{
"id": 6,
"nom": "Au plus profond",
"description": "Rends-toi à la plus basse altitude possible en 15 minutes."
},
{
"id": 7,
"nom": "Luxe :money_with_wings:",
"description": "Trouve un objet vendu le plus cher possible en 15 minutes."
},
{
"id": 8,
"nom": "Bonne affaire :gift:",
"description": "Trouve un objet vendu le moins cher possible en 15 minutes."
},
{
"id": 9,
"nom": "Banques :bank:",
"description": "Photographie le plus de banques en 15 minutes."
},
{
"id": 10,
"nom": "Animaux :bank:",
"description": "Trouve le plus d'espèces animales en 15 minutes."
},
{
"id": 11,
"nom": "Fuis ! :person_running:",
"description": "Quitte ton canton le plus rapidement possible."
}
]
}