Projet : étape 3.4
C'est le printemps!

Buts: Les animaux se reproduisent. Évidemment nous appliquerons les règles de ségrégation de rigueur: nous n'autoriserons pas les lézards à convoler en juste noces avec des scorpions ou des cactus...

Après avoir codé les parties précédentes, vous devriez disposer de suffisamment de bagages pour être un peu plus libre dans le codage de cette partie.

L'énoncé est donc moins détaillé que pour les parties précédentes.

Qui s'intéresse à qui

De la même façon que les animaux sont sélectifs pour la nourriture, ils le sont aussi pour le choix d'un partenaire.

Un schéma de «double dispatch», analogue à celui mis en place pour la nourriture, doit être implémentés pour les entités organiques :

virtual bool matable(OrganicEntity const* other) const = 0;
virtual bool canMate(Scorpion const* scorpion) const = 0;
virtual bool canMate(Lizard const* lizard) const = 0;
virtual bool canMate(Cactus const* food) const = 0;

La méthode matable doit retourner true si this peut avoir pour partenaire de reproduction other.

Par exemple, Lizard::matable(OrganicEntity const* other) const retournera : other->canMate(this); (de façon analogue à ce qui a été fait pour la quête de nourriture).

La méthode canMate(Type const* mate) doit retourner true quand this est du même type que mate, que this et mate sont de sexe opposé, que mate n'est pas enceinte, n'est pas entrain de mettre bas, qu'il a assez d'énergie pour se reproduire et qu'il est suffisamment âgé pour le faire. Le tout, sans test de type, bien sûr !

Attention L'implémentation proposée de matable ne teste que si this peut se reproduire avec other et non l'inverse. Deux partenaires p1 et p2 ne pourront se reproduire que si p1->matable(p2) and p2->matable(p1) (un seul des appels ne suffit pas à tester qu'ils ont tous les deux d'accord :-/).

Test 20  : reproduction («double dispatch»)

Un test non-graphique analogue à celui fourni pour la consommation de nourriture est fourni dans le fichier MatableTest.cpp du répertoire src/Tests/unitTests.

Ouvrez ce test et examinez-le pour comprendre ce qu'il fait. Son lancement au moyen de la cible matableTest devrait produire la sortie suivante :

    ./build/MatableTest
Using ./build/../res/app.json for configuration.
===============================================================================
All tests passed (38 assertions in 2 test cases)

  
On ne peut pas encore vérifier le fait que les partenaires potentielles enceintes ne doivent pas être retenues comme cible. Tester cette situation impliquerait de coder un "setter" pour forcer certaines femelles à être répertoriées comme enceintes : on permettrait ainsi des "fécondations" sans paternité concrète. Nous ne voulons pas ici nous engager sur des voies théologiques et encore moins casser l'encapsulation. Le cas des femelles enceintes sera donc testé un peu plus tard (test graphique plus bas).

Retouche à la méthode updateState

Parmi les entités organiques présentes dans son champ de vision, l'animal doit aussi maintenant tenir compte des partenaires possibles pour la reproduction.

Jusqu'ici, pour la nourriture, l'animal:

  1. sélectionne la cible à manger la plus proche;
  2. s'il y en a une :
    1. il se met en mode FEEDING et la consomme, s'il est en contact avec;
    2. sinon, il se met en mode FOOD_IN_SIGHT
  3. si aucune nourriture n'est en vue, il se met dans l'état WANDERING.

Il faut maintenant faire quelque chose de tout à fait analogue pour les partenaires :

  1. sélectionner le partenaire le plus proche;
  2. s'il y en a un:
    1. se mettre en mode MATING s'il y a rencontre (c'est à dire collision) et ... gérer la rencontre (pas très romantique.. mais c'est de l'informatique après tout);
    2. sinon, se mettre en mode MATE_IN_SIGHT; c'est à dire répertorier le partenaire comme cible;
  3. etc.

Si les cibles sont à distances égales, le choix de celle retenue est laissé à votre libre appréciation.

Rencontre

Nous parlions un peu plus haut de «gérer la rencontre» ... nous y voilà.

Prosaïquement, nous disposons d'un partenaire potentiel, mate, stocké dans une OrganicEntity* et nous voulons programmer une méthode meet gérant la rencontre de this avec mate.

[Question Q3.12] Comment proposez-vous d'utiliser le «double dispatch» pour coder la méthode meet sans faire de test de type ? Répondez à cette question dans votre fichier REPONSES.

La méthode meet devrait gérer de façon adéquate la rencontre d'un animal avec n'importe quelle type d'entité organique : seule la méthode meet s'appliquant aux partenaires de même type devrait donner lieu à des naissances.

La méthode meet ne devrait donc avoir aucun effet lorsqu'elle s'applique à des paires d'entités organiques qui ne sont pas compatibles pour la reproduction.

L'algorithme suivant sera mis en place pour gérer la situation où deux animaux compatibles se rencontrent :

  1. La femelle tombe enceinte (marquée comme telle) d'un nombre donné de bébés. Elle perd de l'énergie à hauteur du nombre de bébé portés (getAppConfig().lizard_energy_loss_female_per_child). On anticipe ainsi l'énergie perdue pendant le temps de gestation. Le nombre de bébés sera aléatoirement généré entre deux bornes (getAppConfig().lizard_min_children, getAppConfig().lizard_max_children pour les lézards et avec comme d'habitude l'équivalent pour les scorpions).
  2. Le mâle perd de l'énergie (getAppConfig().lizard_energy_loss_mating_male);
  3. La femelle reste enceinte pendant un temps de gestation propre à chaque type d'animal (getAppConfig().lizard_gestation_time).
La tournure uniform(min, max) permet de générer aléatoirement un entier positif compris entre les bornes min et max. La fonction uniform est définie dans le matériel fourni du répertoire Random/.

[Question Q3.13] Comment proposez-vous de mettre en place le temps de gestation. Répondez à cette question dans votre fichier REPONSES.

Il faudra aussi désormais gérer, au bon endroit, la fin du temps de gestation de l'animal si c'est une femelle enceinte: lorsque le temps de gestation est écoulé, la femelle se met dans l'état GIVING_BIRTH et donne naissance à ses bébés.

L'animal observait un temps de pause dans l'état FEEDING. Il observera de façon similaire un temps de pause (dans les états MATING et GIVING_BIRTH (vous pouvez utiliser les paramètres getAppConfig().animal_mating_pause_time et getAppConfig().animal_delivery_pause_time).

[Question Q3.14] Un animal ne peut donner naissance qu'à des animaux de son type, comment proposez vous de mettre en oeuvre la méthode give_birth (ou équivalent) dans la hiérarchie d'animaux. Répondez à cette question dans votre fichier REPONSES.

[Question Q3.15] Comment stockez-vous le nombre de bébés attendus (utile pour mettre en place la naissance lorsque le temps de gestation est écoulé). Répondez à cette question dans votre fichier REPONSES.

Une fois les bébés nés, il doivent bien sûr être intégrés à l'environnement.

Affichages propres à la reproduction

Pour faciliter le debugging, il est utile de faire afficher les femelles enceintes d'une façon particulière. Adaptez vos méthodes d'affichage de sorte à ce qu'elles s'affichent selon le mode suivant par exemple (la couleur sf::Color::Magenta a été utilisée ici):

Modele

Test 21 : reproduction

Le fichier de test Tests/GraphicalTest/ReproductionTest est fourni pour tester ces derniers développements. La cible associée est reproductionTest. Un fichier de configuration particulier avec des conditions favorables pour observer la reproduction (grande longévité, âge minimal de reproduction très bas, perte d'énergie minimale lors du déplacement etc.) est aussi fourni. Il s'agit du fichier reprod.json. Ce test vous permet de :

Commencez par utiliser ce test ce façon analogue au test 14 (notamment en mode «debugging» et en observant les états affichés) pour vous assurer que :

Voici un exemple de situation typique créée pour un test :

Les partenaires pour la reproduction priment sur la nourriture (il faut avoir mis l'âge de reproduction à zéro pour observer cela, sinon les partenaires sont trop jeunes et vont d'abord vers la nourriture).

Voici un autre exemple de test possible:

Lorsque deux lézards de sexe opposés se rencontrent, la femelle tombe enceinte et porte alors des petits pendant un certain temps puis donne naissance. Un temps de pause est observé pendant la rencontre et au moment des naissances (ici, le lézard a porté un seul petit, mais ce n'est pas une règle générale). Les petits commencent tout de suite à se déplacer alors que la mère reste un moment immobile dans l'état GIVING_BIRTH. Libre à vous, en bonus, de complexifier le comportement des nouveaux-nés pour le rendre plus réaliste (ils sont plus petits en taille que les adultes, ils restent un moment près de la mère etc.)

Vous vérifierez que les conditions voulues pour la mise en oeuvre de la reproduction sont bien respectées (âge minimal, niveaux d'énergie, femelles portantes ou en train de mettre bas etc.). Notez qu'il peut être utile dans un premier temps de rendre vos animaux stériles (nombre minimal et maximal d'enfants à zéro dans le fichier de configuration).Il est plus facile d'observer que les transitions d'état et les comportements sont ceux attendus s'il n'y a qu'un seul couple.


Retour à l'énoncé du projet (partie 3) Module suivant (partie 3.5)