Игра "Жизнь"- клеточный автомат.

lilik
Offline
Зарегистрирован: 19.10.2017

Наткнулся на интересную поделку (подобная тема на форуме где-то была):

https://adior.ru/index.php/robototekhnika/29-game-life

и сделал на её основе игрушку-сувенир для релаксации.

В отличии от исходника:

  1. Добавлен псевдослучайный набор первого поколения клеток при каждом перерождении.
  2. Добавлен псевдослучайный перебор форм клеток при каждом новом перерождении жизни.
  3. Изменены исходные правила игры под условия клеточного автомата Джона Конвея.
  4. Реализована перезагрузка платы при 1 и 2 условии прекращения игрового цикла (на 3 соображалки не хватило :-)
#include <Wire.h>
//A5-SCL; A4-SDA
#define SSD1306_Address             0x3C
#define SSD1306_Command_Mode        0x80
#define SSD1306_Dats_Mode           0x40

#define SSD1306_MEMORYMODE          0x20 //2< Автоматическая адресация
#define SSD1306_COLUMNADDR          0x21 //3< See datasheet
#define SSD1306_PAGEADDR            0x22 //3< See datasheet
#define SSD1306_SETSTARTLINE        0x40 ///< See datasheet
#define SSD1306_SETCONTRAST         0x81 //2< Контрастность
#define SSD1306_CHARGEPUMP          0x8D //2< Умножитель напряжения
#define SSD1306_SEGREMAP            0xA1 ///< Развёртка слева/направо
#define SSD1306_DISPLAYALLON_RESUME 0xA4 ///< отображение содержимого RAM
#define SSD1306_NORMALDISPLAY       0xA6 ///< Нет инверсии
#define SSD1306_SETMULTIPLEX        0xA8 //2< See datasheet
#define SSD1306_DISPLAYOFF          0xAE ///< Выключить дисплей
#define SSD1306_DISPLAYON           0xAF ///< Дисплей включен
#define SSD1306_COMSCANDEC          0xC8 ///< Развёртка сверху/вниз
#define SSD1306_SETDISPLAYOFFSET    0xD3 //2< See datasheet
#define SSD1306_SETDISPLAYCLOCKDIV  0xD5 //2< Частота обновления
#define SSD1306_SETCOMPINS          0xDA //2< Разрешение 0x02-128x32, 0x12-128x64

unsigned char ledComIni1[] {
  SSD1306_SETMULTIPLEX, 0x3F,
  SSD1306_SETDISPLAYOFFSET, 0x00,
  SSD1306_SETSTARTLINE,
  SSD1306_MEMORYMODE, 0x00,
  SSD1306_COLUMNADDR, 0x00, 0x7F,
  SSD1306_PAGEADDR, 0x00, 0x07,
  SSD1306_SEGREMAP,
  SSD1306_COMSCANDEC,
  SSD1306_SETCOMPINS, 0x12,
  SSD1306_SETCONTRAST, 0x7F,
  SSD1306_NORMALDISPLAY,
  SSD1306_SETDISPLAYCLOCKDIV, 0x20,
  SSD1306_CHARGEPUMP, 0x14,
  SSD1306_DISPLAYALLON_RESUME,
  SSD1306_DISPLAYON
};

unsigned char scrBlock[8], blBlock[8];
unsigned char block1[8]={0X7F,0X7F,0X63,0X63,0X63,0X7F,0X7F,0X00};//1 форма "живой клетки"
unsigned char block2[8]={0X41,0X22,0X14,0X08,0X14,0X22,0X41,0X00};//2 форма "живой клетки"
unsigned char block3[8]={0X08,0X08,0X14,0X63,0X14,0X08,0X08,0X00};//3 форма "живой клетки"
#define X 18  /* 18 maximum */
#define Y 10  /* 10 maximum */
bool state[X][Y], old[X][Y];
int state_K=0;// переменная хранения числа совпадений клеток предыдущего и текущего поколений (128 - полное совпадение, изчезновение или "замирание клеток")
int forma_=1;//

void setup() {
  Wire.begin();
  Wire.setClock(400000);
  for (int n = 0; n < sizeof(ledComIni1); n++) {
    sendCommand(ledComIni1[n]);
  }
  for (int n = 0; n < 512; n++) {
    sendDats(blBlock);
  }
  int n = (X - 2) * 8 - 1;
  ledComIni1[9] = n;
  n = Y - 3;
  ledComIni1[12] = n;
  for(int i=0;i<analogRead(A0)/2;i++){ state[random(0,X)][random(0,Y)] = 1; forma_=i%3+1;}//псевдослучайное начальное заполнение массива "живыми клетками" с выбором их формы
  prnt();
  delay (2000);
}

void loop() {
  ///////////////////
  if(state_K==128){delay(2000);pinMode(2,OUTPUT);}////(вывод 2 соединён с RES) перезагрузка платы - обновление жизни
  ///////////////////
  delay (150);
  play();
  prnt();
 
}

void prnt () {
  for (int n = 0; n < sizeof(ledComIni1); n++) {
    sendCommand(ledComIni1[n]);
  }
  for (int y = 1; y < Y - 1; y++) {
    for (int x = 1; x < X - 1; x++) {
      if (state[x][y]) {
        if(forma_==1){sendDats(block1);}
        if(forma_==2){sendDats(block2);}
        if(forma_==3){sendDats(block3);}
      }
      else {
        sendDats(blBlock);
      }
    }
  }
}

void play() {
  state_K=0;
  for (int x = 1; x < X - 1; x++) {
    for (int y = 1; y < Y - 1; y++) {
      old[x][y] = state[x][y];
      state[x][y] = 0;
    }
  }
  for (int x = 1; x < X - 1; x++) {
    for (int y = 1; y < Y - 1; y++) {
      if ((old[x][y + 1] + old[x][y - 1] + old[x + 1][y] + old[x - 1][y] + old[x + 1][y + 1] + old[x - 1][y - 1] + old[x + 1][y - 1] + old[x - 1][y + 1])==3|| // изменённый вариант условий игры "жизнь"
     (old[x][y + 1] + old[x][y - 1] + old[x + 1][y] + old[x - 1][y] + old[x + 1][y + 1] + old[x - 1][y - 1] + old[x + 1][y - 1] + old[x - 1][y + 1]+old[x][y])==3||
     (old[x][y + 1] + old[x][y - 1] + old[x + 1][y] + old[x - 1][y] + old[x + 1][y + 1] + old[x - 1][y - 1] + old[x + 1][y - 1] + old[x - 1][y + 1]+old[x][y])==4) {
       
       state[x][y] = 1;
       
      }
    if(old[x][y] == state[x][y]){state_K++;}// если клетки соседних поколений совпали
    }
  }
}

void sendDats(unsigned char dim[8]) {
  Wire.beginTransmission(SSD1306_Address);
  Wire.write(SSD1306_Dats_Mode);
  for (int n = 0; n < 8; n++)
    Wire.write(dim[n]);
  Wire.endTransmission();
}

void sendCommand(unsigned char command) {
  Wire.beginTransmission(SSD1306_Address);
  Wire.write(SSD1306_Command_Mode);
  Wire.write(command);
  Wire.endTransmission();
}


Правила самой игры ниже:

Прежде, чем мы их сформулируем, обратим внимание на то, что каждую клетку, окружают восемь соседних клеток: четыре имеют с ней общие стороны, четыре другие — общие вершины.

  1. Выживание. Каждая живая клетка, имеющая вокруг себя две или три соседние живые клетки, выживает и переходит в следующее поколение.
  2. Гибель. Каждая клетка, у которой больше трёх соседей, погибает, то есть исчезает с экрана из-за перенаселённости. Каждая клетка, вокруг которой свободны все соседние клетки или же занята всего одна клетка, погибает от одиночества.
  3. Рождение. Если число живых клеток, с которыми граничат какая-нибудь пустая клетка, в точности равно трём (не больше и не меньше), то на этой клетке происходит рождение нового «организма», то есть клетка становится живой.

Игровой цикл прекращается, если:

  1. На экране не останется ни одной «живой» клетки
  2. Конфигурация на очередном шаге в точности (без сдвигов и поворотов) повторит себя же – жизнь замирает в статичной картинке.
  3. Последовательность конфигураций клеток начнёт повторятся через определённое число шагов - жизнь замирает в динамическом повторе.

Вид на внутренности:

lilik
Offline
Зарегистрирован: 19.10.2017

Формы клеточной  жизни:

lilik
Offline
Зарегистрирован: 19.10.2017

Хочу сделать трёхмерный вариант игры на светодиодном кубе. Есть ли правила игры для объёмного варианта, а не "плосткостного"? И как правильно экстраполировать одни правила к другим?

 Это плоскость:

  1. Выживание. Каждая живая клетка, имеющая вокруг себя две или три соседние живые клетки, выживает и переходит в следующее поколение.
  2. Гибель. Каждая клетка, у которой больше трёх соседей, погибает, то есть исчезает с экрана из-за перенаселённости. Каждая клетка, вокруг которой свободны все соседние клетки или же занята всего одна клетка, погибает от одиночества.
  3. Рождение. Если число живых клеток, с которыми граничат какая-нибудь пустая клетка, в точности равно трём (не больше и не меньше), то на этой клетке происходит рождение нового «организма», то есть клетка становится живой.
b707
Offline
Зарегистрирован: 26.05.2017

lilik пишет:

  1. Рождение. Если число живых клеток, с которыми граничат какая-нибудь пустая клетка, в точности равно трём

это какая-то пропаганда "шведских" семей? :)

 

Для обьемного можно пойти по простому экспансивному пути - теперь число соседей у каждой клетки равно 26, остается подобрать граничные величины для всех трех случаев выше

 

А для цветного экрана правил не попадалось? - видимо надо создавать разные типы клеток...?

sadman41
Offline
Зарегистрирован: 19.10.2016

Согласен с b707.
1) посмотреть вокруг в своём слое;
2) если есть слой выше, обсмотреть квадрат над собой;
3) так же под собой.

Пересчитал соседей, сделал выводы.

Все можно прямо в трехмерном массиве и обустроить.

Для цветного придётся мутации вводить. Типа смешение цвета при рождении. А остальное все так же, как и в ч/б, там же не параллельные миры - в одной клетке двум сущностям не жить.

lilik
Offline
Зарегистрирован: 19.10.2017

Да, я так примерно и мыслю, вопрос в этих граничных условиях. В "плоской жизни" пробовал менять авторский вариант правил, жизнь либо дохнет быстро, либо длится очень долго :)

 Придётся видимо экспериментировать по факту и в объёмном варианте. 

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну, какбэ в реальности так и есть - жизнь скоротечна для одних организмов и почти бесконечна для других.
И режиссёра , который превратил её в увлекательный перформанс , не существует.

lilik
Offline
Зарегистрирован: 19.10.2017
/////////////////////////// КУБ 5*5*5 НА WS2812b
///////////////////////////    игра "жизнь"
#include "Adafruit_NeoPixel.h"
#define LED_COUNT1 125
#define LED_PIN1 6

Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(LED_COUNT1, LED_PIN1, NEO_GRB + NEO_KHZ800);// Создаем переменную strip для управления нашей лентой.
int r = 15; int g = 3; int b = 0; //переменные хранения параметров включений пикселей - определяют цвет и яркость (оранжевый)
#define X 7  /* 7 maximum */
#define Y 7  /* 7 maximum */
#define Z 7  /* 7 maximum */
bool state[X][Y][Z], old[X][Y][Z];// массивы хранения состояний клеток текущего и предыдущего поколений
int state_K = 0; // переменная хранения числа совпадений клеток предыдущего и текущего поколений (125 - полное совпадение, изчезновение или "замирание клеток")
int state_G = 0; // переменная хранения числа живых клеток в поколении
int old_state_G = 0; // переменная хранения числа живых клеток в прошлом поколении
int t = 0; //

void setup () {
  strip1.begin();
  pusto_();//
  zaselenie_();//
  prnt_();//
  delay(2000);
}
///////////////
void loop () {
  ///////////////////
  if (state_K == 125) {
    delay(2000);  ////(вывод 2 соединён с RES) перезагрузка платы - обновление жизни
    pinMode(2, OUTPUT);
  }
  ///////////////////
  delay(150);
  play_(); if (old_state_G != state_G) {
    old_state_G = state_G;  //дополнительное условие перезагрузки платы - обновление жизни
    t = 0;
  } else {
    t++;
    if (t > 30) {
      delay(2000);
      pinMode(2, OUTPUT);
    }
  }
  prnt_();

}
/////////////////////////
///выключение пикселей куба
void pusto_() {
  for (int i = 0; i < LED_COUNT1; i++)
  {
    strip1.setPixelColor(i, 0, 0, 0); //
  } strip1.show();
}
///////////////////////////////////////////
//// псевдослучайное заселение начальное живыми клетками с выбором формы-цвета
void zaselenie_() {
  randomSeed(analogRead(A0));
  for (int i = 0; i < analogRead(A0) / 4; i++) {
    state[random(0, X)][random(0, Y)][random(0, Z)] = 1;
  }
  int cvet = 1; cvet = random(1, 5);
  if (cvet == 1) {
    r = 15;  // оранжевый
    g = 3;
    b = 0;
  }
  if (cvet == 2) {
    r = 0;  // голубой
    g = 10;
    b = 10;
  }
  if (cvet == 3) {
    r = 0;  // зелёный
    g = 15;
    b = 1;
  }
  if (cvet == 4) {
    r = 10;  //сиреневый
    g = 0;
    b = 10;
  }
}
///////////////////////////////////////////
//// прорисовка данных на куб
void prnt_() {
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        if (state[x][y][z]) {
          strip1.setPixelColor((x - 1) + 5 * (y - 1) + 25 * (z - 1), r, g, b);
        }
        else {
          strip1.setPixelColor((x - 1) + 5 * (y - 1) + 25 * (z - 1), 0, 0, 0);
        }
      }
    }
  }
  strip1.show();
}
///////////////////////////////////////////
//// игра - просчёт варианта нового поколения и заполнение массивов обновлёнными данными
void play_() {
  state_K = 0;
  state_G = 0;
  //////////
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        old[x][y][z] = state[x][y][z];
        state[x][y][z] = 0;
      }
    }
  }
  //////////
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        if (uslovie_(x, y, z) == 4 || (old[x][y][z] == 1 && uslovie_(x, y, z) == 5) || (old[x][y][z] == 1 && uslovie_(x, y, z) == 6) ) {
          state[x][y][z] = 1; state_G++;
        }
        if (old[x][y][z] == state[x][y][z]) {
          state_K++; // если клетки соседних поколений совпали
        }
      }
    }
  }
  //////////
}
//////////////////////////////////////////
int uslovie_(int x1, int y1, int z1) {
  int k = 0;
  k = old[x1][y1 + 1][z1] + old[x1][y1 - 1][z1] + old[x1 + 1][y1][z1] + old[x1 - 1][y1][z1] + old[x1 + 1][y1 + 1][z1] + old[x1 - 1][y1 - 1][z1] + old[x1 + 1][y1 - 1][z1] + old[x1 - 1][y1 + 1][z1] +
      old[x1][y1 + 1][z1 + 1] + old[x1][y1 - 1][z1 + 1] + old[x1 + 1][y1][z1 + 1] + old[x1 - 1][y1][z1 + 1] + old[x1 + 1][y1 + 1][z1 + 1] + old[x1 - 1][y1 - 1][z1 + 1] + old[x1 + 1][y1 - 1][z1 + 1] + old[x1 - 1][y1 + 1][z1 + 1] + old[x1][y1][z1 + 1] +
      old[x1][y1 + 1][z1 - 1] + old[x1][y1 - 1][z1 - 1] + old[x1 + 1][y1][z1 - 1] + old[x1 - 1][y1][z1 - 1] + old[x1 + 1][y1 + 1][z1 - 1] + old[x1 - 1][y1 - 1][z1 - 1] + old[x1 + 1][y1 - 1][z1 - 1] + old[x1 - 1][y1 + 1][z1 - 1] + old[x1][y1][z1 - 1];
  return k;
}
/////////////////////////////////////////

Сделал вариант с нормальной живучестью - когда зарождается клетка при 4 рядом, а не умирает если 4,5,6 рядом. Хотел добавить ssd1306 для отображения характеристик процесса, но библиотеки несовместимы - ничего не работает после загрузки. Это для этой пары?, или в принципе для умных светодиодов и дисплеев?

/////////////////////////// КУБ 5*5*5 НА WS2812b
///////////////////////////    игра "жизнь"
#include "Adafruit_NeoPixel.h"
#define LED_COUNT1 125
#define LED_PIN1 6

#include <OLED_I2C.h>
OLED  myOLED(SDA, SCL);// А4,A5

Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(LED_COUNT1, LED_PIN1, NEO_GRB + NEO_KHZ800);// Создаем переменную strip для управления нашей лентой.
int r = 15; int g = 3; int b = 0; //переменные хранения параметров включений пикселей - определяют цвет и яркость (оранжевый)
#define X 7  /* 7 maximum */
#define Y 7  /* 7 maximum */
#define Z 7  /* 7 maximum */
bool state[X][Y][Z], old[X][Y][Z];// массивы хранения состояний клеток текущего и предыдущего поколений
int state_K = 0; // переменная хранения числа совпадений клеток предыдущего и текущего поколений (125 - полное совпадение, изчезновение или "замирание клеток")
int state_G = 0; // переменная хранения числа живых клеток в поколении
int N = 0; // число жизненных циклов
extern uint8_t RusFont[];
extern uint8_t MediumNumbers[];

void setup () {
  myOLED.begin();
  myOLED.invert(0);//инверсия цвета
  strip1.begin();
  pusto_();//
  zaselenie_();//
  prnt_();//
  dannie_();
  delay(2000);
}
///////////////
void loop () {
  ///////////////////
  if (state_K == 125) {
    delay(2000);  ////(вывод 2 соединён с RES) перезагрузка платы - обновление жизни
    pinMode(2, OUTPUT);
  }
  ///////////////////
  delay(150);
  play_();
  prnt_();
  N++;//
  dannie_();
}
/////////////////////////
///выключение пикселей куба
void pusto_() {
  for (int i = 0; i < LED_COUNT1; i++)
  {
    strip1.setPixelColor(i, 0, 0, 0); //
  } strip1.show();
}
///////////////////////////////////////////
//// псевдослучайное заселение начальное живыми клетками с выбором формы-цвета
void zaselenie_() {
  randomSeed(analogRead(A0));
  for (int i = 0; i < analogRead(A0) / 4; i++) {
    state[random(0, X)][random(0, Y)][random(0, Z)] = 1;
  }
  int cvet = 1; cvet = random(1, 5);
  if (cvet == 1) {
    r = 15;  // оранжевый
    g = 3;
    b = 0;
  }
  if (cvet == 2) {
    r = 0;  // голубой
    g = 10;
    b = 10;
  }
  if (cvet == 3) {
    r = 0;  // зелёный
    g = 15;
    b = 1;
  }
  if (cvet == 4) {
    r = 10;  //сиреневый
    g = 0;
    b = 10;
  }
}
///////////////////////////////////////////
//// прорисовка данных на куб
void prnt_() {
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        if (state[x][y][z]) {
          strip1.setPixelColor((x - 1) + 5 * (y - 1) + 25 * (z - 1), r, g, b);
        }
        else {
          strip1.setPixelColor((x - 1) + 5 * (y - 1) + 25 * (z - 1), 0, 0, 0);
        }
      }
    }
  }
  strip1.show();
}
///////////////////////////////////////////
//// игра - просчёт варианта нового поколения и заполнение массивов обновлёнными данными
void play_() {
  state_K = 0;
  state_G = 0;
  //////////
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        old[x][y][z] = state[x][y][z];
        state[x][y][z] = 0;
      }
    }
  }
  //////////
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        if (uslovie_(x, y, z) == 4 || (old[x][y][z] == 1 && uslovie_(x, y, z) == 5) || (old[x][y][z] == 1 && uslovie_(x, y, z) == 6) ) {
          state[x][y][z] = 1; state_G++;
        }
        if (old[x][y][z] == state[x][y][z]) {
          state_K++; // если клетки соседних поколений совпали
        }
      }
    }
  }
  //////////
}
//////////////////////////////////////////
int uslovie_(int x1, int y1, int z1) {
  int k = 0;
  k = old[x1][y1 + 1][z1] + old[x1][y1 - 1][z1] + old[x1 + 1][y1][z1] + old[x1 - 1][y1][z1] + old[x1 + 1][y1 + 1][z1] + old[x1 - 1][y1 - 1][z1] + old[x1 + 1][y1 - 1][z1] + old[x1 - 1][y1 + 1][z1] +
      old[x1][y1 + 1][z1 + 1] + old[x1][y1 - 1][z1 + 1] + old[x1 + 1][y1][z1 + 1] + old[x1 - 1][y1][z1 + 1] + old[x1 + 1][y1 + 1][z1 + 1] + old[x1 - 1][y1 - 1][z1 + 1] + old[x1 + 1][y1 - 1][z1 + 1] + old[x1 - 1][y1 + 1][z1 + 1] + old[x1][y1][z1 + 1] +
      old[x1][y1 + 1][z1 - 1] + old[x1][y1 - 1][z1 - 1] + old[x1 + 1][y1][z1 - 1] + old[x1 - 1][y1][z1 - 1] + old[x1 + 1][y1 + 1][z1 - 1] + old[x1 - 1][y1 - 1][z1 - 1] + old[x1 + 1][y1 - 1][z1 - 1] + old[x1 - 1][y1 + 1][z1 - 1] + old[x1][y1][z1 - 1];
  return k;
}
/////////////////////////////////////////
// вывод данных на дисплей
void dannie_() {
  myOLED.setFont(RusFont); myOLED.print("WBRK -", 0, 5); myOLED.print("CJDGFL -", 0, 25); myOLED.print(":BDST -", 0, 45); //число циклов, число совпадений в соседних поколениях, число живых в поколении
  myOLED.setFont( MediumNumbers); myOLED.printNumI(N, 55, 5); myOLED.printNumI(state_K, 55, 25); myOLED.printNumI(state_G, 55, 45);
}
////////////////////////////////////////

 

b707
Offline
Зарегистрирован: 26.05.2017

lilik пишет:

Хотел добавить ssd1306 для отображения характеристик процесса, но библиотеки несовместимы - ничего не работает после загрузки. Это для этой пары?, или в принципе для умных светодиодов и дисплеев?

а плата какая? Для Нано скорее всего памяти не хватает, ОЛЕД и так неплохо поджирает, а тут же Фастлед буфер для 125 ледов...

lilik
Offline
Зарегистрирован: 19.10.2017

Плата УНО, 87 процентов динамической памяти скетч занимает.

b707
Offline
Зарегистрирован: 26.05.2017

lilik пишет:

Плата УНО, 87 процентов динамической памяти скетч занимает.

точно память

lilik
Offline
Зарегистрирован: 19.10.2017

Пока плюнул на память, воспользовался плоттером из "иде".

  Очень интересно ведут себя графики. Синий показывает процент совпадений живых и неживых клеток соседних поколений, а красный процент живых в текущем поколении. Одно как бы отражение другого в слегка кривом зеркале :)

То есть аксиома типа: чем сильнее похожи соседние поколения тем меньше потомство.

/////////////////////////// КУБ 5*5*5 НА WS2812b
///////////////////////////    игра "жизнь"
#include "Adafruit_NeoPixel.h"
#define LED_COUNT1 125
#define LED_PIN1 6

Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(LED_COUNT1, LED_PIN1, NEO_GRB + NEO_KHZ800);// Создаем переменную strip для управления нашей лентой.
int r = 15; int g = 3; int b = 0; //переменные хранения параметров включений пикселей - определяют цвет и яркость (оранжевый)
#define X 7  /* 7 maximum */
#define Y 7  /* 7 maximum */
#define Z 7  /* 7 maximum */
bool state[X][Y][Z], old[X][Y][Z];// массивы хранения состояний клеток текущего и предыдущего поколений
int state_K = 0; // переменная хранения числа совпадений клеток предыдущего и текущего поколений (125 - полное совпадение, изчезновение или "замирание клеток")
int state_G = 0; // переменная хранения числа живых клеток в поколении
int old_state_G = 0; // переменная хранения числа живых клеток в прошлом поколении
int t = 0; //
int N = 0; //

void setup () {
  Serial.begin(9600);
  strip1.begin();
  pusto_();//
  zaselenie_();//
  prnt_();//
  dano_();
  delay(2000);
}
///////////////
void loop () {
  ///////////////////
  if (state_K == 125) {
    delay(2000);  ////(вывод 2 соединён с RES) перезагрузка платы - обновление жизни
    pinMode(2, OUTPUT);
  }
  ///////////////////
  delay(150);
  play_(); if (old_state_G != state_G) {
    old_state_G = state_G;  //дополнительное условие перезагрузки платы - обновление жизни
    t = 0;
  } else {
    t++;
    if (t > 30) {
      delay(2000);
      pinMode(2, OUTPUT);
    }
  }
  prnt_();
  N++;
  dano_();
}
/////////////////////////
///выключение пикселей куба
void pusto_() {
  for (int i = 0; i < LED_COUNT1; i++)
  {
    strip1.setPixelColor(i, 0, 0, 0); //
  } strip1.show();
}
///////////////////////////////////////////
//// псевдослучайное заселение начальное живыми клетками с выбором формы-цвета
void zaselenie_() {
  randomSeed(analogRead(A0));
  for (int i = 0; i < analogRead(A0) / 4; i++) {
    state[random(0, X)][random(0, Y)][random(0, Z)] = 1;
  }
  int cvet = 1; cvet = random(1, 5);
  if (cvet == 1) {
    r = 15;  // оранжевый
    g = 3;
    b = 0;
  }
  if (cvet == 2) {
    r = 0;  // голубой
    g = 10;
    b = 10;
  }
  if (cvet == 3) {
    r = 0;  // зелёный
    g = 15;
    b = 1;
  }
  if (cvet == 4) {
    r = 10;  //сиреневый
    g = 0;
    b = 10;
  }
}
///////////////////////////////////////////
//// прорисовка данных на куб
void prnt_() {
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        if (state[x][y][z]) {
          strip1.setPixelColor((x - 1) + 5 * (y - 1) + 25 * (z - 1), r, g, b);
        }
        else {
          strip1.setPixelColor((x - 1) + 5 * (y - 1) + 25 * (z - 1), 0, 0, 0);
        }
      }
    }
  }
  strip1.show();
}
///////////////////////////////////////////
//// игра - просчёт варианта нового поколения и заполнение массивов обновлёнными данными
void play_() {
  state_K = 0;
  state_G = 0;
  //////////
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        old[x][y][z] = state[x][y][z];
        state[x][y][z] = 0;
      }
    }
  }
  //////////
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        if (uslovie_(x, y, z) == 4 || (old[x][y][z] == 1 && uslovie_(x, y, z) == 5) || (old[x][y][z] == 1 && uslovie_(x, y, z) == 6) ) {
          state[x][y][z] = 1; state_G++;
        }
        if (old[x][y][z] == state[x][y][z]) {
          state_K++; // если клетки соседних поколений совпали
        }
      }
    }
  }
  //////////
}
//////////////////////////////////////////
int uslovie_(int x1, int y1, int z1) {
  int k = 0;
  k = old[x1][y1 + 1][z1] + old[x1][y1 - 1][z1] + old[x1 + 1][y1][z1] + old[x1 - 1][y1][z1] + old[x1 + 1][y1 + 1][z1] + old[x1 - 1][y1 - 1][z1] + old[x1 + 1][y1 - 1][z1] + old[x1 - 1][y1 + 1][z1] +
      old[x1][y1 + 1][z1 + 1] + old[x1][y1 - 1][z1 + 1] + old[x1 + 1][y1][z1 + 1] + old[x1 - 1][y1][z1 + 1] + old[x1 + 1][y1 + 1][z1 + 1] + old[x1 - 1][y1 - 1][z1 + 1] + old[x1 + 1][y1 - 1][z1 + 1] + old[x1 - 1][y1 + 1][z1 + 1] + old[x1][y1][z1 + 1] +
      old[x1][y1 + 1][z1 - 1] + old[x1][y1 - 1][z1 - 1] + old[x1 + 1][y1][z1 - 1] + old[x1 - 1][y1][z1 - 1] + old[x1 + 1][y1 + 1][z1 - 1] + old[x1 - 1][y1 - 1][z1 - 1] + old[x1 + 1][y1 - 1][z1 - 1] + old[x1 - 1][y1 + 1][z1 - 1] + old[x1][y1][z1 - 1];
  return k;
}
/////////////////////////////////////////
void dano_() {
  //печатаем с помощью плоттера относительные плотности совпадения соседних поколений и живучести (заселённости) текущего
  Serial.print( state_K / 1.250); Serial.print("    "); Serial.print( state_G / 1.250); Serial.println();
}

 

b707
Offline
Зарегистрирован: 26.05.2017

lilik пишет:

То есть аксиома типа: чем сильнее похожи соседние поколения тем меньше потомство.

ну это и в реальной биологии также - близкородственное скрещивание вызывает вырождение

sadman41
Offline
Зарегистрирован: 19.10.2016

И как там, на кубе, интересная картинка?

lilik
Offline
Зарегистрирован: 19.10.2017

Гифку нельзя теперь загрузить чего-то. Вот поймал гармоничное и повторяющиеся до бесконечности. Куб был выключен, заподозрил по графикам. Такое вдруг появилось:

А так причудливо перемигивается из угла в угол, то разрастаясь, то сжимаясь :) Конечно не то, что в симуляторе можно нарисовать для куба.

sadman41
Offline
Зарегистрирован: 19.10.2016

https://www.bibliofond.ru/view.aspx?id=603714

1.3 Экология популяций и сообществ

 

 

...а так я, думаю, пора хищников вводить:

1) за один проход хищник поедает одну клетку.

2) если у хищника нет соседей - жертв, то он сдвигается на одну рандомную позицию (можно, конечно) закодировать направление движения (в сторону предыдущей жертвы)

3) если за 3 хода он никого не сожрал - гибнет.

Закодировать можно в байте. Например: SLLRDDDD

S - признак (жертва/хищник)

LL - кол-во жизни (уменьшается до 0)

R  - резервный бит

DDDD - направление движения.

sadman41
Offline
Зарегистрирован: 19.10.2016

114 old[x][y][z] = state[x][y][z];
115 state[x][y][z] = 0;

memcpy(old, state, sizeof(state));
memset(state, 0, size of(state));

b707
Offline
Зарегистрирован: 26.05.2017

Вот и цветность понадобилась - жертвы и хищники разного цвета

lilik
Offline
Зарегистрирован: 19.10.2017

Идею пока только понял. Сейчас догадался как сделать красиво чтоб мигало :), надо первоначальный засев клеток делать симметрично относительно центра куба:

/////////////////////////// КУБ 5*5*5 НА WS2812b
///////////////////////////    игра "жизнь"
#include "Adafruit_NeoPixel.h"
#define LED_COUNT1 125
#define LED_PIN1 6

Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(LED_COUNT1, LED_PIN1, NEO_GRB + NEO_KHZ800);// Создаем переменную strip для управления нашей лентой.
int r = 15; int g = 3; int b = 0; //переменные хранения параметров включений пикселей - определяют цвет и яркость (оранжевый)
int r_ = 1; int g_ = 0; int b_ = 0; //переменные хранения параметров включений фоновых пикселей - определяют цвет и яркость (красный)
#define X 7  /* 7 maximum */
#define Y 7  /* 7 maximum */
#define Z 7  /* 7 maximum */
bool state[X][Y][Z], old[X][Y][Z];// массивы хранения состояний клеток текущего и предыдущего поколений
int state_K = 0; // переменная хранения числа совпадений клеток предыдущего и текущего поколений (125 - полное совпадение, изчезновение или "замирание клеток")
int state_G = 0; // переменная хранения числа живых клеток в поколении
int old_state_G = 0; // переменная хранения числа живых клеток в прошлом поколении
int t = 0; //
int N = 0; //число циклов перерождений клеток

void setup () {
  Serial.begin(9600);
  strip1.begin();
  pusto_();//
  zaselenie_();//
  prnt_();//
  dano_();
  delay(2000);
}
///////////////
void loop () {
  ///////////////////
  if (state_K == 125) {
    delay(2000);  ////(вывод 2 соединён с RES) перезагрузка платы - обновление жизни
    pinMode(2, OUTPUT);
  }
  ///////////////////
  delay(150);
  play_(); if (old_state_G != state_G) {
    old_state_G = state_G;  //дополнительное условие перезагрузки платы - обновление жизни
    t = 0;
  } else {
    t++;
    if (t > 50) {
      delay(2000);
      pinMode(2, OUTPUT);
    }
  }
  prnt_();
  N++;
  dano_();
}
/////////////////////////
///выключение пикселей куба
void pusto_() {
  for (int i = 0; i < LED_COUNT1; i++)
  {
    strip1.setPixelColor(i, 0, 0, 0); //
  } strip1.show();
}
///////////////////////////////////////////
//// псевдослучайное заселение начальное живыми клетками с выбором формы-цвета
void zaselenie_() {
  randomSeed(5 * analogRead(A0));
  state[random(0, X)][random(0, Y)][random(0, Z)] = 1; //особенная клетка
  state[random(0, X)][random(0, Y)][random(0, Z)] = 1; //особенная клетка
  for (int i = 0; i < analogRead(A0) / 8; i++) {
    int randX = random(0, X);  // остальные, симметрично расположенные
    int randY = random(0, Y);
    int randZ = random(0, Z);
    state[randX][randY][randZ] = 1;
    state[X - randX - 1][Y - randY - 1][Z - randZ - 1] = 1;
  }

  ///////
  int cvet = 1; cvet = random(1, 5);
  if (cvet == 1) {
    r = 15;  // оранжевый на синем
    g = 3;
    b = 0;
    r_ = 0;
    g_ = 0;
    b_ = 1;
  }
  if (cvet == 2) {
    r = 0;  // голубой на белом
    g = 15;
    b = 10;
    r_ = 1;
    g_ = 1;
    b_ = 1;
  }
  if (cvet == 3) {
    r = 0;  // зелёный на красном
    g = 15;
    b = 1;
    r_ = 1;
    g_ = 0;
    b_ = 0;
  }
  if (cvet == 4) {
    r = 10;  //сиреневый на зелёном
    g = 0;
    b = 10;
    r_ = 0;
    g_ = 1;
    b_ = 0;
  }
}
///////////////////////////////////////////
//// прорисовка данных на куб
void prnt_() {
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        if (state[x][y][z]) {
          strip1.setPixelColor((x - 1) + 5 * (y - 1) + 25 * (z - 1), r, g, b);
        }
        else {
          strip1.setPixelColor((x - 1) + 5 * (y - 1) + 25 * (z - 1), r_, g_, b_);
        }
      }
    }
  }
  strip1.show();
}
///////////////////////////////////////////
//// игра - просчёт варианта нового поколения и заполнение массивов обновлёнными данными
void play_() {
  state_K = 0;
  state_G = 0;
  //////////
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        old[x][y][z] = state[x][y][z];
        state[x][y][z] = 0;
      }
    }
  }
  //////////
  for (int z = 1; z < Z - 1; z++) {
    for (int y = 1; y < Y - 1; y++) {
      for (int x = 1; x < X - 1; x++) {
        if (uslovie_(x, y, z) == 4 || (old[x][y][z] == 1 && uslovie_(x, y, z) == 5) || (old[x][y][z] == 1 && uslovie_(x, y, z) == 6) ) {
          state[x][y][z] = 1; state_G++;
        }
        if (old[x][y][z] == state[x][y][z]) {
          state_K++; // если клетки соседних поколений совпали
        }
      }
    }
  }
  //////////
}
//////////////////////////////////////////
int uslovie_(int x1, int y1, int z1) {
  int k = 0;
  k = old[x1][y1 + 1][z1] + old[x1][y1 - 1][z1] + old[x1 + 1][y1][z1] + old[x1 - 1][y1][z1] + old[x1 + 1][y1 + 1][z1] + old[x1 - 1][y1 - 1][z1] + old[x1 + 1][y1 - 1][z1] + old[x1 - 1][y1 + 1][z1] +
      old[x1][y1 + 1][z1 + 1] + old[x1][y1 - 1][z1 + 1] + old[x1 + 1][y1][z1 + 1] + old[x1 - 1][y1][z1 + 1] + old[x1 + 1][y1 + 1][z1 + 1] + old[x1 - 1][y1 - 1][z1 + 1] + old[x1 + 1][y1 - 1][z1 + 1] + old[x1 - 1][y1 + 1][z1 + 1] + old[x1][y1][z1 + 1] +
      old[x1][y1 + 1][z1 - 1] + old[x1][y1 - 1][z1 - 1] + old[x1 + 1][y1][z1 - 1] + old[x1 - 1][y1][z1 - 1] + old[x1 + 1][y1 + 1][z1 - 1] + old[x1 - 1][y1 - 1][z1 - 1] + old[x1 + 1][y1 - 1][z1 - 1] + old[x1 - 1][y1 + 1][z1 - 1] + old[x1][y1][z1 - 1];
  return k;
}
/////////////////////////////////////////
void dano_() {
  //печатаем с помощью плоттера относительные плотности совпадения соседних поколений и живучести (заселённости) текущего
  Serial.print( state_K / 1.250); Serial.print("    "); Serial.print( state_G / 1.250); Serial.println();
}

Так вот всё красиво живёт и гибнет, и чтоб нарушить симметрию приходится вводить 1-2 дополнительных клетки  (строки 64-71)- смысл в том что они иногда портят симметрию, а иногда нет в процессе мигания куба. визуально получается интересно. Может тогда смысл хищника в этом сделать?, его вместо клеток-вирусов. Но я пока концепт понимаю :)

sadman41
Offline
Зарегистрирован: 19.10.2016

Хищники разного цвета - это неплохо.
Запишем байт, как SS LLDDDD и закрепим правило охоты - например, высокоранговая клетка пожирает только низкоранговую. А от других пытается сбежать - сменить позицию. Для этого придётся заводить массив "доступный ход", где отмечать в каком направлении можно сдернуть (клетка не занята) и по рандомайзу делать ход.

lilik
Offline
Зарегистрирован: 19.10.2017

:)

Сам концепт Конвея сформулирован для неподвижных клеток - тела не двигаются, лишь жизнь бурно переливается. То есть здесь аналогия с плесенью на стене - рисунок движется, а конкретная плесень нет :)

И в случае с хищниками, подвижными организмами, надо сначала создать концепт существования (правила прописать текстом) и посмотреть на модели (условия запрограммировать). У Конвея клетка на месте и два варианта - жива, мертва. А у хищника? 

sadman41
Offline
Зарегистрирован: 19.10.2016

У хищника тоже жива или мертва, в принципе. Только он при соприкосновении с другим видом плесени может её немножечко покромсать - сделать соприкасающуюся клетку неживой.
Конечно не стоит сразу создавать имитацию планеты Земля, но, думаю, такая цветная типизация клеток на кубе добавит всполохов интересных.

sadman41
Offline
Зарегистрирован: 19.10.2016

https://youtu.be/vhRxwRr77dk
Искусственная жизнь 2: Адаптация (программа симулятор эволюции ботов)

lilik
Offline
Зарегистрирован: 19.10.2017

Класс! Сложно, явно не для Ардуино и игрушек :), надо автору подумать о "сети самого полигона-экрана", о том как боты менять его св-ва могут.