Pour appliquer avec succès min / max à un jeu de stratégie au tour par tour, vous devez appliquer correctement toutes les techniques d'échecs disponibles ...
Fonction d'évaluation
Même les moteurs d'échecs ont une très mauvaise force, si vos fonctions d'évaluation sont mauvaises. La version la plus simple d'une fonction d'évaluation est: 1 = partie gagnée par les blancs, -1 = partie gagnée par les noirs, 0 = tous les autres cas; Mais cela vous donnerait une très mauvaise performance. Il en va de même pour votre jeu au tour par tour! Si vous voulez utiliser min / max (avec élagage alpha / beta et autres) comme aux échecs, vous devez également implémenter une fonction d'évaluation raisonnable! Sinon, vous ne pouvez pas comparer les performances de ces algorithmes lorsqu'ils sont appliqués à votre jeu de stratégie au cas où il est appliqué aux échecs.
Ce que font les fonctions d'évaluation des moteurs d'échecs, c'est d'évaluer des choses comme:
- Quelle est la position d'une pièce sur le plateau?
- Combien de fois une pièce est-elle attaquée?
- Combien de fois la pièce est-elle protégée?
- Dans quelle mesure chaque pièce peut-elle "bouger" librement sur la planche? (ou: combien de tuiles contrôle-t-elle?)
Les parties de la fonction d'évaluation doivent d'abord être "traduites" dans votre jeu:
- Position de la pièce: est-ce par exemple sur une colline qui étend son champ de tir?
- Attaqué: combien chaque pièce est-elle en danger? (par exemple, somme des valeurs d'attaque des unités capables d'attaquer une unité spéciale multipliée par une probabilité d'être attaqué par elle; la probabilité augmente, si l'unité est déjà endommagée; diminue si de nombreuses autres unités sont à portée de l'unité attaquante)
- Propre attaque: combien d'unités peuvent être attaquées par cette unité?
- Protection: combien de pièces sont à côté (pour aider)? Peut-être qu'une unité ne peut pas attaquer des unités à une distance minimale et il est préférable de la protéger par une unité ayant la possibilité d'attaquer des unités proches.
- Mobilité: dans quelle mesure votre unité est-elle mobile? (peut-il fuir?)
Les différentes notations doivent être résumées par fonction de pondération (factor_a * rating_a + factor_b * ranting_b + ...) pour toutes les unités ...
Dans les jeux de stratégie, les ressources (or, bois, ...) doivent également être prises en compte.
Si votre fonction d'évaluation est assez bien, vous n'avez pas besoin de chercher vraiment "profondément" dans l'arborescence pour la plupart des cas. Vous n'avez donc probablement qu'à regarder de plus près les 3 ou 10 choix les plus prometteurs. Voir le chapitre suivant ...
Mouvements possibles à chaque position
La chose la plus problématique à propos de l'utilisation de min / max pour les jeux de stratégie est que vous pouvez commander plusieurs unités en un tour, alors qu'aux échecs, vous ne pouvez commander qu'une seule unité (sauf pour le roque, mais c'est une combinaison de mouvements clairement définie). Cela provoque 5 ^ N mouvements possibles pour N unités pour chaque "position" (terme d'échecs), si vous décidez seulement entre "se déplacer vers le nord, le sud, l'ouest, l'est ou l'arrêt" pour chaque unité. Vous pouvez résoudre ce problème en décomposant la commande complexe en commandes de bas niveau: par exemple, choisissez l'action pour l'unité A, allez en profondeur et décidez pour l'unité B .... décidez pour l'unité N ... puis terminez ce tour. Mais cela ne change pas à lui seul la complexité! Vous devez optimiser l'ordre dans lequel les actions sont affectées aux unités (par exemple, la première unité B, C, D puis l'unité A). Vous pouvez enregistrer l'impact de la décision pour chaque unité lors du dernier calcul, puis trier par importance. De cette façon, l'élagage alpha-bêta peut être utilisé pour supprimer très tôt toute mauvaise combinaison de l'arbre de recherche. La priorité la plus élevée doit toujours être "ne rien faire de plus et terminer votre tour" (élagage à mouvement nul) à chaque itération. De cette façon, vous pouvez "ignorer" l'attribution de la plupart des tâches à la plupart des unités et les laisser simplement continuer ce qu'elles ont fait auparavant. De cette façon, la recherche ira en profondeur rapidement en jetant simplement un œil aux unités "critiques" (par exemple celles qui sont vraiment en combat en ce moment). Assurez-vous de ne commander chaque unité qu'une seule fois ... Vous pouvez également utiliser un caractère aléatoire pour vous assurer que les unités "importantes" reçoivent également une commande de temps en temps. Surtout, les unités qui terminent un travail (par ex.
Approfondissement itératif + mise en cache / table de hachage
Ensuite, vous pouvez "approfondir l'interaction" pour aller de plus en plus en profondeur jusqu'à ce qu'une certaine limite de temps soit atteinte. Vous chercherez donc plus en profondeur s'il y a moins d'unités, et vous aurez toujours un "résultat" si vous arrêtez de chercher une meilleure solution. L'approfondissement itératif nécessiterait l'utilisation d'une table de hachage pour mettre en cache les anciens résultats des recherches. Cela permet également de réutiliser certains des résultats de la recherche des derniers tours (la branche de l'arbre de recherche qui couvre les commandes qui ont été réellement exécutées au dernier tour). Pour implémenter cela, vous avez besoin d'une très bonne fonction de hachage (jetez un œil à la "clé zobrist"), qui peut être mise à jour de manière itérative. La mise à jour de la clé de hachage signifie que vous pouvez simplement prendre la clé de hachage de l'ancienne "position" et que vous pouvez simplement activer le changement de position (par exemple, retirer l'unité en position x et la placer en position y). De cette façon, le calcul de la clé de hachage est rapide et vous n'avez pas besoin de traiter la situation de la carte entière pour la calculer, juste pour vérifier si le hachage contient une ancienne entrée pour cette position. D'une certaine manière, vous devez vous assurer qu'aucune collision de hachage ne se produit.
Comportement non déterministe
Le comportement non déterministe est un problème pour les recherches min / max. Cela signifie qu'il n'est pas sûr que vous atteigniez une cible attaquée (par exemple, la probabilité est de 10%). Ensuite, vous ne pouvez pas simplement planifier que cela se produise. Dans ce cas, vous devez modifier l'algorithme et mettre une couche "probabilty" entre les deux. C'est un peu comme "ses probabilités tournent". Chaque résultat indépendant doit être considéré séparément. L'évaluation à travers cette "couche" de profondeur doit ensuite être échantillonnée (échantillonnage de Monte Carlo) et le résultat de l'évaluation approfondie doit être pondéré par la probabilité d'occurrence. Différents résultats de la couche de probabilité doivent être considérés comme différents mouvements de l'adversaire (mais au lieu de min / max, la "moyenne" doit être calculée). Cela augmentera bien sûr la complexité de l'arbre de recherche.
Sommaire
Lorsque vous appliquez toutes ces techniques (qui sont toutes utilisées par les moteurs d'échecs actuels) à une partie déterministe, vous serez certainement en mesure d'obtenir des résultats raisonnables pour une partie également. Pour les jeux non déterministes, ce sera probablement plus compliqué, mais je pense toujours gérable.
Une bonne ressource pour expliquer ces techniques (pour les échecs) est http://chessprogramming.wikispaces.com/
Vous pouvez même implémenter une sorte d'aléatoire dirigé dans les recherches min / max. Au lieu d'étudier de manière déterministe les meilleurs résultats en premier dans chaque itération, vous pouvez simplement randomiser cela et laisser son ordre être décidé par une distribution de probabilité basée sur les évaluations actuelles ...