Игра "Жизнь"- клеточный автомат.
- Войдите на сайт для отправки комментариев
Ср, 23/03/2022 - 18:16
Наткнулся на интересную поделку (подобная тема на форуме где-то была):
https://adior.ru/index.php/robototekhnika/29-game-life
и сделал на её основе игрушку-сувенир для релаксации.
В отличии от исходника:
- Добавлен псевдослучайный набор первого поколения клеток при каждом перерождении.
- Добавлен псевдослучайный перебор форм клеток при каждом новом перерождении жизни.
- Изменены исходные правила игры под условия клеточного автомата Джона Конвея.
- Реализована перезагрузка платы при 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(); }
Правила самой игры ниже:
Прежде, чем мы их сформулируем, обратим внимание на то, что каждую клетку, окружают восемь соседних клеток: четыре имеют с ней общие стороны, четыре другие — общие вершины.
- Выживание. Каждая живая клетка, имеющая вокруг себя две или три соседние живые клетки, выживает и переходит в следующее поколение.
- Гибель. Каждая клетка, у которой больше трёх соседей, погибает, то есть исчезает с экрана из-за перенаселённости. Каждая клетка, вокруг которой свободны все соседние клетки или же занята всего одна клетка, погибает от одиночества.
- Рождение. Если число живых клеток, с которыми граничат какая-нибудь пустая клетка, в точности равно трём (не больше и не меньше), то на этой клетке происходит рождение нового «организма», то есть клетка становится живой.
Игровой цикл прекращается, если:
- На экране не останется ни одной «живой» клетки
- Конфигурация на очередном шаге в точности (без сдвигов и поворотов) повторит себя же – жизнь замирает в статичной картинке.
- Последовательность конфигураций клеток начнёт повторятся через определённое число шагов - жизнь замирает в динамическом повторе.
Вид на внутренности:
Формы клеточной жизни:
Хочу сделать трёхмерный вариант игры на светодиодном кубе. Есть ли правила игры для объёмного варианта, а не "плосткостного"? И как правильно экстраполировать одни правила к другим?
Это плоскость:
это какая-то пропаганда "шведских" семей? :)
Для обьемного можно пойти по простому экспансивному пути - теперь число соседей у каждой клетки равно 26, остается подобрать граничные величины для всех трех случаев выше
А для цветного экрана правил не попадалось? - видимо надо создавать разные типы клеток...?
Согласен с b707.
1) посмотреть вокруг в своём слое;
2) если есть слой выше, обсмотреть квадрат над собой;
3) так же под собой.
Пересчитал соседей, сделал выводы.
Все можно прямо в трехмерном массиве и обустроить.
Для цветного придётся мутации вводить. Типа смешение цвета при рождении. А остальное все так же, как и в ч/б, там же не параллельные миры - в одной клетке двум сущностям не жить.
Да, я так примерно и мыслю, вопрос в этих граничных условиях. В "плоской жизни" пробовал менять авторский вариант правил, жизнь либо дохнет быстро, либо длится очень долго :)
Придётся видимо экспериментировать по факту и в объёмном варианте.
Ну, какбэ в реальности так и есть - жизнь скоротечна для одних организмов и почти бесконечна для других.
И режиссёра , который превратил её в увлекательный перформанс , не существует.
Сделал вариант с нормальной живучестью - когда зарождается клетка при 4 рядом, а не умирает если 4,5,6 рядом. Хотел добавить ssd1306 для отображения характеристик процесса, но библиотеки несовместимы - ничего не работает после загрузки. Это для этой пары?, или в принципе для умных светодиодов и дисплеев?
Хотел добавить ssd1306 для отображения характеристик процесса, но библиотеки несовместимы - ничего не работает после загрузки. Это для этой пары?, или в принципе для умных светодиодов и дисплеев?
а плата какая? Для Нано скорее всего памяти не хватает, ОЛЕД и так неплохо поджирает, а тут же Фастлед буфер для 125 ледов...
Плата УНО, 87 процентов динамической памяти скетч занимает.
Плата УНО, 87 процентов динамической памяти скетч занимает.
точно память
Пока плюнул на память, воспользовался плоттером из "иде".
Очень интересно ведут себя графики. Синий показывает процент совпадений живых и неживых клеток соседних поколений, а красный процент живых в текущем поколении. Одно как бы отражение другого в слегка кривом зеркале :)
То есть аксиома типа: чем сильнее похожи соседние поколения тем меньше потомство.
То есть аксиома типа: чем сильнее похожи соседние поколения тем меньше потомство.
ну это и в реальной биологии также - близкородственное скрещивание вызывает вырождение
И как там, на кубе, интересная картинка?
Гифку нельзя теперь загрузить чего-то. Вот поймал гармоничное и повторяющиеся до бесконечности. Куб был выключен, заподозрил по графикам. Такое вдруг появилось:
А так причудливо перемигивается из угла в угол, то разрастаясь, то сжимаясь :) Конечно не то, что в симуляторе можно нарисовать для куба.
https://www.bibliofond.ru/view.aspx?id=603714
1.3 Экология популяций и сообществ
...а так я, думаю, пора хищников вводить:
1) за один проход хищник поедает одну клетку.
2) если у хищника нет соседей - жертв, то он сдвигается на одну рандомную позицию (можно, конечно) закодировать направление движения (в сторону предыдущей жертвы)
3) если за 3 хода он никого не сожрал - гибнет.
Закодировать можно в байте. Например: SLLRDDDD
S - признак (жертва/хищник)
LL - кол-во жизни (уменьшается до 0)
R - резервный бит
DDDD - направление движения.
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));
Вот и цветность понадобилась - жертвы и хищники разного цвета
Идею пока только понял. Сейчас догадался как сделать красиво чтоб мигало :), надо первоначальный засев клеток делать симметрично относительно центра куба:
Так вот всё красиво живёт и гибнет, и чтоб нарушить симметрию приходится вводить 1-2 дополнительных клетки (строки 64-71)- смысл в том что они иногда портят симметрию, а иногда нет в процессе мигания куба. визуально получается интересно. Может тогда смысл хищника в этом сделать?, его вместо клеток-вирусов. Но я пока концепт понимаю :)
Хищники разного цвета - это неплохо.
Запишем байт, как SS LLDDDD и закрепим правило охоты - например, высокоранговая клетка пожирает только низкоранговую. А от других пытается сбежать - сменить позицию. Для этого придётся заводить массив "доступный ход", где отмечать в каком направлении можно сдернуть (клетка не занята) и по рандомайзу делать ход.
:)
Сам концепт Конвея сформулирован для неподвижных клеток - тела не двигаются, лишь жизнь бурно переливается. То есть здесь аналогия с плесенью на стене - рисунок движется, а конкретная плесень нет :)
И в случае с хищниками, подвижными организмами, надо сначала создать концепт существования (правила прописать текстом) и посмотреть на модели (условия запрограммировать). У Конвея клетка на месте и два варианта - жива, мертва. А у хищника?
У хищника тоже жива или мертва, в принципе. Только он при соприкосновении с другим видом плесени может её немножечко покромсать - сделать соприкасающуюся клетку неживой.
Конечно не стоит сразу создавать имитацию планеты Земля, но, думаю, такая цветная типизация клеток на кубе добавит всполохов интересных.
https://youtu.be/vhRxwRr77dk
Искусственная жизнь 2: Адаптация (программа симулятор эволюции ботов)
Класс! Сложно, явно не для Ардуино и игрушек :), надо автору подумать о "сети самого полигона-экрана", о том как боты менять его св-ва могут.