Cartouche

Champ Valeur
Auteur·e Élise
Édition 2023-09-15
Durée une journée
Taille des équipes 1 personne
Rendu via git, dépôt 2023_cpp_c4, droits en lecture à delivery_collector

Barème

Critère Points
Pikachu 2
Infirmière Joëlle 2
Salamèche 2
Salamèche et l’infirmière 1
Interface Pokémon 4
Pokémon Abstrait 4
Interface AsciiMap 5
Total des critères 20
Note maximale 20
Fautes de norme -0,5 par faute

Règlement

La réalisation de cet exercice est assujettie aux règles en vigueur dans l’école en matière de triche et de normalisation. Tricher vous expose à de graves sanctions.

Vous devez être seul·e auteur·e de votre rendu mais vous êtes autorisés et encouragés à communiquer dans le cadre de ce sujet.

Dépôt de référence et norme

Annexe 1 • Norme C++

Dépôt de référence avec règles clang format.

Tâches

Pikachu

Fichiers à rendre : pkmn/{Pikachu.{cpp,hpp},main.cpp}

Consignes

Dans un namespace pkmn, écrivez une classe Pikachu, avec les fonctions membres suivantes :

  • species renvoie l’espèce du pokémon dans une chaîne (ici tout le temps “Pikachu”),
  • type renvoie le type du pokémon dans une chaîne (ici tout le temps “Electric”),
  • name renvoie le surnom du pokémon que le dresseur à choisi,
  • health renvoie une référence vers la santé du Pokémon (par défaut 100 hp)
  • fainted renvoie une booléen indiquant si oui ou non Pikachu est KO (lorsque 0 HP).

Toutes les chaînes renvoyées doivent être des références constantes.

health doit renvoyer une référence modifiable vers la santé, regardez comment elle est utilisée dans l’exemple.

Enfin, à la construction, un surnom doit être passé pour le Pikachu.

Exemple

void dump_pika(const pkmn::Pikachu &pika) {
	std::cout << pika.species() << " -- of type: "
		  << pika.type() << " -- named: "
		  << pika.name() << "-- hp " << pika.health()
		  << std::endl;

	std::string_view given_name{pika.name()};

	auto numberof_e{std::count(given_name.begin(),
				   given_name.end(),
				   'e')};
	std::cout << "its name contains " << numberof_e << " time·s the letter e.";
}

int main(void) {
	pkmn::Pikachu pika("sbeeve");

	dump_pika(pika);
	pika.health() -= 10;
	dump_pika(pika);
	pika.health() -= 90;
	std::cout << "has fainted? " << ((pika.fainted()) ? "yes" : "no") << "\n";
}

Sortie du programme :

Pikachu -- of type: Electric -- named: sbeeve -- hp 100
its name contains 3 time·s the letter e.
Pikachu -- of type: Electric -- named: sbeeve -- hp 90
its name contains 3 time·s the letter e.
has fainted? yes

Infirmière Joëlle

Fichiers à rendre : pkmn/{Nurse.{cpp,hpp},main.cpp}

Dans le namespace pkmn, écrivez une classe Nurse avec les fonctions membres :

  • heal prend un pokémon en paramètre et lui rend toute sa santé,
  • name renvoie le nom de l’infirmière dans une chaîne.

Toutes les infirmières s’appellent “Joy”.

Toutes les chaînes renvoyées doivent être des références constantes.

La fonction heal affiche les phrases suivante sur la sortie standard :

  • “There there $name, you should feel way better now!”:
    • si la santé était plus petite ou égale à 50 ;
  • “That should do it, $name!”
    • si la santé est entre 50 et 100 (exclus)
  • “You already are in tip-top shape, $name!”
    • si le pokémon est en parfaite santé

Remplacez $name par le surnom du Pikachu.

int main(void) {
	pkmn::Pikachu pika("Daniel");
	pkmn::Nurse nurse;

	pika.health() -= 13;
	std::cout << pika.health() << std::endl;
	nurse.heal(pika);
	std::cout << pika.health() << std::endl;
}
87
That should do it, Daniel!
100

Salamèche

Fichiers à rendre : pkmn/{Charmander.{cpp,hpp},main.cpp}

Consignes

Dans un namespace pkmn, écrivez une classe Charmander, avec les fonctions membres suivantes :

  • species renvoie l’espèce du pokémon dans une chaîne (ici tout le temps “Charmander”),
  • type renvoie le type du pokémon dans une chaîne (ici tout le temps “Fire”),
  • name renvoie le surnom du pokémon que le dresseur à choisi,
  • health renvoie une référence vers la santé du Pokémon (par défaut 100 hp)
  • fainted renvoie une booléen indiquant si oui ou non Pikachu est KO (lorsque 0 HP).

Toutes les chaînes renvoyées doivent être des références constantes.

health doit renvoyer une référence modifiable vers la santé, regardez comment elle est utilisée dans l’exemple.

Exemple

void dump_charm(const pkmn::Charmander &charm) {
	std::cout << charm.species() << " -- of type: " << charm.type() << " -- named: " << charm.name() << "-- hp " << charm.health() << std::endl;
}

int main(void) {
	pkmn::Char charm("Antoine");

	dump_charm(charm);
	charm.health() -= 10;
	dump_charm(charm);
}
Charmander -- of type: Fire -- named: Antoine -- hp 100
Charmander -- of type: Fire -- named: Antoine -- hp 90

Salamèche et l’infirmière

Fichiers à modifier : pkmn/{Nurse.{cpp,hpp},main.cpp}

Ajoutez une fonction membre heal à votre Nurse qui peut soigner Charmander et affiche les mêmes messages que celle qui soigne Pikachu.

Interface Pokémon

Fichiers à rendre : pkmn/{IPokemon.hpp,Pikachu.{cpp,hpp},Charmander.{cpp,hpp},Nurse.{cpp,hpp}}

Contexte

Vous avez peut-être remarqué que l’on est obligés d’avoir une fonction membre en double chez notre Nurse alors qu’elles font la même chose, avec des fonctions membres appelées pareils sur deux classes différentes.

On est dans une situation de sur-spécificité et on peut faire deux choses pour en sortir :

  • transformer heal en une fonction template
  • fusionner les deux fonctions heal et leur faire prendre une interface en paramètre (ce qui induit créer une interface pour nos deux Pokémon)

Ce qui nous intéresse ici c’est la deuxième méthode.

Consignes

Créez une interface donc les fonctions membres sont toutes virtuelles pures et ont la même signature que dans votre Pikachu et votre Charmander.

Ensuite, faites hériter cette interface à Pikachu et Charmander. Rajoutez le mot clé override aux fonctions membres de ses deux-dernier.

Enfin, fusionnez les deux fonctions membres heal de l’infirmière en une seule dont le paramètre est une référence sur IPokemon.

Pokémon Abstrait

Fichiers à rendre : pkmn/{APokemon.{cpp,hpp},Pikachu.{cpp,hpp},Charmander.{cpp,hpp},Nurse.{cpp,hpp}}

Contexte

Malgré notre interface, même s’il n’y a plus de code qui se répète dans celui l’infirmière, il en reste au sein de Pikachu et Charmander.

Avec une classe abstraite il est possible d’implémenter certaines des fonctions d’une interface tout en laissant les autres virtuelles pures (sans les re-déclarer et les surcharger).

Consignes

Écrivez la classe abstraite APokemon qui hérite de IPokemon et dont Pikachu et Charmander doivent hériter.

Copier le code en commun entre les deux Pokémons pour le mettre dans la classe abstraite, et retirez-le de Pikachu et Charmander.

Il est possible qu’après ça, vous n’ayez plus du tout de code dans Pikachu et Charmander. Auquel cas votre classe APokemon ne serait plus réellement un classe abstraite. Que APokemon soit réellement abstraite ou non, ça n’est pas grave pour avoir les points de cet exercice.

AsciiMap interface

Fichiers à rendre : ami/{IAsciiMap.hpp,AsciiMap.{cpp,hpp},AsciiMapClip.{cpp,hpp},main.cpp}

Contexte

Pasted image 20230918120649.png

Souvenez-vous lorsque vous avec implémenté votre AsciiMap et votre AsciiMapClip dans le chapitre 2, je vous avais donné des exemples qui étaient capables de dessiner soit sur un AsciiMap, soit sur un AsciiMapClip à l’aide de la fonction memberset_cell.

void mk_line(AsciiMap &map, unsigned int y)
{
    auto i{0U};

    while (i < map.width()) {
        if (map.get_cell(i, y) == '|') {
            map.set_cell(i, y, '+');
        } else {
            map.set_cell(i, y, '-');
        }
        i += 1;
    }
}

void draw_x(AsciiMapClip &clip)
{
    clip.set_cell(0, 0, '\\');
    clip.set_cell(2, 2, '\\');
    clip.set_cell(1, 1, 'X');
    clip.set_cell(2, 0, '/');
    clip.set_cell(0, 2, '/');
}

Le problème ici c’est que draw_x ne peux que marcher si on lui passe un clip et mk_line ne peux que marcher sur une AsciiMap. Même si on utilise des fonctions membres qui existent sur les deux classes. (set_cell et get_cell)

Consignes

Écrivez l’interface IAsciiMap qui définie les fonctions virtuelles pures set_cell et get_cell avec la même signature qu’elles ont sur vos AsciiMap et AsciiMapClip.

Faites hériter AsciiMap et AsciiMapClip de IAsciiMap.

Adaptez les fonctions mk_line et draw_x de sorte à ce qu’elles puissent fonctionner avec n’importe laquelle des deux classes.

Rendez également un main avec lequel vous les avez testées.