Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.
javaquarium [2016/05/09 13:23] gbdivers |
javaquarium [2016/05/30 20:08] (Version actuelle) gbdivers [Exercice 3.3 : Mais… la sexualité des poissons est horriblement compliquée !] |
||
---|---|---|---|
Ligne 19: | Ligne 19: | ||
Les codes sont testés directement sur Coliru.com. | Les codes sont testés directement sur Coliru.com. | ||
- | ===== Exercice 1.1 : Remplissage de l'aquarium ===== | + | ===== Partie 1 : Peuplons notre Javaquarium ===== |
+ | |||
+ | ==== Exercice 1.1 : Remplissage de l'aquarium ==== | ||
Dans cette première partie, le bu est de simplement créer une liste de poisson et d'algues, et de les afficher. | Dans cette première partie, le bu est de simplement créer une liste de poisson et d'algues, et de les afficher. | ||
Ligne 122: | Ligne 124: | ||
</code> | </code> | ||
- | ==== Contrat et encapsulation ==== | + | === Contrat et encapsulation === |
Cette implémentation peut paraître surprenante : je ne crées aucune classe. Surtout que cet exercice est à la base un exercice de programmation orientée objet. Mais le but est justement d'expliquer l'intérêt de la programmation orientée objet. | Cette implémentation peut paraître surprenante : je ne crées aucune classe. Surtout que cet exercice est à la base un exercice de programmation orientée objet. Mais le but est justement d'expliquer l'intérêt de la programmation orientée objet. | ||
Ligne 207: | Ligne 209: | ||
- | ===== Partie 1.2 ===== | + | ==== Exercice 1.2 : un peu de diversité ==== |
Dans cette seconde partie, le but est de créer différentes races de poissons et d'ajouter une fonction pour que les poisson puissent manger. | Dans cette seconde partie, le but est de créer différentes races de poissons et d'ajouter une fonction pour que les poisson puissent manger. | ||
Ligne 372: | Ligne 374: | ||
</code> | </code> | ||
- | ==== Typage fort ==== | + | === Typage fort === |
Dans cette partie, j'ai choisis d'utiliser une énumération pour définir toutes les races (algues, carnivores et herbivores). | Dans cette partie, j'ai choisis d'utiliser une énumération pour définir toutes les races (algues, carnivores et herbivores). | ||
Ligne 424: | Ligne 426: | ||
Cette technique est très puissante et est l'une des forces des langages de programmation a typage fort. Au lieu de devoir vérifier les valeurs a l’exécution, on construit un type qui exprime directement les contraintes sur les données, ce qui permet de faire les vérifications a la compilation (améliorer la qualité logicielle) et gagner en performances. | Cette technique est très puissante et est l'une des forces des langages de programmation a typage fort. Au lieu de devoir vérifier les valeurs a l’exécution, on construit un type qui exprime directement les contraintes sur les données, ce qui permet de faire les vérifications a la compilation (améliorer la qualité logicielle) et gagner en performances. | ||
- | ===== Partie 2.1 ===== | + | ===== Partie 2 : Mange, tu ne sais pas qui te mangera ===== |
+ | |||
+ | ==== Exercice 2.1 : Miam miam miam ! ==== | ||
Dans cette partie, on ajoute un système de tours : a chaque tour, tous les poissons mangent quelque chose au hasard. | Dans cette partie, on ajoute un système de tours : a chaque tour, tous les poissons mangent quelque chose au hasard. | ||
Ligne 432: | Ligne 436: | ||
Dans un ECS, il est préférable de séparer le traitement des entités en deux ou trois phases : | Dans un ECS, il est préférable de séparer le traitement des entités en deux ou trois phases : | ||
- | * l'utilisation des composants par les systemes ; | + | * l'utilisation des composants par les systèmes ; |
- | * la creation et la suppression des entites ; | + | * la création et la suppression des entités ; |
* la création et la suppression des composants. | * la création et la suppression des composants. | ||
- | Dans ce cas, je prefere modifier legerement le gameplay, de facon a avoir quelque chose de plus simple au niveau gestion des elements. Au lieu de supprimer directement un poisson qui se fait manger, je fais un premier passage pour determiner qui mange qui, en conservant dans une liste separee les entites qui doivent etre supprimee. | + | Dans ce cas, je préfère modifier légèrement le gameplay, de façon a avoir quelque chose de plus simple au niveau gestion des éléments. Au lieu de supprimer directement un poisson qui se fait manger, je fais un premier passage pour déterminer qui mange qui, en conservant dans une liste séparée les entités qui doivent être supprimée. |
- | Cette approche n'est pas forcement incoherente. Si A mange B et B mange A, pourquoi l'une des deux entites gagnerait plus que l'autre ? Surtout que le choix de l'entite qui gagne depend de l'ordre d'apparition dans le ''vector'', ce qui est un peu arbitraire. En separant en deux phases, cela est equivalent a avoir une double mort. | + | Cette approche n'est pas forcement incohérente. Si A mange B et B mange A, pourquoi l'une des deux entités gagnerait plus que l'autre ? Surtout que le choix de l’entité qui gagne dépend de l'ordre d'apparition dans le ''vector'', ce qui est un peu arbitraire. En séparant en deux phases, cela est équivalent a avoir une double mort. |
<code cpp main.cpp> | <code cpp main.cpp> | ||
Ligne 646: | Ligne 650: | ||
</code> | </code> | ||
- | ===== Partie 2.2 ===== | + | |
+ | ==== Exercice 2.2 : un peu de douceur dans ce monde de brutes ==== | ||
Dans cette partie, on ajoute la gestion des points de vie. | Dans cette partie, on ajoute la gestion des points de vie. | ||
- | Note : comme ce code est volontairement mal encapsule, la fonction main fait beaucoup (trop) de choses. Beaucoup de ces choses devraient etre dans des systemes et correctement organisees en fonctions. | + | Note : comme ce code est volontairement mal encapsule, la fonction main fait beaucoup (trop) de choses. Beaucoup de ces choses devraient être dans des systèmes et correctement organisées en fonctions. |
<code cpp main.cpp> | <code cpp main.cpp> | ||
Ligne 672: | Ligne 677: | ||
using name = std::string; | using name = std::string; | ||
using is_male = bool; | using is_male = bool; | ||
- | using detail = std::pair<name, is_male>; | + | using detail_component = std::tuple<entity::id, name, is_male>; |
- | using detail_component = std::pair<entity::id, detail>; | + | |
enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe }; | enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe }; | ||
Ligne 717: | Ligne 721: | ||
std::vector<entity::id> entities; | std::vector<entity::id> entities; | ||
std::for_each(begin(internal::_types), end(internal::_types), | std::for_each(begin(internal::_types), end(internal::_types), | ||
- | [&entities, predicat](auto t){ if (predicat(std::get<1>(t))) { entities.push_back(std::get<0>(t)); } } | + | [&entities, predicat](const auto type){ if (predicat(type)) { entities.push_back(std::get<0>(type)); } } |
); | ); | ||
return entities; | return entities; | ||
Ligne 744: | Ligne 748: | ||
const auto id = create_entity(); | const auto id = create_entity(); | ||
internal::_types.push_back(std::make_tuple(id, t, 10)); | internal::_types.push_back(std::make_tuple(id, t, 10)); | ||
- | internal::_details.push_back(std::make_pair(id, std::make_pair(n, m))); | + | internal::_details.push_back(std::make_tuple(id, n, m)); |
return id; | return id; | ||
} | } | ||
Ligne 753: | Ligne 757: | ||
const auto details_it = std::remove_if(internal::_details.begin(), internal::_details.end(), | const auto details_it = std::remove_if(internal::_details.begin(), internal::_details.end(), | ||
- | [id](auto p){ return (p.first == id); }); | + | [id](auto t){ return (std::get<0>(t) == id); }); |
internal::_details.erase(details_it, internal::_details.end()); | internal::_details.erase(details_it, internal::_details.end()); | ||
Ligne 790: | Ligne 794: | ||
ecs::print(); | ecs::print(); | ||
- | for (size_t tour {}; tour < 20; ++tour) { | + | for (size_t tour {}; tour < 21; ++tour) { |
std::cout << "===== Tour " << tour << " ======" << std::endl; | std::cout << "===== Tour " << tour << " ======" << std::endl; | ||
- | // update pv | + | // update pv and age |
for (auto entity: ecs::internal::_entities) { | for (auto entity: ecs::internal::_entities) { | ||
- | auto const& type = ecs::get_component(ecs::internal::_types, entity); | + | const auto type = ecs::get_component(ecs::internal::_types, entity); |
+ | // pv | ||
if (ecs::is_algue_t(*type)) { | if (ecs::is_algue_t(*type)) { | ||
ecs::get_point_vie(*type) += 1; | ecs::get_point_vie(*type) += 1; | ||
Ligne 804: | Ligne 809: | ||
// eat | // eat | ||
- | const auto herbivores { ecs::get_entities(ecs::is_herbivore) }; | + | const auto herbivores = ecs::get_entities(ecs::is_herbivore_t); |
- | const auto carnivores { ecs::get_entities(ecs::is_carnivore) }; | + | const auto algues = ecs::get_entities(ecs::is_algue_t); |
- | const auto algues { ecs::get_entities(ecs::is_algue) }; | + | |
if (!algues.empty()) { | if (!algues.empty()) { | ||
- | for (auto herbivore: herbivores) { | + | for (const auto herbivore: herbivores) { |
- | auto herbivore_type = ecs::get_component(ecs::internal::_types, herbivore); | + | const auto herbivore_type = ecs::get_component(ecs::internal::_types, herbivore); |
- | + | ||
- | if (herbivore_type != end(ecs::internal::_types)) | + | if (herbivore_type != end(ecs::internal::_types) && ecs::get_point_vie(*herbivore_type) < 5) |
{ | { | ||
std::uniform_int_distribution<size_t> algue_distribution(0, algues.size() - 1); | std::uniform_int_distribution<size_t> algue_distribution(0, algues.size() - 1); | ||
const auto index { algue_distribution(ecs::internal::_random_engine) }; | const auto index { algue_distribution(ecs::internal::_random_engine) }; | ||
const auto algue = algues[index]; | const auto algue = algues[index]; | ||
- | + | ||
- | auto const& algue_type = ecs::get_component(ecs::internal::_types, algue); | + | const auto algue_type = ecs::get_component(ecs::internal::_types, algue); |
ecs::get_point_vie(*herbivore_type) += 3; | ecs::get_point_vie(*herbivore_type) += 3; | ||
ecs::get_point_vie(*algue_type) -= 2; | ecs::get_point_vie(*algue_type) -= 2; | ||
Ligne 825: | Ligne 829: | ||
} | } | ||
- | if (ecs::internal::_entities.size() > 1) { // le carnivore n'est pas seul | + | const auto carnivores = ecs::get_entities(ecs::is_carnivore_t); |
- | for (auto carnivore: carnivores) { | + | for (const auto carnivore_id: carnivores) { |
- | auto carnivore_type = ecs::get_component(ecs::internal::_types, carnivore); | + | const auto carnivore_type = ecs::get_component(ecs::internal::_types, carnivore_id); |
- | if (carnivore_type != end(ecs::internal::_types)) | + | if (carnivore_type != end(ecs::internal::_types) && ecs::get_point_vie(*carnivore_type) < 5) // a faim ? |
- | { | + | { |
- | auto entity = carnivore; | + | const auto carnivore_race = std::get<1>(*carnivore_type); |
- | while (entity == carnivore) { | + | |
- | std::uniform_int_distribution<size_t> entities_distribution(0, ecs::internal::_entities.size() - 1); | + | const auto bons_petits_plats = ecs::get_entities( |
- | const auto index { entities_distribution(ecs::internal::_random_engine) }; | + | [carnivore_id, carnivore_race](const auto type){ |
- | entity = ecs::internal::_entities[index]; | + | const auto id = std::get<0>(type); |
+ | const auto race = std::get<1>(type); | ||
+ | return ( | ||
+ | ecs::is_poisson(race) && | ||
+ | (id != carnivore_id) && | ||
+ | (race != carnivore_race) | ||
+ | ); | ||
} | } | ||
+ | ); | ||
+ | |||
+ | if (!bons_petits_plats.empty()) { | ||
+ | std::uniform_int_distribution<size_t> entities_distribution(0, bons_petits_plats.size() - 1); | ||
+ | const auto index { entities_distribution(ecs::internal::_random_engine) }; | ||
+ | |||
+ | const auto entity_id = bons_petits_plats[index]; | ||
+ | const auto entity_type = ecs::get_component(ecs::internal::_types, entity_id); | ||
- | auto const& entity_type = ecs::get_component(ecs::internal::_types, entity); | ||
if (entity_type != end(ecs::internal::_types)) | if (entity_type != end(ecs::internal::_types)) | ||
{ | { | ||
Ligne 855: | Ligne 872: | ||
std::vector<ecs::entity::id> entities_to_remove; | std::vector<ecs::entity::id> entities_to_remove; | ||
for (auto entity: ecs::internal::_entities) { | for (auto entity: ecs::internal::_entities) { | ||
- | auto type = ecs::get_component(ecs::internal::_types, entity); | + | const auto type = ecs::get_component(ecs::internal::_types, entity); |
- | if (type != end(ecs::internal::_types) && ecs::get_point_vie(*type) <= 0) { | + | if (type != end(ecs::internal::_types) && (ecs::get_point_vie(*type) <= 0 )) |
+ | { | ||
entities_to_remove.push_back(entity); | entities_to_remove.push_back(entity); | ||
} | } | ||
} | } | ||
- | + | ||
for (auto entity: entities_to_remove) { | for (auto entity: entities_to_remove) { | ||
ecs::remove_entity(entity); | ecs::remove_entity(entity); | ||
Ligne 892: | Ligne 910: | ||
id type pv | id type pv | ||
0 Algue 11 | 0 Algue 11 | ||
- | 1 Algue 9 | + | 1 Algue 11 |
2 Algue 11 | 2 Algue 11 | ||
3 Algue 11 | 3 Algue 11 | ||
- | 4 Algue 9 | + | 4 Algue 11 |
- | 5 Sole 8 | + | 5 Sole 9 |
- | 6 Sole 8 | + | 6 Sole 9 |
- | 7 Mérou 14 | + | 7 Mérou 9 |
- | 8 Mérou 14 | + | 8 Mérou 9 |
===== Tour 1 ====== | ===== Tour 1 ====== | ||
9 entites: | 9 entites: | ||
id type pv | id type pv | ||
0 Algue 12 | 0 Algue 12 | ||
- | 1 Algue 10 | + | 1 Algue 12 |
- | 2 Algue 10 | + | 2 Algue 12 |
- | 3 Algue 10 | + | 3 Algue 12 |
- | 4 Algue 6 | + | 4 Algue 12 |
- | 5 Sole 10 | + | 5 Sole 8 |
- | 6 Sole 10 | + | 6 Sole 8 |
- | 7 Mérou 16 | + | 7 Mérou 8 |
- | 8 Mérou 16 | + | 8 Mérou 8 |
===== Tour 2 ====== | ===== Tour 2 ====== | ||
9 entites: | 9 entites: | ||
id type pv | id type pv | ||
- | 0 Algue 11 | + | 0 Algue 13 |
- | 1 Algue 7 | + | 1 Algue 13 |
- | 2 Algue 11 | + | 2 Algue 13 |
- | 3 Algue 11 | + | 3 Algue 13 |
- | 4 Algue 7 | + | 4 Algue 13 |
- | 5 Sole 8 | + | 5 Sole 7 |
- | 6 Sole 12 | + | 6 Sole 7 |
- | 7 Mérou 18 | + | 7 Mérou 7 |
- | 8 Mérou 20 | + | 8 Mérou 7 |
===== Tour 3 ====== | ===== Tour 3 ====== | ||
9 entites: | 9 entites: | ||
id type pv | id type pv | ||
- | 0 Algue 10 | + | 0 Algue 14 |
- | 1 Algue 8 | + | 1 Algue 14 |
- | 2 Algue 8 | + | 2 Algue 14 |
- | 3 Algue 12 | + | 3 Algue 14 |
- | 4 Algue 6 | + | 4 Algue 14 |
- | 5 Sole 10 | + | 5 Sole 6 |
- | 6 Sole 14 | + | 6 Sole 6 |
- | 7 Mérou 20 | + | 7 Mérou 6 |
- | 8 Mérou 22 | + | 8 Mérou 6 |
===== Tour 4 ====== | ===== Tour 4 ====== | ||
9 entites: | 9 entites: | ||
id type pv | id type pv | ||
- | 0 Algue 11 | + | 0 Algue 15 |
- | 1 Algue 9 | + | 1 Algue 15 |
- | 2 Algue 9 | + | 2 Algue 15 |
- | 3 Algue 13 | + | 3 Algue 15 |
- | 4 Algue 3 | + | 4 Algue 15 |
- | 5 Sole 12 | + | 5 Sole 5 |
- | 6 Sole 8 | + | 6 Sole 5 |
- | 7 Mérou 24 | + | 7 Mérou 5 |
- | 8 Mérou 26 | + | 8 Mérou 5 |
===== Tour 5 ====== | ===== Tour 5 ====== | ||
- | 9 entites: | + | 8 entites: |
id type pv | id type pv | ||
- | 0 Algue 8 | + | 0 Algue 14 |
- | 1 Algue 8 | + | 1 Algue 16 |
- | 2 Algue 10 | + | 2 Algue 16 |
3 Algue 14 | 3 Algue 14 | ||
- | 4 Algue 2 | + | 4 Algue 16 |
- | 5 Sole 14 | + | 6 Sole 7 |
- | 6 Sole 10 | + | 7 Mérou 9 |
- | 7 Mérou 26 | + | 8 Mérou 9 |
- | 8 Mérou 28 | + | |
===== Tour 6 ====== | ===== Tour 6 ====== | ||
8 entites: | 8 entites: | ||
id type pv | id type pv | ||
- | 0 Algue 9 | + | 0 Algue 15 |
- | 1 Algue 9 | + | 1 Algue 17 |
- | 2 Algue 11 | + | 2 Algue 17 |
3 Algue 15 | 3 Algue 15 | ||
- | 5 Sole 8 | + | 4 Algue 17 |
- | 6 Sole 12 | + | 6 Sole 6 |
- | 7 Mérou 30 | + | 7 Mérou 8 |
- | 8 Mérou 32 | + | 8 Mérou 8 |
===== Tour 7 ====== | ===== Tour 7 ====== | ||
8 entites: | 8 entites: | ||
id type pv | id type pv | ||
- | 0 Algue 6 | + | 0 Algue 16 |
- | 1 Algue 8 | + | 1 Algue 18 |
- | 2 Algue 10 | + | 2 Algue 18 |
3 Algue 16 | 3 Algue 16 | ||
- | 5 Sole 10 | + | 4 Algue 18 |
- | 6 Sole 14 | + | 6 Sole 5 |
- | 7 Mérou 32 | + | 7 Mérou 7 |
- | 8 Mérou 34 | + | 8 Mérou 7 |
===== Tour 8 ====== | ===== Tour 8 ====== | ||
8 entites: | 8 entites: | ||
id type pv | id type pv | ||
- | 0 Algue 3 | + | 0 Algue 17 |
- | 1 Algue 5 | + | 1 Algue 19 |
- | 2 Algue 11 | + | 2 Algue 19 |
- | 3 Algue 17 | + | 3 Algue 15 |
- | 5 Sole 12 | + | 4 Algue 19 |
- | 6 Sole 16 | + | 6 Sole 7 |
- | 7 Mérou 34 | + | 7 Mérou 6 |
- | 8 Mérou 36 | + | 8 Mérou 6 |
===== Tour 9 ====== | ===== Tour 9 ====== | ||
- | 7 entites: | + | 8 entites: |
id type pv | id type pv | ||
- | 1 Algue 6 | + | 0 Algue 18 |
- | 2 Algue 8 | + | 1 Algue 20 |
- | 3 Algue 18 | + | 2 Algue 20 |
- | 5 Sole 14 | + | 3 Algue 16 |
- | 6 Sole 18 | + | 4 Algue 20 |
- | 7 Mérou 36 | + | 6 Sole 6 |
- | 8 Mérou 38 | + | 7 Mérou 5 |
+ | 8 Mérou 5 | ||
===== Tour 10 ====== | ===== Tour 10 ====== | ||
7 entites: | 7 entites: | ||
id type pv | id type pv | ||
- | 1 Algue 5 | + | 0 Algue 19 |
- | 2 Algue 7 | + | 1 Algue 21 |
- | 3 Algue 15 | + | 2 Algue 21 |
- | 5 Sole 16 | + | 3 Algue 17 |
- | 6 Sole 20 | + | 4 Algue 21 |
- | 7 Mérou 38 | + | 7 Mérou 9 |
- | 8 Mérou 40 | + | 8 Mérou 9 |
===== Tour 11 ====== | ===== Tour 11 ====== | ||
7 entites: | 7 entites: | ||
id type pv | id type pv | ||
- | 1 Algue 4 | + | 0 Algue 20 |
- | 2 Algue 6 | + | 1 Algue 22 |
- | 3 Algue 14 | + | 2 Algue 22 |
- | 5 Sole 14 | + | 3 Algue 18 |
- | 6 Sole 22 | + | 4 Algue 22 |
- | 7 Mérou 40 | + | 7 Mérou 8 |
- | 8 Mérou 44 | + | 8 Mérou 8 |
===== Tour 12 ====== | ===== Tour 12 ====== | ||
7 entites: | 7 entites: | ||
id type pv | id type pv | ||
- | 1 Algue 5 | + | 0 Algue 21 |
- | 2 Algue 5 | + | 1 Algue 23 |
- | 3 Algue 13 | + | 2 Algue 23 |
- | 5 Sole 8 | + | 3 Algue 19 |
- | 6 Sole 24 | + | 4 Algue 23 |
- | 7 Mérou 44 | + | 7 Mérou 7 |
- | 8 Mérou 48 | + | 8 Mérou 7 |
===== Tour 13 ====== | ===== Tour 13 ====== | ||
7 entites: | 7 entites: | ||
id type pv | id type pv | ||
- | 1 Algue 2 | + | 0 Algue 22 |
- | 2 Algue 6 | + | 1 Algue 24 |
- | 3 Algue 10 | + | 2 Algue 24 |
- | 5 Sole 10 | + | 3 Algue 20 |
- | 6 Sole 26 | + | 4 Algue 24 |
- | 7 Mérou 46 | + | 7 Mérou 6 |
- | 8 Mérou 50 | + | 8 Mérou 6 |
===== Tour 14 ====== | ===== Tour 14 ====== | ||
7 entites: | 7 entites: | ||
id type pv | id type pv | ||
- | 1 Algue 1 | + | 0 Algue 23 |
- | 2 Algue 7 | + | 1 Algue 25 |
- | 3 Algue 7 | + | 2 Algue 25 |
- | 5 Sole 12 | + | 3 Algue 21 |
- | 6 Sole 24 | + | 4 Algue 25 |
- | 7 Mérou 48 | + | 7 Mérou 5 |
- | 8 Mérou 54 | + | 8 Mérou 5 |
===== Tour 15 ====== | ===== Tour 15 ====== | ||
- | 6 entites: | + | 7 entites: |
id type pv | id type pv | ||
- | 2 Algue 6 | + | 0 Algue 24 |
- | 3 Algue 8 | + | 1 Algue 26 |
- | 5 Sole 14 | + | 2 Algue 26 |
- | 6 Sole 22 | + | 3 Algue 22 |
- | 7 Mérou 50 | + | 4 Algue 26 |
- | 8 Mérou 58 | + | 7 Mérou 4 |
+ | 8 Mérou 4 | ||
===== Tour 16 ====== | ===== Tour 16 ====== | ||
- | 6 entites: | + | 7 entites: |
id type pv | id type pv | ||
- | 2 Algue 5 | + | 0 Algue 25 |
- | 3 Algue 3 | + | 1 Algue 27 |
- | 5 Sole 16 | + | 2 Algue 27 |
- | 6 Sole 24 | + | 3 Algue 23 |
- | 7 Mérou 52 | + | 4 Algue 27 |
- | 8 Mérou 60 | + | 7 Mérou 3 |
+ | 8 Mérou 3 | ||
===== Tour 17 ====== | ===== Tour 17 ====== | ||
- | 5 entites: | + | 7 entites: |
id type pv | id type pv | ||
- | 2 Algue 4 | + | 0 Algue 26 |
- | 5 Sole 14 | + | 1 Algue 28 |
- | 6 Sole 26 | + | 2 Algue 28 |
- | 7 Mérou 56 | + | 3 Algue 24 |
- | 8 Mérou 62 | + | 4 Algue 28 |
+ | 7 Mérou 2 | ||
+ | 8 Mérou 2 | ||
===== Tour 18 ====== | ===== Tour 18 ====== | ||
- | 4 entites: | + | 7 entites: |
id type pv | id type pv | ||
- | 5 Sole 16 | + | 0 Algue 27 |
- | 6 Sole 24 | + | 1 Algue 29 |
- | 7 Mérou 60 | + | 2 Algue 29 |
- | 8 Mérou 64 | + | 3 Algue 25 |
+ | 4 Algue 29 | ||
+ | 7 Mérou 1 | ||
+ | 8 Mérou 1 | ||
===== Tour 19 ====== | ===== Tour 19 ====== | ||
- | 4 entites: | + | 5 entites: |
id type pv | id type pv | ||
- | 5 Sole 11 | + | 0 Algue 28 |
- | 6 Sole 23 | + | 1 Algue 30 |
- | 7 Mérou 60 | + | 2 Algue 30 |
- | 8 Mérou 68 | + | 3 Algue 26 |
+ | 4 Algue 30 | ||
+ | ===== Tour 20 ====== | ||
+ | 5 entites: | ||
+ | id type pv | ||
+ | 0 Algue 29 | ||
+ | 1 Algue 31 | ||
+ | 2 Algue 31 | ||
+ | 3 Algue 27 | ||
+ | 4 Algue 31 | ||
===== Fini ====== | ===== Fini ====== | ||
</code> | </code> | ||
+ | ===== Partie 3 : Reproductions ===== | ||
+ | |||
+ | ==== Exercice 3.1 : Le désastre du vieillissement ==== | ||
- | ===== Partie 3.1 ===== | + | Dans cette partie, on ajoute l'age des êtres vivants. |
<code cpp main.cpp> | <code cpp main.cpp> | ||
Ligne 1120: | Ligne 1157: | ||
using name = std::string; | using name = std::string; | ||
using is_male = bool; | using is_male = bool; | ||
- | using detail = std::pair<name, is_male>; | + | using detail_component = std::tuple<entity::id, name, is_male>; |
- | using detail_component = std::pair<entity::id, detail>; | + | |
enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe }; | enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe }; | ||
- | using point_vie = size_t; | + | using point_vie = int8_t; |
- | using age = size_t; | + | using age = uint8_t; |
using type_component = std::tuple<entity::id, type, point_vie, age>; | using type_component = std::tuple<entity::id, type, point_vie, age>; | ||
} | } | ||
Ligne 1144: | Ligne 1180: | ||
bool is_algue(component::type t) { return (t == component::type::algue); } | bool is_algue(component::type t) { return (t == component::type::algue); } | ||
- | |||
bool is_poisson(component::type t) { return !is_algue(t); } | bool is_poisson(component::type t) { return !is_algue(t); } | ||
+ | bool is_carnivore(component::type t) { return (t == component::type::Mérou || t == component::type::Thon || t == component::type::PoissonClown); } | ||
+ | bool is_herbivore(component::type t) { return (t == component::type::Sole || t == component::type::Bar || t == component::type::Carpe); } | ||
- | bool is_carnivore(component::type t) { return (t == component::type::Mérou || | + | bool is_algue_t(component::type_component const& t) { return is_algue(std::get<1>(t)); } |
- | t == component::type::Thon || t == component::type::PoissonClown); } | + | bool is_poisson_t(component::type_component const& t) { return is_poisson(std::get<1>(t)); } |
+ | bool is_carnivore_t(component::type_component const& t) { return is_carnivore(std::get<1>(t)); } | ||
+ | bool is_herbivore_t(component::type_component const& t) { return is_herbivore(std::get<1>(t)); } | ||
- | bool is_herbivore(component::type t) { return (t == component::type::Sole || | + | component::point_vie& get_point_vie(component::type_component & t) { return std::get<2>(t); } |
- | t == component::type::Bar || t == component::type::Carpe); } | + | component::age& get_age(component::type_component & t) { return std::get<3>(t); } |
std::string to_string(component::type t) { | std::string to_string(component::type t) { | ||
Ligne 1159: | Ligne 1198: | ||
return types[i]; | return types[i]; | ||
} | } | ||
- | + | ||
template<typename Predicat> | template<typename Predicat> | ||
std::vector<entity::id> get_entities(Predicat&& predicat) { | std::vector<entity::id> get_entities(Predicat&& predicat) { | ||
- | std::vector<entity::id> algues; | + | std::vector<entity::id> entities; |
std::for_each(begin(internal::_types), end(internal::_types), | std::for_each(begin(internal::_types), end(internal::_types), | ||
- | [&algues, predicat](auto t){ if (predicat(std::get<1>(t))) { algues.push_back(std::get<0>(t)); } } | + | [&entities, predicat](const auto type){ if (predicat(type)) { entities.push_back(std::get<0>(type)); } } |
); | ); | ||
- | return algues; | + | return entities; |
} | } | ||
- | + | ||
template<typename Collection> | template<typename Collection> | ||
typename Collection::iterator get_component(Collection & components, entity::id id) { | typename Collection::iterator get_component(Collection & components, entity::id id) { | ||
Ligne 1191: | Ligne 1230: | ||
const auto id = create_entity(); | const auto id = create_entity(); | ||
internal::_types.push_back(std::make_tuple(id, t, 10, 0)); | internal::_types.push_back(std::make_tuple(id, t, 10, 0)); | ||
- | internal::_details.push_back(std::make_pair(id, std::make_pair(n, m))); | + | internal::_details.push_back(std::make_tuple(id, n, m)); |
return id; | return id; | ||
} | } | ||
Ligne 1200: | Ligne 1239: | ||
const auto details_it = std::remove_if(internal::_details.begin(), internal::_details.end(), | const auto details_it = std::remove_if(internal::_details.begin(), internal::_details.end(), | ||
- | [id](auto p){ return (p.first == id); }); | + | [id](auto t){ return (std::get<0>(t) == id); }); |
internal::_details.erase(details_it, internal::_details.end()); | internal::_details.erase(details_it, internal::_details.end()); | ||
Ligne 1207: | Ligne 1246: | ||
internal::_types.erase(types_it, internal::_types.end()); | internal::_types.erase(types_it, internal::_types.end()); | ||
} | } | ||
- | + | ||
void remove_entities(std::vector<ecs::entity::id> const& ids) { | void remove_entities(std::vector<ecs::entity::id> const& ids) { | ||
for (auto id: ids) | for (auto id: ids) | ||
Ligne 1219: | Ligne 1258: | ||
} | } | ||
std::cout << internal::_types.size() << " entites: " << std::endl; | std::cout << internal::_types.size() << " entites: " << std::endl; | ||
- | std::cout << "\tid\tpv\tage" << std::endl; | + | std::cout << "\tid\ttype\tpv" << std::endl; |
- | auto print = [](auto t){ return ( | + | auto print = [](auto t){ return ('\t' + std::to_string(std::get<0>(t)) + '\t' + |
- | '\t' + std::to_string(std::get<0>(t)) + | + | to_string(std::get<1>(t)) + '\t' + std::to_string(std::get<2>(t))); }; |
- | '\t' + std::to_string(std::get<2>(t)) + | + | |
- | '\t' + std::to_string(std::get<3>(t)) | + | |
- | ); }; | + | |
std::transform(begin(internal::_types), end(internal::_types) - 1, | std::transform(begin(internal::_types), end(internal::_types) - 1, | ||
std::ostream_iterator<std::string>(std::cout, "\n"), print); | std::ostream_iterator<std::string>(std::cout, "\n"), print); | ||
Ligne 1232: | Ligne 1268: | ||
int main() { | int main() { | ||
- | for (size_t i {}; i < 10; ++i) | + | for (size_t i {}; i < 5; ++i) |
ecs::add_algue(); | ecs::add_algue(); | ||
- | for (size_t i {}; i < 10; ++i) | + | for (size_t i {}; i < 2; ++i) |
ecs::add_poisson(ecs::component::type::Sole, "Sole", false); | ecs::add_poisson(ecs::component::type::Sole, "Sole", false); | ||
- | for (size_t i {}; i < 5; ++i) | + | for (size_t i {}; i < 2; ++i) |
ecs::add_poisson(ecs::component::type::Mérou, "Mérou", false); | ecs::add_poisson(ecs::component::type::Mérou, "Mérou", false); | ||
ecs::print(); | ecs::print(); | ||
- | for (size_t tour {}; tour < 20; ++tour) { | + | for (size_t tour {}; tour < 21; ++tour) { |
std::cout << "===== Tour " << tour << " ======" << std::endl; | std::cout << "===== Tour " << tour << " ======" << std::endl; | ||
- | + | ||
- | std::vector<ecs::entity::id> entities_to_update; | + | // update pv and age |
- | const auto poissons { ecs::get_entities(ecs::is_poisson) }; | + | for (auto entity: ecs::internal::_entities) { |
- | const auto algues { ecs::get_entities(ecs::is_algue) }; | + | const auto type = ecs::get_component(ecs::internal::_types, entity); |
- | + | // pv | |
- | std::uniform_int_distribution<size_t> entities_distribution(0, ecs::internal::_entities.size() - 1); | + | if (ecs::is_algue_t(*type)) { |
- | std::uniform_int_distribution<size_t> algue_distribution(0, algues.size() - 1); | + | ecs::get_point_vie(*type) += 1; |
- | + | } else { | |
- | for (auto poisson: poissons) { | + | ecs::get_point_vie(*type) -= 1; |
- | auto const& poisson_type = ecs::get_component(ecs::internal::_types, poisson); | + | } |
- | + | // age | |
- | if (poisson_type != end(ecs::internal::_types)) { | + | ecs::get_age(*type) += 1; |
- | if (ecs::is_herbivore(std::get<1>(*poisson_type)) && !algues.empty()) { | + | } |
+ | |||
+ | // eat | ||
+ | const auto herbivores = ecs::get_entities(ecs::is_herbivore_t); | ||
+ | const auto algues = ecs::get_entities(ecs::is_algue_t); | ||
+ | |||
+ | if (!algues.empty()) { | ||
+ | for (const auto herbivore: herbivores) { | ||
+ | const auto herbivore_type = ecs::get_component(ecs::internal::_types, herbivore); | ||
+ | |||
+ | if (herbivore_type != end(ecs::internal::_types) && ecs::get_point_vie(*herbivore_type) < 5) | ||
+ | { | ||
+ | std::uniform_int_distribution<size_t> algue_distribution(0, algues.size() - 1); | ||
const auto index { algue_distribution(ecs::internal::_random_engine) }; | const auto index { algue_distribution(ecs::internal::_random_engine) }; | ||
- | const auto target = algues[index]; | + | const auto algue = algues[index]; |
- | entities_to_update.push_back(target); | + | |
- | } else if (ecs::is_carnivore(std::get<1>(*poisson_type)) && !ecs::internal::_entities.empty()) { | + | const auto algue_type = ecs::get_component(ecs::internal::_types, algue); |
+ | ecs::get_point_vie(*herbivore_type) += 3; | ||
+ | ecs::get_point_vie(*algue_type) -= 2; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | const auto carnivores = ecs::get_entities(ecs::is_carnivore_t); | ||
+ | for (const auto carnivore_id: carnivores) { | ||
+ | const auto carnivore_type = ecs::get_component(ecs::internal::_types, carnivore_id); | ||
+ | if (carnivore_type != end(ecs::internal::_types) && ecs::get_point_vie(*carnivore_type) < 5) // a faim ? | ||
+ | { | ||
+ | const auto carnivore_race = std::get<1>(*carnivore_type); | ||
+ | |||
+ | const auto bons_petits_plats = ecs::get_entities( | ||
+ | [carnivore_id, carnivore_race](const auto type){ | ||
+ | const auto id = std::get<0>(type); | ||
+ | const auto race = std::get<1>(type); | ||
+ | return ( | ||
+ | ecs::is_poisson(race) && | ||
+ | (id != carnivore_id) && | ||
+ | (race != carnivore_race) | ||
+ | ); | ||
+ | } | ||
+ | ); | ||
+ | |||
+ | if (!bons_petits_plats.empty()) { | ||
+ | std::uniform_int_distribution<size_t> entities_distribution(0, bons_petits_plats.size() - 1); | ||
const auto index { entities_distribution(ecs::internal::_random_engine) }; | const auto index { entities_distribution(ecs::internal::_random_engine) }; | ||
- | const auto target = ecs::internal::_entities[index]; | + | |
- | entities_to_update.push_back(target); | + | const auto entity_id = bons_petits_plats[index]; |
+ | const auto entity_type = ecs::get_component(ecs::internal::_types, entity_id); | ||
+ | |||
+ | if (entity_type != end(ecs::internal::_types)) | ||
+ | { | ||
+ | if (ecs::is_algue_t(*entity_type)) { | ||
+ | ecs::get_point_vie(*carnivore_type) += 3; | ||
+ | ecs::get_point_vie(*entity_type) -= 2; | ||
+ | } else { | ||
+ | ecs::get_point_vie(*carnivore_type) += 5; | ||
+ | ecs::get_point_vie(*entity_type) -= 4; | ||
+ | } | ||
+ | } | ||
} | } | ||
} | } | ||
} | } | ||
- | + | ||
- | // update pv | + | // deads |
- | for (auto entity: entities_to_update) { | + | std::vector<ecs::entity::id> entities_to_remove; |
- | auto type_component = ecs::get_component(ecs::internal::_types, entity); | + | for (auto entity: ecs::internal::_entities) { |
- | + | const auto type = ecs::get_component(ecs::internal::_types, entity); | |
- | if (type_component != end(ecs::internal::_types)) { | + | |
- | if (std::get<2>(*type_component) > 2) | + | if (type != end(ecs::internal::_types) && |
- | std::get<2>(*type_component) -= 2; | + | (ecs::get_point_vie(*type) <= 0 || ecs::get_age(*type) > 20)) |
- | else | + | { |
- | ecs::remove_entity(std::get<0>(*type_component)); | + | entities_to_remove.push_back(entity); |
} | } | ||
} | } | ||
- | + | ||
- | // update age | + | for (auto entity: entities_to_remove) { |
- | for (auto & entity: ecs::internal::_types) { | + | ecs::remove_entity(entity); |
- | std::get<3>(entity) += 1; | + | |
- | if (std::get<3>(entity) >= 20) | + | |
- | ecs::remove_entity(std::get<0>(entity)); | + | |
} | } | ||
- | + | ||
ecs::print(); | ecs::print(); | ||
} | } | ||
+ | std::cout << "===== Fini ======" << std::endl; | ||
return 0; | return 0; | ||
Ligne 1295: | Ligne 1380: | ||
<code> | <code> | ||
- | 25 entites: | + | 9 entites: |
- | id pv age | + | id type pv |
- | 0 10 0 | + | 0 Algue 10 |
- | 1 10 0 | + | 1 Algue 10 |
- | 2 10 0 | + | 2 Algue 10 |
- | 3 10 0 | + | 3 Algue 10 |
- | 4 10 0 | + | 4 Algue 10 |
- | 5 10 0 | + | 5 Sole 10 |
- | 6 10 0 | + | 6 Sole 10 |
- | 7 10 0 | + | 7 Mérou 10 |
- | 8 10 0 | + | 8 Mérou 10 |
- | 9 10 0 | + | |
- | 10 10 0 | + | |
- | 11 10 0 | + | |
- | 12 10 0 | + | |
- | 13 10 0 | + | |
- | 14 10 0 | + | |
- | 15 10 0 | + | |
- | 16 10 0 | + | |
- | 17 10 0 | + | |
- | 18 10 0 | + | |
- | 19 10 0 | + | |
- | 20 10 0 | + | |
- | 21 10 0 | + | |
- | 22 10 0 | + | |
- | 23 10 0 | + | |
- | 24 10 0 | + | |
===== Tour 0 ====== | ===== Tour 0 ====== | ||
- | 25 entites: | + | 9 entites: |
- | id pv age | + | id type pv |
- | 0 8 1 | + | 0 Algue 11 |
- | 1 8 1 | + | 1 Algue 11 |
- | 2 8 1 | + | 2 Algue 11 |
- | 3 6 1 | + | 3 Algue 11 |
- | 4 6 1 | + | 4 Algue 11 |
- | 5 8 1 | + | 5 Sole 9 |
- | 6 10 1 | + | 6 Sole 9 |
- | 7 8 1 | + | 7 Mérou 9 |
- | 8 6 1 | + | 8 Mérou 9 |
- | 9 10 1 | + | |
- | 10 10 1 | + | |
- | 11 8 1 | + | |
- | 12 10 1 | + | |
- | 13 10 1 | + | |
- | 14 10 1 | + | |
- | 15 10 1 | + | |
- | 16 8 1 | + | |
- | 17 10 1 | + | |
- | 18 10 1 | + | |
- | 19 10 1 | + | |
- | 20 10 1 | + | |
- | 21 8 1 | + | |
- | 22 10 1 | + | |
- | 23 8 1 | + | |
- | 24 10 1 | + | |
===== Tour 1 ====== | ===== Tour 1 ====== | ||
- | 24 entites: | + | 9 entites: |
- | id pv age | + | id type pv |
- | 1 2 2 | + | 0 Algue 12 |
- | 2 8 2 | + | 1 Algue 12 |
- | 3 4 2 | + | 2 Algue 12 |
- | 4 6 2 | + | 3 Algue 12 |
- | 5 6 2 | + | 4 Algue 12 |
- | 6 10 2 | + | 5 Sole 8 |
- | 7 8 2 | + | 6 Sole 8 |
- | 8 4 2 | + | 7 Mérou 8 |
- | 9 8 2 | + | 8 Mérou 8 |
- | 10 10 2 | + | |
- | 11 8 2 | + | |
- | 12 10 2 | + | |
- | 13 6 2 | + | |
- | 14 10 2 | + | |
- | 15 10 2 | + | |
- | 16 8 2 | + | |
- | 17 10 2 | + | |
- | 18 10 2 | + | |
- | 19 10 2 | + | |
- | 20 10 2 | + | |
- | 21 8 2 | + | |
- | 22 10 2 | + | |
- | 23 6 2 | + | |
- | 24 8 2 | + | |
===== Tour 2 ====== | ===== Tour 2 ====== | ||
- | 23 entites: | + | 9 entites: |
- | id pv age | + | id type pv |
- | 2 6 3 | + | 0 Algue 13 |
- | 3 2 3 | + | 1 Algue 13 |
- | 4 4 3 | + | 2 Algue 13 |
- | 5 6 3 | + | 3 Algue 13 |
- | 6 4 3 | + | 4 Algue 13 |
- | 7 4 3 | + | 5 Sole 7 |
- | 8 2 3 | + | 6 Sole 7 |
- | 9 8 3 | + | 7 Mérou 7 |
- | 10 8 3 | + | 8 Mérou 7 |
- | 11 8 3 | + | |
- | 12 10 3 | + | |
- | 13 4 3 | + | |
- | 14 10 3 | + | |
- | 15 10 3 | + | |
- | 16 8 3 | + | |
- | 17 8 3 | + | |
- | 18 10 3 | + | |
- | 19 10 3 | + | |
- | 20 10 3 | + | |
- | 21 8 3 | + | |
- | 22 6 3 | + | |
- | 23 6 3 | + | |
- | 24 8 3 | + | |
===== Tour 3 ====== | ===== Tour 3 ====== | ||
- | 19 entites: | + | 9 entites: |
- | id pv age | + | id type pv |
- | 2 6 4 | + | 0 Algue 14 |
- | 6 2 4 | + | 1 Algue 14 |
- | 7 4 4 | + | 2 Algue 14 |
- | 9 4 4 | + | 3 Algue 14 |
- | 10 8 4 | + | 4 Algue 14 |
- | 11 6 4 | + | 5 Sole 6 |
- | 12 8 4 | + | 6 Sole 6 |
- | 13 2 4 | + | 7 Mérou 6 |
- | 14 10 4 | + | 8 Mérou 6 |
- | 15 10 4 | + | |
- | 16 8 4 | + | |
- | 17 8 4 | + | |
- | 18 10 4 | + | |
- | 19 10 4 | + | |
- | 20 10 4 | + | |
- | 21 8 4 | + | |
- | 22 6 4 | + | |
- | 23 6 4 | + | |
- | 24 6 4 | + | |
===== Tour 4 ====== | ===== Tour 4 ====== | ||
- | 15 entites: | + | 9 entites: |
- | id pv age | + | id type pv |
- | 10 6 5 | + | 0 Algue 15 |
- | 11 4 5 | + | 1 Algue 15 |
- | 12 8 5 | + | 2 Algue 15 |
- | 13 2 5 | + | 3 Algue 15 |
- | 14 10 5 | + | 4 Algue 15 |
- | 15 10 5 | + | 5 Sole 5 |
- | 16 8 5 | + | 6 Sole 5 |
- | 17 2 5 | + | 7 Mérou 5 |
- | 18 10 5 | + | 8 Mérou 5 |
- | 19 10 5 | + | |
- | 20 10 5 | + | |
- | 21 8 5 | + | |
- | 22 6 5 | + | |
- | 23 6 5 | + | |
- | 24 6 5 | + | |
===== Tour 5 ====== | ===== Tour 5 ====== | ||
- | 15 entites: | + | 8 entites: |
- | id pv age | + | id type pv |
- | 10 6 6 | + | 0 Algue 16 |
- | 11 2 6 | + | 1 Algue 14 |
- | 12 6 6 | + | 2 Algue 14 |
- | 13 2 6 | + | 3 Algue 16 |
- | 14 8 6 | + | 4 Algue 16 |
- | 15 10 6 | + | 6 Sole 7 |
- | 16 4 6 | + | 7 Mérou 9 |
- | 17 2 6 | + | 8 Mérou 9 |
- | 18 10 6 | + | |
- | 19 10 6 | + | |
- | 20 10 6 | + | |
- | 21 8 6 | + | |
- | 22 6 6 | + | |
- | 23 6 6 | + | |
- | 24 6 6 | + | |
===== Tour 6 ====== | ===== Tour 6 ====== | ||
- | 14 entites: | + | 8 entites: |
- | id pv age | + | id type pv |
- | 10 6 7 | + | 0 Algue 17 |
- | 11 2 7 | + | 1 Algue 15 |
- | 12 6 7 | + | 2 Algue 15 |
- | 14 6 7 | + | 3 Algue 17 |
- | 15 10 7 | + | 4 Algue 17 |
- | 16 2 7 | + | 6 Sole 6 |
- | 17 2 7 | + | 7 Mérou 8 |
- | 18 10 7 | + | 8 Mérou 8 |
- | 19 10 7 | + | |
- | 20 10 7 | + | |
- | 21 4 7 | + | |
- | 22 6 7 | + | |
- | 23 6 7 | + | |
- | 24 6 7 | + | |
===== Tour 7 ====== | ===== Tour 7 ====== | ||
- | 12 entites: | + | 8 entites: |
- | id pv age | + | id type pv |
- | 10 6 8 | + | 0 Algue 18 |
- | 12 6 8 | + | 1 Algue 16 |
- | 14 4 8 | + | 2 Algue 16 |
- | 15 10 8 | + | 3 Algue 18 |
- | 16 2 8 | + | 4 Algue 18 |
- | 18 10 8 | + | 6 Sole 5 |
- | 19 10 8 | + | 7 Mérou 7 |
- | 20 10 8 | + | 8 Mérou 7 |
- | 21 4 8 | + | |
- | 22 6 8 | + | |
- | 23 6 8 | + | |
- | 24 4 8 | + | |
===== Tour 8 ====== | ===== Tour 8 ====== | ||
- | 11 entites: | + | 8 entites: |
- | id pv age | + | id type pv |
- | 10 6 9 | + | 0 Algue 19 |
- | 12 6 9 | + | 1 Algue 17 |
- | 14 2 9 | + | 2 Algue 17 |
- | 15 8 9 | + | 3 Algue 19 |
- | 18 10 9 | + | 4 Algue 17 |
- | 19 8 9 | + | 6 Sole 7 |
- | 20 10 9 | + | 7 Mérou 6 |
- | 21 2 9 | + | 8 Mérou 6 |
- | 22 6 9 | + | |
- | 23 6 9 | + | |
- | 24 4 9 | + | |
===== Tour 9 ====== | ===== Tour 9 ====== | ||
- | 10 entites: | + | 8 entites: |
- | id pv age | + | id type pv |
- | 10 6 10 | + | 0 Algue 20 |
- | 12 4 10 | + | 1 Algue 18 |
- | 14 2 10 | + | 2 Algue 18 |
- | 15 8 10 | + | 3 Algue 20 |
- | 18 10 10 | + | 4 Algue 18 |
- | 19 8 10 | + | 6 Sole 6 |
- | 20 8 10 | + | 7 Mérou 5 |
- | 22 2 10 | + | 8 Mérou 5 |
- | 23 6 10 | + | |
- | 24 4 10 | + | |
===== Tour 10 ====== | ===== Tour 10 ====== | ||
- | 9 entites: | + | 7 entites: |
- | id pv age | + | id type pv |
- | 10 6 11 | + | 0 Algue 21 |
- | 12 2 11 | + | 1 Algue 19 |
- | 15 8 11 | + | 2 Algue 19 |
- | 18 10 11 | + | 3 Algue 21 |
- | 19 8 11 | + | 4 Algue 19 |
- | 20 8 11 | + | 7 Mérou 9 |
- | 22 2 11 | + | 8 Mérou 9 |
- | 23 6 11 | + | |
- | 24 4 11 | + | |
===== Tour 11 ====== | ===== Tour 11 ====== | ||
- | 8 entites: | + | 7 entites: |
- | id pv age | + | id type pv |
- | 10 4 12 | + | 0 Algue 22 |
- | 12 2 12 | + | 1 Algue 20 |
- | 15 8 12 | + | 2 Algue 20 |
- | 18 8 12 | + | 3 Algue 22 |
- | 19 6 12 | + | 4 Algue 20 |
- | 20 8 12 | + | 7 Mérou 8 |
- | 23 6 12 | + | 8 Mérou 8 |
- | 24 4 12 | + | |
===== Tour 12 ====== | ===== Tour 12 ====== | ||
- | 6 entites: | + | 7 entites: |
- | id pv age | + | id type pv |
- | 10 4 13 | + | 0 Algue 23 |
- | 15 8 13 | + | 1 Algue 21 |
- | 18 8 13 | + | 2 Algue 21 |
- | 19 6 13 | + | 3 Algue 23 |
- | 20 8 13 | + | 4 Algue 21 |
- | 23 6 13 | + | 7 Mérou 7 |
+ | 8 Mérou 7 | ||
===== Tour 13 ====== | ===== Tour 13 ====== | ||
- | 6 entites: | + | 7 entites: |
- | id pv age | + | id type pv |
- | 10 4 14 | + | 0 Algue 24 |
- | 15 8 14 | + | 1 Algue 22 |
- | 18 8 14 | + | 2 Algue 22 |
- | 19 6 14 | + | 3 Algue 24 |
- | 20 8 14 | + | 4 Algue 22 |
- | 23 2 14 | + | 7 Mérou 6 |
+ | 8 Mérou 6 | ||
===== Tour 14 ====== | ===== Tour 14 ====== | ||
- | 5 entites: | + | 7 entites: |
- | id pv age | + | id type pv |
- | 10 4 15 | + | 0 Algue 25 |
- | 15 8 15 | + | 1 Algue 23 |
- | 18 8 15 | + | 2 Algue 23 |
- | 19 4 15 | + | 3 Algue 25 |
- | 20 8 15 | + | 4 Algue 23 |
+ | 7 Mérou 5 | ||
+ | 8 Mérou 5 | ||
===== Tour 15 ====== | ===== Tour 15 ====== | ||
- | 5 entites: | + | 7 entites: |
- | id pv age | + | id type pv |
- | 10 2 16 | + | 0 Algue 26 |
- | 15 8 16 | + | 1 Algue 24 |
- | 18 8 16 | + | 2 Algue 24 |
- | 19 4 16 | + | 3 Algue 26 |
- | 20 8 16 | + | 4 Algue 24 |
+ | 7 Mérou 4 | ||
+ | 8 Mérou 4 | ||
===== Tour 16 ====== | ===== Tour 16 ====== | ||
- | 4 entites: | + | 7 entites: |
- | id pv age | + | id type pv |
- | 15 8 17 | + | 0 Algue 27 |
- | 18 8 17 | + | 1 Algue 25 |
- | 19 4 17 | + | 2 Algue 25 |
- | 20 8 17 | + | 3 Algue 27 |
+ | 4 Algue 25 | ||
+ | 7 Mérou 3 | ||
+ | 8 Mérou 3 | ||
===== Tour 17 ====== | ===== Tour 17 ====== | ||
- | 4 entites: | + | 7 entites: |
- | id pv age | + | id type pv |
- | 15 8 18 | + | 0 Algue 28 |
- | 18 8 18 | + | 1 Algue 26 |
- | 19 4 18 | + | 2 Algue 26 |
- | 20 6 18 | + | 3 Algue 28 |
+ | 4 Algue 26 | ||
+ | 7 Mérou 2 | ||
+ | 8 Mérou 2 | ||
===== Tour 18 ====== | ===== Tour 18 ====== | ||
- | 4 entites: | + | 7 entites: |
- | id pv age | + | id type pv |
- | 15 8 19 | + | 0 Algue 29 |
- | 18 6 19 | + | 1 Algue 27 |
- | 19 4 19 | + | 2 Algue 27 |
- | 20 6 19 | + | 3 Algue 29 |
+ | 4 Algue 27 | ||
+ | 7 Mérou 1 | ||
+ | 8 Mérou 1 | ||
===== Tour 19 ====== | ===== Tour 19 ====== | ||
- | 1 entites: | + | 5 entites: |
- | id pv age | + | id type pv |
- | 18 6 19 | + | 0 Algue 30 |
+ | 1 Algue 28 | ||
+ | 2 Algue 28 | ||
+ | 3 Algue 30 | ||
+ | 4 Algue 28 | ||
+ | ===== Tour 20 ====== | ||
+ | Aucune entite | ||
+ | ===== Fini ====== | ||
</code> | </code> | ||
- | ===== Partie 3.2 ===== | + | ==== Exercice 3.2 : Le miracle de la jeunesse ==== |
- | ===== Partie 3.3 ===== | + | |
+ | La gestion des points de vie et de la reproduction sont un peu lourde, ce qui m'a obligée de modifier en partie le code correspondant. | ||
+ | |||
+ | <code cpp main.cpp> | ||
+ | #include <iostream> | ||
+ | #include <vector> | ||
+ | #include <string> | ||
+ | #include <algorithm> | ||
+ | #include <numeric> | ||
+ | #include <iterator> | ||
+ | #include <cassert> | ||
+ | #include <random> | ||
+ | #include <tuple> | ||
+ | |||
+ | namespace ecs { | ||
+ | namespace entity { | ||
+ | using id = size_t; | ||
+ | using entities = std::vector<id>; | ||
+ | } | ||
+ | |||
+ | namespace component { | ||
+ | using name = std::string; | ||
+ | using is_male = bool; | ||
+ | using detail_component = std::tuple<entity::id, name, is_male>; | ||
+ | |||
+ | enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe }; | ||
+ | using point_vie = int8_t; | ||
+ | using age = uint8_t; | ||
+ | using type_component = std::tuple<entity::id, type, point_vie, age>; | ||
+ | } | ||
+ | |||
+ | namespace system { | ||
+ | using details = std::vector<component::detail_component>; | ||
+ | using types = std::vector<component::type_component>; | ||
+ | } | ||
+ | |||
+ | namespace internal { | ||
+ | entity::entities _entities; | ||
+ | system::details _details; | ||
+ | system::types _types; | ||
+ | |||
+ | std::random_device _random_device; | ||
+ | std::default_random_engine _random_engine { _random_device() }; | ||
+ | } | ||
+ | |||
+ | bool is_algue(component::type t) { return (t == component::type::algue); } | ||
+ | bool is_poisson(component::type t) { return !is_algue(t); } | ||
+ | bool is_carnivore(component::type t) { return (t == component::type::Mérou || t == component::type::Thon || t == component::type::PoissonClown); } | ||
+ | bool is_herbivore(component::type t) { return (t == component::type::Sole || t == component::type::Bar || t == component::type::Carpe); } | ||
+ | |||
+ | bool is_algue_t(component::type_component const& t) { return is_algue(std::get<1>(t)); } | ||
+ | bool is_poisson_t(component::type_component const& t) { return is_poisson(std::get<1>(t)); } | ||
+ | bool is_carnivore_t(component::type_component const& t) { return is_carnivore(std::get<1>(t)); } | ||
+ | bool is_herbivore_t(component::type_component const& t) { return is_herbivore(std::get<1>(t)); } | ||
+ | |||
+ | component::point_vie& get_point_vie(component::type_component & t) { return std::get<2>(t); } | ||
+ | component::age& get_age(component::type_component & t) { return std::get<3>(t); } | ||
+ | |||
+ | bool random_sexe() { | ||
+ | static std::uniform_int_distribution<size_t> algue_distribution(0,1); | ||
+ | return (algue_distribution(ecs::internal::_random_engine) == 0); | ||
+ | } | ||
+ | |||
+ | component::type random_race() { | ||
+ | static std::uniform_int_distribution<size_t> algue_distribution(0,6); | ||
+ | return static_cast<component::type>(algue_distribution(ecs::internal::_random_engine)); | ||
+ | } | ||
+ | |||
+ | std::string to_string(component::type t) { | ||
+ | static const std::string types[] = { "Algue", "Mérou", "Thon", "Poisson-clown", "Sole", "Bar", "Carpe" }; | ||
+ | const auto i = static_cast<size_t>(t); | ||
+ | return types[i]; | ||
+ | } | ||
+ | |||
+ | template<typename Predicat> | ||
+ | std::vector<entity::id> get_entities(Predicat&& predicat) { | ||
+ | std::vector<entity::id> entities; | ||
+ | std::for_each(begin(internal::_types), end(internal::_types), | ||
+ | [&entities, predicat](const auto type){ if (predicat(type)) { entities.push_back(std::get<0>(type)); } } | ||
+ | ); | ||
+ | return entities; | ||
+ | } | ||
+ | |||
+ | template<typename Collection> | ||
+ | typename Collection::iterator get_component(Collection & components, entity::id id) { | ||
+ | auto it = find_if(begin(components), end(components), [id](auto t){ return (std::get<0>(t) == id); }); | ||
+ | return it; | ||
+ | } | ||
+ | |||
+ | entity::id create_entity() { | ||
+ | const auto id = internal::_entities.empty() ? 0 : internal::_entities.back() + 1; | ||
+ | internal::_entities.push_back(id); | ||
+ | return id; | ||
+ | } | ||
+ | |||
+ | entity::id add_algue(ecs::component::point_vie pv = 10) { | ||
+ | const auto id = create_entity(); | ||
+ | internal::_types.push_back(std::make_tuple(id, component::type::algue, pv, 0)); | ||
+ | return id; | ||
+ | } | ||
+ | |||
+ | entity::id add_poisson(component::type t, component::name n, component::is_male m, ecs::component::point_vie pv = 10) { | ||
+ | assert(is_poisson(t)); | ||
+ | const auto id = create_entity(); | ||
+ | internal::_types.push_back(std::make_tuple(id, t, pv, 0)); | ||
+ | internal::_details.push_back(std::make_tuple(id, n, m)); | ||
+ | return id; | ||
+ | } | ||
+ | |||
+ | void remove_entity(entity::id id) { | ||
+ | const auto entities_it = std::remove(internal::_entities.begin(), internal::_entities.end(), id); | ||
+ | internal::_entities.erase(entities_it, internal::_entities.end()); | ||
+ | |||
+ | const auto details_it = std::remove_if(internal::_details.begin(), internal::_details.end(), | ||
+ | [id](auto t){ return (std::get<0>(t) == id); }); | ||
+ | internal::_details.erase(details_it, internal::_details.end()); | ||
+ | |||
+ | const auto types_it = std::remove_if(internal::_types.begin(), internal::_types.end(), | ||
+ | [id](auto t){ return (std::get<0>(t) == id); }); | ||
+ | internal::_types.erase(types_it, internal::_types.end()); | ||
+ | } | ||
+ | |||
+ | void remove_entities(std::vector<ecs::entity::id> const& ids) { | ||
+ | for (auto id: ids) | ||
+ | remove_entity(id); | ||
+ | } | ||
+ | |||
+ | void print(size_t tour, size_t entites, size_t algues, size_t herbivores, size_t carnivores) { | ||
+ | std::cout << tour << "\t\t" << entites << "\t\t" << algues << "\t\t" << herbivores << "\t\t" << carnivores << std::endl; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | std::cout << "Tour\t\tEntities\tAlgues\t\tHerbivores\tCarnivores" << std::endl; | ||
+ | |||
+ | for (size_t i {}; i < 20; ++i) { | ||
+ | const auto race = ecs::random_race(); | ||
+ | if (ecs::is_algue(race)) | ||
+ | ecs::add_algue(); | ||
+ | else | ||
+ | ecs::add_poisson(race, ecs::to_string(race), ecs::random_sexe()); | ||
+ | } | ||
+ | |||
+ | for (size_t tour {}; tour < 100; ++tour) { | ||
+ | // update pv and age | ||
+ | for (auto entity: ecs::internal::_entities) { | ||
+ | const auto type = ecs::get_component(ecs::internal::_types, entity); | ||
+ | // pv | ||
+ | if (ecs::is_algue_t(*type)) { | ||
+ | if (ecs::get_point_vie(*type) > 10) { | ||
+ | ecs::get_point_vie(*type) /= 2; | ||
+ | ecs::add_algue(ecs::get_point_vie(*type)); | ||
+ | } else { | ||
+ | ecs::get_point_vie(*type) += 1; | ||
+ | } | ||
+ | } else { | ||
+ | ecs::get_point_vie(*type) -= 1; | ||
+ | } | ||
+ | // age | ||
+ | ecs::get_age(*type) += 1; | ||
+ | } | ||
+ | |||
+ | const auto herbivores = ecs::get_entities(ecs::is_herbivore_t); | ||
+ | const auto algues = ecs::get_entities(ecs::is_algue_t); | ||
+ | ecs::print(tour, ecs::internal::_entities.size(), algues.size(), herbivores.size(), ecs::internal::_entities.size() - algues.size() - herbivores.size()); | ||
+ | |||
+ | const auto poissons = ecs::get_entities(ecs::is_poisson_t); | ||
+ | for (const auto poisson_id: poissons) { | ||
+ | const auto type = ecs::get_component(ecs::internal::_types, poisson_id); | ||
+ | if (type != end(ecs::internal::_types)) | ||
+ | { | ||
+ | const auto race = std::get<1>(*type); | ||
+ | |||
+ | if (ecs::get_point_vie(*type) < 5) | ||
+ | { // manger | ||
+ | if (ecs::is_herbivore_t(*type) && !algues.empty()) | ||
+ | { | ||
+ | std::uniform_int_distribution<size_t> algue_distribution(0, algues.size() - 1); | ||
+ | const auto index { algue_distribution(ecs::internal::_random_engine) }; | ||
+ | const auto algue_id = algues[index]; | ||
+ | |||
+ | const auto herbivore_type = ecs::get_component(ecs::internal::_types, poisson_id); | ||
+ | const auto algue_type = ecs::get_component(ecs::internal::_types, algue_id); | ||
+ | |||
+ | if ((herbivore_type != end(ecs::internal::_types)) && (algue_type != end(ecs::internal::_types))) { | ||
+ | ecs::get_point_vie(*herbivore_type) += 3; | ||
+ | ecs::get_point_vie(*algue_type) -= 2; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | const auto bons_petits_plats = ecs::get_entities( | ||
+ | [poisson_id, race](const auto _type){ | ||
+ | const auto _id = std::get<0>(_type); | ||
+ | const auto _race = std::get<1>(_type); | ||
+ | return ( | ||
+ | ecs::is_poisson(_race) && | ||
+ | (_id != poisson_id) && | ||
+ | (_race != race) | ||
+ | ); | ||
+ | } | ||
+ | ); | ||
+ | |||
+ | if (!bons_petits_plats.empty()) { | ||
+ | std::uniform_int_distribution<size_t> entities_distribution(0, bons_petits_plats.size() - 1); | ||
+ | const auto index { entities_distribution(ecs::internal::_random_engine) }; | ||
+ | const auto entity_id = bons_petits_plats[index]; | ||
+ | const auto entity_type = ecs::get_component(ecs::internal::_types, entity_id); | ||
+ | |||
+ | if (entity_type != end(ecs::internal::_types)) | ||
+ | { | ||
+ | if (ecs::is_algue_t(*entity_type)) { | ||
+ | ecs::get_point_vie(*type) += 3; | ||
+ | ecs::get_point_vie(*entity_type) -= 2; | ||
+ | } else { | ||
+ | ecs::get_point_vie(*type) += 5; | ||
+ | ecs::get_point_vie(*entity_type) -= 4; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { // bais... se reproduire | ||
+ | std::uniform_int_distribution<size_t> poissons_distribution(0, poissons.size() - 1); | ||
+ | const auto index { poissons_distribution(ecs::internal::_random_engine) }; | ||
+ | const auto poisson_2_id = poissons[index]; | ||
+ | |||
+ | const auto poisson_sex = ecs::get_component(ecs::internal::_details, poisson_id); | ||
+ | const auto poisson_2_sex = ecs::get_component(ecs::internal::_details, poisson_2_id); | ||
+ | const auto poisson_2_race = ecs::get_component(ecs::internal::_types, poisson_2_id); | ||
+ | |||
+ | if ((poisson_sex != end(ecs::internal::_details)) && (poisson_2_sex != end(ecs::internal::_details)) && (poisson_2_race != end(ecs::internal::_types)) && | ||
+ | (std::get<2>(*poisson_sex) != std::get<2>(*poisson_2_sex)) && (race != std::get<1>(*poisson_2_race))) | ||
+ | { | ||
+ | ecs::add_poisson(race, "Bebe", ecs::random_sexe(), 5); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // deads | ||
+ | std::vector<ecs::entity::id> entities_to_remove; | ||
+ | for (auto entity: ecs::internal::_entities) { | ||
+ | const auto type = ecs::get_component(ecs::internal::_types, entity); | ||
+ | |||
+ | if (type != end(ecs::internal::_types) && | ||
+ | (ecs::get_point_vie(*type) <= 0 || ecs::get_age(*type) > 20)) | ||
+ | { | ||
+ | entities_to_remove.push_back(entity); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | for (auto entity: entities_to_remove) { | ||
+ | ecs::remove_entity(entity); | ||
+ | } | ||
+ | } | ||
+ | std::cout << "===== Fini ======" << std::endl; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | affiche : | ||
+ | |||
+ | <code> | ||
+ | Tour Entities Algues Herbivores Carnivores | ||
+ | 0 20 1 8 11 | ||
+ | 1 24 2 10 12 | ||
+ | 2 33 2 13 18 | ||
+ | 3 45 1 19 25 | ||
+ | 4 57 0 25 32 | ||
+ | 5 63 0 28 35 | ||
+ | 6 62 0 28 34 | ||
+ | 7 69 0 31 38 | ||
+ | 8 72 0 31 41 | ||
+ | 9 81 0 34 47 | ||
+ | 10 84 0 33 51 | ||
+ | 11 97 0 40 57 | ||
+ | 12 106 0 44 62 | ||
+ | 13 110 0 44 66 | ||
+ | 14 120 0 45 75 | ||
+ | 15 130 0 52 78 | ||
+ | 16 137 0 58 79 | ||
+ | 17 159 0 61 98 | ||
+ | 18 179 0 69 110 | ||
+ | 19 190 0 72 118 | ||
+ | 20 206 0 77 129 | ||
+ | 21 228 0 82 146 | ||
+ | 22 242 0 85 157 | ||
+ | 23 273 0 92 181 | ||
+ | 24 288 0 97 191 | ||
+ | 25 310 0 97 213 | ||
+ | 26 332 0 100 232 | ||
+ | 27 365 0 107 258 | ||
+ | 28 390 0 114 276 | ||
+ | 29 427 0 119 308 | ||
+ | 30 449 0 119 330 | ||
+ | 31 499 0 132 367 | ||
+ | 32 533 0 140 393 | ||
+ | 33 580 0 144 436 | ||
+ | 34 641 0 150 491 | ||
+ | 35 668 0 156 512 | ||
+ | 36 713 0 159 554 | ||
+ | 37 741 0 156 585 | ||
+ | 38 806 0 176 630 | ||
+ | 39 875 0 189 686 | ||
+ | 40 931 0 187 744 | ||
+ | 41 982 0 183 799 | ||
+ | 42 1019 0 177 842 | ||
+ | 43 1053 0 179 874 | ||
+ | 44 1093 0 175 918 | ||
+ | 45 1138 0 175 963 | ||
+ | 46 1189 0 163 1026 | ||
+ | 47 1239 0 158 1081 | ||
+ | 48 1282 0 143 1139 | ||
+ | 49 1329 0 132 1197 | ||
+ | 50 1319 0 109 1210 | ||
+ | 51 1341 0 92 1249 | ||
+ | 52 1321 0 69 1252 | ||
+ | 53 1302 0 47 1255 | ||
+ | 54 1244 0 24 1220 | ||
+ | 55 1163 0 3 1160 | ||
+ | 56 1137 0 1 1136 | ||
+ | 57 1113 0 1 1112 | ||
+ | 58 1090 0 1 1089 | ||
+ | 59 1045 0 0 1045 | ||
+ | 60 997 0 0 997 | ||
+ | 61 948 0 0 948 | ||
+ | 62 896 0 0 896 | ||
+ | 63 843 0 0 843 | ||
+ | 64 626 0 0 626 | ||
+ | 65 442 0 0 442 | ||
+ | 66 258 0 0 258 | ||
+ | 67 128 0 0 128 | ||
+ | 68 0 0 0 0 | ||
+ | ===== Fini ====== | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ==== Exercice 3.3 : Mais… la sexualité des poissons est horriblement compliquée ! ==== | ||
+ | |||
+ | Dans cette partie, on ajoute differents types de comportement sexuels. | ||
+ | |||
+ | <code cpp main.cpp> | ||
+ | #include <iostream> | ||
+ | #include <vector> | ||
+ | #include <string> | ||
+ | #include <algorithm> | ||
+ | #include <numeric> | ||
+ | #include <iterator> | ||
+ | #include <cassert> | ||
+ | #include <random> | ||
+ | #include <tuple> | ||
+ | |||
+ | namespace ecs { | ||
+ | namespace entity { | ||
+ | using id = size_t; | ||
+ | using entities = std::vector<id>; | ||
+ | } | ||
+ | |||
+ | namespace component { | ||
+ | using name = std::string; | ||
+ | using is_male = bool; | ||
+ | using detail_component = std::tuple<entity::id, name, is_male>; | ||
+ | |||
+ | enum class race { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe }; | ||
+ | using point_vie = int8_t; | ||
+ | using age = uint8_t; | ||
+ | using type_component = std::tuple<entity::id, race, point_vie, age>; | ||
+ | } | ||
+ | |||
+ | namespace system { | ||
+ | using details = std::vector<component::detail_component>; | ||
+ | using types = std::vector<component::type_component>; | ||
+ | } | ||
+ | |||
+ | namespace internal { | ||
+ | entity::entities _entities; | ||
+ | system::details _details; | ||
+ | system::types _types; | ||
+ | |||
+ | std::random_device _random_device; | ||
+ | std::default_random_engine _random_engine { _random_device() }; | ||
+ | } | ||
+ | |||
+ | bool is_algue(component::race r) { return (r == component::race::algue); } | ||
+ | bool is_poisson(component::race r) { return !is_algue(r); } | ||
+ | bool is_carnivore(component::race r) { return (r == component::race::Mérou || r == component::race::Thon || r == component::race::PoissonClown); } | ||
+ | bool is_herbivore(component::race r) { return (r == component::race::Sole || r == component::race::Bar || r == component::race::Carpe); } | ||
+ | |||
+ | component::race& get_race(component::type_component & t) { return std::get<1>(t); } | ||
+ | component::race get_race(component::type_component const& t) { return std::get<1>(t); } | ||
+ | component::point_vie& get_point_vie(component::type_component & t) { return std::get<2>(t); } | ||
+ | component::age& get_age(component::type_component & t) { return std::get<3>(t); } | ||
+ | |||
+ | component::is_male is_male(component::detail_component const& d) { return std::get<2>(d); } | ||
+ | component::is_male & is_male(component::detail_component & d) { return std::get<2>(d); } | ||
+ | |||
+ | bool is_algue_t(component::type_component const& t) { return is_algue(get_race(t)); } | ||
+ | bool is_poisson_t(component::type_component const& t) { return is_poisson(get_race(t)); } | ||
+ | bool is_carnivore_t(component::type_component const& t) { return is_carnivore(get_race(t)); } | ||
+ | bool is_herbivore_t(component::type_component const& t) { return is_herbivore(get_race(t)); } | ||
+ | |||
+ | bool can_reproduce(component::type_component const& type_papa, component::type_component const& type_maman, | ||
+ | component::detail_component const& detail_papa, component::detail_component const& detail_maman) | ||
+ | { | ||
+ | if (get_race(type_papa) != get_race(type_maman)) | ||
+ | return false; | ||
+ | |||
+ | return (is_male(detail_papa) != is_male(detail_maman)); | ||
+ | } | ||
+ | |||
+ | bool random_sexe(component::race r) { | ||
+ | if (r == component::race::Carpe || r == component::race::Thon) { | ||
+ | static std::uniform_int_distribution<size_t> algue_distribution(0,1); | ||
+ | return (algue_distribution(ecs::internal::_random_engine) == 0); | ||
+ | } else { | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void update_sexe(component::type_component type_poisson, component::is_male & is_male_poisson) { | ||
+ | if ((get_race(type_poisson) == component::race::Bar || get_race(type_poisson) == component::race::Mérou) && | ||
+ | (get_age(type_poisson) >= 10)) | ||
+ | { | ||
+ | is_male_poisson = false; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | component::race random_race() { | ||
+ | static std::uniform_int_distribution<size_t> algue_distribution(0,6); | ||
+ | return static_cast<component::race>(algue_distribution(ecs::internal::_random_engine)); | ||
+ | } | ||
+ | |||
+ | std::string to_string(component::race r) { | ||
+ | static const std::string races[] = { "Algue", "Mérou", "Thon", "Poisson-clown", "Sole", "Bar", "Carpe" }; | ||
+ | const auto i = static_cast<size_t>(r); | ||
+ | assert(i < (sizeof(races) / sizeof(*races))); | ||
+ | return races[i]; | ||
+ | } | ||
+ | |||
+ | template<typename Predicat> | ||
+ | std::vector<entity::id> get_entities(Predicat&& predicat) { | ||
+ | std::vector<entity::id> entities; | ||
+ | std::for_each(begin(internal::_types), end(internal::_types), | ||
+ | [&entities, predicat](const auto type){ if (predicat(type)) { entities.push_back(std::get<0>(type)); } } | ||
+ | ); | ||
+ | return entities; | ||
+ | } | ||
+ | |||
+ | template<typename Collection> | ||
+ | typename Collection::iterator get_component(Collection & components, entity::id id) { | ||
+ | auto it = find_if(begin(components), end(components), [id](auto t){ return (std::get<0>(t) == id); }); | ||
+ | return it; | ||
+ | } | ||
+ | |||
+ | entity::id create_entity() { | ||
+ | const auto id = internal::_entities.empty() ? 0 : internal::_entities.back() + 1; | ||
+ | internal::_entities.push_back(id); | ||
+ | return id; | ||
+ | } | ||
+ | |||
+ | entity::id add_algue(ecs::component::point_vie pv = 10) { | ||
+ | const auto id = create_entity(); | ||
+ | internal::_types.push_back(std::make_tuple(id, component::race::algue, pv, 0)); | ||
+ | return id; | ||
+ | } | ||
+ | |||
+ | entity::id add_poisson(component::race t, component::name n, component::is_male m, ecs::component::point_vie pv = 10) { | ||
+ | assert(is_poisson(t)); | ||
+ | const auto id = create_entity(); | ||
+ | internal::_types.push_back(std::make_tuple(id, t, pv, 0)); | ||
+ | internal::_details.push_back(std::make_tuple(id, n, m)); | ||
+ | return id; | ||
+ | } | ||
+ | |||
+ | void remove_entity(entity::id id) { | ||
+ | const auto entities_it = std::remove(internal::_entities.begin(), internal::_entities.end(), id); | ||
+ | internal::_entities.erase(entities_it, internal::_entities.end()); | ||
+ | |||
+ | const auto details_it = std::remove_if(internal::_details.begin(), internal::_details.end(), | ||
+ | [id](auto t){ return (std::get<0>(t) == id); }); | ||
+ | internal::_details.erase(details_it, internal::_details.end()); | ||
+ | |||
+ | const auto types_it = std::remove_if(internal::_types.begin(), internal::_types.end(), | ||
+ | [id](auto t){ return (std::get<0>(t) == id); }); | ||
+ | internal::_types.erase(types_it, internal::_types.end()); | ||
+ | } | ||
+ | |||
+ | void remove_entities(std::vector<ecs::entity::id> const& ids) { | ||
+ | for (auto id: ids) | ||
+ | remove_entity(id); | ||
+ | } | ||
+ | |||
+ | void print(size_t tour, size_t entites, size_t algues, size_t herbivores, size_t carnivores) { | ||
+ | std::cout << tour << "\t\t" << entites << "\t\t" << algues << "\t\t" << herbivores << "\t\t" << carnivores << std::endl; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | std::cout << "Tour\t\tEntities\tAlgues\t\tHerbivores\tCarnivores" << std::endl; | ||
+ | |||
+ | for (size_t i {}; i < 20; ++i) { | ||
+ | const auto race = ecs::random_race(); | ||
+ | if (ecs::is_algue(race)) | ||
+ | ecs::add_algue(); | ||
+ | else | ||
+ | ecs::add_poisson(race, ecs::to_string(race), ecs::random_sexe(race)); | ||
+ | } | ||
+ | |||
+ | for (size_t tour {}; tour < 100; ++tour) { | ||
+ | // update pv and age | ||
+ | for (auto entity: ecs::internal::_entities) { | ||
+ | const auto type = ecs::get_component(ecs::internal::_types, entity); | ||
+ | // pv | ||
+ | if (ecs::is_algue_t(*type)) { | ||
+ | if (ecs::get_point_vie(*type) > 10) { | ||
+ | ecs::get_point_vie(*type) /= 2; | ||
+ | ecs::add_algue(ecs::get_point_vie(*type)); | ||
+ | } else { | ||
+ | ecs::get_point_vie(*type) += 1; | ||
+ | } | ||
+ | } else { | ||
+ | ecs::get_point_vie(*type) -= 1; | ||
+ | } | ||
+ | // age | ||
+ | ecs::get_age(*type) += 1; | ||
+ | } | ||
+ | |||
+ | const auto herbivores = ecs::get_entities(ecs::is_herbivore_t); | ||
+ | const auto algues = ecs::get_entities(ecs::is_algue_t); | ||
+ | ecs::print(tour, ecs::internal::_entities.size(), algues.size(), herbivores.size(), ecs::internal::_entities.size() - algues.size() - herbivores.size()); | ||
+ | |||
+ | const auto poissons = ecs::get_entities(ecs::is_poisson_t); | ||
+ | for (const auto poisson_id: poissons) { | ||
+ | const auto type = ecs::get_component(ecs::internal::_types, poisson_id); | ||
+ | if (type != end(ecs::internal::_types)) | ||
+ | { | ||
+ | const auto race = std::get<1>(*type); | ||
+ | |||
+ | if (ecs::get_point_vie(*type) < 5) | ||
+ | { // manger | ||
+ | if (ecs::is_herbivore_t(*type) && !algues.empty()) | ||
+ | { | ||
+ | std::uniform_int_distribution<size_t> algue_distribution(0, algues.size() - 1); | ||
+ | const auto index { algue_distribution(ecs::internal::_random_engine) }; | ||
+ | const auto algue_id = algues[index]; | ||
+ | |||
+ | const auto herbivore_type = ecs::get_component(ecs::internal::_types, poisson_id); | ||
+ | const auto algue_type = ecs::get_component(ecs::internal::_types, algue_id); | ||
+ | |||
+ | if ((herbivore_type != end(ecs::internal::_types)) && (algue_type != end(ecs::internal::_types))) { | ||
+ | ecs::get_point_vie(*herbivore_type) += 3; | ||
+ | ecs::get_point_vie(*algue_type) -= 2; | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | const auto bons_petits_plats = ecs::get_entities( | ||
+ | [poisson_id, race](const auto _type){ | ||
+ | const auto _id = std::get<0>(_type); | ||
+ | const auto _race = std::get<1>(_type); | ||
+ | return ( | ||
+ | ecs::is_poisson(_race) && | ||
+ | (_id != poisson_id) && | ||
+ | (_race != race) | ||
+ | ); | ||
+ | } | ||
+ | ); | ||
+ | |||
+ | if (!bons_petits_plats.empty()) { | ||
+ | std::uniform_int_distribution<size_t> entities_distribution(0, bons_petits_plats.size() - 1); | ||
+ | const auto index { entities_distribution(ecs::internal::_random_engine) }; | ||
+ | const auto entity_id = bons_petits_plats[index]; | ||
+ | const auto entity_type = ecs::get_component(ecs::internal::_types, entity_id); | ||
+ | |||
+ | if (entity_type != end(ecs::internal::_types)) | ||
+ | { | ||
+ | if (ecs::is_algue_t(*entity_type)) { | ||
+ | ecs::get_point_vie(*type) += 3; | ||
+ | ecs::get_point_vie(*entity_type) -= 2; | ||
+ | } else { | ||
+ | ecs::get_point_vie(*type) += 5; | ||
+ | ecs::get_point_vie(*entity_type) -= 4; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { // bais... se reproduire | ||
+ | std::uniform_int_distribution<size_t> poissons_distribution(0, poissons.size() - 1); | ||
+ | const auto index { poissons_distribution(ecs::internal::_random_engine) }; | ||
+ | const auto poisson_2_id = poissons[index]; | ||
+ | |||
+ | const auto poisson_sex = ecs::get_component(ecs::internal::_details, poisson_id); | ||
+ | const auto poisson_2_sex = ecs::get_component(ecs::internal::_details, poisson_2_id); | ||
+ | const auto poisson_2_race = ecs::get_component(ecs::internal::_types, poisson_2_id); | ||
+ | |||
+ | if ((poisson_sex != end(ecs::internal::_details)) && (poisson_2_sex != end(ecs::internal::_details)) && (poisson_2_race != end(ecs::internal::_types)) && | ||
+ | (std::get<2>(*poisson_sex) != std::get<2>(*poisson_2_sex)) && (race != std::get<1>(*poisson_2_race))) | ||
+ | { | ||
+ | ecs::add_poisson(race, "Bebe", ecs::random_sexe(race), 5); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // update sex | ||
+ | const auto details = ecs::get_component(ecs::internal::_details, poisson_id); | ||
+ | if (details != end(ecs::internal::_details)) | ||
+ | { | ||
+ | ecs::update_sexe(*type, ecs::is_male(*details)); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // deads | ||
+ | std::vector<ecs::entity::id> entities_to_remove; | ||
+ | for (auto entity: ecs::internal::_entities) { | ||
+ | const auto type = ecs::get_component(ecs::internal::_types, entity); | ||
+ | |||
+ | if (type != end(ecs::internal::_types) && | ||
+ | (ecs::get_point_vie(*type) <= 0 || ecs::get_age(*type) > 20)) | ||
+ | { | ||
+ | entities_to_remove.push_back(entity); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | for (auto entity: entities_to_remove) { | ||
+ | ecs::remove_entity(entity); | ||
+ | } | ||
+ | } | ||
+ | std::cout << "===== Fini ======" << std::endl; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Affiche : | ||
+ | |||
+ | <code> | ||
+ | Tour Entities Algues Herbivores Carnivores | ||
+ | 0 20 2 10 8 | ||
+ | 1 27 4 13 10 | ||
+ | 2 33 4 15 14 | ||
+ | 3 37 3 17 17 | ||
+ | 4 45 3 23 19 | ||
+ | 5 48 0 27 21 | ||
+ | 6 44 0 25 19 | ||
+ | 7 54 0 31 23 | ||
+ | 8 54 0 30 24 | ||
+ | 9 58 0 31 27 | ||
+ | 10 57 0 30 27 | ||
+ | 11 67 0 33 34 | ||
+ | 12 72 0 32 40 | ||
+ | 13 74 0 35 39 | ||
+ | 14 85 0 37 48 | ||
+ | 15 88 0 36 52 | ||
+ | 16 95 0 40 55 | ||
+ | 17 102 0 39 63 | ||
+ | 18 111 0 43 68 | ||
+ | 19 125 0 48 77 | ||
+ | 20 135 0 48 87 | ||
+ | 21 142 0 47 95 | ||
+ | 22 138 0 46 92 | ||
+ | 23 142 0 46 96 | ||
+ | 24 145 0 44 101 | ||
+ | 25 149 0 43 106 | ||
+ | 26 146 0 36 110 | ||
+ | 27 153 0 34 119 | ||
+ | 28 160 0 35 125 | ||
+ | 29 157 0 28 129 | ||
+ | 30 163 0 25 138 | ||
+ | 31 156 0 16 140 | ||
+ | 32 148 0 8 140 | ||
+ | 33 142 0 3 139 | ||
+ | 34 136 0 0 136 | ||
+ | 35 130 0 0 130 | ||
+ | 36 125 0 0 125 | ||
+ | 37 120 0 0 120 | ||
+ | 38 115 0 0 115 | ||
+ | 39 88 0 0 88 | ||
+ | 40 58 0 0 58 | ||
+ | 41 41 0 0 41 | ||
+ | 42 23 0 0 23 | ||
+ | 43 0 0 0 0 | ||
+ | ===== Fini ====== | ||
+ | </code> | ||
+ | |||
+ | === Comment choisir les composants ? === | ||
+ | |||
+ | Dans cette partie, le but n'est pas simplement de discuter d’implémentation, mais plus le choix des composants du point de vue du jeu (//game design//). | ||
+ | |||
+ | Dans un ECS (tout au moins, dans la façon dont je comprends les ECS), les composants sont les données du jeu. La question est donc quelles données doivent (peuvent) être ensemble dans un composant et celle qui doivent (peuvent) être séparées dans des composants différents. | ||
+ | |||
+ | (Note : ces approches sont également valides si on considere que la première donnée est la liste des identifiants des entités et la seconde un composant ou une donnée quelconque. C'est également valide avec un composant et une liste de ressources dans un système.) | ||
+ | |||
+ | Pour bien comprendre, il est possible de représenter les composants dans un tableau a deux dimensions (similaire a une matrice creuse). | ||
+ | |||
+ | {{http://cowboyprogramming.com/images/eyh/Fig-2.gif}} | ||
+ | |||
+ | (Image provenant de http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/) | ||
+ | |||
+ | Le premier critère pour savoir si deux données peuvent appartenir au même composant est de savoir si une entité peut ou non avoir que l'une des deux données. | ||
+ | |||
+ | {{ :ecs1.png |}} | ||
+ | |||
+ | Par exemple, dans le javaquarium, toutes les entités "poisson" possèdent une données "nom" et "age". Il est donc possible de les mettre dans un même composant. Au contraire, la donnée "points de vie" concerne toutes les entités "être vivant", pas uniquement les entités "poisson". Il sera donc logique de la placer dans un composant différent. | ||
+ | |||
+ | Il faut cependant modérer un peu ce critère. Supposons que 99 % des entités sont des poissons. Dans ce cas, cela signifie que si on regroupe les trois données (nom, age et points de vie), il y aura 1 % des composants qui auront deux données vides (nom et age, pour les entités "algue"). Or, séparer ces données dans deux composants aura un coût (mémoire et/ou performances). Il peut être intéressant d'avoir un seul composant qui regroupe toutes les données dans ce cas. | ||
+ | |||
+ | <code cpp> | ||
+ | using Component = std::vector<A, B>; | ||
+ | </code> | ||
+ | |||
+ | Un autre point a prendre en compte et qui peut modifier ce premier critère est l'utilisation des données par les algorithmes. Dans le javaquarium, la donnée "age" sera utilisée a chaque tour (pour la mettre a jour, pour mettre a jour le sexe des poissons hermaphrodites, etc.). Au contraire, la donnée "nom" ne sera pas utilisée aussi souvent, elle sera par exemple utilisée dans un éditeur graphique. Dans cette situation, il peut être intéressant d'optimiser le cache pour les données utilisées a chaque tour et donc a séparer les données "nom" et "age" dans deux composants différents. | ||
+ | |||
+ | {{ :ecs2.png |}} | ||
+ | |||
+ | <code cpp> | ||
+ | using ComponentA = std::vector<A>; | ||
+ | using ComponentB = std::vector<B>; | ||
+ | </code> | ||
+ | |||
+ | Se pose alors le probleme de comment faire le lien entre les données dans des composants ? Plusieurs solutions sont possibles. | ||
+ | |||
+ | {{ :ecs3.png |}} | ||
+ | |||
+ | Dans la première approche, les données sont dans le même composant. Supprimer un composant supprime donc directement les deux données. L’accès au composant permet d'obtenir directement les deux données. | ||
+ | |||
+ | Dans le seconde approche, les données sont séparées dans deux composants et chaque composant est manipulés dans deux collections de **même** taille. | ||
+ | |||
+ | <code cpp> | ||
+ | struct Data { | ||
+ | using ComponentA = std::vector<A>; | ||
+ | using ComponentB = std::vector<B>; | ||
+ | |||
+ | void insert(); // insert dans A et B | ||
+ | void remove(); // remove dans A et B | ||
+ | ... | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | Dans cette approche, il est nécessaire de conserver la cohérence des données, c'est a dire que les données correspondant au même indice dans les deux collections appartiennent a la même entité. Avec cette structure de données, il est assez facile de maintenir la cohérence, il suffit d'effectuer les mêmes opérations sur les deux collections en même temps (insertion, suppression, tri, etc.). | ||
+ | |||
+ | Lorsque l'on accède a un élément d'une de deux collections, il est possible d’accéder directement a la seconde collection, en utilisant l'indice ou avec deux boucles synchronisées (une autre approche est d’utiliser boost.zip_iterator). L'indice peut être déterminé via une variable (par exemple dans une boucle) ou avec ''std::distance'' (complexité algorithmique constant). | ||
+ | |||
+ | <code cpp> | ||
+ | // boucles synchronisées | ||
+ | auto it_a = begin(v_a); | ||
+ | auto it_b = begin(v_b); | ||
+ | for (; it_a != end(v_a) && it_b != end(v_b); ++it_a, ++it_b) { ... } | ||
+ | |||
+ | // une boucle avec indice | ||
+ | for (auto it = begin(v_a); it != end(v_a); ++it) { | ||
+ | const auto i = std::distance(begin(v_a), it); | ||
+ | assert(i < v_b.size()); | ||
+ | b = v_b[i]; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Dans la troisièmement approche, les éléments de la seconde collection ne sont plus identifiés par rapport a leur position dans la collection, mais par l'ajout d'une donnée supplémentaire permettant d'identifier chaque élément (par exemple l'identifiant des entités). | ||
+ | |||
+ | <code cpp> | ||
+ | using ComponentA = std::vector<A>; | ||
+ | using ComponentB = std::vector<std::pair<entity_id, B>>; | ||
+ | </code> | ||
+ | |||
+ | Dans cette approche, il n'est pas nécessaire de synchroniser les données, il est possible d’ajouter ou de supprimer un élément dans la collection A sans modifier la collection B (et réciproquement). Par contre, les accès aux composants A et B nécessiteront de faire une recherche dans les deux collections (par exemple pour supprimer les composants lors de la suppression d'une entité). | ||
+ | |||
+ | Dans ce cas, plus une collection sera grande, plus le temps d’accès sera longue. | ||
+ | |||
+ | <code cpp> | ||
+ | const auto it = std::find(begin(v_b), end(v_b), [entity_id](auto p){ return (p.second == entity_id); ); | ||
+ | </code> | ||
+ | |||
+ | La dernière approche consiste a conserver une indirection dans la donnée A vers la donnée B, ce qui permet un accès direct, sans devoir faire une recherche (coûteuse) dans la collection B. Cette indirection peut être un pointeur (nu a priori), un itérateur ou un indice. (Ou encore un pointeur vers une classe de base d'une hiérarchie d'objets, mais cela répondrait a d'autres problématiques.) | ||
+ | |||
+ | <code cpp> | ||
+ | using ComponentA = std::vector<std::pair<A, ComponentB::iterator>>; | ||
+ | using ComponentB = std::vector<B>; | ||
+ | </code> | ||
+ | |||
+ | L’intérêt de cette approche est bien sur la rapidité d’accès vers les composants B depuis les composants A. Par contre, cela implique qu'il faut mettre a jour les indirections a chaque fois qu'un élément est ajouté ou supprimé dans la collection B. | ||
+ | |||
+ | Un critère intéressant a prendre en compte est l'efficace d'utilisation de la mémoire pour chaque approche. Le calcul de la taille totale des collections selon les différentes approches est simple. (Sans prendre en compte le surcoût lié a ''std::vector''). | ||
+ | |||
+ | <code> | ||
+ | // Approche 1 | ||
+ | size = v.size() * (sizeof(A) + sizeof(B)); | ||
+ | |||
+ | // Approche 2 | ||
+ | size = v_a.size() * (sizeof(A) + sizeof(B)); | ||
+ | |||
+ | // Approche 3 | ||
+ | size = v_a.size() * sizeof(A) + v_b.size() * (sizeof(id) + sizeof(B)); | ||
+ | |||
+ | // Approche 4 | ||
+ | size = v_a.size() * (sizeof(A) + sizeof(void*)) + v_b.size() * sizeof(B); | ||
+ | </code> | ||
+ | |||
+ | Avec ces formules, il est intéressant de simuler quelques valeurs. Pour cela, je prend ''sizeof(id) == sizeof(void*) == 1'', un rapport ''v_b.size() / v_a.size()'' (en ligne) compris entre 0,1 et 1 et ''sizeof(B) / sizeof(A)'' (en colonne) qui vaut 1, 2, 4 et 8. Et comme c'est la différence entre les approches qui nous intéresse, je représente uniquement les approches 2, 3 et 4, en pourcentage de taille par rapport a l'approche 1. (Donc par exemple "50" signifie que l'approche prend deux fois moins de mémoire que l'approche 1). | ||
+ | |||
+ | {{ :ecs_mem.png |}} | ||
+ | |||
+ | Sans surprise, l'approche 2 consomme la même quantité de mémoire que l'approche 1. L'approche 3 sera plus intéressante lorsque les données seront volumineuses et qu'il y aura beaucoup plus d’entités que de composants. L’approche 4 sera plus intéressant quand les données seront volumineuses et qu'il y aura beaucoup d’entités avec ces composants. | ||
+ | |||
+ | Bien sur, ces analyses ne prennent pas en compte l’efficacité du cache ou la corrélation avec l'algorithme. Dans tous les cas, une analyse plus approfondie (profiling) sera nécessaire pour bien choisir l'approche a utiliser. | ||
+ | |||
+ | Voila les principales approches qui me semblent intéressantes pour un ECS. Mais je ne prétends pas être exhaustif. Par exemple, si la majorité des entités ont une majorité de composants ou si le nombre d’entités et de composants n'est pas important, il est possible de stocker toutes les données directement dans un tableau 2D, avec les entités en ligne et les systèmes en colonne. | ||
+ | |||
+ | Je me suis également limite aux collections triées, pour permettre les accès les plus rapides. Mais il est par exemple possible d'utiliser l'approche 4 sans trier les objets dans la collection B. Lors de la suppression d'un composant, on laisse simplement un "trou" dans la collection et on l'utilise lorsque l'on ajoute un nouveau composant. Dans ce cas, il y a un surcoût pour trouver les "trous" et un cache qui peut être moins efficace, mais on gagne par rapport au l’étape de "compactage" de la mémoire. Voire il est possible de conserver les composants B sans collection. Dans ce cas, on gagne sur les étapes de "compactage" et de gestion des "trous", mais on perd au niveau du cache mémoire et des allocations et désallocations. | ||
+ | |||
+ | {{ :ecs4.png |}} | ||
+ | |||
+ | De plus, l'approche ECS sera intéressante surtout si le tableau contient plus de ligne que de colonne. Si par exemple, on a 1000 composants possible et 10 entités utilisant 2 ou 3 composants différents, alors chaque collection de composants pourra contenir 0 ou 1 éléments. Et la complexité d'un ECS sera au final pénalisant. Dans ce cas, une approche orientée objet classique, basée sur une hiérarchie de classes, pourra être une approche intéressante. | ||
+ | |||
+ | La conclusion est que le choix de l'approche ne peut pas être définie uniquement sur des considérations techniques d’implémentation. Le //game design// influencera beaucoup l'approche (ou plus probablement "les approches") selon les données a manipuler et les traitements appliquées. Par exemple, on pourra choisir l'approche 4 pour le système de rendu (la majorité des entités sera visibles) et l'approche 3 pour les armes (si les ennemis humains portant une arme sont minoritaires). | ||
+ | |||
+ | Un bon moteur d'ECS, a mon sens, ne proposera donc pas une approche fixe dans son design, mais proposera aux développeurs de choisir le design qu'ils préfèrent utiliser, selon les besoin du //game design//. (Le C++ permet de proposer plusieurs approches sans surcoût a l’exécution. Ici, les classes de traits et de politique est une approche intéressante pour implémenter cela.) | ||
+ | |||
+ | |||
+ | === Réécriture de la partie 3.3 === | ||
+ | |||
+ | Dans cette partie, je vais réécrire le code, mais en séparant a l’extrême les composants, c'est a dire en considérant que une information correspond a un composant. | ||
+ | |||
+ | {{ :ecs5.png |}} | ||
+ | |||
+ | |||
+ | |||
+ | ===== Partie 4 : Rendons notre simulateur d'aquarium pratique ===== | ||
+ | |||
+ | ==== Exercice 4.1 : Sauvez Willy ! ==== | ||
+ | |||
+ | ==== Exercice 4.2 : Un fichier pour les enregistrer tous… ==== | ||
+ | |||
+ | ==== Exercice 4.3 : … et dans un fichier les charger ==== | ||
+ | |||
+ | ==== Exercice 4.4 : Tourne, la roue tourne… ==== | ||
+ | |||
+ | ==== Exercice 4.5 : Le petit neveu ==== |