Repository navigation
Labo IA
À distinguer du Solveur d'éjection forcée — module de preuve exhaustive
côté backend, sans évaluation. Cette page couvre le moteur qui joue réellement les
parties : evaluateBoard, minimax, searchBestMove, et depuis la v1.47,
« Pourquoi ce coup ? ».
Une proposition détaillée (« Meta IA ») suggérait de transformer le « labo IA » avec alpha-bêta, table de transposition, tri des coups, WebWorker, une heuristique de danger d'éjection, et un panneau d'explication. Vérifié avant de coder, poste par poste :
| Suggéré comme manquant | État réel |
|---|---|
| Alpha-bêta + tri des coups |
Déjà là — minimax(depth, alpha, beta, ...), coups triés par le code de priorité ULA (éjections d'abord) |
| Table de transposition (Zobrist) |
Déjà là — ZOBRIST, hashBoard()
|
| WebWorker (éviter que l'UI gèle) |
Déjà là — AI_WORKER_CODE, calcul en thread séparé |
| Heuristique de danger d'éjection |
Déjà là — dngScore dans evaluateBoard
|
| Bibliothèque d'ouvertures nommées avec winrate | Contredit un principe déjà établi — voir Ouvertures : les noms (Belgian Daisy, etc.) désignent des positions de départ, pas des séquences ; il n'existe pas de théorie d'ouvertures façon échecs pour Abalone |
| Génération automatique de puzzles en masse | Contredit un principe déjà établi — voir Solveur : les puzzles doivent être certifiés depuis de vraies parties, jamais générés sans vérification |
| « Pourquoi ce coup ? » (panneau d'explication) | Réellement absent — seule idée retenue |
Vérifier avant de construire coûte moins cher que de reconstruire. Cinq des sept points d'une proposition détaillée décrivaient déjà l'existant ou allaient à l'encontre de principes déjà tranchés pour de bonnes raisons. Une recherche dans le fichier réel (
grepsur les noms de fonctions) a suffi à trancher chaque point avant d'écrire une seule ligne — la même discipline que pour toute proposition externe reçue sur ce projet.
evaluateBoard(color) décomposait déjà la position en 8 composantes (matériel,
centre, cohésion, bord adverse, capture, mobilité, isolement adverse, danger
d'éjection adverse) avant de les sommer en un seul nombre. La décomposition
existait — seule la somme sortait de la fonction.
evaluateBoard(color, detail) ajoute un second paramètre optionnel : sans lui,
comportement identique à avant (juste le total, tous les appels existants —
minimax, quiescence — inchangés) ; avec detail=true, renvoie l'objet complet des
8 composantes.
calculerExplicationCoup(move, color) simule le coup avec applyMove/
undoMove — les mêmes fonctions déjà utilisées des milliers de fois par coup à
l'intérieur de minimax, pas une nouvelle mécanique à risque — compare
l'évaluation détaillée avant/après, annule proprement. Le delta obtenu est donc
exactement ce qui a pesé dans la décision, jamais une estimation reconstruite
séparément qui pourrait diverger de la vraie IA.
Calculée dans
executeAIMove()avant que le coup choisi ne soit appliqué au plateau réel — la simulation a besoin de l'état d'avant-coup pour que la comparaison ait un sens.
formaterExplicationCoup() garde les 3 composantes les plus significatives par
poids absolu — au-delà, la phrase devient illisible et noie le signal utile.
Le mode Coach existant (coachEnabled) sauvegarde son état dans localStorage
mais ne le relit jamais au démarrage — le réglage ne survit donc pas à un
rechargement de page, malgré l'appel à setItem. Pas corrigé ici (hors sujet du
correctif en cours), mais le nouveau réglage aiExplainEnabled a été fait
correctement dès le départ : restauré via getItem au chargement, avec
synchronisation de l'apparence du bouton.
750+ tests fonctionnels, dont un fichier dédié (test_ai_explain.js) : le mode
détaillé de evaluateBoard reste rétrocompatible, le plateau est identique après
simulation (comparaison canonique, insensible à l'ordre d'insertion des clés —
une égalité de chaîne JSON brute aurait donné un faux négatif, delete puis
réinsertion change l'ordre d'énumération sans changer les valeurs), le delta est
réellement mesurable, la phrase est correctement triée et limitée à 3 composantes.
Vérifié aussi par exécution réelle dans un navigateur headless : un vrai coup
choisi par l'IA, une explication calculée, une bulle affichée et lue à l'écran.
Depuis la v2.49. Les critères de KAAH (sumito, menace, fourchette, piège, compacité, cases, gain et perte selon le score) sont dans le moteur, éteints par défaut — voir Réflexion de l'IA. Les activer rend-il l'IA plus forte ? Ils ont un coût : chaque évaluation est plus lente, donc l'IA voit moins loin dans le même temps. Seul un match peut trancher.
Page Labo, encadré « Test : les critères de KAAH » :
-
Duels « IA actuelle + critères de KAAH » contre « IA actuelle seule » — le même
duelGameque le Labo, dans un fil de calcul à part (ni le Labo ni une partie n'en sont gênés). - Par cycles de 4 parties sur la même ouverture : chaque version joue Noirs puis Blancs, et tient le rôle A puis B. L'avantage d'ouverture s'annule, et l'arbitrage des parties trop longues (fait avec l'évaluation du camp A) aussi.
- Verdict SPRT avec les seuils du Labo (H0 = +0 Elo, H1 = +35 Elo, α = β = 5 %), après 16 parties au moins et en fin de cycle — sans ce minimum, une série de victoires au début annule la variance mesurée et le verdict tombait dès la 2ᵉ partie (trouvé par les tests). « Indécis » après 400 parties.
- Rien n'est adopté automatiquement.
- Limite : 600 ms par coup, 80 coups au plus — un régime de recherche courte. Le verdict porte sur lui, pas forcément sur les niveaux 8 à 10.
Depuis la v2.54. Revue du Labo d'auto-amélioration (le banc moteur contre moteur, page Labo) : ses verdicts ne tenaient pas.
| Défaut | Effet |
|---|---|
| Verdict compté partie par partie, minimum 8 parties, plafond 60 | 8 victoires de suite donnaient une variance mesurée nulle : verdict immédiat. Et 60 parties, c'est bien trop peu pour trancher : en simulation (≈ 30 % de nulles, le taux mesuré), 96 % des tests d'un mutant sans effet finissaient « indécis », et un mutant nettement meilleur (+70 Elo) n'était adopté que 14 fois sur 100 |
| Parties trop longues arbitrées par l'évaluation du mutant | le mutant arbitrait ses propres parties |
SPSA : champion = θ à chaque itération (2 parties) |
les poids de l'IA changeaient sans aucune vérification |
| « Élo cumulé » = somme des estimations des tests adoptés | chaque test adopté l'est parce qu'il a eu de la chance : la somme gonfle (malédiction du vainqueur) et ne baisse jamais |
| « ▶ revoir » | le rejeu tirait l'ouverture une 2ᵉ fois puis rejouait les coups enregistrés, ouverture comprise : position fausse dès le 7ᵉ coup, captures différentes de la vraie partie |
| Réglages « Ouverture : demi-coups » et « fenêtre d'équilibre » | affichés, jamais transmis au moteur (6 et 600 en dur) |
- Paires d'ouvertures. Chaque ouverture tirée au sort est jouée deux fois, couleurs inversées. Le verdict compte des paires : 0, ½, 1, 1½ ou 2 points (« pentanomial »). L'avantage qu'une ouverture donne à une couleur s'annule dans la paire.
- GSPRT pentanomial (l'approximation de Van den Bergh, celle de Fishtest) sur les moyennes de paires, avec un a priori de Jeffreys : ½ paire fictive dans chacune des 5 cases. 8 paires gagnées 2-0 ne suffisent plus (LLR 1,90 < 2,94) ; 11 suffisent. Plafond : 250 paires, puis « indécis ».
- Même modèle simulé, mêmes 30 % de nulles : +70 Elo adopté plus de 99 fois sur 100 (≈ 140 parties), +35 Elo 68 fois, un mutant sans effet adopté à tort ≈ 4 fois sur 100 (le seuil α = 5 %), −35 Elo rejeté 99,9 fois sur 100.
- Arbitrage par consensus (comme les bancs d'essai d'échecs) : à la limite de coups, captures égales, la victoire n'est donnée que si les deux évaluations, chacune avec ses poids, sont d'accord. Mesuré sur 40 vraies parties avec de petites mutations : les deux règles donnaient le même résultat. L'écart peut venir des grosses valeurs, comme celles des critères de KAAH.
- Tri : les 3 mutants jouent les mêmes ouvertures (comparaison plus juste) ; la confirmation, elle, part sur des ouvertures neuves. Si aucun mutant n'atteint 50 % au tri, pas de confirmation : génération suivante.
- SPSA vérifié : θ n'est plus jamais appliqué à l'IA pendant les itérations ; toutes les 50 itérations, un SPRT par paires le confronte au champion, et seule une adoption change l'IA. SPSA asynchrone (chaque itération part du θ du moment), comme les régleurs distribués.
- Gain réel mesuré : après chaque adoption, le champion joue 25 paires contre les poids d'origine ; Elo ± marge à 95 %, courbe. Bouton « Mesurer maintenant ».
- Plusieurs parties à la fois : un fil de calcul par partie, la moitié des cœurs (4 au plus), réglable de 1 à 8. Débit affiché. Une partie bloquée (chien de garde) remplace son fil et se rejoue ; trois de suite mettent en pause. Les résultats d'une phase déjà tranchée sont ignorés (numéro d'époque).
- Critères de KAAH réglables : 16 poids au lieu de 8. Des mutants allument un critère à la valeur de KAAH (« sumito 0→20 ») puis l'ajustent ×1,5 ou ×0,6 ; SPSA les règle avec les autres (départ à la moitié de la valeur de KAAH, sinon θ− reste collé à 0). Case « Régler aussi les critères de KAAH ». Un critère adopté est utilisé par l'IA et apparaît dans le tableau « Réflexion de l'IA ».
- Le test des critères de KAAH utilise les mêmes paires, le même verdict et les mêmes fils de calcul.
Mesuré sur des parties jouées par le moteur lui-même :
| Régime | Parties | Nulles | Décidées par 1 à 2 billes d'avance à la limite | 6 éjections |
|---|---|---|---|---|
| Tri (350 ms/coup, 60 coups) | 24 | 16 (67 %) | 8 | 0 |
| Confirmation (600 ms/coup, 80 coups) | 16 | 5 (31 %) | 10 | 1 |
Au tri, deux parties sur trois finissent sans écart de billes en 60 coups : le tri informe peu. Allonger ses parties le rendrait plus utile, au prix du temps.
12 tests (section 49 de tests/engine.test.js) : statistiques par paires ; plusieurs fils en même temps, mêmes ouvertures au tri ; cycle complet tri → confirmation → adoption → bilan → génération suivante ; rejet ; SPSA sans toucher l’IA puis vérifié, et sa partie abandonnée rejouée ; mutants et réglages des critères de KAAH ; reprise d'un état de l'ancien Labo ; chien de garde ; arbitrage par consensus et rejeu exact, sur de vraies parties du moteur. Et un essai réel dans Chromium : 4 fils, 630 parties en deux minutes en réglages rapides, phases enchaînées, aucune erreur.