Créer votre Bot
Créer votre propre bot, c’est la manière la plus directe de prouver que vous êtes le meilleur à ce jeu. Pas de trash talk. Pas de gaslighting pour faire croire à l’adversaire qu’il s’est trompé dans le compte des atouts. Rien que des résultats, froids et vérifiables.
Des templates prêts à l’emploi sont disponibles pour Python, TypeScript, C#, Go et Java. Si vous savez écrire une fonction dans l’un de ces langages, vous pouvez créer un bot. Inutile de savoir comment marche HTTP, comment parser du JSON ou comment un moteur de jeu communique. Prenez un template, dupliquez le dossier, modifiez un fichier. C’est tout.
Prérequis
Section intitulée « Prérequis »- .NET 10, nécessaire pour lancer
giretra-manage, l’outil en ligne de commande qui sert à tester votre bot - Le runtime de votre langage : Node 20+ pour TypeScript, Python 3 pour Python, Go 1.23+ pour Go, Java 17+ pour Java
C’est tout. Pas de base de données, pas de file de messages, pas d’orchestration de conteneurs.
Étape 1 : Cloner le dépôt
Section intitulée « Étape 1 : Cloner le dépôt »git clone https://github.com/giretra/giretra.gitcd giretraTout est dans ce repo : les templates, les outils de test, et le dossier où votre bot prendra ses quartiers.
Étape 2 : Copier un template
Section intitulée « Étape 2 : Copier un template »Copiez l’un des dossiers template dans external-bots/ et renommez-le :
cp -r external-bots/random-python-bot external-bots/my-botcp -r external-bots/random-node-bot external-bots/my-botcp -r external-bots/random-dotnet-bot external-bots/my-botcp -r external-bots/random-go-bot external-bots/my-botcp -r external-bots/random-java-bot external-bots/my-botChaque dossier template contient :
| Fichier | Rôle | À modifier ? |
|---|---|---|
bot.meta.json | Identité du bot et configuration de lancement | Oui |
Fichier bot (bot.py, bot.ts, Bot.cs, bot.go, Bot.java) | Votre logique de jeu | Oui |
| Fichier serveur | Boilerplate HTTP | Non |
| Définitions de types | Structures de données pour les requêtes et réponses | Non |
Étape 3 : Configurer bot.meta.json
Section intitulée « Étape 3 : Configurer bot.meta.json »Ouvrez bot.meta.json et renseignez les champs d’identité :
{ "name": "my-bot", "displayName": "My Awesome Bot", "pun": "I never bluff... except when I do", "author": "Votre Nom", "authorGithub": "votre-pseudo-github"}Référence des champs :
| Champ | Description |
|---|---|
name | Identifiant interne. Doit être unique, en minuscules, sans espaces. Sert aussi de nom de dossier. |
displayName | Le nom affiché dans les classements et résultats de matchs. |
pun | Une petite phrase affichée à côté du nom de votre bot. Facultatif mais encouragé. |
author | Votre nom. |
authorGithub | Votre pseudo GitHub. |
init | Comment installer les dépendances ou compiler. Exécuté une seule fois avant le démarrage de votre bot. |
launch | Comment démarrer le serveur de votre bot. Le moteur fournit le port via la variable d’environnement PORT. |
Ne touchez pas à init et launch sauf si vous devez changer la façon dont votre bot se compile ou se lance. Les templates sont déjà bien configurés.
Étape 4 : Implémenter la logique de votre bot
Section intitulée « Étape 4 : Implémenter la logique de votre bot »Votre bot prend exactement 3 types de décisions :
- Choisir une position de coupe. Avant chaque donne, quelqu’un coupe le jeu. Choisissez un nombre entre 6 et 26.
- Choisir une action d’enchère. C’est la phase de négociation. Le moteur vous donne la liste des actions possibles (annoncer un mode, accepter, contrer, surcontrer). À vous de choisir.
- Choisir une carte à jouer. Le cœur de votre stratégie. Le moteur vous donne la liste des cartes jouables. Choisissez-en une.
Voici un bot complet dans chaque langage. C’est le seul fichier à modifier :
bot.py
import randomfrom bot_types import *
class Bot: def __init__(self, match_id: str): self.match_id = match_id
def choose_cut(self, ctx: ChooseCutContext) -> CutResult: position = random.randint(6, 26) return CutResult(position=position, from_top=True)
def choose_negotiation_action( self, ctx: ChooseNegotiationActionContext ) -> NegotiationActionChoice: # ctx.valid_actions contient tout ce que vous pouvez faire return ctx.valid_actions[0]
def choose_card(self, ctx: ChooseCardContext) -> Card: # ctx.valid_plays contient toutes les cartes légales jouables return random.choice(ctx.valid_plays)bot.ts
import type { ChooseCutContext, ChooseNegotiationActionContext, ChooseCardContext, CutResult, NegotiationActionChoice, Card} from './types';
export class Bot { constructor(public readonly matchId: string) {}
chooseCut(ctx: ChooseCutContext): CutResult { const position = Math.floor(Math.random() * 21) + 6; return { position, fromTop: true }; }
chooseNegotiationAction(ctx: ChooseNegotiationActionContext): NegotiationActionChoice { // ctx.validActions contient tout ce que vous pouvez faire return ctx.validActions[0]; }
chooseCard(ctx: ChooseCardContext): Card { // ctx.validPlays contient toutes les cartes légales jouables return ctx.validPlays[Math.floor(Math.random() * ctx.validPlays.length)]; }}Bot.cs
namespace RandomDotnetBot;
public class Bot{ private readonly Random Rng = new(); public string MatchId { get; }
public Bot(string matchId) => MatchId = matchId;
public CutResult ChooseCut(ChooseCutContext ctx) { return new CutResult { Position = Rng.Next(6, 27), FromTop = true }; }
public NegotiationActionChoice ChooseNegotiationAction( ChooseNegotiationActionContext ctx) { // ctx.ValidActions contient tout ce que vous pouvez faire return ctx.ValidActions[0]; }
public Card ChooseCard(ChooseCardContext ctx) { // ctx.ValidPlays contient toutes les cartes légales jouables return ctx.ValidPlays[Rng.Next(ctx.ValidPlays.Count)]; }}bot.go
package main
import "math/rand/v2"
type Bot struct { MatchID string}
func NewBot(matchID string) *Bot { return &Bot{MatchID: matchID}}
func (b *Bot) ChooseCut(ctx ChooseCutContext) CutResult { return CutResult{Position: rand.IntN(21) + 6, FromTop: true}}
func (b *Bot) ChooseNegotiationAction( ctx ChooseNegotiationActionContext,) NegotiationActionChoice { // ctx.ValidActions contient tout ce que vous pouvez faire return ctx.ValidActions[0]}
func (b *Bot) ChooseCard(ctx ChooseCardContext) Card { // ctx.ValidPlays contient toutes les cartes légales jouables return ctx.ValidPlays[rand.IntN(len(ctx.ValidPlays))]}Bot.java
package randomjavabot;
import java.util.concurrent.ThreadLocalRandom;import randomjavabot.BotTypes.*;
public class Bot { private final String matchId;
public Bot(String matchId) { this.matchId = matchId; }
public CutResult chooseCut(ChooseCutContext ctx) { int position = ThreadLocalRandom.current().nextInt(6, 27); return new CutResult(position, true); }
public NegotiationActionChoice chooseNegotiationAction( ChooseNegotiationActionContext ctx) { // ctx.validActions() contient tout ce que vous pouvez faire return ctx.validActions().get(0); }
public Card chooseCard(ChooseCardContext ctx) { // ctx.validPlays() contient toutes les cartes légales jouables var plays = ctx.validPlays(); return plays.get(ThreadLocalRandom.current().nextInt(plays.size())); }}C’est tout. Trois méthodes. Le moteur vous dit ce qui est autorisé via validActions et validPlays, donc impossible de faire un coup illégal par accident. Vous piochez dans le menu. Commencez simple, affinez ensuite.
Les contextes passés en paramètre contiennent aussi votre main, le pli en cours, les scores et l’historique des enchères — tout ce qu’il faut pour élaborer une vraie stratégie.
Étape 5 : Tester votre bot
Section intitulée « Étape 5 : Tester votre bot »Toutes les commandes ci-dessous supposent que vous êtes à la racine de votre clone Giretra. Utilisez ./giretra-manage.sh sur Linux/macOS ou giretra-manage.cmd sur Windows.
C’est la première commande à lancer. Elle fait jouer votre bot pendant 100 matchs contre Kialasoa et vérifie que tout tourne :
./giretra-manage.sh validate my-botVous obtenez un rapport complet avec :
- Violations de règles et nombre de crashs (objectif : zéro)
- Temps de réponse par type de décision (min, moy, P50, P95, P99, max)
- Couverture des modes de jeu (est-ce que votre bot a été testé sur les six modes)
- Tendance de performance (stabilité des temps de réponse au fil de l’exécution)
Vous pouvez affiner avec des options comme -n 500 pour plus de matchs, -o Razavavy pour un adversaire plus coriace, -d pour un test de déterminisme, ou -v pour le détail de chaque violation.
Benchmark
Section intitulée « Benchmark »Une fois la validation passée sans erreur, confrontez votre bot aux adversaires intégrés :
./giretra-manage.sh benchmark my-bot Kialasoa./giretra-manage.sh benchmark my-bot Razavavy./giretra-manage.sh benchmark my-bot Eva| Bot | Difficulté | Stratégie |
|---|---|---|
Kialasoa | Facile | Joue au hasard. Si vous n’arrivez pas à le battre, c’est qu’il y a un problème. |
Razavavy | Moyen | Suit les cartes et interprète les signaux du partenaire. |
Eva | Difficile | Comptage de cartes, détection de coupes, jeu positionnel. Le boss du moment. |
Le benchmark joue 1000 matchs par défaut et produit les taux de victoire avec intervalles de confiance à 95%, les classements ELO et les tests de significativité. Utilisez -n pour changer le nombre de matchs.
Tournoi suisse
Section intitulée « Tournoi suisse »Pour voir où votre bot se situe face à tous les bots disponibles :
./giretra-manage.sh swissCette commande détecte tous les bots (intégrés et externes) et lance un tournoi au format suisse complet avec classement final, bilans victoires/défaites et classements ELO. Vous pouvez aussi passer des noms de bots en argument pour limiter les participants.
Essayez dans le navigateur
Section intitulée « Essayez dans le navigateur »Vous pouvez lancer l’application web complète de play.giretra.com en local pour affronter votre bot en conditions réelles. Commencez par installer les dépendances frontend :
cd src/Giretra.Web/ClientApp/giretra-webnpm installPuis lancez l’application depuis la racine du dépôt avec le flag --offline :
dotnet run --project src/Giretra.Web -- --offlineCela démarre le backend ASP.NET et le frontend Angular avec une authentification simulée — aucun service externe requis. Ouvrez http://localhost:4200 dans votre navigateur pour accéder à l’application.
Gardez un œil sur la sortie console. Si votre bot externe ne se charge pas (dépendances manquantes, script init cassé, conflit de port), l’erreur apparaîtra dans stdout. C’est le moyen le plus rapide de repérer un problème de démarrage avant de se retrouver avec des échecs de test incompréhensibles.
Événements d’observation (optionnel)
Section intitulée « Événements d’observation (optionnel) »Pour que votre bot suive ce que font les autres joueurs, abonnez-le aux notifications en ajoutant les types d’événements souhaités dans bot.meta.json :
"notifications": ["card-played", "trick-completed", "deal-ended"]Puis implémentez les handlers correspondants :
def on_card_played(self, ctx: CardPlayedContext) -> None: # Suivre les cartes jouées pass
def on_trick_completed(self, ctx: TrickCompletedContext) -> None: # Suivre les plis remportés par chaque équipe pass
def on_deal_ended(self, ctx: DealEndedContext) -> None: # Analyser les résultats de la donne passonCardPlayed(ctx: CardPlayedContext): void { // Suivre les cartes jouées}
onTrickCompleted(ctx: TrickCompletedContext): void { // Suivre les plis remportés par chaque équipe}
onDealEnded(ctx: DealEndedContext): void { // Analyser les résultats de la donne}public virtual void OnCardPlayed(CardPlayedContext ctx){ // Suivre les cartes jouées}
public virtual void OnTrickCompleted(TrickCompletedContext ctx){ // Suivre les plis remportés par chaque équipe}
public virtual void OnDealEnded(DealEndedContext ctx){ // Analyser les résultats de la donne}func (b *Bot) OnCardPlayed(ctx CardPlayedContext) { // Suivre les cartes jouées}
func (b *Bot) OnTrickCompleted(ctx TrickCompletedContext) { // Suivre les plis remportés par chaque équipe}
func (b *Bot) OnDealEnded(ctx DealEndedContext) { // Analyser les résultats de la donne}public void onCardPlayed(CardPlayedContext ctx) { // Suivre les cartes jouées}
public void onTrickCompleted(TrickCompletedContext ctx) { // Suivre les plis remportés par chaque équipe}
public void onDealEnded(DealEndedContext ctx) { // Analyser les résultats de la donne}Événements disponibles : deal-started, card-played, trick-completed, deal-ended, match-ended.
Idéal pour construire une mémoire : suivre les cartes tombées, repérer les coupes, compter les points. Mais c’est entièrement facultatif. Un bot sans mémoire fonctionne très bien pour démarrer.
Une seule règle : pas d’appel à des ressources externes en cours de partie. Pas de requête vers une IA cloud, pas de communication avec un serveur distant, pas de téléchargement de fichiers de stratégie en plein match.
Votre bot tourne en local et décide en local. Vous pouvez utiliser toutes les librairies et dépendances que vous voulez : calcul mathématique, structures de données, voire un modèle ML embarqué. Du moment que tout reste autonome.
Prochaines étapes
Section intitulée « Prochaines étapes »Commencez par le bot le plus bête qui fonctionne. Validez-le. Puis rendez-le plus malin, une décision à la fois. Battez Kialasoa, puis attaquez-vous à Razavavy, puis essayez de détrôner Eva.
Prêt à passer aux choses sérieuses ? Suivez le guide Publier votre Bot pour ouvrir une pull request et rendre votre bot jouable sur play.giretra.com.
Envie de comprendre le protocole sous le capot ou de créer un bot dans un langage sans template ? Direction Créer de zéro.