• Aucun résultat trouvé

Description de l’implémentation

3.2 SHPE : un moteur de planification HTN

3.2.1 Description de l’implémentation

La majorité des systèmes de planification disponibles ont été influencés, lors de leur conception, par les règles de l’IPC (International Planning Competition). Ils sont conçus pour prendre en entrée un fichier contenant la modélisation d’un domaine de planification, ainsi qu’un fichier contenant une modélisation d’un problème associé à ce domaine. Ces deux fichiers sont modélisés via le langage PDDL [63] (Planning Domain Definition

Language), et doivent être dynamiquement interprétés par le planificateur. L’implémentation

de cette étape est donc générique, et la nécessité de pouvoir s’adapter à des domaines et des problèmes de n’importe quelle taille (différents nombres de prédicats, d’actions ou d’objets) impose d’allouer dynamiquement ces structures de données. Or, dans le cadre de l’architecture de planification en ligne présentée dans ce manuscrit, le planificateur tactique doit résoudre des problèmes qui sont toujours associés au même domaine de planification. Il peut donc s’avérer judicieux de tenter d’optimiser l’implémentation du planificateur en fonction de ce domaine, et ainsi d’en améliorer les performances.

Afin de mettre en pratique une telle approche, SHPE s’inspire de Pyhop [72]. Pyhop est une implémentation de SHOP dans le langage Python qui présente deux caractéristiques remarquables : elle est très courte (environ 150 lignes de code), et les données de planification ne sont pas toutes implémentées sous la forme de structure de données, mais sous la forme de procédures en code Python pour les actions et les méthodes, comme illustré par la figure 3.2. La première de ces caractéristiques est par ailleurs liée à la simplicité algorithmique de la planification HTN simplifiée, mais est aussi fortement renforcée par l’absence de moteur d’inférence (utilisé dans SHOP et SHOP2 pour générer les substitutions) qui est une conséquence de la seconde caractéristique. La possibilité d’implémenter les éléments de planification via un langage impératif est rendu possible par le fonctionnement des planificateurs HTN simplifiées. En effet ces planificateurs fonctionnent en représentant explicitement les états et en les « progressant » par les actions, c’est-à-dire en appliquant une action sur un état pour obtenir l’état suivant. Ainsi il est possible d’encoder la structure des états sous la forme d’une structure de données d’un langage impératif, et d’encoder les actions directement comme des fonctions prenant en entrée un état et retournant un autre état en sortie. De même, les méthodes étant associées à des préconditions, celles-ci peuvent être encodées sous forme de fonctions prenant en entrée un état et retournant une liste de décompositions en sortie.

Pyhop est mis en avant comme étant un système de planification adapté au développement des jeux vidéo, car il permet à un programmeur d’encoder les données de planification dans

def shoot_player(state, npc, gun):

if state.visible[state.at[npc]][state.player_at] and \ state.holding[npc][gun] and state.loaded[gun]:

state.loaded[gun] = False state.player_wounded = True return state

else: return False

Figure 3.2 – Exemple d’action encodée en Python pour Pyhop

un langage impératif « classique », et lui évite d’apprendre un langage de planification qui pourrait lui paraitre « ésotérique ». Ce n’est cependant pas cet argument qui a motivé la conception de SHPE. En effet, c’est pour améliorer les performances du planificateur que SHPE suit une approche similaire à celle de Pyhop. Puisque le domaine traité par le planificateur est connu à l’avance, il n’est donc pas nécessaire d’avoir recours à des données allouées dynamiquement, ni même à des données statiques qui devront encore être interprétées par une procédure générique non optimisée, mais il est possible de les représenter sous la forme de procédures spécialisées directement exécutables. Ainsi, la quantité d’opérations à exécuter pour chaque nœud de l’espace de recherche peut être réduite, de même que la quantité de mémoire à allouer dynamiquement (ce qui peut aussi avoir un impact sur le temps de calcul, du fait de la fragmentation de la mémoire).

Le système se présente donc, non pas comme un planificateur autonome, mais comme une bibliothèque C++ offrant un moteur de planification. Les différentes structures, classes et fonctions associées à ce moteur possèdent ainsi un paramètre « template » C++ correspondant à la structure des états, et qui doit donc être défini au préalable. Il est ainsi possible d’implémenter n’importe quelle structure de données pour représenter les états, et celle-ci peut donc être adaptée au problème traité pour minimiser la quantité de mémoire nécessaire ou accélérer l’accès aux données. Par exemple, il est ainsi possible de représenter l’état d’une partie de jeu d’échec simplement par un tableau en deux dimensions pour représenter le plateau et un type énuméré pour identifier l’état de chaque case (vide ou contenant une pièce d’un type et d’une couleur donnés). Afin d’encoder les tâches, ce moteur de planification exhibe une interface pouvant être implémentée pour définir les tâches primitives et composées du domaine. Cette interface est présentée en figure 3.3. Elle permet de définir trois méthodes pour les tâches primitives (« méthodes » dans le sens de la programmation orientée objet, c’est-à-dire de procédures attachées à une classe, et non pas dans le sens de « méthodes de décomposition ») :

— une méthode applicable, qui évalue si la précondition de l’action est satisfaite dans l’état courant ;

— une méthode apply qui applique l’action à l’état courant pour obtenir un nouvel état ;

— une méthode cost qui retourne le coût de cette action en fonction de l’état courant. Pour les tâches non-primitives, cette interface propose une méthode decompose qui prend en entrée l’état courant et retourne un ensemble de décompositions.

L’algorithmie au cœur de SHPE correspond à la théorie de la planification HTN simplifiée et totalement ordonnée : un plan est construit en appliquant des actions à l’état initiale et ces actions sont obtenues en décomposant les tâches du réseau de tâches qui ne sont pas contraintes à être ordonnées après d’autres tâches. Lorsqu’une action ou une méthode ne peut être appliquée, le planificateur effectue un retour arrière dans un espace de recherche parcouru en profondeur d’abord. De plus, SHPE implémente la procédure de séparation et évaluation de SHOP2 permettant d’améliorer l’efficacité de la recherche

template <typename State> class Task {

public:

Task(bool primitive); virtual ~Task();

bool is_primitive() const;

virtual bool applicable(const State&) const; virtual void apply(State& state) const;

virtual double cost(const State& state) const; virtual void decompose(

const State& state,

TaskNetworks<State>& task_networks ) const;

protected:

bool primitive; };

Figure 3.3 – Interface C++ pour l’implémentation des tâches dans SHPE

d’une solution optimale. Enfin, l’algorithme de planification est implémenté en suivant une structure itérative permettant de l’interrompre et de le redémarrer après un nombre donné d’itérations ou une limite de temps. Cette propriété lui permet d’être utilisé avec des moteurs de jeu qui imposent que tous les calculs s’exécutent entre deux rafraichissements de l’image de l’environnement virtuel. La procédure exécutée à chaque itération est décrite dans l’algorithme 7, tandis que les procédures pour la recherche de solutions non-optimales et optimales sont respectueusement présentées par les algorithmes 8 et 9.

Documents relatifs