#include <FastLED.h>
#define HEIGHT 16 //Высота матрицы
#define WIDTH 16 //Ширина матрицы
#define LED_PIN 13 //Пин подключения матрицы на ардуино
#define BUTTON_PIN 2 //Пин подключения кнопки на ардуино
#define NUM_LEDS (HEIGHT * WIDTH) //Общее количество светодиодов
#define BRIGHTNESS 255 //Яркость (0-255)
char ALGORITHM = 1; //1 - Жизнь, 2 - Мозг Брейна, 3 - Семена
char MODE = 1; //1 - автосброс, 2 - бесконечный
char INTENSITY = 3; //Интенсивность для 2 режима
CRGB leds[NUM_LEDS]; //Массив для светодиодов
char board[HEIGHT][WIDTH] = {0}; //Инициализация игрового поля
unsigned long hash1 = 0; //Хеш-сумма прошлой итерации
unsigned long hash2 = 0; //Хеш-сумма позапрошлой итерации
unsigned long hash3 = 0; //Хеш-сумма позапозапрошлой итерации
char lenPush = 0; //Длина нажатия
void SetRandValues() { //Обновление поля
for (char i = 0; i < HEIGHT; i++) {
for (char j = 0; j < WIDTH; j++) {
board[i][j] = 0;
}
}
int count = random(NUM_LEDS / 1.3);
for (int i = 0; i < count; i++) {
board[random(HEIGHT)][random(WIDTH)] = 1;
}
}
void setup() {
FastLED.addLeds<WS2811, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
pinMode(BUTTON_PIN, INPUT_PULLUP);
randomSeed(analogRead(0));
if (MODE == 1)
SetRandValues();
else if (MODE == 2)
NewPixels();
}
char liveNeighbors(char row, char col) { //Функция для подсчёта живых соседей
char lives = 0;
for (char i = -1; i <= 1; i++) {
for (char j = -1; j <= 1; j++) {
char r = row + i;
char c = col + j;
if ((r >= 0 && r < HEIGHT) && (c >= 0 && c < WIDTH) && (board[r][c] == 1))
lives++;
}
}
if (board[row][col] == 1) //Исключение самой клетки
lives -= 1;
return lives;
}
void NextStep() { //Функция обновления состояния доски
char tempBoard[HEIGHT][WIDTH] = {0};
for (char i = 0; i < HEIGHT; i++) {
for (char j = 0; j < WIDTH; j++) {
char neighbors = liveNeighbors(i, j);
if (ALGORITHM == 1) { //Жизнь
if (board[i][j] == 0 && neighbors == 3)
tempBoard[i][j] = 1;
else if (board[i][j] == 1 && (neighbors == 2 || neighbors == 3))
tempBoard[i][j] = 1;
else if (board[i][j] == 1 && (neighbors < 2 || neighbors > 3))
tempBoard[i][j] = 0;
}
else if (ALGORITHM == 2) { //Мозг Брейна
if (board[i][j] == 0 && neighbors == 2)
tempBoard[i][j] = 1;
else if (board[i][j] == 1)
tempBoard[i][j] = 2;
else if (board[i][j] == 2)
tempBoard[i][j] = 0;
}
else if (ALGORITHM == 3) { //Семена
if (board[i][j] == 0 && neighbors == 2)
tempBoard[i][j] = 1;
else if (board[i][j] == 1)
tempBoard[i][j] = 0;
}
}
}
for (char i = 0; i < HEIGHT; i++) { //Копирование временного массива в основной
for (char j = 0; j < WIDTH; j++) {
board[i][j] = tempBoard[i][j];
}
}
}
int XY(char x, char y) { //Функция преобразования координат
//Для wokwi
return ((WIDTH - x) * WIDTH - (HEIGHT - y));
//Для реальной матрицы
/*if (x % 2 == 0)
return (x * WIDTH + y);
return (x * WIDTH + (WIDTH - y - 1));*/
}
void CheckingHash() { //Сравнение с предыдущими состояниями
unsigned long hash = 0;
for (char i = 0; i < HEIGHT; i++) {
for (char j = 0; j < WIDTH; j++) {
hash = hash * 31 + board[i][j];
}
}
if (hash == hash1 || hash == hash2 || hash == hash3)
SetRandValues(); //Если игра завершена
else {
hash3 = hash2;
hash2 = hash1;
hash1 = hash;
}
}
void NewPixels() { //Добавление пикселей
bool flag = false;
for (char i = 0; i < HEIGHT; i++) {
for (char j = 0; j < WIDTH; j++) {
if (board[i][j] == 2)
flag = true;
}
}
if (flag == false) { //Если игра завершена
char var = random(4);
char shift = random(-6, 7);
if (var == 0) {
board[8 + shift][8 + shift] = 1;
board[8 + 1 + shift][8 + shift] = 1;
board[8 + shift][8 + 1 + shift] = 1;
board[8 + 1 + shift][8 + 1 + shift] = 1;
}
else if (var == 2) {
board[8 + shift][8 + shift] = 1;
board[8 + 1 + shift][8 + shift] = 1;
board[8 + 2 + shift][8 + shift] = 1;
board[8 - 1 + shift][8 + shift] = 1;
}
else if (var == 2) {
board[8 + shift][8 + shift] = 1;
board[8 + shift][8 + 1 + shift] = 1;
board[8 + shift][8 + 2 + shift] = 1;
board[8 + shift][8 - 1 + shift] = 1;
}
}
else {
for (char i = 0; i < INTENSITY; i++)
board[random(HEIGHT)][random(WIDTH)] = 1;
}
}
void SetNextAlgorithm() { //Смена алгоритма
for (char i = 0; i < HEIGHT; i++) {
for (char j = 0; j < WIDTH; j++) {
board[i][j] = 0;
}
}
if (ALGORITHM == 1)
ALGORITHM = 2;
else if (ALGORITHM == 2)
ALGORITHM = 3;
else if (ALGORITHM == 3)
ALGORITHM = 1;
}
void SetNextMode() { //Смена режима
for (char i = 0; i < HEIGHT; i++) {
for (char j = 0; j < WIDTH; j++) {
board[i][j] = 0;
}
}
if (MODE == 1)
MODE = 2;
else if (MODE == 2)
MODE = 1;
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW)
lenPush += 1;
else if (digitalRead(BUTTON_PIN) != LOW && lenPush != 0) {
if (lenPush < 10)
SetNextMode();
else
SetNextAlgorithm();
lenPush = 0;
}
for (char i = 0; i < HEIGHT; i++) { //Отображение текущего состояния на матрице
for (char j = 0; j < WIDTH; j++) {
if (board[i][j] == 0) //Чёрный цвет для мёртвых клеток
leds[XY(i, j)] = CRGB(0, 0, 0);
else if (board[i][j] == 1) //Белый цвет для живых клеток
leds[XY(i, j)] = CRGB(255, 255, 255);
else if (board[i][j] == 2) //Синий цвет для отмирающих клеток
leds[XY(i, j)] = CRGB(0, 0, 255);
}
}
if (MODE == 1)
CheckingHash();
else if (MODE == 2)
NewPixels();
FastLED.show();
delay(100);
NextStep(); //Переход к следующему поколению
}