Tout cela est la reine du Queen Mab.
Elle tisse dans les écuries d'une crinière
Et les cheveux font tomber un enchevêtrement ...
William ShakespeareCe fut une longue
sortie , mais beaucoup a été fait. Un
gestionnaire de session est apparu , vous permettant d'annuler les mouvements effectués par erreur. À certains endroits, la conception sonore a été ajoutée. Et pourtant, j'ai trouvé un
moyen cool de proposer plusieurs options alternatives pour le placement initial dans un jeu. Et le plus important - je suis finalement arrivé aux jeux avec des informations incomplètes.
Je vais vous expliquer ce qui est en jeu. Dans les jeux de société habituels, tels que les
échecs ou les
dames , les joueurs, à tout moment du jeu, ont des informations complètes sur l'emplacement des pièces (les leurs et celles de l'adversaire), les règles de déplacement, les objectifs du jeu, etc. Ces jeux sont assez bien étudiés et entrent dans la catégorie des "
jeux avec informations complètes ". Maintenant, imaginez que certaines de ces informations puissent être cachées au lecteur.
Le brouillard de guerre est une excellente illustration du thème. Selon les règles de "
Blind Chess ", les joueurs ne peuvent pas voir toutes les pièces de l'ennemi, mais seulement celles qui sont placées sur les champs, qui peuvent être atteintes en un seul mouvement de l'une de leurs pièces. J'ai fait deux ajouts à cette règle:
- Bien sûr, le joueur voit toujours ses pièces, mais par la façon dont elles sont affichées - sous forme normale ou translucide, il peut juger si l'adversaire les voit.
- À des fins décoratives uniquement, j'ai placé des «nuages» sur des zones actuellement invisibles.
Ayant maîtrisé le
principe général, je me suis un peu emporté et j'ai fait un très grand nombre de jeux avec le "brouillard de guerre". En plus des
échecs , j'ai des options «sombres» pour
Xiang ,
Changi ,
Shatrange ,
Sittuyin et bien d'autres jeux. Il y a même un "
Blind Guns "! Tous ces jeux ont une chose en commun:
L'ordinateur triche!Je n'ai même pas essayé de modifier les algorithmes des bots pour ces jeux, car j'ai fait un pari que des conditions inégales compensent au moins partiellement leur jeu extrêmement faible par rapport aux humains. Comme je l'ai
écrit plus tôt, le développement d'une IA de haute qualité pour les jeux de société est une tâche très difficile. Bien sûr, les règles ont des exceptions. Même avec un jeu de bot très faible, il sera difficile pour une personne de jouer à un
jeu inconnu, littéralement bourré de pièges. Que dire de sa
version "sombre"
Cependant, en général, ce n'est pas une approche très correcte. Je veux voir un bot qui peut faire exactement avec les données de son adversaire - l'homme. Pourquoi est-ce important? Tout est très simple - par la façon dont le bot joue, il est parfois très facile de deviner s'il a accès à des informations cachées (peeps) ou non. Et bien sûr, il est beaucoup plus intéressant pour une personne de jouer avec ce bot qui
ne lorgne
pas (jouer avec une autre personne est encore plus intéressant, mais c'est une autre histoire).
Et ici, cela vaut la peine de choisir un jeu légèrement différent des échecs (puisque je ne suis pas prêt à développer un bot "honnête" jouant aux échecs "à l'aveugle"). Il y a beaucoup de ces jeux et on ne peut pas dire qu'ils sont plus simples que les échecs ou les dames. Ils sont juste différents et nécessitent une approche individuelle.
Par exempleIl y a un jeu pour enfants avec lequel je n'ai pas encore réussi à développer un bot. On l'appelle la "Jungle" ou
Dou Shou Qi . Le but du jeu est de pénétrer en territoire ennemi. Chacun des joueurs a un «repaire» - un champ central sur la première ligne. Si l'une des figures ennemies entre dans l'antre, il a gagné (vous ne pouvez pas occuper l'antre avec vos propres figures).
Les chiffres sont classés par ancienneté. L'éléphant bat tous les personnages, suivi par: Lion, Tigre, Léopard, Chien, Loup, Chat et Rat. Un rat ne peut battre qu'un éléphant et un autre rat, en plus, c'est la seule figure qui peut se déplacer dans l'eau (au milieu du plateau il y a deux réservoirs). Un tigre et un lion peuvent sauter au-dessus de l'eau, mais seulement si le rat ne bloque pas l'eau. À l'exception des sauts, toutes les figures se déplacent de la même manière - vers un champ adjacent verticalement ou horizontalement. Le repaire est entouré de pièges. La figure dans le piège est vulnérable à
toute figure ennemie.
Comme vous pouvez le voir, les règles sont assez simples. Qu'est-ce qui empêche le développement d'un bot pour ce jeu? Tout d'abord, les chiffres à basse vitesse. S'il y a des menaces, je peux apprécier les avantages des échanges, mais pour la majeure partie du jeu, les pièces tournent simplement les unes après les autres sur des distances assez longues. Je ne peux pas me permettre de voir le jeu pour un grand nombre de mouvements vers l'avant (en raison de restrictions sur la durée du calcul du mouvement), à la suite de quoi les changements se situent en dehors de l'horizon de visualisation et tous les mouvements deviennent équivalents pour moi.
Pour commencer, j'ai décidé de m'attarder sur
BanQi - Chinese Blind Chess. Il s'agit d'un jeu très original avec des informations cachées, semblable à la "Jungle". Il est important pour moi que les développements, liés à la création d'un bot pour ce jeu, puissent être utilisés dans d'autres jeux, comme
Dou Shou Qi ,
Luzhan Qi ,
Stratego ou même (éventuellement)
Tafl .
Je vais vous parler des règles. Le jeu se déroule sur la moitié du plateau pour "Chinese Chess" (
Xiang Qi ), tandis que la disposition d'origine du plateau ne joue aucun rôle. Les pièces sont placées à l'intérieur des cellules (comme dans les cellules traditionnelles), et non aux intersections des lignes (comme dans les échecs chinois). Au début du jeu, toutes les pièces sont soigneusement mélangées et placées face cachée sur le plateau (puisque les pièces traditionnelles de Syants sont une sorte de barils, et leur nombre coïncide avec le nombre de champs sur la moitié du plateau, il n'y a aucune difficulté).
Ensuite, les joueurs alternent leurs mouvements. En effectuant un mouvement, le joueur peut retourner n'importe laquelle des pièces fermées ou déplacer une pièce précédemment ouverte de sa couleur. Les couleurs des joueurs sont déterminées par le tout premier mouvement. Si la première pièce noire est ouverte, le joueur qui l'a ouverte jouera noir. Toutes les figures du jeu vont de la même manière (à l'exception du «canon» dans la version taïwanaise, dont je parlerai plus tard) - sur une cellule adjacente verticalement ou horizontalement. La possibilité de prendre est déterminée par l'ordre d'ancienneté des chiffres:
Général> Conseiller> Éléphant> Wagon> Cheval> Canon> SoldatLes personnages plus âgés les battent plus jeunes ou égaux, à une exception près: un soldat frappe le général (une sorte de "
Rock-Paper-Scissors "). Reste à dire quelques mots sur le BanQi taïwanais:
- Contrairement à la version chinoise, à Taiwan BanQi, un général ne peut pas battre un soldat.
- Le pistolet se déplace selon les règles de XiangQi , c'est-à-dire vers n'importe quel nombre de champs le long de la basse vitesse orthogonale (comme un char) ou frappe n'importe quelle figure ennemie, avec un saut à travers le "chariot", lors d'un mouvement d'attaque.
Il existe également une version hongkongaise, mais elle n'est pratiquement pas différente de la version chinoise, sauf que l'ordre d'ancienneté des chiffres a été modifié. J'ai décidé de me concentrer sur la version taiwanaise des règles, la plus intéressante tactiquement.
Que dois-je rechercher lors du développement d'un bot?Premièrement, le jeu semble très simple, mais ce n'est pas le cas. Même si vous ne considérez pas les nuances associées aux armes taiwanaises, le coût des chiffres est contre-intuitif. Bien que le «conseiller» puisse battre moins de chiffres que le «général», il est le principal protagoniste du jeu. Premièrement, le joueur a deux conseillers. De plus, un seul général ennemi est supérieur en force à chaque conseiller, tandis que le général peut être attaqué par jusqu'à cinq soldats! Pour la même raison, le coût d'un soldat dans un jeu est plus élevé que celui d'un général. Au final, il peut battre le chiffre le plus fort! La deuxième considération importante suggère l'une des énigmes "Canterbury" de Henry Dudeney.

C'est plus une tâche de plaisanterie qu'un puzzle complet. Toutes les figures peuvent aller dans un champ adjacent verticalement ou horizontalement. Les blancs se déplacent en premier, tandis que les blancs et les noirs font toujours deux mouvements (dans des pièces différentes)! Dans ces conditions, le bouffon gauche ne peut jamais attraper l'âne gauche, et le buff droit ne peut jamais attraper le bon (vous pouvez le vérifier vous-même). Bien sûr, le bouffon droit peut attraper l'âne gauche sans aucune difficulté. Tout est question de parité!
Ce problème m'a donné quelques réflexions. Premièrement, la tâche du bot, dans des jeux comme BanQi ou DouShouQi, est tout d'abord de trouver le chemin le plus court. A partir de chacune des pièces actives (la sienne ou son adversaire), il est nécessaire de construire des chaînes de mouvements vers tous les objectifs possibles (y compris leurs propres pièces, pour calculer les échanges possibles). Après cela, les chaînes doivent être évaluées et les options suivantes sont possibles ici.
- La figure attaquante bat l'attaquant - une chaîne rentable (bonus) estimée par le coût de la figure attaquée (moins le coût de l'attaquant, si celui-ci est protégé), en tenant compte de la longueur de la chaîne.
- La figure attaquante bat l'attaquant - pas une chaîne (de pénalité) rentable, estimée par la valeur de la figure attaquante.
- Les pièces se battent (par exemple, elles sont égales) - ici tout dépend de la parité, les chaînes impaires sont avantageuses, et les paires doivent être considérées comme pénales (s'il n'y avait pas d'autres chiffres sur le terrain, la parité déterminerait complètement le résultat du jeu).
Bien sûr, tout n'est pas si simple. À tout le moins, vous devez vous souvenir du parcours spécifique des canons dans le BanQi de Taïwan (Quant à la «Jungle», il y a des cas encore plus spéciaux), mais c'est là que vous pouvez commencer. Avec un ensemble complet de chaînes évaluées, vous pouvez évaluer les mouvements. Le coût du mouvement doit être composé du coût des chaînes (à la fois bonus et gratuit), dont la durée diminue.
Tout d'abord, il est important de comprendre qu'il est peu probable qu'il puisse utiliser efficacement les algorithmes
minimax ici. Les mouvements qui révèlent des pièces précédemment cachées changent trop radicalement l'estimation de la position. N'ayant aucune information sur les pièces cachées, il est presque impossible de visualiser une position que de nombreux mouvements avancent. Mais chaque nuage a une doublure argentée, mais nous pouvons utiliser des heuristiques beaucoup plus complexes (en termes de calcul) pour évaluer les mouvements eux-mêmes!
J'ai déjà
un bot qui évalue les mouvements par leur heuristique (nécessaire pour un
jeu amusant). Il s'agit d'un algorithme très simple. Tous les mouvements sont triés par ordre décroissant par heuristique (les mouvements avec une valeur heuristique négative sont généralement rejetés), après quoi ils sont analysés dans l'ordre. Si le coup suivant mène à une position à partir de laquelle aucune réponse ennemie ne mène à une victoire immédiate, le bot la considère comme la meilleure. En utilisant cet algorithme, vous ne pouvez pas vous embêter avec l'estimation de position, mais vous devez transpirer sur l'
heuristique .
Tout d'abord, nous construisons des chaînesvar getChains = function(design, board) { var player = board.getValue(board.player); if (player === null) return []; if (_.isUndefined(board.chains)) { board.chains = []; var pieces = getGoals(design, board); var targets = getTargets(design, board, pieces); _.each(pieces.positions, function(pos) { var goals = pieces; var f = true; var piece = board.getPiece(pos); if (piece === null) return; if (!chinese && (piece.type == 12)) { goals = targets; f = false; } var group = [ pos ]; var level = []; level[pos] = 0; for (var i = 0; i < group.length; i++) { if (_.indexOf(goals.positions, group[i]) >= 0) {
Bien sûr, je cache toutes les données intermédiaires dans l'état du jeu, afin de ne pas les lire plusieurs fois. De plus, une astuce est utilisée ici, ce qui est très utile pour calculer les zones connectées. J'itère le tableau de
groupe , en y ajoutant des éléments supplémentaires dans la boucle, selon les besoins. Toutes les difficultés sont liées aux armes à feu. Pour eux, les buts des chaînes ne sont pas les figures elles-mêmes, mais les champs à partir desquels ces dernières peuvent être attaquées.
Les chaînes sont évaluées exactement comme je l'ai dit var getChainPrice = function(design, board, attacker, attacking, len) { var player = board.getValue(board.player); if ((player === null) || (attacker == null) || (attacking === null)) return 0; if (attacker.player == attacking.player) return 0; var isAttacking = isAttacker(design, attacker.type, attacking.type); var isAttacked = isAttacker(design, attacking.type, attacker.type); if (!chinese && (attacker.type == 12)) { isAttacking = true; isAttacked = (attacking.type == attacker.type) && (len == 1); } var price = 0; var f = (len % 2 == 0); if (attacker.player != player) f = !f; if (isAttacking) { if (isAttacked) { price = f ? (len - design.price[attacker.type]) : (design.price[attacking.type] - len); } else { price = design.price[attacking.type] - len; if (f) price = (price / 2) | 0; } } else { if (isAttacked) { price = len - design.price[attacker.type]; } } return price; }
... en fonction de la longueur et de la parité de la chaîne, ainsi que de la prise en compte des coûts des personnages attaquants et attaqués. Mais ce n'est que la moitié de la bataille! Il est nécessaire d'évaluer chacun des mouvements possibles à l'aide des chaînes construites. J'introduis une autre structure intermédiaire - souhaite agréger les données disponibles. L'évaluation du cours consiste en des évaluations de souhaits, qu'il satisfait:
Quelque chose comme ça var addWish = function(board, comment, price, src, dst) { if (_.isUndefined(board.wish[src])) { board.wish[src] = []; } if (_.isUndefined(dst)) dst = src; if (_.isUndefined(board.wish[src][dst])) { board.wish[src][dst] = price; } else { board.wish[src][dst] += price; } } var getWish = function(design, board) { if (_.isUndefined(board.wish)) { ... } return board.wish; } Dagaz.AI.heuristic = function(ai, design, board, move) { var wish = getWish(design, board); if (move.isSimpleMove() && !_.isUndefined(wish[ move.actions[0][0][0] ]) && !_.isUndefined(wish[ move.actions[0][0][0] ][ move.actions[0][1][0] ])) { return wish[ move.actions[0][0][0] ][ move.actions[0][1][0] ]; } return 0; }
Quant à la fonction
getWish elle -
même , la magie commence ici (et c'est l'endroit où j'ai probablement labouré plus d'une fois). Tout d'abord, je partage l'évaluation des mouvements basée sur des informations ouvertes et l'introduction de nouvelles pièces dans le jeu. Ce n'est pas tout à fait exact, mais jusqu'à présent, je ne sais tout simplement pas comment concilier des opinions aussi diverses. Si sur la base d'informations ouvertes aucun souhait n'a été formé, le bot essaie d'ouvrir de nouvelles figures (il y a aussi quelques astuces ici).
- Si un canon ennemi est ouvert, entouré de figurines fermées, il est logique d'ouvrir l'une des figurines à côté de lui, car il est probable qu'il sera en mesure d'attaquer le canon, et le canon ne pourra en aucun cas le battre.
- Si une figurine autre qu'un canon est ouverte, vous pouvez essayer d'ouvrir une figurine située à travers le "chariot", car il y a une chance que ce soit un canon.
- S'il y a une chaîne attaquante du côté de l'ennemi, une des pièces peut être ouverte, à côté de la chaîne, pour intercepter l'attaque.
- Si vous ne pouvez pas protéger la figure, vous pouvez ouvrir la figure à côté de lui, en essayant de réduire la situation à un échange.
Bien sûr, il est utile d'évaluer la probabilité d'ouvrir une figure particulière. var getShadow = function(design, board) { var player = board.getValue(board.player); if (player === null) return []; if (_.isUndefined(board.shadow)) { board.shadow = []; _.each(design.allPositions(), function(pos) { var piece = board.getPiece(pos); if ((piece !== null) && (piece.type < 7)) { var value = piece.type + 7; if (piece.player != player) { value = -value; } board.shadow.push(value); } }); } return board.shadow; } var isFriend = function(design, x) { return x > 0; } var isPiece = function(design, x, y) { return x == y; } var isAttacker = function(design, x, enemy) { if (x < 0) return false; if ((x == 13) && (enemy == 7)) return true; if (!chinese && (x == 7) && (enemy == 13)) return false; if (!chinese && (x == 12)) return false; return x <= enemy; } var isDefender = function(design, x, enemy, friend) { if (!isAttacker(design, x, enemy)) return false; return design.price[friend] <= design.price[enemy]; } var estimate = function(design, board, p, y, z) { var shadow = getShadow(design, board); if (shadow.length == 0) return 0; var r = 0; _.each(shadow, function(x) { if (p(design, x, y, z)) r++; }); return (100 * r) / shadow.length; }
Le joueur peut évaluer les probabilités en gardant une trace des chiffres qui ont abandonné le jeu. En principe, le bot peut faire la même chose, mais il existe un moyen plus simple - d'examiner les chiffres encore non ouverts en vrac et d'évaluer la probabilité d'ouvrir celui souhaité en fonction des informations collectées. De plus, le succès du mouvement sélectionné n'est pas garanti, mais si la probabilité d'un résultat favorable est faible, le mouvement ne sera pas sélectionné du tout.
En principe, l'approche a porté ses fruits, mais il reste du travail à faire.Alors que les mouvements défensifs ne sont pas très bons. Certaines figures rencontrent courageusement l'ennemi le plus fort, au lieu de s'enfuir (bien que s'enfuir dans leur cas, en règle générale, soit déjà inutile). De plus, il est difficile de coordonner les actions de différentes figures (cela peut être utile pour «chasser» les restes de figures ennemies). L'approche elle-même semble très prometteuse, mais l'heuristique reste à considérer.
L'heuristique basée sur des «chaînes» de mouvements peut être utile non seulement dans BanQi, mais aussi dans de nombreux autres jeux, avec une prédominance de morceaux «lents» (sinon comme critère déterminant, puis pour une évaluation préliminaire de la qualité des mouvements dans des algorithmes plus complexes, au moins moins). Cette approche est particulièrement demandée dans les jeux pour lesquels l'utilisation d'algorithmes minimax est difficile voire impossible (comme
Yonin Shogi par exemple).
Bien sûr, je vais continuer à travailler sur des jeux avec des informations incomplètes. La photo montre le "
jeu des généraux " philippin, pas encore prêt. C'est le jeu le plus simple d'une grande famille, y compris des jeux tels que
LuzhanQi et
Stratego . Et bien sûr, je m'attends toujours à faire un bot fonctionnel pour la "
Jungle "!
Et pour ceux qui me lisent encore, je peux offrir un autre jeu de puzzle amusant avec des informations cachées:
Je l'ai joué dans mon enfance, sur une calculatrice programmable appelée Fox Hunt. Huit renards sont cachés au hasard sur le terrain, qui doivent être trouvés à l'aide de la «méthode poke». Lorsque vous sélectionnez une zone vide, le nombre total de renards dans les huit directions s'affiche. Il est impossible de perdre, mais vous pouvez rivaliser pour le nombre minimum de clics. Et si vous jouez avec des écouteurs, baissez le son. Je l'ai peut-être exagéré avec des effets sonores.