Je travaille sur un jeu avec des cartes qui ressemblent à des puzzles de verrouillage et de clé . L'IA doit naviguer vers un objectif qui peut être derrière une porte rouge verrouillée, mais la clé rouge peut être derrière une porte bleue verrouillée, et ainsi de suite ...
Ce puzzle est similaire à un donjon de style Zelda, comme cette image:
Pour atteindre le but, vous devez vaincre le boss, ce qui nécessite d'aller au-dessus de la fosse, ce qui nécessite de récupérer la plume, ce qui nécessite de récupérer la clé
Les donjons Zelda ont tendance à être linéaires. Cependant, je dois résoudre le problème dans le cas général. Alors:
- L'objectif pourrait nécessiter l'une d'un ensemble de clés. Alors peut-être que vous devez obtenir la clé rouge ou la clé bleue. Ou il pourrait y avoir une porte déverrouillée le long du chemin!
- Il pourrait y avoir plusieurs portes et clés d'une même sorte. Par exemple, il pourrait y avoir plusieurs clés rouges sur la carte, et en collecter une vous donnera accès à toutes les portes rouges.
- Le but peut être inaccessible car les bonnes touches sont derrière des portes verrouillées
Comment puis-je effectuer la recherche de chemin sur une telle carte? À quoi ressemblerait le graphique de recherche?
Remarque: le dernier point concernant la détection d'objectifs inaccessibles est important; Un *, par exemple, est extrêmement inefficace si l'objectif est inaccessible. Je voudrais traiter cela efficacement.
Supposons que l'IA sache où tout se trouve sur la carte.
la source
Réponses:
La recherche de chemin standard est assez bonne - vos états sont votre emplacement actuel + votre inventaire actuel. «déménager», c'est soit changer de salle, soit changer d'inventaire. Pas couvert dans cette réponse, mais pas trop d'efforts supplémentaires, est d'écrire une bonne heuristique pour A * - il peut vraiment accélérer la recherche en préférant ramasser les choses plutôt que de s'en éloigner, préférant déverrouiller une porte près de la cible sur la recherche d'un long chemin, etc.
Cette réponse a obtenu beaucoup de votes positifs depuis qu'elle est arrivée en premier et a une démo, mais pour une solution beaucoup plus optimisée et spécialisée, vous devriez également lire la réponse "Faire en arrière est beaucoup plus rapide" /gamedev/ / a / 150155/2624
Preuve de concept Javascript entièrement opérationnelle ci-dessous. Désolé pour la réponse sous forme de vidage de code - j'avais en fait implémenté cela avant d'être convaincu que c'était une bonne réponse, mais cela me semble assez flexible.
Pour commencer en pensant au pathfinding, rappelez-vous que la hiérarchie des algorithmes de pathfinding simples est
Dans notre cas, le simple encodage d'un «état» comme «emplacement + inventaire» et de «distances» comme «mouvement ou utilisation d'objets» nous permet d'utiliser Djikstra ou A * pour résoudre notre problème.
Voici du code réel illustrant votre niveau d'exemple. Le premier extrait est juste pour comparaison - passez à la deuxième partie si vous voulez voir la solution finale. Nous commençons avec une implémentation de Djikstra qui trouve le chemin correct, mais nous avons ignoré tous les obstacles et les clés. (Essayez-le, vous pouvez le voir juste avant l'arrivée, depuis la salle 0 -> 2 -> 3-> 4-> 6-> 5)
Alors, comment ajouter des éléments et des clés à ce code? Simple! au lieu de chaque "état" commencez juste le numéro de la chambre, c'est maintenant un tuple de la salle et notre état d'inventaire:
Les transitions passent désormais d'un tuple (coût, salle) à un tuple (coût, état).
enfin, nous apportons quelques modifications mineures liées au type à la fonction Djikstra (par exemple, elle correspond toujours à un numéro de salle d'objectif au lieu d'un état complet), et nous obtenons notre réponse complète! Notez que le résultat imprimé va d'abord dans la salle 4 pour récupérer la clé, puis dans la salle 1 pour récupérer la plume, puis dans la salle 6, tue le boss, puis dans la salle 5)
En théorie, cela fonctionne même avec BFS et nous n'avions pas besoin de la fonction de coût pour Djikstra, mais avoir le coût nous permet de dire "récupérer une clé est sans effort, mais combattre un boss est vraiment difficile, et nous préférerions revenir en arrière 100 pas plutôt que de combattre le boss, si on avait le choix ":
la source
En arrière A * fera l'affaire
Comme indiqué dans cette réponse à une question sur la recherche de chemin vers l' avant ou vers l'arrière , la recherche de chemin vers l'arrière est une solution relativement simple à ce problème. Cela fonctionne de manière très similaire à GOAP (Goal Oriented Action Planning), en planifiant des solutions efficaces tout en minimisant les interrogations sans but.
Au bas de cette réponse, j'ai une ventilation de la façon dont il gère l'exemple que vous avez donné.
En détail
Pathfind de la destination au départ. Si, dans votre recherche de chemin, vous tombez sur une porte verrouillée, vous avez une nouvelle branche à votre chemin qui continue à travers la porte comme si elle était déverrouillée, la branche principale continuant de chercher un autre chemin. La branche qui continue à travers la porte comme si elle était déverrouillée ne cherche plus l'agent IA - elle cherche maintenant une clé qu'elle peut utiliser pour traverser la porte. Avec A *, sa nouvelle heuristique est la distance à la clé + la distance à l'agent AI, au lieu de simplement la distance à l'agent AI.
Si la branche porte déverrouillée trouve la clé, elle continue de chercher l'agent AI.
Cette solution est rendue un peu plus compliquée lorsque plusieurs clés viables sont disponibles, mais vous pouvez vous connecter en conséquence. Parce que les branches ont une destination fixe, il vous permet toujours d'utiliser une heuristique pour optimiser la recherche de chemin (A *), et les chemins impossibles seront, espérons-le, coupés rapidement - s'il n'y a pas de moyen de contourner la porte verrouillée, la branche qui ne fonctionne pas ne passez pas la porte à court d'options rapidement et la branche qui passe par la porte et cherche la clé continue d'elle-même.
Bien sûr, là où il existe une variété d'options viables disponibles (plusieurs clés, d'autres éléments pour contourner la porte, un long chemin autour de la porte), de nombreuses branches seront maintenues, affectant les performances. Mais vous trouverez également l'option la plus rapide et pourrez l'utiliser.
En action
Dans votre exemple spécifique, la recherche de chemin depuis l'objectif jusqu'au début:
Nous rencontrons rapidement une porte de boss. La branche A continue à travers la porte, à la recherche d'un boss pour se battre. La branche B reste coincée dans la pièce et expirera bientôt lorsqu'elle découvrira qu'il n'y a pas d'issue.
La branche A trouve le boss et cherche maintenant le départ, mais rencontre une fosse.
La branche A continue au-dessus de la fosse, mais maintenant elle cherche la plume et fera une ligne d'abeille vers la plume en conséquence. La branche C est créée qui essaie de trouver un moyen de contourner la fosse, mais expire dès qu'elle ne peut pas. Cela, ou il est ignoré pendant un certain temps, si votre heuristique A * constate que la branche A semble toujours la plus prometteuse.
La branche A rencontre la porte verrouillée et continue à travers la porte verrouillée comme si elle était déverrouillée, mais maintenant elle cherche la clé. La branche D continue également à travers la porte verrouillée, toujours à la recherche de la plume, mais elle cherchera ensuite la clé. C'est parce que nous ne savons pas si nous devons d'abord trouver la clé ou la plume, et en ce qui concerne la recherche de chemin, le Start pourrait être de l'autre côté de cette porte. La branche E essaie de trouver un moyen de contourner la porte verrouillée et échoue.
La branche D trouve rapidement la plume et continue de chercher la clé. Il est autorisé à passer à nouveau par la porte verrouillée, car il cherche toujours la clé (et il recule dans le temps). Mais une fois qu'il a la clé, il ne pourra pas passer par la porte verrouillée (car il ne pouvait pas passer par la porte verrouillée avant d'avoir trouvé la clé).
Les branches A et D continuent de rivaliser, mais lorsque la branche A atteint la clé, elle cherche la plume, et elle ne parviendra pas à atteindre la plume car elle doit repasser par la porte verrouillée. La branche D, d'autre part, en atteignant la clé, tourne son attention vers le départ et la trouve sans complication.
La branche D gagne. Il a trouvé le chemin inverse. Le chemin final est: Démarrer -> Clé -> Plume -> Boss -> Objectif.
la source
Modifier : Ceci est écrit du point de vue d'une IA qui est prête à explorer et à découvrir un objectif, et qui ne connaît pas à l'avance l'emplacement des clés, des verrous ou des destinations.
Tout d'abord, supposons que l'IA ait une sorte d'objectif global. Par exemple, "Trouvez le patron" dans votre exemple. Oui, vous voulez le battre, mais il s'agit vraiment de le trouver. Supposons qu'il ne sache pas comment atteindre le but, juste qu'il existe. Et il le saura quand il le trouvera. Une fois l'objectif atteint, l'IA peut cesser de travailler pour résoudre le problème.
De plus, je vais utiliser ici le terme générique "verrou" et "clé", même s'il peut s'agir d'un gouffre et d'une plume. C'est-à-dire que la plume "déverrouille" le gouffre "verrouille".
Approche de solution
Il semble que vous commenciez d'abord avec une IA qui était essentiellement un explorateur de labyrinthe (si vous considérez votre carte comme un labyrinthe). Explorer et cartographier tous les endroits où il peut aller serait le principal objectif de l'IA. Cela pourrait être basé uniquement sur quelque chose de simple comme, "Allez toujours vers le chemin le plus proche que j'ai vu mais pas encore visité."
Cependant, quelques règles entreraient en jeu tout en explorant cela pourrait changer la priorité ...
Une note sur ce dernier point. S'il doit choisir entre aller vérifier une zone inexplorée qu'il a vue avant (mais non visitée) par rapport à une zone inexplorée derrière un chemin nouvellement déverrouillé, il devrait faire de ce chemin nouvellement déverrouillé la priorité. C'est probablement là qu'il y a de nouvelles clés (ou verrous) qui seront utiles. Cela suppose qu'un chemin verrouillé ne sera probablement pas une impasse inutile.
Élargir l'idée avec des touches "verrouillables"
Vous pourriez potentiellement avoir des clés qui ne peuvent pas être prises sans une autre clé. Ou des clés verrouillées pour ainsi dire. Si vous connaissez vos anciennes grottes colossales, vous devez avoir la cage à oiseaux pour attraper l'oiseau - dont vous aurez besoin plus tard pour un serpent. Donc vous "déverrouillez" l'oiseau avec la cage (qui ne bloque pas le chemin mais ne peut pas être ramassé sans la cage), puis "déverrouillez" le serpent (qui bloque votre chemin) avec l'oiseau.
Donc, ajouter quelques règles ...
Je n'entrerai même pas dans le détail de la façon dont le port d'une certaine clé pourrait annuler l'effet d'une autre clé (Grottes colossales, la tige fait peur aux oiseaux et doit être lâchée avant que l'oiseau puisse être ramassé, mais est nécessaire plus tard pour créer un pont magique) .
la source