Simulateur d'écosystème
Posté le 07/05/2014 21:54
J'expose ici un projet sur lequel je
m'arrache les cheveux planche depuis plusieurs semaines déjà : un add-in capable de simuler un écosystème, sous forme des sprites simples pour avoir une vue globale. 8)
Le panel d'éléments est très succinct, il y aura des petites plantes, des arbres, des herbivores et des carnivores. Il seront capables d'interagir entre eux en se courant après, en se bouffant et en se reproduisant.
La particularité de ce simulateur résidera dans l'introduction de l'évolution. Par exemple, si une population importante d'herbivores devient trop résistante, alors vous pourrez peut-être observer que certains carnivores arrivent à manger en adoptant un comportement charognard (et ce de façon indirecte, c'est-à-dire que le programme sera prévu pour que des mutations apparaissent, mais le fait que ces mutations se répandent si elles sont favorables ne sera dû qu'aux lois de la sélection naturelle).
Bon, j'espère que je ne vous ai pas déjà paumé, de toute façon les explications précédentes sont plus de l'ordre du détail technique (heureusement parce que sinon je n'aurai pas beaucoup de téléchargements
).
Bien sûr, pour que cet add-in reste ludique, l'utilisateur pourra agir à sa guise sur l'écosystème (je ne sais pas encore comment). Au final, ce ne sera pas vraiment un jeu mais plutôt une espèce de "bac à sable".
Pour l'anecdote, l'idée m'est venue en lisant un article de Science & vie sur la définition de la vie, qui abordait des éléments (chimiques, informatiques, physiques...) troublants, car très semblables à des formes de vie. Et ce qui a attiré mon attention est un programme (dont le nom m'échappe) d'un chercheur (dont le nom m'échappe aussi
) qui affichait des formes très simples, mais capables d'évoluer et de se parasiter entre elles.
Bref, ça m'avait plutôt fasciné, notamment le fait que le programme aboutit à des résultats non prévus car découlant du développement de l'I.A., et ça m'a donnait envie de faire pareil.
L'idée a germé et a aboutit à ce projet d’écosystème
Voilà, j'aurai préféré vous exposer ce projet plus tard car j'aurais bien aimé que vous ayez une démo digne de ce nom, mais il se trouve que je planche sur les I.A. et que j'ai actuellement un problème tenace que je ne parvient pas à résoudre moi-même
cf premier post
Citer : Posté le 08/05/2014 18:02 | #
On ne va pas s'énerver, cela ne servira à rien.
Tu as dit que les erreurs venaient de pointeurs de cibles qui prenaient des valeurs farfelues. J'ai remarqué que les éléments dont l'état est INEXISTANT continuent d'avoir des prédateurs, et c'est d'ailleurs la raison d'être de cet état (si je ne m'abuse).
Or donc, pour pouvoir trouver la provenance des erreurs quelles qu'elles soient, il est infiniment plus facile de commencer chaque itération par une configuration complètement "stable", "valide" où aucun élément n'est à ce stade temporaire. Et ton programme ne fonctionne pas de cette manière, il effectue les opérations comme suit.
[b]Alors[/b] on modifie la valeur des pointeurs
[b]FinSi[/b]
[b]Si[/b] [i]condition[/i]
[b]Alors[/b] l'element prend l'etat INEXISTANT
[b]FinSi[/b]
Schéma simplifié. Or ce code a un inconvénient car, à la fin de l'itération, les éléments dont l'état est INEXISTANT n'ont pas été supprimés, ce qui a des incidences sur l'itération suivante.
Je te propose de modifier ta boucle pour qu'elle fasse l'inverse.
[b]Alors[/b] l'element prend l'etat INEXISTANT
[b]FinSi[/b]
[b]Si[/b] les cibles sont nulles
[b]Alors[/b] on modifie la valeur des pointeurs
[b]FinSi[/b]
[b]Si[/b] un element est INEXISTANT
[b]Alors[/b] on le supprime
[b]FinSi[/b]
Ici, chaque itération est indépendante de la précédente et n'a aucune incidence sur la suivante. L'avantage de cette configuration est qu'elle simplifie largement la compréhension du programme, son édition et la recherche des erreurs.
Ce genre de simplification de la structure du programme a, de plus, tendance à résoudre d'elle-même les bugs.
Citer : Posté le 08/05/2014 18:22 | #
Si je prend ton deuxième algorithme, le pointeur de l'élément à supprimer sera bel et bien initialisé, mais celui de ses prédateurs ne le sera pas, ce qui engendre des erreurs.
Tiens, je vais même faire un schéma. Nous avons donc une boucle qui passe chaque structure en revue :
| structure
| structure
| structure
| structure
V
Considérons un être ciblé par un autre :
| être ciblé //je suis vivant
| structure
| prédateur //je pointe sur ma proie
| structure
V
Dans ton code, tu voudrais supprimer l'être dès sa disparition. Mais ça donnerait ceci :
| [i]suppression immédiate[/i]
| structure
| prédateur //je pointe sur du vide
| structure
V
Or cela engendre une erreur !
Avec ma méthode, je procède en deux temps. Première itération :
| être ciblé //J'ai disparu donc ma composante "etat" prend pour valeur INEXISTANT
| structure
| prédateur //La cible que je pointe a pour état la valeur INEXISTANT. J'initialise mon pointeur.
| structure
V
Seconde itération :
| [i]suppression car plus de prédateurs[/i]
| structure
| prédateur //Je pointe sur NULL
| structure
V
Citer : Posté le 08/05/2014 18:27 | #
Eh non ! Voilà ce que fait mon code.
| etre cible // devient INEXISTANT
| structure
| predateur // ma cible a disparu, je passe a NULL
| structure
V
On supprime tous les INEXISTANTS.
Qu'en dis-tu ?
Citer : Posté le 08/05/2014 18:31 | #
Mais pour supprimer tous les INEXISTANTS à la fin de la boucle, le seul moyen est de refaire une boucle rien que pour ça:
| etre cible // devient INEXISTANT
| structure
| predateur // ma ciblea disparu, je passe a NULL
| structure
V
| structure // On supprime s'il est INEXISTANT
| structure //etc.
| structure
| structure
| structure
V
Autant intégrer cette action dans la boucle principale, c'est nettement plus simple
Ajouté le 08/05/2014 à 18:34 :
Autre chose, ton code ne gère pas ce cas de figure :
| predateur // ma cible est encore là
| structure
| etre cible // disparaît et devient INEXISTANT
| structure
V
On supprime tous les INEXISTANTS.
Et on garde le pointeur sur une structure supprimée...
Citer : Posté le 08/05/2014 18:36 | #
Ok, trois boucles donc. Moins optimisé. Et nettement moins générateur de bugs.
Je te le dis encore une fois, ce n'est qu'une solution parmi d'autres, mais ça rend tout plus facile.
Et puis ce n'est qu'une suggestion, tu peux te débrouiller autrement.
N'empêche qu'il me semble que jusqu'ici tu était coincé donc...
Citer : Posté le 08/05/2014 18:46 | #
Coincé, oui, mais sur tout autre chose :/
Jusque là je n'ai fait qu'expliquer mon code
J'ai étudié mes structures à coup de Breakpoints et il y a une adresse mémoire récurrente : 0x88023F04
Donc c'est sûrement une affectation de mon pointeur quelque part qui me colle cette adresse bidon, mais où ? Je ne le sais toujours pas...
Citer : Posté le 08/05/2014 18:55 | #
Essaie d'afficher l'adresse de ta liste chaînée ou de ta map. On va vite savoir ce que ça donne.
Citer : Posté le 08/05/2014 19:02 | #
Fausse piste, ce ne sont pas les mêmes adresses mémoire.
Je vais faire un test sans utiliser la fonction recherche, en mettant simplement un herbivore qui cible une petite plante.
Ajouté le 08/05/2014 à 19:12 :
O.K., ça change quand même...mais ce n'est plus 0x88023F04
Je vous tiens au courant
Ajouté le 27/05/2014 à 18:06 :
Y a du nouveau (enfin, pas beaucoup en fait).
En programmant un démineur j'avais eu droit à un bug similaire dans une fonction récursive :
{
char i,j;
GRILLE(x,y)=0;
*cases_devoilees++; //l'adresse mémoire changeait entre les appels, sans explication
if(!MAP(x,y))
for(i=x-1;i<=x+1;i++)
for(j=y-1;j<=y+1;j++)
if((i!=x || j!=y) && i && i<=largeur && j && j<=hauteur)
if(MAP(i,j)>=0 && GRILLE(i,j)==1)devoiler_case(grille,map,i,j,largeur,hauteur,cases_devoilees);
}
Mais quand j'ai bazardé le pointeur au profit d'un return ça s'est mis à fonctionner sans problème. Je me pose donc la question : serait-ce un problème d'envoi de paramètre ? Je vous ai déjà vu écrire des types de variables dans des appels de fonction mais je ne connais rien à cette méthode.
Citer : Posté le 27/05/2014 18:14 | #
Peut-être du cast ?
Ce que je peux te dire, c'est que lorsque l'on appelle une fonction, les quatre premiers paramètres sont stockés dans des registres prévus à cet effet, et que les autres sont placés sur la pile.
Or lors de l'appel d'une foncion, l'adresse de retour est placée sur la pile, ce qui peut avoir créé ton interférence.
Citer : Posté le 27/05/2014 19:40 | #
J'ai modifié les fonctions de mon simulateur de telle sorte que tous les pointeurs soient dans les 4 premiers paramètres, mais ça n'a rien changé :aie2:
Est-ce que cette histoire de registre pourrait aussi affecter les autres variables ?