Que les IA de chacun se départagent sur la même carte
L’équité ne peut être garantie que par un arbitre, pas par la bonne volonté des Bots. Un Bot ne touche jamais à la mémoire partagée : il ne reçoit que des observations filtrées par l’arbitre selon la vision, ne peut que soumettre des actions, et chaque action est d’abord contrôlée (propriété de l’unité).
Classées selon « l’équité peut-elle être garantie »
Les capacités d’observation de ce projet viennent justement du fait que « le client détient l’état de tous les joueurs » — c’est aussi vrai sur votre propre machine. L’équité ne peut donc venir que d’un arbitre en qui tout le monde a confiance. On commence par A ; le même protocole permet de passer directement à B ; C reste réservé au divertissement.
Arène locale
Une machine, une partie ; chaque Bot occupe un emplacement, et le processus arbitre donne les ordres à leur place
Ladder hébergé
A déplacé sur un serveur ; les joueurs envoient leur Bot, le serveur l’exécute dans un bac à sable
Chacun chez soi
Chacun exécute le jeu + son Bot sur sa machine, match en réseau local
Un processus qui donne les ordres pour les deux camps
- Orchestration Choisir la carte, lancer la partie, fixer race et difficulté par emplacement
- Cadence Un tick toutes les T millisecondes de jeu (200 par défaut) ; en mode lockstep, le jeu est mis en pause pendant l’envoi des observations
- Observation Instantané → filtré selon la vision de chaque emplacement → JSON
- Actions Vérifier que « l’unité appartient à cet emplacement » → budget (commandes par tick, APM) → envoi en lot par la voie rapide
- Enregistrement Résumé des observations de chaque tick + chaque action : rejouable, analysable, utilisable comme données d’entraînement
- Verdict Un emplacement sans bâtiment → éliminé ; délai dépassé → décision selon la force restante / les ressources
Observations en entrée, actions en sortie
Tout programme capable d’envoyer et de recevoir du JSON peut jouer — y compris un LLM qui produit directement ses actions à chaque tick. Un Bot Python écrit avec le SDK n’a pas besoin d’être modifié.
| Observation | Uniquement nos unités + les unités ennemies dans notre vision ; creeps et mines d’or selon la vision, ou rendus publics en début de partie |
| Limite d’actions | 32 par tick au maximum, le surplus est tronqué (pour éviter qu’un flot de commandes ralentisse l’arbitre) |
| Même unité | Seule la dernière commande du tick compte |
| Budget de temps | Durée du tick × 0.9 ; au-delà, le tick est passé ; le plus lent en pâtit seul, sans ralentir les autres |
| Lockstep | Pour une équité stricte, le jeu est mis en pause pendant l’envoi des observations et ne reprend qu’une fois toutes les réponses reçues (ou le délai écoulé) — la vitesse des machines n’influe pas sur le résultat |
| Échange des points de départ | Chaque paire de Bots joue une fois de chaque côté, pour neutraliser l’asymétrie de la carte |
| Ladder | Elo / TrueSkill ; au moins 20 parties par paire avant de conclure (sur 2 parties, la plus petite différence de taux de victoire détectable est d’environ 60 points de pourcentage) |
{"t": "obs", "tick": 57, "gameMs": 11400, "me": 1,
"res": {"gold": 320, "lumber": 150, "food": [18, 30]},
"units": [
{"id": 101, "type": "hfoo", "owner": 1,
"x": -4500, "y": 2200, "hp": 380, "hpMax": 420}],
"visibleEnemies": [
{"id": 733, "type": "ogru", "owner": 2,
"x": -3900, "y": 2500, "hp": 700}],
"deadlineMs": 180} id est un identifiant stable attribué par l’arbitre, valable toute la partie. {"t": "act", "tick": 57, "actions": [
{"do": "attack", "unit": 101, "target": 733},
{"do": "train", "unit": 5, "code": "hfoo"},
{"do": "cast", "unit": 7, "spell": "thunderbolt", "target": 733},
{"do": "move", "unit": 102, "x": -5000, "y": 2000}]} category == "command", mêmes noms de paramètres. Un act en retard est ignoré, et le tick compte comme passé. from openwar3 import Bot
class MyBot(Bot):
def on_tick(self, g): # même code : en local g est un Game, dans l'arène g est un ArenaGame
for w in g.idle_workers():
g.gather(w, g.nearest(g.gold_mines(), w))
# En local : python tools/play.py --bot my_bot.py --fair
# Dans l'arène : mêmes méthodes, WebSocket en dessous — pas une ligne du Bot ne change Game par ArenaGame. Dans l’ordre ; tant qu’une étape échoue, on ne passe pas à la suivante
| # | Expérience | Critère | Acquis |
|---|---|---|---|
| 1 | Ouvrir 2 emplacements dans une partie, sans IA intégrée | Les unités des deux camps restent immobiles | Le marqueur d’IA et le champ de difficulté des emplacements sont connus |
| 2 | Donner des ordres à un emplacement autre que le local | Les paysans de l’emplacement ennemi vont réellement miner | Le point le plus critique : en cas d’échec, il faudra se rabattre sur « un client par Bot, en vrai réseau » |
| 3 | Interroger la vision pour n’importe quel numéro de joueur | Le même point donne des résultats différents pour les deux emplacements | Le contrôle de visibilité du moteur accepte n’importe quel numéro de joueur |
| 4 | Déterminer le vainqueur par programme | Savoir dans la seconde qu’un camp a perdu tous ses bâtiments | Le bus d’événements offre un point d’entrée plus direct |
| 5 | Pause / reprise en lockstep | Actions collectées pendant la pause, appliquées après la reprise | La pause est vérifiée |
Un test au passage : le cerveau de référence dépend encore beaucoup de l’information de toute la carte (par exemple la cible d’attaque de l’IA de l’ordinateur). Pour entrer dans l’arène, il devra passer par la façade et n’utiliser que les unités dans sa vision — sans l’information globale, que vaudra-t-il encore ?