Problème avec DeleteElement() dans un algo de Labyrinthe

Programmation d'applications complexes
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Problème avec DeleteElement() dans un algo de Labyrinthe

Message par graph100 »

Bonjour à tous !

je suis en train de faire un algo pour générer des labyrinthes, et je me retrouve bloqué par une ou deux erreurs bizarres ;'(
Le problème de ces erreurs est qu'elle ne sont pas systématiques, et que je n'ai pas réussi à mettre en évidence ce qui la provoquais.

le code est un peu long, mais je ne peux pas en enlever plus pour que le programme fonctionne normalement.

voici le code :

Code : Tout sélectionner


If InitSprite() = 0 Or InitKeyboard() = 0
    End
EndIf

#w0 = 50
#h0 = 50

#d = 5

#w = (#w0 + 1) * #d
#h = (#h0 + 1) * #d

If OpenWindow(0, 0, 0, #d * #w0 + 1, #d * #h0 + 1 + 20, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
    ProgressBarGadget(0, 0, WindowHeight(0) - 20, WindowWidth(0), 20, 0, 100)
    
    If OpenWindowedScreen(WindowID(0), 0, 0, WindowWidth(0), WindowHeight(0) - 20, 0, 0, 0)
        
    EndIf
EndIf

KeyboardMode(1)

; Dim laby(#w, #h)

Procedure Hazard(*point.POINT) ; retourne le point suivant, ou (-1, -1) si il n'est pas possible de trouver un point libre autour du point courant
    fin = 0
    
    a0 = 0
    a1 = 0
    a2 = 0
    a3 = 0
    
    NewList tab()
    
    AddElement(tab())
    tab() = 0
    AddElement(tab())
    tab() = 1
    AddElement(tab())
    tab() = 2
    AddElement(tab())
    tab() = 3
    
    Repeat
        SelectElement(tab(), Random(ListSize(tab()) - 1))
        
        a = tab()
        DeleteElement(tab())
        
        If a = 0
            If *point\x > 0
                If Point(*point\x - #d, *point\y) = 0
                    *point\x = *point\x - #d
                    fin = 1
                EndIf
            EndIf
            
        ElseIf a = 1
            If *point\y > 0
                If Point(*point\x, *point\y - #d) = 0
                    *point\y = *point\y - #d
                    fin = 1
                EndIf
            EndIf
            
        ElseIf a = 2
            If *point\x < #w - #d - 1
                If Point(*point\x + #d, *point\y) = 0
                    *point\x = *point\x + #d
                    fin = 1
                EndIf
            EndIf
            
        ElseIf a = 3
            If *point\y < #h - #d - 1
                If Point(*point\x, *point\y + #d) = 0
                    *point\y = *point\y + #d
                    fin = 1
                EndIf
            EndIf
            
        EndIf    
        
        
    Until fin = 1 Or ListSize(tab()) = 0
    
    If ListSize(tab()) = 0 And fin = 0
        *point\x = -1
        *point\y = -1
    EndIf
    
EndProcedure

;{ initialisation
x = 0
y = -#d

debut = 0
mode = 0
ordre = 0
stop = 0

solve = 0

chrono = ElapsedMilliseconds()

start.POINT\x = 1
start.POINT\y = 1

goal.POINT\x = #d * ((#w - 1) / #d) - #d + 1
goal.POINT\y = #d * ((#h - 1) / #d) - #d + 1

nb_mur = 0
nb_max_mur = (#w0 - 1) * (#h0 - 1)

; ((pos_x - 1 + #d) / #d) * #d + 1 = x

;}

NewList carte.point()
Dim MemCarte(#w0, #h0)

;{ remplissage de la liste des positions : carte()
Repeat
    event = WindowEvent()
    ExamineKeyboard()
    
    y + #d
    If y > #h - #d - 1
        y = 0
        x + #d
    EndIf
    
    If x > #w - #d - 1
        debut = 1
    Else
        AddElement(carte())
        
        carte()\x = x
        carte()\y = y
        
        MemCarte(x / #d, y / #d) = @carte() ; récupéraiton des adresses mémoires de tout les éléments de la ligne chainée
        
        SetGadgetState(0, 100 * ListSize(carte()) / #w0 / #h0)
    EndIf
    
    
    If KeyboardReleased(#PB_Key_Escape) : event = #PB_Event_CloseWindow : EndIf
    
Until event = #PB_Event_CloseWindow Or debut = 1

If event = #PB_Event_CloseWindow : End : EndIf

;}

x = 0
y = 0

Debug ListSize(carte())
Debug #w0 * #h0

Repeat
    event = WindowEvent()
    ExamineKeyboard()
    
    ;{ relance et recherche
    If mode = 0 And ordre = 0 And stop = 0; relance
        
        ; selectionne un element de la liste au hazard et démarre un mur à partir des positions stockées dans la liste
        
        ;########################################
        SelectElement(carte(), Random(ListSize(carte()) - 1)) ; Erreur beaucoup plus rare que l'autre : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 1)
        ;########################################
        
        x = carte()\x
        y = carte()\y
        
        mode = 1
    EndIf
    ;}
    
    ;{ dessin et construction du labyrinthe
    If stop = 0
        
        If StartDrawing(ScreenOutput())
            If debut = 1 ; tour blanc de l'ecran (contour du labyrinthe), nécessaire pour contenir les murs formés
                LineXY(0, 0, #d * #w0, 0, #White)
                LineXY(0, 0, 0, #d * #h0, #White)
                LineXY(0, #d * #h0, #d * #w0, #d * #h0, #White)
                LineXY(#d * #w0, 0, #d * #w0, #d * #h0, #White)
            EndIf
            
            ; on cherche le point suivant du mur
            point.POINT\x = x
            point.POINT\y = y
            
            Hazard(@point)
            
            If point\x = -1 And point\y = -1 ; pas de point possibles --> le mur n'a nul part ou grandir --> on en crée un autre mais avant il faut supprimer la position courante (bloquée) de la liste des choix
                mode = 0
                
                ChangeCurrentElement(carte(), MemCarte(x / #d, y / #d)) ; on met l'element courant à l'élement qui a les bonnes coordonées (doit être en rapport avec le bug.
                
                
                Debug "---"
                Debug ListSize(carte())
                Debug MemCarte(x/#d, y/#d)
                Debug carte()
                Debug "#"
                Debug carte()\x
                Debug carte()\y
                Debug x
                Debug x / #d
                Debug y
                Debug y / #d
                
                ;###########################
                DeleteElement(carte(), 1) ; BUG étrange : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 8)
                ;###########################
                
                SetGadgetState(0, 100 * (#w0 * #h0 - ListSize(carte())) / #w0 / #h0) ; affichage de la progression
                
                If ListSize(carte()) = 0 ; 1ère étape finie ^^, le reste viendras quand le bug seras corrigé
                    Debug nb_mur
                    Debug nb_max_mur
                    Debug StrD(100 * nb_mur / nb_max_mur, 1) + " %"
                    Debug #w0
                    Debug #h0
                    
                    chrono = ElapsedMilliseconds() - chrono
                    
                    Debug "stop - labyrinthe généré en : " + StrD(chrono / 1000, 3) + " s"
                    
                    stop = 1
                EndIf
            Else ; sinon, on dessine le mur, et on passe au point suivant
                LineXY(x, y, point\x, point\y, #White)
                
                nb_mur + 1
                
                x = point\x
                y = point\y
            EndIf
            
            Box(start\x, start\y, #d - 1, #d - 1, #Red) ; position de départ (code qui s'en occupe supprimé pour la longueur)
            Box(goal\x, goal\y, #d - 1, #d - 1, #Green) ; position d'arrivé
            
            StopDrawing()
        EndIf
        
    EndIf
    ;}
    
    FlipBuffers()
    
Until event = #PB_Event_CloseWindow Or KeyboardReleased(#PB_Key_Escape)

End
Les deux problèmes doivent être liés. Mais je ne vois vraiment pas.
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Problème avec DeleteElement() dans un algo de Labyrinthe

Message par G-Rom »

Il faut testé si les élement existe :
Element = Random(ListSize(carte()) - 1)
If Element => 0 And Element < ListSize(carte())
SelectElement(carte(), Element) ; Erreur beaucoup plus rare que l'autre : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 1)
EndIf
Idem ici , si il y a qu'un element et que tu le supprime , la commande essaye de changé l'élément précèdent

en élément courant , comme il y en a pas , bug...
If ListSize(carte())>1
DeleteElement(carte(), 1) ; BUG étrange : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 8)
EndIf
Avatar de l’utilisateur
case
Messages : 1546
Inscription : lun. 10/sept./2007 11:13

A

Message par case »

sachant que si tu met

deletelement (liste(),1)

l'élément suivant deviens l'élément courant. voir la documentation pour plus d'information ;)
ImageImage
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: Problème avec DeleteElement() dans un algo de Labyrinthe

Message par graph100 »

G-Rom a écrit :Il faut testé si les élement existe :
Element = Random(ListSize(carte()) - 1)
If Element => 0 And Element < ListSize(carte())
SelectElement(carte(), Element) ; Erreur beaucoup plus rare que l'autre : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 1)
EndIf
Le problème n'est pas si simple,
tout d'abord,

Code : Tout sélectionner

Random(Max)
retourne un nombre entier compris en 0 et Max, donc si je met

Code : Tout sélectionner

Random(ListSize(carte()) - 1)
je suis sur d'avoir un "element" compris entre 0 et ListSize(carte()) - 1
De plus,
Aide PureBasic a écrit :SelectElement(Liste(), Position) :
Déplace l'élément courant de la liste à la position spécifiée. Le premier élément de la liste est à la position 0, le suivant à la position 1
Donc le random tombe forcement dans les limites imposés à SelectElement(). D'ou mon incompréhension à la vu de l'erreur à cet endroit

Code : Tout sélectionner

SelectElement(carte(), Element) ; Erreur beaucoup plus rare que l'autre : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 1)
------
G-Rom a écrit : Idem ici , si il y a qu'un element et que tu le supprime , la commande essaye de changé l'élément précèdent

en élément courant , comme il y en a pas , bug...
If ListSize(carte())>1
DeleteElement(carte(), 1) ; BUG étrange : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 8)
EndIf
Pour l'autre erreur, effectivement, comme l'a dit Case, le soucis n'est pas avec les options ou comportement de la commande DeleteElement().
On peut effacer le dernier element d'une liste :
Aide PureBasic a écrit :DeleteElement(Liste() [, Options]) :
Si la liste ne contenait plus qu'un seul élément, alors il n'y a plus d'élément courant.
Le soucis est qu'il s'agit d'une erreur sur la commande DeleteElement(), alors que dans les cas que tu as mentionné, l'erreur aurais surgis lors d'un essaie de lecture du nouvel element courant.
Or, ça plante, alors qu'on peut lire l'element avant d'essayer de l'effacer !

Code : Tout sélectionner

                ChangeCurrentElement(carte(), MemCarte(x / #d, y / #d)) ; on met l'element courant à l'élement qui a les bonnes coordonées (doit être en rapport avec le bug.
                
                
                Debug "---"
                Debug ListSize(carte())
                Debug MemCarte(x/#d, y/#d)
                Debug carte()
                Debug "#"
                Debug carte()\x
                Debug carte()\y
                Debug x
                Debug x / #d
                Debug y
                Debug y / #d
                
                ;###########################
                DeleteElement(carte(), 1) ; BUG étrange : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 8)
                ;###########################
Les Debug effectués juste avant l'erreur fonctionnent et affiche bien les bonnes valeurs (à savoir carte()\x = x et carte()\y = y à ce moment)
Donc je pédale dans la choucroute d'autant plus que j'ai déjà utilisé cette méthode dans le prog :

Code : Tout sélectionner

Procedure Hazard(*point.POINT) ; retourne le point suivant, ou (-1, -1) si il n'est pas possible de trouver un point libre autour du point courant
    fin = 0
    
    a0 = 0
    a1 = 0
    a2 = 0
    a3 = 0
    
    NewList tab()
    
    AddElement(tab())
    tab() = 0
    AddElement(tab())
    tab() = 1
    AddElement(tab())
    tab() = 2
    AddElement(tab())
    tab() = 3
    
    Repeat
        SelectElement(tab(), Random(ListSize(tab()) - 1))
        
        a = tab()
        DeleteElement(tab())
        
        If a = 0
            If *point\x > 0
                If Point(*point\x - #d, *point\y) = 0
                    *point\x = *point\x - #d
                    fin = 1
                EndIf
            EndIf
            
        ElseIf a = 1
            If *point\y > 0
                If Point(*point\x, *point\y - #d) = 0
                    *point\y = *point\y - #d
                    fin = 1
                EndIf
            EndIf
            
        ElseIf a = 2
            If *point\x < #w - #d - 1
                If Point(*point\x + #d, *point\y) = 0
                    *point\x = *point\x + #d
                    fin = 1
                EndIf
            EndIf
            
        ElseIf a = 3
            If *point\y < #h - #d - 1
                If Point(*point\x, *point\y + #d) = 0
                    *point\y = *point\y + #d
                    fin = 1
                EndIf
            EndIf
            
        EndIf    
        
        
    Until fin = 1 Or ListSize(tab()) = 0
    
    If ListSize(tab()) = 0 And fin = 0
        *point\x = -1
        *point\y = -1
    EndIf
    
EndProcedure
Et qu'il n'y a pas d'erreur. Je pense que le soucis viens du fait de l'utilisation de la commande ChangeCurrentElement()

Dans mon prog, l'objectif est de :

-> Lister toutes les cases utilisables.
-> sélectionner une case au hazard, qui n'a pas déjà remplis toutes les conditions de non utilisation.
-> Supprimer de la liste les cases qui remplissent ces conditions, jusqu'à vider la liste et à ce moment la, on sait qu'on a finit.

Pour arriver à cela, j'ai fait :

# création d'une liste chainée qui va contenir les coordonées des cases qui m'interressent.

Code : Tout sélectionner

NewList carte.point()
#création d'un tableau, du nombre de case (w0, h0) qui va contenir les adresses des éléments de la liste chainée contenant les coordonée correspondantes :

Code : Tout sélectionner

Dim MemCarte(#w0, h0)
# remplissage de la liste et du tableau :

Code : Tout sélectionner

x = 0
y = -#d

Repeat
    y + #d
    If y > #h - #d - 1
        y = 0
        x + #d
    EndIf
    
    If x > #w - #d - 1
        debut = 1
    Else
        AddElement(carte())
        
        carte()\x = x
        carte()\y = y
        
        MemCarte(x / #d, y / #d) = @carte() ; récupération des adresses mémoires de tout les éléments de la ligne chainée
    EndIf
Until debut = 1
Dans la boucle principale sont enfin effectué les actions suivantes :
# Si il n'y a pas de mur en cours, on choisi un nouvel élément de départ :

Code : Tout sélectionner

        ;########################################
        SelectElement(carte(), Random(ListSize(carte()) - 1)) ; Erreur beaucoup plus rare que l'autre : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 1)
        ;########################################
        
        x = carte()\x
        y = carte()\y
# Si la procedure créant le mur ne peut plus avancer car elle est dans un cul de sac, supprimer l'élément de la liste pour ne plus jamais retomber dessus lors de la relance d'un mur :

Code : Tout sélectionner

                ChangeCurrentElement(carte(), MemCarte(x / #d, y / #d)) ; on met l'element courant à l'élement qui a les bonnes coordonées (doit être en rapport avec le bug.
                
                
                Debug "---"
                Debug ListSize(carte())
                Debug MemCarte(x/#d, y/#d)
                Debug carte()
                Debug "#"
                Debug carte()\x
                Debug carte()\y
                Debug x
                Debug x / #d
                Debug y
                Debug y / #d
                
                ;###########################
                DeleteElement(carte(), 1) ; BUG étrange : [ERREUR] Accès mémoire invalide. (erreur de lecture à l'adresse 8)
                ;###########################
Voila, expliqué rapidement la logique du prog.
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
case
Messages : 1546
Inscription : lun. 10/sept./2007 11:13

Re: Problème avec DeleteElement() dans un algo de Labyrinthe

Message par case »

en fait je ne comprend pas bien la logique derrière cette routine cependant le problème viens effectivement de cette ligne

Code : Tout sélectionner

                ;ChangeCurrentElement(carte(), MemCarte(x / #d, y / #d)) ; on met l'element courant à l'élement qui a les bonnes coordonées (doit être en rapport avec le bug.
en la remplacant par celle ci

Code : Tout sélectionner

               SelectElement(carte(),Random(ListSize(carte())-1))
plus de plantage.

par contre pas sur que le labyrinthe soit aussi précis que ce que tu recherches


pour moi le problème viens du fait que dans ton tableau memcarte() tu conserve l'adresse d'un élément de la liste qui a peut être été effacé donc en modifiant l'élément courant de carte() avec l'adresse memoire de memcarte tu te retrouve hors de la liste et plantage.
ImageImage
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: Problème avec DeleteElement() dans un algo de Labyrinthe

Message par graph100 »

Je suis d'accord avec la 2eme partie, mais pas la 1ère ^^

car

Code : Tout sélectionner

ChangeCurrentElement(carte(), MemCarte(x / #d, y / #d))
permet de retrouver l'élément de la liste qui contient les mêmes valeurs que x et y ^^ donc point de hazard à cet endroit.

Le but de l'algo etant de rapidement trouver les derniers points à compléter sur le laby. On peut le faire avec un random() sur x et y, cependant on ne saura jamais si on a completer TOUT les point de la carte !! D'ou la liste chainée contenant les positions et dont on supprime les éléments compléter au fur à et mesure.

Quand à ce que tu dis dans la 2ème partie, effectivement, le pb viens de ChangeCurrentElement() qui me fait pointer ma liste sur un element supprimé ! Ce que je comprend pas, c'est que cette commande ne détecte pas d'erreur, et que même si j'ai effacer l'élément, on peut quand même y accéder (puisque le debug l'affiche) !!

Code : Tout sélectionner

NewList test()

AddElement(test())

test() = 5


*res = AddElement(test())

test() = 7


AddElement(test())

test() = 3

ForEach test()
    Debug test()
Next
Debug "#######"

ChangeCurrentElement(test(), *res)
Debug test() ; affiche la valeur de l'element 1

DeleteElement(test()) ; on supprime l'element 1

Debug test() ; l'element à bien changé

ChangeCurrentElement(test(), *res) ; on retourne sur l'element qu'on viens de supprimer avec son ancienne adresse
Debug test() ; on peut toujours l'afficher, mais on ne peux pas le supprimer car il n'existe plus dans la liste

Debug "#######"

ForEach test()
    Debug test()
Next
J'ai trafiqué un peu le prog et trouver qu'il arrive qu'un élément soit appelé 2 fois d'ou le plantage.
Donc il faut maintenant que je comprenne d'ou viens ce 2nd appel foireux ;'(
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
case
Messages : 1546
Inscription : lun. 10/sept./2007 11:13

Re: Problème avec DeleteElement() dans un algo de Labyrinthe

Message par case »

beuh... je comprend toujours pas ton raisonnement sur le besoin de bouger des pointeurs alors que tu peux simplement suivre la liste chainée ...

Code : Tout sélectionner

;{ dessin et construction du labyrinthe
    If stop = 0
       
        If StartDrawing(ScreenOutput())
            If debut = 1 ; tour blanc de l'ecran (contour du labyrinthe), nécessaire pour contenir les murs formés
                LineXY(0, 0, #d * #w0, 0, #White)
                LineXY(0, 0, 0, #d * #h0, #White)
                LineXY(0, #d * #h0, #d * #w0, #d * #h0, #White)
                LineXY(#d * #w0, 0, #d * #w0, #d * #h0, #White)
            EndIf
           
            ; on cherche le point suivant du mur
            point.POINT\x = x
            point.POINT\y = y
           
            Hazard(@point)
           
            If point\x = -1 And point\y = -1 ; pas de point possibles --> le mur n'a nul part ou grandir --> on en crée un autre mais avant il faut supprimer la position courante (bloquée) de la liste des choix
                mode = 0                              
                ;###########################
                DeleteElement(carte(), 1) ; On vire l'element actuel de la liste car bloqué
                ;###########################                 
                ;
                ;
                ;
                FirstElement(carte()) ; On prend un element restant de la liste
                ;
                ;
                ;
                ;
                SetGadgetState(0, 100 * (#w0 * #h0 - ListSize(carte())) / #w0 / #h0) ; affichage de la progression
               
                If ListSize(carte()) = 0 ; 1ère étape finie ^^, le reste viendras quand le bug seras corrigé
                    Debug nb_mur
                    Debug nb_max_mur
                    Debug StrD(100 * nb_mur / nb_max_mur, 1) + " %"
                    Debug #w0
                    Debug #h0
                   
                    chrono = ElapsedMilliseconds() - chrono
                   
                    Debug "stop - labyrinthe généré en : " + StrD(chrono / 1000, 3) + " s"
                   
                    stop = 1
                EndIf
            Else ; sinon, on dessine le mur, et on passe au point suivant
                LineXY(x, y, point\x, point\y, #White)
               
                nb_mur + 1
               
                x = point\x
                y = point\y
            EndIf
           
            Box(start\x, start\y, #d - 1, #d - 1, #Red) ; position de départ (code qui s'en occupe supprimé pour la longueur)
            Box(goal\x, goal\y, #d - 1, #d - 1, #Green) ; position d'arrivé
           
            StopDrawing()
        EndIf
       
    EndIf
    ;}
ca marche très bien comme cela :)
ImageImage
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: Problème avec DeleteElement() dans un algo de Labyrinthe

Message par graph100 »

^^

ta méthode fonctionne bien, par contre l'explication contient une petite erreur. Mais je vais quand même prendre cette méthode (plus simple effectivement)

petite erreur : le point que tu efface n'est pas bloqué ! Car le point contenu dans Carte() est le départ du mur ! Alors que le point bloqué qui déclenche l'arrivé à ce point du prog est le point de fin de mur !
Je voulais supprimer les points de fin, pour être sur que ces points-ci ne peuvent plus servir. Mais bon, après un instant de réflexion, on vois que supprimer les points de départ ne cause pas de création de "trou" dans le labyrinthe ^^

Zamer'ss !
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Répondre