Poisson qui mange qui des cadavres

a marqué ce sujet comme résolu.

Bonjour je suis arrivé à la partie 2.1 du Javaquarium où les poissons doivent manger. J’ai donc une classe Fish avec un booléen isAlive (par défaut true) et une classe Aquarium avec une méthode eating.

Cette méthode parcours le vecteur contenant les poissons et chacun d’entre choisi eux un poisson aléatoire à manger. Si le poisson choisi de se manger lui même ou de manger un poisson de son espèce alors il ne mange pas et passe son tour. Un poisson mangé meurt (isAlive = false) mais ne peut pas être supprimé directement du vecteur puisque la méthode est en train de le parcourir c’est une méthode clear qui se charge de "nettoyer" ensuite l’aquarium.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void Aquarium::eating()
{
    for (size_t i{ 0 }; i < m_fishesCount; ++i)
    {
        if (m_fishes[i]->is_alive())
        {
            std::random_device rd;
            if (m_fishes[i]->is_carnivorous() && m_fishesCount > 1)
            {
                std::uniform_int_distribution<> dis(0, m_fishesCount - 1);
                int prey{ dis(rd) }; // Cherche un poisson aléatoire
                // Le poisson ne peut pas se manger lui même ou un poisson de la même race
                if (prey != i && m_fishes[prey]->get_race() != m_fishes[i]->get_race())
                {
                    m_fishes[i]->eat(m_fishes[prey]);
                }
            }
            else if (!m_fishes[i]->is_carnivorous() && m_seaweedsCount > 0)
            {
                std::uniform_int_distribution<> dis(0, m_seaweedsCount - 1);
                int prey{ dis(rd) };
                m_fishes[i]->eat(m_seaweeds[prey]);
                m_seaweeds.erase(begin(m_seaweeds) + prey);
                --m_seaweedsCount;
            }
        }
    }
}

Le problème arrive quand on choisit de manger un poisson déjà mort. Comme il n’est pas encore supprimé rien ne l’empêche d’être remangé.

J’aimerais donc savoir comment éviter ce problème.

J’espère avoir été claire dans mes explications et merci d’avance pour vos réponses.

+0 -0

Tu peut alors faire une boucle.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
do {
    // (1) Tirage aléatoire de la proie
} while (m_fishes[prey]->is_alive() == false || prey == i || m_fishes[prey]->get_race() == m_fishes[i]->get_race());
// (Boucle Do-While --> V )
// (2) Tant que le poisson tiré au hasard est soit déjà mort, 
// -> soit de la même race que le mangeur, soit c'est le mangeur -> Retour à (1)

// Si on arrive ici, c'est que le poisson tiré aléatoirement est valide.
// On mange donc le poisson.
m_fishes[i]->eat(m_fishes[prey]);

Attention cependant aux boucles infinies. Il faut que tu vérifie qu’il reste des poissons qui peuvent êtres mangés avant de lancer cette boucle, sans quoi ton programme tournera en boucle à la recherche d’un poisson valide alors qu’il n’y en à pas. ;)

+0 -0

Si je comprends bien (pas lu le sujet depuis bien longtemps), chaque poisson vivant doit choisir un et un seul poisson à manger parmi les autres poissons vivants. Ce que tu veux faire ici, c’est empêcher que deux poissons choisissent la même proie : chaque poisson a donc une et une seule proie, et chaque proie a au plus un prédateur. Comme il y a autant de proies que de poissons, chaque proie a donc exactement un prédateur.

Autrement dit, tu cherches, à partir du tableau des poissons vivants, à obtenir une permutation aléatoire de ce tableau qui sera le tableau des proies. Avec des termes moins barbares, tu veux mélanger aléatoirement le tableau des poissons pour obtenir le tableau des proies (avec, à l’indice i, la proie du poisson qui est au même indice i dans le tableau des poissons). Il y a des algorithmes pour faire ça qui sont un peu plus fûtés que ce que tu essayes actuellement de faire, je te laisse chercher et poser des questions si tu as du mal.

+0 -0

J’ai fini par trouver une autre solution c’est de créer dans la fonction eating un second vecteur preys qui contient des pointeurs vers toutes les prois. Le poisson choisit aléatoirement une proie dans ce vecteur. La proie est supprimé du vecteur preys mais pas de m_fishes. C’est encore une fois la fonction clear qui se charge de "nettoyer" m_fishes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
void Aquarium::eating()
{
    std::vector<std::shared_ptr<Fish>> preysF; // Tous les poissons mangeables
    unsigned int preysFCount{ m_fishesCount };
    for (size_t i{ 0 }; i < m_fishesCount; ++i)
    {
        preysF.push_back(m_fishes[i]);
    }

    std::vector<std::shared_ptr<Seaweed>> preysA; // Toutes les algues mangeables
    unsigned int preysACount{ m_seaweedsCount };
    for (size_t i{ 0 }; i < m_seaweedsCount; ++i)
    {
        preysA.push_back(m_seaweeds[i]);
    }

    for (size_t i{ 0 }; i < m_fishesCount; ++i)
    {
        if (m_fishes[i]->is_alive())
        {
            std::random_device rd;

            if (m_fishes[i]->is_carnivorous() && preysFCount > 0) // Si le poisson est carnivore
            {
                std::uniform_int_distribution<> dis(0, preysFCount - 1);
                int prey{ dis(rd) }; // Trouve une proie aléatoire

                // Si le poisson choisi de se manger lui même ou poisson de la même race alors il ne mange pas
                if (prey != i && preysF[prey]->get_race() != m_fishes[i]->get_race())
                {
                    m_fishes[i]->eat(preysF[prey]);

                    if (!preysF[prey]->is_alive()) // Si la proie est morte
                    {
                        preysF.erase(std::begin(preysF) + prey); // On la supprime
                        --preysFCount;
                    }
                }

            }
            else if (!m_fishes[i]->is_carnivorous() && preysACount > 0) // Si le poisson est herbivore
            {
                std::uniform_int_distribution<> dis(0, preysACount - 1);
                int prey{ dis(rd) }; // Trouve une proie aléatoire

                m_fishes[i]->eat(preysA[prey]);

                if (!preysA[prey]->is_alive()) // Si la proie est morte...
                {
                    preysA.erase(std::begin(preysA) + prey); // ...on la supprime
                    --preysACount;
                }
            }

        }
    }
}

Merci pour vos conseils et j’espère que ma méthode servira à quelqu’un.

Lu’!

Cette fonction est beaucoup, beaucoup, trop longue, découpe tout ça. Tu pourras aussi chercher sur le forum des posts sur le dispatch multiple en C++ pour l’exercice du Javaquarium. Il y a des posts qui exposent une solution plus modulaire que ce que tu fais actuellement :) .

(Exemple ici, tu auras beaucoup de code à modifier si on te demande d’ajouter des charognards).

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte