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 !
- Les valeurs seuils pour l'énergie sont paramétrables et accessibles via getAppConfig().lizard_energy_min_mating_female, getAppConfig().lizard_energy_min_mating_male (avec l'équivalent pour les scorpions).
- L'âge minimal pour la reproduction est aussi paramétrable et est donné par getAppConfig().lizard_min_age_mating et getAppConfig().scorpion_min_age_mating
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)
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:
- sélectionne la cible à manger la plus proche;
- s'il y en a une :
- il se met en mode FEEDING et la consomme, s'il est en contact avec;
- sinon, il se met en mode FOOD_IN_SIGHT
- 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 :
- sélectionner le partenaire le plus proche;
- s'il y en a un:
- 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);
- sinon, se mettre en mode MATE_IN_SIGHT; c'est à dire répertorier le partenaire comme cible;
- etc.
- On considérera que la reproduction prime sur la consommation de nourriture. Si l'animal voit aussi bien de la nourriture qu'un partenaire, il s'intéressera d'abord au partenaire (passera à MATING ou MATE_IN_SIGHT en priorité).
- La méthode updateState doit désormais faire divers usages des entités vues par l'animal : déterminer la source de nourriture la plus proche et déterminer le partenaire le plus proche. Plus tard, viendra aussi la détermination de l'ennemi le plus proche. Comme déjà suggéré, il peut être judicieux de programmer une méthode analyzeEnvironment qui permet de différencier parmi les entités vue laquelle est une source de nourriture, laquelle est un partenaire potentiel (et laquelle est un ennemi). Ces informations peuvent être stockées dans une sorte de «mémoire» de l'animal, sous la forme d'attributs.
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.
L'algorithme suivant sera mis en place pour gérer la situation où deux animaux compatibles se rencontrent :
- 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).
- Le mâle perd de l'énergie (getAppConfig().lizard_energy_loss_mating_male);
- La femelle reste enceinte pendant un temps de gestation propre à chaque type d'animal (getAppConfig().lizard_gestation_time).
[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.
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):

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 :
- créer des lézards mâles au moyen de la touche 'Ctrl-L' et des lézards femelles au moyen de la touche 'L'
- créer des scorpions mâles au moyen de la touche 'Ctrl-S' et des scorpions femelles au moyen de la touche 'S'
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 :
- les lézards se ciblent entre eux pour la reproduction, les scorpions également, mais que seuls les animaux de sexes opposés se ciblent;
- que les autres interactions ne concernent que la quête de nourriture;
- que des animaux ayant trop peu d'énergie ou trop jeunes ne sont pas intéressés par la reproduction (n'oubliez pas que vous pouvez configurer l'énergie de départ des animaux ou les seuils d'énergie nécessaires à la reproduction);
- et que la reproduction prime sur la quête de nourriture (pour tester cela vous pouvez mettre momentanément l'âge minimal de reproduction à zéro dans reprod.json).
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.