Processing:Примеры/Игра жизни Конвея

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Описание[1]

Нажмите на пробел, чтобы остановить клеточный автомат, или на кнопку мышки, чтобы изменить значения клеток. Во время паузы на клетки можно нажимать, активируя и деактивируя их. Нажмите на клавишу «R», чтобы случайно сбросить исходное расположение клеток. Нажмите на клавишу «C», чтобы очистить фоновую решетку.

Клеточный автомат «Игра жизни» был придуман Джоном Конвеем в 1970 году.

Пример

// размер клеток:
int cellSize = 5;

// насколько высока вероятность того,
// что клетка будет жива в начале игры (в процентах):
float probabilityOfAliveAtStart = 15;

// переменные для таймера:
int interval = 100;
int lastRecordedTime = 0;

// цвета для активных/неактивных клеток:
color alive = color(0, 200, 0);
color dead = color(0);

// массив клеток:
int[][] cells; 
// буфер для записи состояния клеток;
// он также будет использоваться
// для изменения других клеток во время взаимодействий:
int[][] cellsBuffer; 

// пауза:
boolean pause = false;

void setup() {
  size (640, 360);

  // инициализируем массивы: 
  cells = new int[width/cellSize][height/cellSize];
  cellsBuffer = new int[width/cellSize][height/cellSize];

  // строка для отрисовки фоновой клеточной решетки:
  stroke(48);

  noSmooth();

  // инициализируем клетки:
  for (int x=0; x<width/cellSize; x++) {
    for (int y=0; y<height/cellSize; y++) {
      float state = random (100);
      if (state > probabilityOfAliveAtStart) { 
        state = 0;
      }
      else {
        state = 1;
      }
      cells[x][y] = int(state); // сохраняем состояние каждой клетки
    }
  }
  background(0); // закрашиваем фон черным на случай,
                 // если клетки не заполнят все ячейки решетки
}


void draw() {

  // рисуем решетку:
  for (int x=0; x<width/cellSize; x++) {
    for (int y=0; y<height/cellSize; y++) {
      if (cells[x][y]==1) {
        fill(alive); // если жива
      }
      else {
        fill(dead); // если мертва
      }
      rect (x*cellSize, y*cellSize, cellSize, cellSize);
    }
  }
  // итерируем, если таймер дает отсчет:
  if (millis()-lastRecordedTime>interval) {
    if (!pause) {
      iteration();
      lastRecordedTime = millis();
    }
  }

  // во время паузы вручную создаем новые клетки:
  if (pause && mousePressed) {
    // масштабируем друг к другу размер экрана
    // и количество ячеек в фоновой решетке;
    // также задаем границы для фоновой решетки:
    int xCellOver = int(map(mouseX, 0, width, 0, width/cellSize));
    xCellOver = constrain(xCellOver, 0, width/cellSize-1);
    int yCellOver = int(map(mouseY, 0, height, 0, height/cellSize));
    yCellOver = constrain(yCellOver, 0, height/cellSize-1);

    // сверяемся с клетками в буфере:
    if (cellsBuffer[xCellOver][yCellOver]==1) { // клетка жива
      cells[xCellOver][yCellOver]=0; // убита
      fill(dead); // заполняем цветом убитой клетки
    }
    else { // клетка мертва
      cells[xCellOver][yCellOver]=1; // оживляем
      fill(alive); // заполняем цветом живой клетки
    }
  } 
  else if (pause && !mousePressed) { // и сохраняем, когда 
                                     // пользователь отпустил мышку
    // сохраняем клетки в буфер
    // (это дает возможность работать 
    // с одним массивом, другой оставляя нетронутым):
    for (int x=0; x<width/cellSize; x++) {
      for (int y=0; y<height/cellSize; y++) {
        cellsBuffer[x][y] = cells[x][y];
      }
    }
  }
}



void iteration() { // если счетчик дает отчет
  // сохраняем клетки в буфер 
  // (это дает возможность работать
  // с одним массивом, другой оставляя нетронутым):
  for (int x=0; x<width/cellSize; x++) {
    for (int y=0; y<height/cellSize; y++) {
      cellsBuffer[x][y] = cells[x][y];
    }
  }

  // проходим через все клетки:
  for (int x=0; x<width/cellSize; x++) {
    for (int y=0; y<height/cellSize; y++) {
      // проходим через всех соседей каждой клетки:
      int neighbours = 0; // считаем соседей
      for (int xx=x-1; xx<=x+1;xx++) {
        for (int yy=y-1; yy<=y+1;yy++) {  
          // проверяем, не вышли ли за пределы экрана:
          if (((xx>=0)&&(xx<width/cellSize))&&((yy>=0)&&(yy<height/cellSize))) {
            if (!((xx==x)&&(yy==y))) { // проверяем клетку
                                       // относительно самой себя
              if (cellsBuffer[xx][yy]==1){
                neighbours ++; // проверяем живых соседей и считаем их 
              }
            } // заканчиваем цикл if()
          } // заканчиваем цикл if()
        } // заканчиваем цикл yy 
      } // заканчиваем цикл xx
      // проверили всех соседей: применяем правила!
      if (cellsBuffer[x][y]==1) { // клетка жива: 
                                  // если нужно, убиваем ее
        if (neighbours < 2 || neighbours > 3) {
          cells[x][y] = 0; // убиваем, разве что у нее нет 2-3 соседей 
        }
      } 
      else { // клетка мертва: если нужно, оживляем 
        if (neighbours == 3 ) {
          cells[x][y] = 1; // только если у нее 3 соседа
        }
      } // заканчиваем цикл if()
    } // заканчиваем цикл y
  } // заканчиваем цикл x
} // конец функции

void keyPressed() {
  if (key=='r' || key == 'R') {
    // перезапуск - повторная инициализация клеток:
    for (int x=0; x<width/cellSize; x++) {
      for (int y=0; y<height/cellSize; y++) {
        float state = random (100);
        if (state > probabilityOfAliveAtStart) {
          state = 0;
        }
        else {
          state = 1;
        }
        cells[x][y] = int(state); // сохраняем состояние каждой клетки 
      }
    }
  }
  if (key==' ') { // вкл/выкл для паузы
    pause = !pause;
  }
  if (key=='c' || key == 'C') { // очищаем все
    for (int x=0; x<width/cellSize; x++) {
      for (int y=0; y<height/cellSize; y++) {
        cells[x][y] = 0; // сохраняем все значения как «0»
      }
    }
  }
}

См.также

Внешние ссылки