Conways Spiel des Lebens (Game of Life)

Hallo Community,

auf der Suche nach einem Code-Snippet für das Spiel des Lebens, bin ich auf wenig wiederverwendbare Beispiele gestoßen.
Verwunderlich, wo dieses Spiel doch eigentlich so verbreitet ist...
Also in die Hände gespuckt und mit ein paar Vorlagen ein eigenes Spiel gebastelt:

#include <LEDPixels.h>
#define DELAY 500
#define SIZE 10
LEDPixels LP;
int MyDisplay[100];
byte world[SIZE][SIZE][2];
unsigned int count;

void setup()
{
 count = 0;
 Serial.begin(9600);
 setupLeds(); 
 
  switch(1)
  {
    case 1:
      Shuffle(); 
      break;
    case 2:
      Blink();
      break;      
    case 3:
      Tripole();
      break;      
    case 4:
      Oktagon(); 
      break;      
    case 5:
      Tumbler();
      break;      
    case 6:
      Glider();   
      break;      
  } 
}

void loop()
{  
  DisplayCurrentGeneration();
  delay(DELAY);
  
  // Birth and death cycle
  for (int x = 0; x < SIZE; x++)
  {
    for (int y = 0; y < SIZE; y++)
    {
      // Default is for cell to stay the same
      world[x][y][1] = world[x][y][0];
      int count = neighbours(x, y);
      if (count == 3 && world[x][y][0] == 0)
      {
        // A new cell is born
        world[x][y][1] = 1;
      }
      if ((count < 2 || count > 3) && world[x][y][0] == 1)
      {
        // Cell dies
        world[x][y][1] = 0;
      }
    }
  }

// Copy next generation into place
  for (int x = 0; x < SIZE; x++)
    for (int y = 0; y < SIZE; y++)
      world[x][y][0] = world[x][y][1];
}

int neighbours(int x, int y)
{
 return world[(x + 1) % SIZE][y][0] +
         world[x][(y + 1) % SIZE][0] +
         world[(x + SIZE - 1) % SIZE][y][0] +
         world[x][(y + SIZE - 1) % SIZE][0] +
         world[(x + 1) % SIZE][(y + 1) % SIZE][0] +
         world[(x + SIZE - 1) % SIZE][(y + 1) % SIZE][0] +
         world[(x + SIZE - 1) % SIZE][(y + SIZE - 1) % SIZE][0] +
         world[(x + 1) % SIZE][(y + SIZE - 1) % SIZE][0];
}

void DisplayCurrentGeneration()
{
  boolean alive = 0;
  clearLeds();
  // Display current generation
  for (int i = 0; i < SIZE; i++)
    for (int j = 0; j < SIZE; j++)
      if(world[i][j][0] == 1)
      {
        SetLED(i,j,LP.color(0,16,0));     
        alive = 1;
      }

  ShowLED();
  if(alive)
    Serial.print("Alive :");
  else
    Serial.print("Death :");
  Serial.println(count);
  count++;
}

/****Object Patterns****/
void Shuffle()
{
  Serial.println("Shuffle");
  for (int i = 0; i < SIZE; i++)
  {
    randomSeed(analogRead(5));
    for (int j = 0; j < SIZE; j++)
    {      
      if (int(random(0,4))==1)
        world[i][j][0]=1;     
    }
  }  
}

void Blink()
{
  Serial.println("Blinker");
  world[4][3][0]=1;
  world[5][3][0]=1;
  world[6][3][0]=1;
}

void Tripole()
{
  Serial.println("Tripole");
  world[3][3][0]=1;
  world[3][4][0]=1;
  world[4][3][0]=1;
  world[5][4][0]=1;
  world[5][6][0]=1;
  world[6][7][0]=1;
  world[7][6][0]=1;
  world[7][7][0]=1;  
}

void Oktagon()
{
  Serial.println("Oktagon");
  world[8][4][0]=1;
  world[8][5][0]=1;
  world[7][3][0]=1;
  world[7][6][0]=1;
  world[6][2][0]=1;
  world[6][7][0]=1;
  world[5][1][0]=1;
  world[5][8][0]=1;  
  world[4][1][0]=1;
  world[4][8][0]=1;  
  world[3][2][0]=1;
  world[3][7][0]=1;  
  world[2][3][0]=1;
  world[2][6][0]=1;  
  world[1][4][0]=1;
  world[1][5][0]=1;  
}

void Tumbler()
{
  Serial.println("Tumbler");
  world[4][2][0]=1;
  world[4][3][0]=1;
  world[4][5][0]=1;
  world[4][6][0]=1;
  world[5][2][0]=1;
  world[5][6][0]=1;
  world[6][0][0]=1;
  world[6][3][0]=1;  
  world[6][5][0]=1;
  world[6][8][0]=1;  
  world[7][0][0]=1;
  world[7][2][0]=1;  
  world[7][6][0]=1;
  world[7][8][0]=1;  
  world[8][1][0]=1;
  world[8][7][0]=1;  
}

void Glider()
{
  Serial.println("Glider");
  world[7][7][0]=1;  
  world[7][8][0]=1;
  world[7][9][0]=1;  
  world[8][7][0]=1;
  world[9][8][0]=1;  
}

/****LED Stuff****/
void setupLeds()
{
  LP.initialize(20, &MyDisplay[0],SIZE*SIZE, 8, 7 );  //Frequence, address of display, no of leds,clock (green), data (yellow)  
  LP.gridWidth=SIZE;
  LP.gridHeight=SIZE;   
  clearLeds();
}
 
void clearLeds()
{
   LP.setRange(0,(SIZE*SIZE)-1,LP.color(0,0,0));  
   LP.show();
}

void SetLED(byte x, byte y, unsigned int color)
{
  LP.setLEDFast(LP.Translate(x,y),color);  
}

void ShowLED()
{
  LP.show();
}

Ziel war es, dass mit wenig Anpassungen der letzten 4 Funktionen Jeder seine eigenen LED´s oder sein Display ansteuern kann. Einfach seine eigene Library importieren und die Anzeige umschreiben.

Durch umsetzen der Switch-case Anweisung im Setup(), kann man Oszillierende Objekte generieren, die endlos über das Spielfeld laufen.
Shuffle, setzt wahllos ein paar Punkte und startet dann mit diesen. Aber Achtung:

randomSeed(analogRead(5));

ist in meinem Fall auf einen Poti gelegt. Sonst passiert es, dass die Zufallsverteilung doch sehr reproduzierbar abläuft.

Fragen und Anregungen sind willkommen!
Gruß,
trib

Da der Arduino recht wenig Speicher besitzt, wäre es sehr praktisch, wenn die "Spielwelt" nicht mit bytes sonder mit bits arbeiten würde. Damit wird zwar der Code ein wenig komplizierter, aber man kann viel größere Welten verwalten. Ein "Welt" bei Dir braucht bei 20x20 Feldern schon 800 Byte und damit nahezu 50% des SRAM eines Arduino UNO. Verwendet man Bits, sind es nur 100 byte. Außerdem könnte man dann z.B. ein Byte mit 8 "Zellen" direkt per Shiftout() auf eine LED-Matrix schreiben.

Hmm, das klingt logisch. Einfacher wäre doch dann ein boolean draus zu machen...

Natürlich kann man die Werte so auch gleich auf das Display schreiben und sich noch einiges an Code sparen. Mein Ziel war es primär die Kompatibilität zu anderen LED´s und Displays zu schaffen. Für mich persönlich kann ich auch auf eine Dimension verzichten, da ich meine LED Matrix auslesen kann um den letzten Status auszuwerten. Das wären dann nochmal 50byte gespart.

Ansonsten: Feel free to modify :slight_smile:

Leider bringt Dir ein boolean auch keinen Vorteil. Siehe
/Java/hardware/arduino/cores/arduino/Arduino.h:

...
typedef uint8_t boolean;
typedef uint8_t byte;
...

Damit ist boolean am Ende auch wieder ein byte.

Er meint vermutlich Booleans die in Bit Felder abgelegt werden:

Die brauchen dann tatsächlich nur 1/8 des Platzes

http://arduino.cc/en/Reference/BitRead
Diese Funktionen ermöglichen einzelne Bits einer Byte-Variablen zu manipulieren. (falls man es nicht mit AND, OR und Shift machen möchte).
Viele Grüße Uwe