Conway's Game of Life



  • Hallo zusammen!

    ich habe heute versucht das Spiel des Lebens zu programmieren.
    Abgesehen vom Programmierstil habe ich das Problem, dass das Programm nur bis zur 4 Generation richtig läuft.

    Lasse ich 5 Generationen Simulieren, wird die dritte Generation am Rand falsch, obwohl sie vorher beim Durchlauf bis zu vier Generationen richtig lief!

    Vielleicht seht ihr ja einen Fehler (ich weiß, eine "runde Welt" habe ich nicht mit drin. Die war aber im Programm vorher, was das gleiche Problem hatte. Der Fehler tritt nur am Rand auf!)

    #include <stdio.h>
    
    int main() {
        int i, i_max, j, j_max; //i Zeilen, j Spalten
        int gen, gen_max;
    
        i_max=10;
        j_max=10;
        gen_max=5;
    
        int zelle[i_max][j_max][gen_max];
        int sum[i_max][j_max];
    
        for(gen=0; gen<gen_max; gen++){
    
            if(gen==0){
                for(j=0; j<j_max; j++){
                    for(i=0; i<i_max; i++){
                        zelle[i][j][gen] = 0;
    
                        zelle[4][3][gen] = 1;
                        zelle[4][4][gen] = 1;
                        zelle[4][5][gen] = 1;
                        zelle[5][4][gen] = 1;
    
                        if(zelle[i][j][gen]==1){printf(" o ");}
                        else{printf(" . ");}
    
                    }
                    printf("\n");
                }
            }
    
            else{
                for(j=0; j<j_max; j++){
                    for(i=0; i<i_max; i++){
    
                        for(int n=0; n<8; n++){
                            sum[i][j]= zelle[i-1] [j-1] [gen-1] + zelle[i] [j-1] [gen-1] + zelle[i+1] [j-1] [gen-1]
                                    +  zelle[i+1] [j]   [gen-1]
                                    +  zelle[i+1] [j+1] [gen-1] + zelle[i] [j+1][gen-1] + zelle[i-1][j+1][gen-1]
                                    +  zelle[i-1] [j]   [gen-1];
                        }
    
                        if(sum[i][j]<2){
                            zelle[i][j][gen]=0;
                        }
    
                        if(sum[i][j]==2){
                            zelle[i][j][gen]=zelle[i][j][gen-1];
                        }
    
                        if(sum[i][j]==3){
                            zelle[i][j][gen]=1;
                        }
    
                        if(sum[i][j]>3){
                            zelle[i][j][gen]=0;
                        }
    
                        if(zelle[i][j][gen]==1){printf(" o ");}
                        else{printf(" . ");}
                    }
    
                    printf("\n");
                }
            }
    
            printf("\n\n\n");
        }
    
        return 0;
    }
    

  • Mod

    Solche Sachen wie i-1 oder j+1 werden am Rand deines Spielfeldes überlaufen. Du unternimmst derzeit überhaupt gar nichts, um diese Randeffekte korrekt zu behandeln. Guck dir mal den Modulooperator % an.

    Es gibt auch ein paar Sachen, die nicht direkt falsch, aber ungeschickt sind. Zum Beispiel deine ganze Initialisierung, auf gleich mehrfache Art und Weise:
    1. So eine Koppelung von for mit if wie bei dir ist sehr ungeschickt. Wieso nicht einfach den Fall 0 vor dem for machen, dann das for von 1 aus loslaufen lassen? Wenn es sowieso keine Gemeinsamkeit zwischen dem Fall 0 und dem Fall >0 gibt, dann mach es keinen Sinn, diese zusammen in die gleiche Schleife zu packen.
    2. Dir ist schon klar, dass deine Zeilen 21-24 bei jedem Durchgang der Schleifen in Zeilen 17-18 ausgeführt werden?



  • Sepp3 hat Recht. Figuren im mittleren Spielfeldbereich werden allerdings korrekt generiert. Mit der Ausgangsfigur des TE erscheinen ab der 10. Generation 4 oszillierende Figuren zu je 3 in einer Reihe benachbarten Zellen.



  • SeppJ schrieb:

    Solche Sachen wie i-1 oder j+1 werden am Rand deines Spielfeldes überlaufen. Du unternimmst derzeit überhaupt gar nichts, um diese Randeffekte korrekt zu behandeln. Guck dir mal den Modulooperator % an.

    Die Ränder habe bewusst nicht behandelt, da dort bis in die siebte oder achte Generation kein Leben entstehen sollte. Um so mehr wundere ich mich darüber, dass dies scheinbar der Fall ist.

    SeppJ schrieb:

    1. So eine Koppelung von for mit if wie bei dir ist sehr ungeschickt. Wieso nicht einfach den Fall 0 vor dem for machen, dann das for von 1 aus loslaufen lassen? Wenn es sowieso keine Gemeinsamkeit zwischen dem Fall 0 und dem Fall >0 gibt, dann mach es keinen Sinn, diese zusammen in die gleiche Schleife zu packen.
    2. Dir ist schon klar, dass deine Zeilen 21-24 bei jedem Durchgang der Schleifen in Zeilen 17-18 ausgeführt werden?

    Jup. Hasse Recht. 🙂
    Ist behoben!
    Trotzdem: Das Randproblem bleibt bestehen obwohl es eigentlich erst viel später auftreten sollte.



  • Der Code für die Randfelder wird in jeder Runde ausgeführt, auch wenn da noch kein Leben ist. Und weil Du dort in jeder Richtung über die Grenzen Deines Arrays hinaus auf Speicher zugreifst, der Dir nicht gehört, hast Du großes Glück, dass Dein Programm nicht gleich ganz abstürzt, sondern nur "zufällig" Leben entsteht.

    Wenn Du den Rand erstmal nicht behandeln willst, lass einfach die Randfelder beim Berechnen der nächsten Generation weg.

    Also Zeile 36 und 37:

    for(j=1; j<j_max-1; j++){
          for(i=1; i<i_max-1; i++){
    


  • Ja das ist mir auch aufgefallen. Aber trotzdem lässt sich das Problem nicht beheben 😞

    #include <stdio.h>
    
    int main() {
        int i, i_max, j, j_max; //i Zeilen, j Spalten
        int gen, gen_max;
    
        i_max=12;
        j_max=12;
        gen_max=5;
    
        int zelle[i_max][j_max][gen_max];
        int sum[i_max][j_max];
    
        for(j=1; j<j_max; j++){
            for(i=1; i<i_max; i++){
                zelle[i][j][gen] = 0;
    
                zelle[5][4][gen] = 1;
                zelle[5][5][gen] = 1;
                zelle[5][6][gen] = 1;
                zelle[6][5][gen] = 1;
    
                if(zelle[i][j][gen]==1){printf(" o ");}
                else{printf(" . ");}
    
            }
            printf("\n");
        }
    
        printf("\n\n\n");
    
        for(gen=1; gen<gen_max; gen++){
    
            for(j=1; j<j_max; j++){
                for(i=1; i<i_max; i++){
    
                    for(int n=0; n<8; n++){
                        sum[i][j]= zelle[i-1] [j-1] [gen-1] + zelle[i] [j-1] [gen-1] + zelle[i+1] [j-1] [gen-1]
                                +  zelle[i+1] [j]   [gen-1]
                                +  zelle[i+1] [j+1] [gen-1] + zelle[i] [j+1][gen-1] + zelle[i-1][j+1][gen-1]
                                +  zelle[i-1] [j]   [gen-1];
                    }
    
                    if(sum[i][j]<2){
                        zelle[i][j][gen]=0;
                    }
    
                    if(sum[i][j]==2){
                        zelle[i][j][gen]=zelle[i][j][gen-1];
                    }
    
                    if(sum[i][j]==3){
                        zelle[i][j][gen]=1;
                    }
    
                    if(sum[i][j]>3){
                        zelle[i][j][gen]=0;
                    }
    
                    if(zelle[i][j][gen]==1){printf(" o ");}
                    else{printf(" . ");}
                }
    
                printf("\n");
            }
    
            printf("\n\n\n");
        }
    
        return 0;
    }
    

    Trotzdem ist die Ausgabe in der 4. Generation die folgende:

    . . . . . . . . . . .
    . . . . . . . . . . .
    . . . . . . . . . . .
    . . . o o o . . . . .
    . . . o . o . . . . .
    . . . o o o . . . . .
    . . . . . . . . . . .
    . . . . . . . . . . .
    . . . . . . . . . . .
    . . . . . . . . . . .
    . . . . . . . . . o .

    Und da frage ich mich: was mache ich falsch 😕 😕


  • Mod

    Komm, denk doch mal mit! Wenn i-1 und j-1 außerhalb des Arrays liegen, wenn man bis ganz an die Grenzen geht, was ist dann wohl mit i+1 und j+1?

    Mach es doch einfach gleich komplett richtig mit Modulo, wie dir schon in der ersten Antwort empfohlen wurde. Das wäre eine kleine Modifikation an deinem Programm gewesen. Jetzt schreibst du gerade dein Programm komplett um, was du am Ende sowieso wieder rückgängig machen muss, und hast am Ende viel mehr Arbeit, weil du Angst hattest, einmalig gründlich nachzudenken.



  • Ich gehe doch nicht bis an die Grenzen.
    Die Schleife geht von 1 bis kleiner max
    Und nicht von 0 bis max.

    Damit komme ich doch nicht an den Rand


  • Mod

    Freumel schrieb:

    Ich gehe doch nicht bis an die Grenzen.
    Die Schleife geht von 1 bis kleiner max
    Und nicht von 0 bis max.

    Damit komme ich doch nicht an den Rand

    Doch! Wie ich schon sagte: Denk doch bitte mal mit! Vorher ging deine Schleife von 0 bis kleiner max. Nun geht sie von 1 bis kleiner max. Also entweder hat dein Programm vorher nicht den Rand mit abgedeckt oder du gehst jetzt immer noch an den Rand. Du hast die obere Grenze nicht verändert, aber behauptest trotzdem, sie würde nun etwas anderes machen als vorher, selbst nachdem dir jemand, den du um Hilfe gefragt hast, wieso dein Programm nicht richtig funktioniert, genau gesagt hat, dass es da dran liegt. Nicht einmal ausprobiert hast du es.



  • Das Problem vom TE ist glaube ich ein grundsätzlicheres.

    In C (und in den meisten anderen Programmiersprachen) gehen die Indizes von einem Array von 0 bis max-1, was dann genau max Elementen entspricht.


  • Mod

    Schlangenmensch schrieb:

    Das Problem vom TE ist glaube ich ein grundsätzlicheres.

    In C (und in den meisten anderen Programmiersprachen) gehen die Indizes von einem Array von 0 bis max-1, was dann genau max Elementen entspricht.

    Aber das weiß er doch offensichtlich, denn in seinem ersten Programm hat er es richtig gemacht!



  • vielleicht erklärt ihr es ihm einfach unverständlich. 🙄

    mein vorschlag wäre jetzt ja, das array einfach um jeweils zwei zeilen und spalten zu vergrößern, und die elemente dann ausdrücklich als "tot" festzulegen.

    nur die grenzen des array müssen dann irgendwie verändert werden. evtl. zeiger verwenden?



  • Vermutlich stelle ich mich auch blöder als es eigentlich sein muss 🙂

    Ich dachte halt wenn mein maximaler Bereich bis 12 geht, geht die Schleife 1 bis kleiner max nur von 1 bis 11.
    Weil die 12 wäre ja schon gleich dem Maximalwert.

    Gleiches gilt ja für den Array.

    Aber gut - ich probiere des einfach mal aus 💡



  • nein genau das ist nicht der fall. wenn du ein array von 12 * 12 erzeugst, hast du horizontal und vertikal die elemente [0] ... [11].

    wenn du jetzt nur von [1] bis [11] auswertest, überschreitest du den rand, weil das array nur bis 11 geht, du aber wegen [i + 1] bzw. [j + 1] auch in [12] guckst. das hat man versucht, dir zu erklären.

    das nächste problem ist eben, dass wenn du ein array erstellst, "irgendetwas" in den elementen drin steht, was du nur bedingt behoben hast. mit [i - i] für i == 0 greifst du dann auch [0] zu, was aber nicht initialisiert wurde.

    wenn du ein array von 14 * 14 erstellst und komplett mit 0 initialisierst, hast du mit [1] ... [12] deine 12 elemente, kannst entspannt von 1 bis 12 auswerten und verursachst keine speicherfehler.


  • Mod

    int array[3] ist ein Array mit 3 Werten. array[0] , array[1] , und array[2] .

    Eine Schleife

    for (int i = N; i < M; ++i)
    

    läuft durch max(M-N, 0) Werte, zum Beispiel

    for (int i = 8; i < 11; ++i)
    

    läuft durch 11-8=3 Werte: 8, 9, 10.
    So etwas passt daher auch besonders schön zu den Arraygrenzen, denn das Array array von oben durchläuft man vollständig, indem man seine Größe angibt:

    for (int i = 0; i < 3; ++i)
    

    Dies gibt einem genau array[0] , array[1] , und array[2] .

    Das hattest du in deinem ersten Beitrag ja auch alles schon richtig.

    Ich wiederhole noch einmal dringend meinen Tipp, das derzeitige Gefrickel zu unterlassen. Hättest du gleich nach der ersten Antwort angefangen, das Programm komplett richtig zu machen, indem du modulo benutzt, dann wärst du schon lange fertig. Stattdessen hast du nun schon einen Tag darauf verschwendet eine halbrichtige Version zu basteln, die aber bisher doch noch nicht funktioniert, und dich auch keinen Schritt weiter in die richtige Richtung bringt.



  • Whup - es passt! 👍 👍

    Danke für die ausdauernde Hilfe.
    Das mit dem Array war ziemlich blöd nachgedacht!
    Habe das mit einer besseren Initialisierung ans laufen gekriegt.

    Den Modulo-Operator schaue ich mir in dem Kontext auch noch an.
    Nullen und Einsen fand ich auf dem ersten Blick einfach am praktischsten (mit dem Kopf durch die Wand tut es ja manchmal auch 😃 )

    Eine letzte blöde Frage habe ich noch:
    Einen Array wie

    int var=3;
    int array[var];
    

    lässt sich initialisieren.

    Aber

    int var=3;
    int array[var]={0};
    

    nicht.

    Ich verstehe nicht ganz woran das liegt 😕



  • Freumel schrieb:

    Einen Array wie

    int var=3;
    int array[var];
    

    lässt sich initialisieren.

    Da wird nichts initialisiert.

    Freumel schrieb:

    Aber

    int var=3;
    int array[var]={0};
    

    nicht.

    Das geht bei Variable Length Arrays halt nicht. Brauchst du die überhaupt?

    Mehr unter https://stackoverflow.com/questions/17332360/initializing-variable-length-array



  • DirkB schrieb:

    Freumel schrieb:

    Aber

    int var=3;
    int array[var]={0};
    

    nicht.

    Das geht bei Variable Length Arrays halt nicht. Brauchst du die überhaupt?

    Hätte sich an einer Ecke angeboten um die Feldgröße variabel zu halten, aber brauchen tu ich's eigentlich nicht wirklich.
    Bin das Problem auch umgangen.



  • malloc und realloc kennst du? calloc initialisiert sogar automatisch.


Anmelden zum Antworten