Outils d'utilisateurs

Outils du Site


javaquarium

Ceci est une ancienne révision du document !


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.

main.cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <iterator>
 
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 = std::pair<name, is_male>;
        using detail_component = std::pair<entity::id, detail>;
 
        enum class type { algue, poisson };
        using type_component = std::pair<entity::id, type>;
    }
 
    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;
    }
 
    entity::id create_entity() {
        const auto id = internal::_entities.empty() ? 1 : internal::_entities.back() + 1;
        internal::_entities.push_back(id);
        return id;
    }
 
    void add_algue() {
        const auto id = create_entity();
        internal::_types.push_back(std::make_pair(id, component::type::algue));
    }
 
    void add_poisson(component::name n, component::is_male m) {
        const auto id = create_entity();
        internal::_types.push_back(std::make_pair(id, component::type::poisson));
        internal::_details.push_back(std::make_pair(id, make_pair(n, m)));
    }
 
    void print_algues() {
        const auto count = std::count_if(begin(internal::_types), end(internal::_types), 
            [](auto p){ return (p.second == component::type::algue); });
        std::cout << count << " algues";
    }
 
    void print_poissons() {
        if (internal::_details.empty()) {
            std::cout << "No poissons";
            return;
        }
        std::cout << internal::_details.size() << " poissons: ";
        auto print = [](auto p){ return (p.second.first + " [" + (p.second.second ? 'M' : 'F') + ']'); };
        std::transform(begin(internal::_details), end(internal::_details) - 1, 
            std::ostream_iterator<std::string>(std::cout, ", "), print);
        std::cout << print(internal::_details.back());
    }
 
    void print() {
        print_algues();
        std::cout << ". ";
        print_poissons();
        std::cout << '.' << std::endl;
    }
}
 
int main() {
    ecs::add_algue();
    ecs::add_algue();
    ecs::add_poisson("toto", true);
    ecs::add_algue();
    ecs::add_poisson("titi", false);
    ecs::add_poisson("tata", true);
    ecs::add_algue();
 
    ecs::print();
 
    return 0;   
}

Affiche :

4 algues. 3 poissons: toto [M], titi [F], tata [M].

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.

Quel est le problème avec cette approche ?

Une première critique que l'on peut faire est l’utilisation de variables globales (dans internal). Cela implique que si on veut avoir plusieurs ECS en même temps, ce n'est pas possible. Et bien sur, le code est moins réutilisable, il est écrit spécifiquement autour de ces variables globales, les fonctions ne sont pas réutilisables sans elles.

Il est facile de corriger ce problème, en passant ces variables en paramètres de fonctions et en déclarant les variables globales dans main.

entity::id create_entity(entity::entities & entites) {
    const auto id = entities.empty() ? 1 : entities.back() + 1;
    entities.push_back(id);
    return id;
}

Le code devient un peu plus lourd dans les fonctions qui utilisent plusieurs variables globales, mais le principe est le même.

Au final, c'est l'approche que l'on utilise en C pour faire de l'objet : on crées des structures contenant les données et des fonctions qui prennent un pointeur sur un objet. (J'utilise std::pair et std::tuple, qui sont en pratique des classes dont les variables membres n'ont pas de noms, et des références, mais ça serait la même chose si j'utilisais struct et des pointeurs).

Quel est l'intérêt de l'approche objet du C++ par rapport à celle utilisée en C ?

Une première réponse serait de dire que l'on est obligé de donner des noms différents à des fonctions qui font la même chose, comme par exemple print, print_algues et print_poissons dans l'exemple. En mettant les fonctions comme membre des classes plutôt que des fonctions libres, on pourrait utiliser le même nom.

algues.print();
poissons.print();
print();

Mais en fait, ce n'est pas un argument valide. En C++, il est possible de surcharger des fonctions, contrairement au C. Il est donc possible de donner le même nom à plusieurs fonctions, le compilateur appellera la fonction correcte en utilisant les paramètres.

print(algues);
print(poissons);
print();

Une seconde est de dire qu'il n'est pas possible en C de regrouper (“encapsuler”) ensemble les données et les fonctions qui manipulent ces données ensemble, pour avoir un tout cohérent et plus lisible.

class Poissons {
public:
    entity::id add();
    void print();
private:
    std::vector<Poisson> poissons;
};    

L'argument n'est pas faux, mais est limité. Pourquoi par exemple ne pas remplacer la création d'une classe dans le code précédent par un namespace ? Quelle sera la différence ?

Il y a bien sûr l'accessibilité des membres, publique ou privée. Mais cela sera vite limité aussi : dans un ECS (et dans de nombreux cas), les données sont manipulées par plusieurs classes, ce qui implique de mettre les membres en publique ou d'ajouter des mutateurs (getters et setters)… ce qui est fait en pratique dans de nombreux langages de programmation.

Mais il y a plus à attendre d'une encapsulation. Qu'est-ce que c'est ? Quel problème veut-on corriger avec la programmation orientée objets ?

La réponse est simple : on veut que les données soient utilisées correctement.

Par exemple, dans un ECS, chaque entité doit être unique. Il est nécessaire et indispensable de ne pas avoir de doublons dans la liste des entités. Or, dans l’implémentation proposée, rien n'interdit à l’utilisateur d'écrire le code suivant :

int main() {
    ecs::internal::_entities.push_back(0);
    ecs::internal::_entities.push_back(0);
}

Ce qui violerait la règle de l'entité unique.

Les règles imposées aux données sont appelées les invariants en programmation par contrat. Les invariants sont importants, puisqu'ils signifient : “si les invariants sont respectés, le programme fera ce qu'on attend de lui. Si les invariants ne sont pas respectés, il n'est pas possible de garantir le comportement du programme”.

L'autre aspect de la programmation par contrat est l'utilisation des pré et post-conditions. Ces conditions s'appliquent sur les fonctions et garantissent que si les pré-conditions et les invariants sont respectés lors de l'appel d'une fonction, alors les post-conditions et les invariants seront aussi respectés à la fin de l'appel de la fonction.

On arrive donc naturellement au principe d'encapsulation. Une encapsulation correcte est donc une encapsulation qui apportera des garanties fortes sur l'utilisation correcte des données. Cela peut être résumé par la phrase suivante :

Facile a utiliser correctement, difficile a utiliser de façon incorrect.
Scott Meyers

Beaucoup pensent que l'encapsulation consiste simplement a mettre des variables en private et des fonctions en public dans une classe. Mais en pratique, ce n'est aussi simple : des fonctions libres peuvent respecter l’encapsulation (voir par exemple les fonctions std::begin et std::end) et au contraire des fonctions membres peuvent briser l'encapsulation (en premier lieu, les mutateurs : les setters et getters).

Pour concevoir correctement les classes, on conseille souvent de penser les classes en termes de services rendus, pas en termes d'agrégation de données. Dit autrement, il faut se poser la question “quels sont les services que doivent rendre ma classe ?” et pas “quelles sont les données contenues dans ma classe ?”.

Partie 1.2

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.

main.cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <iterator>
#include <cassert>
 
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 = std::pair<name, is_male>;
        using detail_component = std::pair<entity::id, detail>;
 
        enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe };
        using type_component = std::pair<entity::id, type>;
    }
 
    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;
    }
 
    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); }
 
    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];
    }
 
    entity::id create_entity() {
        const auto id = internal::_entities.empty() ? 1 : internal::_entities.back() + 1;
        internal::_entities.push_back(id);
        return id;
    }
 
    entity::id add_algue() {
        const auto id = create_entity();
        internal::_types.push_back(std::make_pair(id, component::type::algue));
        return id;
    }
 
    entity::id add_poisson(component::type t, component::name n, component::is_male m) {
        assert(is_poisson(t));
        const auto id = create_entity();
        internal::_types.push_back(std::make_pair(id, t));
        internal::_details.push_back(std::make_pair(id, make_pair(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 p){ return (p.first == id); });
        internal::_details.erase(details_it, internal::_details.end());
 
        const auto types_it = std::remove_if(internal::_types.begin(), internal::_types.end(),
            [id](auto p){ return (p.first == id); });
        internal::_types.erase(types_it, internal::_types.end());
    }
 
    void eat(entity::id eater, entity::id target) {
        assert(eater != target);
 
        const auto eater_it = std::find_if(begin(internal::_types), end(internal::_types),
            [eater](auto p){ return (p.first == eater); });
        const auto target_it = std::find_if(begin(internal::_types), end(internal::_types),
            [target](auto p){ return (p.first == target); });
 
        assert(is_poisson(eater_it->second));
        assert(is_carnivore(eater_it->second) || (is_herbivore(eater_it->second) && is_algue(target_it->second)));
 
        remove_entity(target); //eat
    }
 
    void print_algues() {
        const auto count = std::count_if(begin(internal::_types), end(internal::_types), 
            [](auto p){ return (p.second == component::type::algue); });
        std::cout << "algues: " << count;
    }
 
    void print_poissons() {
        if (internal::_details.empty()) {
            std::cout << "No poissons";
            return;
        }
        std::cout << internal::_details.size() << " poissons: ";
        auto print = [](auto p){ return (p.second.first + " [" + (p.second.second ? 'M' : 'F') + ']'); };
        std::transform(begin(internal::_details), end(internal::_details) - 1, 
            std::ostream_iterator<std::string>(std::cout, ", "), print);
        std::cout << print(internal::_details.back());
    }
 
    void print() {
        print_algues();
        std::cout << ". ";
        print_poissons();
        std::cout << '.' << std::endl;
    }
}
 
int main() {
    ecs::add_algue();
    ecs::add_algue();
    ecs::add_algue();
    ecs::add_algue();
    ecs::add_algue();
    ecs::add_algue();
    ecs::add_algue();
    const auto algue = ecs::add_algue();
    const auto carnivore = ecs::add_poisson(ecs::component::type::Mérou, "toto", true);
    const auto poisson = ecs::add_poisson(ecs::component::type::Mérou, "titi", false);
    const auto herbivore = ecs::add_poisson(ecs::component::type::Mérou, "tata", true);
    ecs::add_poisson(ecs::component::type::Mérou, "tuto", false);
    ecs::add_poisson(ecs::component::type::Mérou, "tyty", true);
    ecs::print();
 
    ecs::remove_entity(poisson);
    ecs::print();
 
    ecs::eat(herbivore, algue);
    ecs::eat(carnivore, herbivore);
    ecs::print();
 
    return 0;   
}

affiche :

algues: 8. 5 poissons: toto [M], titi [F], tata [M], tuto [F], tyty [M].
algues: 8. 4 poissons: toto [M], tata [M], tuto [F], tyty [M].
algues: 7. 3 poissons: toto [M], tuto [F], tyty [M].

Typage fort

Dans cette partie, j'ai choisis d'utiliser une énumération pour définir toutes les races (algues, carnivores et herbivores).

enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe };

L'intérêt est que l'on a besoin que d'une seule variable pour manipuler tous les types. Quels sont les défauts de cette approche ? (Uniquement en termes de C++, pas d'ECS. Je reviendrais sur l'impact de l'encodage des données sur l'ECS par la suite).

Une conséquence est que l'on ne sait pas lors de la compilation si le type correspond à un algue ou à un poisson. Or, certaines fonctions sont spécifiques aux algues ou aux poissons. Par exemple, la fonction add, qui prend des paramètres différents selon le type. Il est donc nécessaire d'avoir deux fonctions, add_algue et add_poisson.

void add_algue();
void add_poisson(name, is_male, type);

Je pense que vous voyez tout de suite le problème ici : type peut être une algue, ce qui n'est pas valide.

Il n'y a pas d'autre solution que de vérifier le type à l'exécution (avec if ou assert).

void add_poisson(component::name n, component::is_male m, component::type t) {
    assert(is_poisson(t));
    ...
}

Cela pose plusieurs problèmes :

  • surcoût à l'exécution (uniquement en debug pour assert) ;
  • on est prévenu du problème tardivement (à l'exécution, et sous condition que les tests unitaires couvrent ce problème) ;
  • on peut faire une erreur (souvenez vous de la citation de Scott Meyers : “Facile a utiliser correctement, difficile a utiliser de façon incorrect”).

Est-il possible d’améliorer cela ?

A ce niveau des explications, je pense que la solution est évidente : il suffit de créer un type représentant uniquement les poissons, pas les algues. Par exemple :

enum class type { algue, poisson };
enum class race { Mérou, Thon, PoissonClown, Sole, Bar, Carpe };
 
void add_poisson(component::name n, component::is_male m, component::race r) {
    // assert(is_poisson(t)); pas nécessaire
    ...
}

Dans cette situation, il n'est plus nécessaire de vérifier a l’exécution que type n'a pas la valeur algue, puisque par conception, ce n'est pas possible.

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

Dans cette partie, on ajoute un système de tours : a chaque tour, tous les poissons mangent quelque chose au hasard.

En termes d’implémentation, cela pose une petite difficulté : il faut parcourir un vector que l'on va modifier en même temps. Si on parcours le vector avec des itérateurs, l'itérateur courant sera invalide après avoir supprimé un élément au début. Idem en parcourant avec des indices, si la suppression a lieu a la fin de vector, il faut décrémenter l'indice de 1.

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 ;
  • la creation et la suppression des entites ;
  • 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.

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.

main.cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <iterator>
#include <cassert>
#include <random>
 
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 = std::pair<name, is_male>;
        using detail_component = std::pair<entity::id, detail>;
 
        enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe };
        using type_component = std::pair<entity::id, type>;
    }
 
    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); }
 
    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> algues;
        std::for_each(begin(internal::_types), end(internal::_types),
            [&algues, predicat](auto p){ if (predicat(p.second)) { algues.push_back(p.first); } }
        );
        return algues;
    }
 
    template<typename Collection>
    typename Collection::value_type const& get_component(Collection const& components, entity::id id) {
        const auto it = find_if(begin(components), end(components), [id](auto p){ return (p.first == id); });
        assert(it != end(components));
        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() {
        const auto id = create_entity();
        internal::_types.push_back(std::make_pair(id, component::type::algue));
        return id;
    }
 
    entity::id add_poisson(component::type t, component::name n, component::is_male m) {
        assert(is_poisson(t));
        const auto id = create_entity();
        internal::_types.push_back(std::make_pair(id, t));
        internal::_details.push_back(std::make_pair(id, make_pair(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 p){ return (p.first == id); });
        internal::_details.erase(details_it, internal::_details.end());
 
        const auto types_it = std::remove_if(internal::_types.begin(), internal::_types.end(),
            [id](auto p){ return (p.first == 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_algues() {
        const auto count = std::count_if(begin(internal::_types), end(internal::_types), 
            [](auto p){ return (p.second == component::type::algue); });
        std::cout << "algues: " << count;
    }
 
    void print_poissons() {
        if (internal::_details.empty()) {
            std::cout << "No poissons";
            return;
        }
        std::cout << internal::_details.size() << " poissons: ";
        auto print = [](auto p){ return (p.second.first + " [" + (p.second.second ? 'M' : 'F') + ']'); };
        std::transform(begin(internal::_details), end(internal::_details) - 1, 
            std::ostream_iterator<std::string>(std::cout, ", "), print);
        std::cout << print(internal::_details.back());
    }
 
    void print() {
        print_algues();
        std::cout << ". ";
        print_poissons();
        std::cout << '.' << std::endl;
    }
}
 
int main() {
    for (size_t i {}; i < 50; ++i)
        ecs::add_algue();
    for (size_t i {}; i < 10; ++i)
        ecs::add_poisson(ecs::component::type::Sole, "toto", false);
    for (size_t i {}; i < 5; ++i)
        ecs::add_poisson(ecs::component::type::Mérou, "titi", false);
    ecs::print();
 
    for (size_t tour {}; tour < 10; ++tour) {
        std::cout << "===== Tour " << tour << " ======" << std::endl;
 
        std::vector<ecs::entity::id> entities_to_remove;
        const auto poissons { ecs::get_entities(ecs::is_poisson) };
        const auto algues { ecs::get_entities(ecs::is_algue) };
 
        std::uniform_int_distribution<size_t> entities_distribution(0, ecs::internal::_entities.size() - 1);
        std::uniform_int_distribution<size_t> algue_distribution(0, algues.size() - 1);
 
        for (auto poisson: poissons) {
            auto const& poisson_type = ecs::get_component(ecs::internal::_types, poisson);
 
            if (ecs::is_herbivore(poisson_type.second) && !algues.empty()) {
                const auto index { algue_distribution(ecs::internal::_random_engine) };
                const auto target = algues[index];
                entities_to_remove.push_back(target);
            } else if (ecs::is_carnivore(poisson_type.second) && !ecs::internal::_entities.empty()) {
                const auto index { entities_distribution(ecs::internal::_random_engine) };
                const auto target = ecs::internal::_entities[index];
                entities_to_remove.push_back(target);
            }
        }
 
        ecs::remove_entities(entities_to_remove);
        ecs::print();
    }
 
    return 0;   
}

affiche :

algues: 50. 15 poissons: toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], titi [F], titi [F], titi [F], titi [F], titi [F].
===== Tour 0 ======
algues: 37. 15 poissons: toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], titi [F], titi [F], titi [F], titi [F], titi [F].
===== Tour 1 ======
algues: 22. 15 poissons: toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], titi [F], titi [F], titi [F], titi [F], titi [F].
===== Tour 2 ======
algues: 11. 14 poissons: toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], titi [F], titi [F], titi [F], titi [F].
===== Tour 3 ======
algues: 4. 13 poissons: toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], titi [F], titi [F], titi [F], titi [F].
===== Tour 4 ======
algues: 0. 10 poissons: toto [F], toto [F], toto [F], toto [F], toto [F], toto [F], titi [F], titi [F], titi [F], titi [F].
===== Tour 5 ======
algues: 0. 7 poissons: toto [F], toto [F], toto [F], titi [F], titi [F], titi [F], titi [F].
===== Tour 6 ======
algues: 0. 4 poissons: toto [F], toto [F], titi [F], titi [F].
===== Tour 7 ======
algues: 0. 2 poissons: toto [F], titi [F].
===== Tour 8 ======
algues: 0. 1 poissons: titi [F].
===== Tour 9 ======
algues: 0. No poissons.
===== Tour 10 ======
algues: 0. No poissons.

Partie 2.2

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.

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 = std::pair<name, is_male>;
        using detail_component = std::pair<entity::id, detail>;
 
        enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe };
        using point_vie = int8_t;
        using type_component = std::tuple<entity::id, type, point_vie>;
    }
 
    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); }
 
    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](auto t){ if (predicat(std::get<1>(t))) { entities.push_back(std::get<0>(t)); } }
        );
        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() {
        const auto id = create_entity();
        internal::_types.push_back(std::make_tuple(id, component::type::algue, 10));
        return id;
    }
 
    entity::id add_poisson(component::type t, component::name n, component::is_male m) {
        assert(is_poisson(t));
        const auto id = create_entity();
        internal::_types.push_back(std::make_tuple(id, t, 10));
        internal::_details.push_back(std::make_pair(id, std::make_pair(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 p){ return (p.first == 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() {
        if (internal::_types.empty()) {
            std::cout << "Aucune entite" << std::endl;
            return;
        }
        std::cout << internal::_types.size() << " entites: " << std::endl;
        std::cout << "\tid\ttype\tpv" << std::endl;
        auto print = [](auto t){ return ('\t' + std::to_string(std::get<0>(t)) + '\t' + 
            to_string(std::get<1>(t)) + '\t' + std::to_string(std::get<2>(t))); };
        std::transform(begin(internal::_types), end(internal::_types) - 1, 
            std::ostream_iterator<std::string>(std::cout, "\n"), print);
        std::cout << print(internal::_types.back()) << std::endl;
    }
}
 
int main() {
    for (size_t i {}; i < 5; ++i)
        ecs::add_algue();
    for (size_t i {}; i < 2; ++i)
        ecs::add_poisson(ecs::component::type::Sole, "Sole", false);
    for (size_t i {}; i < 2; ++i)
        ecs::add_poisson(ecs::component::type::Mérou, "Mérou", false);
    ecs::print();
 
    for (size_t tour {}; tour < 20; ++tour) {
        std::cout << "===== Tour " << tour << " ======" << std::endl;
 
        // update pv
        for (auto entity: ecs::internal::_entities) {
            auto const& type = ecs::get_component(ecs::internal::_types, entity);
            if (ecs::is_algue_t(*type)) {
                ecs::get_point_vie(*type) += 1;
            } else {
                ecs::get_point_vie(*type) -= 1;
            }
        }
 
        // eat
        const auto herbivores { ecs::get_entities(ecs::is_herbivore) };
        const auto carnivores { ecs::get_entities(ecs::is_carnivore) };
        const auto algues { ecs::get_entities(ecs::is_algue) };
 
        if (!algues.empty()) {
            for (auto herbivore: herbivores) {
                auto herbivore_type = ecs::get_component(ecs::internal::_types, herbivore);
 
                if (herbivore_type != end(ecs::internal::_types))
                {
                    std::uniform_int_distribution<size_t> algue_distribution(0, algues.size() - 1);
                    const auto index { algue_distribution(ecs::internal::_random_engine) };
                    const auto algue = algues[index];
 
                    auto const& algue_type = ecs::get_component(ecs::internal::_types, algue);
                    ecs::get_point_vie(*herbivore_type) += 3;
                    ecs::get_point_vie(*algue_type) -= 2;
                }
            }
        }
 
        if (ecs::internal::_entities.size() > 1) { // le carnivore n'est pas seul
            for (auto carnivore: carnivores) {
                auto carnivore_type = ecs::get_component(ecs::internal::_types, carnivore);
                if (carnivore_type != end(ecs::internal::_types))
                {
                    auto entity = carnivore;
                    while (entity == carnivore) {
                        std::uniform_int_distribution<size_t> entities_distribution(0, ecs::internal::_entities.size() - 1);
                        const auto index { entities_distribution(ecs::internal::_random_engine) };
                        entity = ecs::internal::_entities[index];
                    }
 
                    auto const& entity_type = ecs::get_component(ecs::internal::_types, entity);
                    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;
                        }
                    }
                }
            }
        }
 
        // deads
        std::vector<ecs::entity::id> entities_to_remove;
        for (auto entity: ecs::internal::_entities) {
            auto type = ecs::get_component(ecs::internal::_types, entity);
 
            if (type != end(ecs::internal::_types) && ecs::get_point_vie(*type) <= 0) {
                entities_to_remove.push_back(entity);
            }
        }
 
        for (auto entity: entities_to_remove) {
            ecs::remove_entity(entity);
        }
 
        ecs::print();
    }
    std::cout << "===== Fini ======" << std::endl;
 
    return 0;   
}

affiche :

9 entites: 
	id	type	pv
	0	Algue	10
	1	Algue	10
	2	Algue	10
	3	Algue	10
	4	Algue	10
	5	Sole	10
	6	Sole	10
	7	Mérou	10
	8	Mérou	10
===== Tour 0 ======
9 entites: 
	id	type	pv
	0	Algue	11
	1	Algue	9
	2	Algue	11
	3	Algue	11
	4	Algue	9
	5	Sole	8
	6	Sole	8
	7	Mérou	14
	8	Mérou	14
===== Tour 1 ======
9 entites: 
	id	type	pv
	0	Algue	12
	1	Algue	10
	2	Algue	10
	3	Algue	10
	4	Algue	6
	5	Sole	10
	6	Sole	10
	7	Mérou	16
	8	Mérou	16
===== Tour 2 ======
9 entites: 
	id	type	pv
	0	Algue	11
	1	Algue	7
	2	Algue	11
	3	Algue	11
	4	Algue	7
	5	Sole	8
	6	Sole	12
	7	Mérou	18
	8	Mérou	20
===== Tour 3 ======
9 entites: 
	id	type	pv
	0	Algue	10
	1	Algue	8
	2	Algue	8
	3	Algue	12
	4	Algue	6
	5	Sole	10
	6	Sole	14
	7	Mérou	20
	8	Mérou	22
===== Tour 4 ======
9 entites: 
	id	type	pv
	0	Algue	11
	1	Algue	9
	2	Algue	9
	3	Algue	13
	4	Algue	3
	5	Sole	12
	6	Sole	8
	7	Mérou	24
	8	Mérou	26
===== Tour 5 ======
9 entites: 
	id	type	pv
	0	Algue	8
	1	Algue	8
	2	Algue	10
	3	Algue	14
	4	Algue	2
	5	Sole	14
	6	Sole	10
	7	Mérou	26
	8	Mérou	28
===== Tour 6 ======
8 entites: 
	id	type	pv
	0	Algue	9
	1	Algue	9
	2	Algue	11
	3	Algue	15
	5	Sole	8
	6	Sole	12
	7	Mérou	30
	8	Mérou	32
===== Tour 7 ======
8 entites: 
	id	type	pv
	0	Algue	6
	1	Algue	8
	2	Algue	10
	3	Algue	16
	5	Sole	10
	6	Sole	14
	7	Mérou	32
	8	Mérou	34
===== Tour 8 ======
8 entites: 
	id	type	pv
	0	Algue	3
	1	Algue	5
	2	Algue	11
	3	Algue	17
	5	Sole	12
	6	Sole	16
	7	Mérou	34
	8	Mérou	36
===== Tour 9 ======
7 entites: 
	id	type	pv
	1	Algue	6
	2	Algue	8
	3	Algue	18
	5	Sole	14
	6	Sole	18
	7	Mérou	36
	8	Mérou	38
===== Tour 10 ======
7 entites: 
	id	type	pv
	1	Algue	5
	2	Algue	7
	3	Algue	15
	5	Sole	16
	6	Sole	20
	7	Mérou	38
	8	Mérou	40
===== Tour 11 ======
7 entites: 
	id	type	pv
	1	Algue	4
	2	Algue	6
	3	Algue	14
	5	Sole	14
	6	Sole	22
	7	Mérou	40
	8	Mérou	44
===== Tour 12 ======
7 entites: 
	id	type	pv
	1	Algue	5
	2	Algue	5
	3	Algue	13
	5	Sole	8
	6	Sole	24
	7	Mérou	44
	8	Mérou	48
===== Tour 13 ======
7 entites: 
	id	type	pv
	1	Algue	2
	2	Algue	6
	3	Algue	10
	5	Sole	10
	6	Sole	26
	7	Mérou	46
	8	Mérou	50
===== Tour 14 ======
7 entites: 
	id	type	pv
	1	Algue	1
	2	Algue	7
	3	Algue	7
	5	Sole	12
	6	Sole	24
	7	Mérou	48
	8	Mérou	54
===== Tour 15 ======
6 entites: 
	id	type	pv
	2	Algue	6
	3	Algue	8
	5	Sole	14
	6	Sole	22
	7	Mérou	50
	8	Mérou	58
===== Tour 16 ======
6 entites: 
	id	type	pv
	2	Algue	5
	3	Algue	3
	5	Sole	16
	6	Sole	24
	7	Mérou	52
	8	Mérou	60
===== Tour 17 ======
5 entites: 
	id	type	pv
	2	Algue	4
	5	Sole	14
	6	Sole	26
	7	Mérou	56
	8	Mérou	62
===== Tour 18 ======
4 entites: 
	id	type	pv
	5	Sole	16
	6	Sole	24
	7	Mérou	60
	8	Mérou	64
===== Tour 19 ======
4 entites: 
	id	type	pv
	5	Sole	11
	6	Sole	23
	7	Mérou	60
	8	Mérou	68
===== Fini ======

Partie 3.1

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 = std::pair<name, is_male>;
        using detail_component = std::pair<entity::id, detail>;
 
        enum class type { algue, Mérou, Thon, PoissonClown, Sole, Bar, Carpe };
        using point_vie = size_t;
        using age = size_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); }
 
    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> algues;
        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)); } }
        );
        return algues;
    }
 
    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() {
        const auto id = create_entity();
        internal::_types.push_back(std::make_tuple(id, component::type::algue, 10, 0));
        return id;
    }
 
    entity::id add_poisson(component::type t, component::name n, component::is_male m) {
        assert(is_poisson(t));
        const auto id = create_entity();
        internal::_types.push_back(std::make_tuple(id, t, 10, 0));
        internal::_details.push_back(std::make_pair(id, std::make_pair(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 p){ return (p.first == 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() {
        if (internal::_types.empty()) {
            std::cout << "Aucune entite" << std::endl;
            return;
        }
        std::cout << internal::_types.size() << " entites: " << std::endl;
        std::cout << "\tid\tpv\tage" << std::endl;
        auto print = [](auto t){ return (
            '\t' + std::to_string(std::get<0>(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::ostream_iterator<std::string>(std::cout, "\n"), print);
        std::cout << print(internal::_types.back()) << std::endl;
    }
}
 
int main() {
    for (size_t i {}; i < 10; ++i)
        ecs::add_algue();
    for (size_t i {}; i < 10; ++i)
        ecs::add_poisson(ecs::component::type::Sole, "Sole", false);
    for (size_t i {}; i < 5; ++i)
        ecs::add_poisson(ecs::component::type::Mérou, "Mérou", false);
    ecs::print();
 
    for (size_t tour {}; tour < 20; ++tour) {
        std::cout << "===== Tour " << tour << " ======" << std::endl;
 
        std::vector<ecs::entity::id> entities_to_update;
        const auto poissons { ecs::get_entities(ecs::is_poisson) };
        const auto algues { ecs::get_entities(ecs::is_algue) };
 
        std::uniform_int_distribution<size_t> entities_distribution(0, ecs::internal::_entities.size() - 1);
        std::uniform_int_distribution<size_t> algue_distribution(0, algues.size() - 1);
 
        for (auto poisson: poissons) {
            auto const& poisson_type = ecs::get_component(ecs::internal::_types, poisson);
 
            if (poisson_type != end(ecs::internal::_types)) {
                if (ecs::is_herbivore(std::get<1>(*poisson_type)) && !algues.empty()) {
                    const auto index { algue_distribution(ecs::internal::_random_engine) };
                    const auto target = algues[index];
                    entities_to_update.push_back(target);
                } else if (ecs::is_carnivore(std::get<1>(*poisson_type)) && !ecs::internal::_entities.empty()) {
                    const auto index { entities_distribution(ecs::internal::_random_engine) };
                    const auto target = ecs::internal::_entities[index];
                    entities_to_update.push_back(target);
                }
            }
        }
 
        // update pv
        for (auto entity: entities_to_update) {
            auto type_component = ecs::get_component(ecs::internal::_types, entity);
 
            if (type_component != end(ecs::internal::_types)) {
                if (std::get<2>(*type_component) > 2) 
                    std::get<2>(*type_component) -= 2;
                else
                    ecs::remove_entity(std::get<0>(*type_component));
            }
        }
 
        // update age
        for (auto & entity: ecs::internal::_types) {
            std::get<3>(entity) += 1;
            if (std::get<3>(entity) >= 20) 
                ecs::remove_entity(std::get<0>(entity));
        }
 
        ecs::print();
    }
 
    return 0;   
}

affiche :

25 entites: 
	id	pv	age
	0	10	0
	1	10	0
	2	10	0
	3	10	0
	4	10	0
	5	10	0
	6	10	0
	7	10	0
	8	10	0
	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 ======
25 entites: 
	id	pv	age
	0	8	1
	1	8	1
	2	8	1
	3	6	1
	4	6	1
	5	8	1
	6	10	1
	7	8	1
	8	6	1
	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 ======
24 entites: 
	id	pv	age
	1	2	2
	2	8	2
	3	4	2
	4	6	2
	5	6	2
	6	10	2
	7	8	2
	8	4	2
	9	8	2
	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 ======
23 entites: 
	id	pv	age
	2	6	3
	3	2	3
	4	4	3
	5	6	3
	6	4	3
	7	4	3
	8	2	3
	9	8	3
	10	8	3
	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 ======
19 entites: 
	id	pv	age
	2	6	4
	6	2	4
	7	4	4
	9	4	4
	10	8	4
	11	6	4
	12	8	4
	13	2	4
	14	10	4
	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 ======
15 entites: 
	id	pv	age
	10	6	5
	11	4	5
	12	8	5
	13	2	5
	14	10	5
	15	10	5
	16	8	5
	17	2	5
	18	10	5
	19	10	5
	20	10	5
	21	8	5
	22	6	5
	23	6	5
	24	6	5
===== Tour 5 ======
15 entites: 
	id	pv	age
	10	6	6
	11	2	6
	12	6	6
	13	2	6
	14	8	6
	15	10	6
	16	4	6
	17	2	6
	18	10	6
	19	10	6
	20	10	6
	21	8	6
	22	6	6
	23	6	6
	24	6	6
===== Tour 6 ======
14 entites: 
	id	pv	age
	10	6	7
	11	2	7
	12	6	7
	14	6	7
	15	10	7
	16	2	7
	17	2	7
	18	10	7
	19	10	7
	20	10	7
	21	4	7
	22	6	7
	23	6	7
	24	6	7
===== Tour 7 ======
12 entites: 
	id	pv	age
	10	6	8
	12	6	8
	14	4	8
	15	10	8
	16	2	8
	18	10	8
	19	10	8
	20	10	8
	21	4	8
	22	6	8
	23	6	8
	24	4	8
===== Tour 8 ======
11 entites: 
	id	pv	age
	10	6	9
	12	6	9
	14	2	9
	15	8	9
	18	10	9
	19	8	9
	20	10	9
	21	2	9
	22	6	9
	23	6	9
	24	4	9
===== Tour 9 ======
10 entites: 
	id	pv	age
	10	6	10
	12	4	10
	14	2	10
	15	8	10
	18	10	10
	19	8	10
	20	8	10
	22	2	10
	23	6	10
	24	4	10
===== Tour 10 ======
9 entites: 
	id	pv	age
	10	6	11
	12	2	11
	15	8	11
	18	10	11
	19	8	11
	20	8	11
	22	2	11
	23	6	11
	24	4	11
===== Tour 11 ======
8 entites: 
	id	pv	age
	10	4	12
	12	2	12
	15	8	12
	18	8	12
	19	6	12
	20	8	12
	23	6	12
	24	4	12
===== Tour 12 ======
6 entites: 
	id	pv	age
	10	4	13
	15	8	13
	18	8	13
	19	6	13
	20	8	13
	23	6	13
===== Tour 13 ======
6 entites: 
	id	pv	age
	10	4	14
	15	8	14
	18	8	14
	19	6	14
	20	8	14
	23	2	14
===== Tour 14 ======
5 entites: 
	id	pv	age
	10	4	15
	15	8	15
	18	8	15
	19	4	15
	20	8	15
===== Tour 15 ======
5 entites: 
	id	pv	age
	10	2	16
	15	8	16
	18	8	16
	19	4	16
	20	8	16
===== Tour 16 ======
4 entites: 
	id	pv	age
	15	8	17
	18	8	17
	19	4	17
	20	8	17
===== Tour 17 ======
4 entites: 
	id	pv	age
	15	8	18
	18	8	18
	19	4	18
	20	6	18
===== Tour 18 ======
4 entites: 
	id	pv	age
	15	8	19
	18	6	19
	19	4	19
	20	6	19
===== Tour 19 ======
1 entites: 
	id	pv	age
	18	6	19

Partie 3.2

Partie 3.3

javaquarium.1462792981.txt.gz · Dernière modification: 2016/05/09 13:23 par gbdivers