Entraînez-vous à sauver la planète en C++ 🌍

Ils nous faut être prêts pour le prochain assaut des extraterrestres ! Et pour cela, rien de mieux que de s’entraîner sur un simulateur à intelligences artificielles en C++ !

A vos claviers et souris, on commence ici !

Prérequis

N’hésitez pas à me contacter ici pour toutes questions sur les notions de C++. A votre disposition si vous souhaitez aussi des cours particuliers 😉

Installation de l’environnement de développement

On utilisera Code::Blocks comme environnement avec le compilateur mingw téléchargeable depuis ce lien.

Pour bien comprendre comment installer et utiliser l’IDE, je vous renvoie sur le tutoriel d’openclassroom.

Récupération des sources du simulateur

Entrez votre nom, prénom et mail puis cliquer sur le bouton télécharger pour obtenir les sources.

Compilation du projet

Une fois l’archive .zip téléchargée, décompressez-la, puis ouvrez le fichier j2l-SimulateurIA.workspace via double-clic.

Décompressions de l’archive zip une fois téléchargée

Ouvrez bien le fichier avec l’extension .workspace (et non le fichier projet .cpb). Si l’extension n’est pas visible, dans l’Explorateur de fichiers -> Affichage -> Afficher/Masquer cocher Extensions de nom de fichiers.

Si le double-clic n’ouvre pas automatiquement Code::Blocks, ouvrez Code::Blocks manuellement, puis glisser-déposer le fichier j2l-SimulateurIA.workspace dans la fenêtre.

Une fois ouvert, dans la fenêtre « Management » vous devriez voir les sources du projet j2l-SimulateurIA, triées dans 3 dossiers virtuels :

Arborescence du projet
  • Agent : Contient les sources de vos classes agents, classes qui devront implémenter l’intelligence artificiel décrivant comment les entités du jeu vont agir de manière autonome
  • Jeu : Contient les variables et fonctions pour interagir avec le jeu ; Vous ne pouvez pas modifier ces sources (les règles du jeu sont impénétrables ^^)
  • MoteurRendu : Contient vos classes pour afficher les rendus et animations de vos agents

Pour lancer l’application et voir les agents évoluer dans le jeu, cliquez Build -> Build and run (ou le raccourci clavier F9). Des lignes de commande devraient apparaître dans la fenêtre « Logs & others », puis l’application devrait démarrer en plein écran.

Modifier le comportements des agents

Pour cela, explorons la classe AgentSpecial implémentée dans les fichiers AgentSpecial.cpp et AgentSpecial.hpp.

Dans le fichier header (le fichier d’en-tête avec l’extension .hpp), vous trouverez les signatures des méthodes et attributs de la classe AgentSpecial. Tandis que dans le fichier .cpp, vous trouverez l’implémentation (le code) de chaque fonctions de la classe (les méthodes).

En général, un header donne les informations aux utilisateurs pour savoir comment utiliser une classe, mais ne livre surtout pas le secret de fabrication caché dans le fichier cpp.

class AgentSpecial:
    public j2l::Agent
{
    public:
        AgentSpecial(int x, int y, int orientation);
        virtual ~AgentSpecial() {}
        const char* nom() const;

    protected:
        void quandActualiser();
        void quandDistanceFrontaleChange(int ancienneValeur, int nouvelleValeur);
        void quandMunitionsChange(int ancienneValeur, int nouvelleValeur);
        void quandVieChange(int ancienneValeur, int nouvelleValeur);
        void quandPoseChange(int ancienX, int ancienY, int ancienneOrientation, int nouvelX, int nouvelY, int nouvelleOrientation);
};

Les deux premières lignes indiquent que la classe AgentSpecial hérite des méthodes et attributs de la classe Agent, implémentée dans l’espace de nom j2l. Cela signifie que cette classe fille contient aussi les méthodes et attributs de la classe parente Agent, définis dans le fichier Agent.hpp.

class Agent
	{
		public:
			Agent(int x, int y, int orientation);
			virtual ~Agent() {}
			void actualiser(int x, int y, int orientation, int distance, int vie, int munitions, int& ordreX, int& ordreY, int& ordreOrientation, bool& ordreTir);
			void orienter(int orientation);
			void deplacer(int x, int y);
			void tirer(bool tirerOuNon);
			int munitionsRestantes() const;
			int viesRestantes() const;
			int munitionsInitiales() const;
			int viesInitiales() const;
			int distanceFrontale() const;
			int x() const;
			int y() const;
			int orientation() const;
			virtual const char* nom() const;

		protected:
			virtual void quandActualiser() {}
			virtual void quandDistanceFrontaleChange(int ancienneValeur, int nouvelleValeur) {}
			virtual void quandMunitionsChange(int ancienneValeur, int nouvelleValeur) {}
			virtual void quandVieChange(int ancienneValeur, int nouvelleValeur) {}
			virtual void quandPoseChange(int ancienX, int ancienY, int ancienneOrientation, int nouvelX, int nouvelY, int nouvelleOrientation) {}
	};

Cependant elle ne pourra accéder qu’aux méthodes et attributs public et protected (private étant accessible uniquement à l’intérieur des méthodes la classe Agent).

Éléments intéressants, la classe Agent parent dispose de méthodes virtuelles, ce qui signifie que la classe enfant peut les « surcharger » et redéfinir ces propres comportement, par dessus ou à la place des méthodes parentes :

  • quandDistanceFrontaleChange() : appelée à chaque tour de jeu, quand l’agent détecte un enemi dans son champs de vision
  • quandMunitionsChange() : appelée à chaque tour de jeu, quand l’agent perd une munition, signifiant qu’un tir a bien été effectué
  • quandVieChange() : appelée à chaque tour de jeu, quand l’agent est sous le feu ennemi
  • quandPoseChange() : appelée à chaque tour de jeu, quand l’agent bouge ou change d’orientation
  • quandActualiser() : appelée en dernier à chaque tour du jeu

Sachant çela, il ne vous reste plus qu’à implémenter le corps des méthodes suivantes dans le fichier AgentSpecial.cpp, de manière à définir un comportement intelligent !

Maintenant, à vous de jouer !

Les règles du jeu

Les agents combattent sur un plateau constitué de 15 lignes (y allant de 0 à 14) et 15 colonnes (x allant de 0 à 14).

Plateau du jeu

Chaque tour du jeu, un agent peut effectuer jusqu’à 4 actions en même temps parmi les suivantes :

Actions possibles par un agent à chaque tour de jeu

Ces ordres d’actions peuvent être effectués en appelant les méthodes suivantes (depuis les méthodes surchargées dans la classe AgentSpecial) :

  • tirer() : en passant « true » pour activer tir en rafale, ou false pour désactiver
  • déplacer() : en passant les coordonnées de la case destination (attention de bien choisir une case adjacente dans la limite du plateau)
  • orienter() : en passant un des quatre points cardinaux (Nord, Sud, Est, Ouest)

Chaque tour de jeu, l’agent reçoit 5 variables actualisées qui l’informe de son état dans son environnement :

Données renseignées à l’agent à chaque tour de jeu

Ces données d’état peuvent être récupérées en appelant les méthodes suivantes (depuis les méthodes surchargées dans la classe AgentSpecial) :

  • viesRestantes() : renvoie un entier compris entre 0 et VIE_MAX (égal à 100)
  • munitionsRestantes() : renvoie un entier compris entre 0 et MUNITIONS_MAX (égal à 10)
  • x(), y(): renvoie un entier compris respectivement entre 0 et X_MAX et 0 et Y_MAX
  • orientation() : renvoie un des quatre points cardinaux (Nord, Sud, Est, Ouest)
  • distanceFrontale() : renvoie la distance à un obstacle devant l’agent (0 si pas d’obstacle en vue)

Modifier le comportement des rendus

Dans MoteurRenduAgentSpecial.hpp et MoteurRenduAgentSpecial.cpp

Initialisation du jeu

Dans main.cpp