Interactive Game of Life: ICM Final

As promised in an earlier post I wanted to combine motion detection video and Conway’s Game of Life sketch so that I can play god and create and destroy life. Mission Accomplished.

Code Below

//OK! THis is an interactive Game of Life — when motion is detected, dead cells become alive
//Natalie & Todd ICM final project & Applications presentation

//basic setup for video:
import processing.video.*;
// Variable for capture device
Capture video;
// Previous Frame
PImage prevFrame;
//make a frame where we will apply the pixelation (can’t apply to prevFrame)
PImage golFrame;
// How different must a pixel be to be a “motion” pixel
float threshold = 50;
// Size of each cell in the grid, ratio of window size to video size
int cols, rows;
int videoScale = 8;
//int videoScale = 16;
//int videoScale = 4;

//need to make this 2D array a global variable, not just within the GOL Class
//because we have to write to it after doing the threshold evaluation
int [][] board;

//declare the gol object
GOL gol;

void setup() {
//initialize the gol object
gol = new GOL();

size(1280,720);
//doubling the size is possible but makes it run a lot slower
//size(1280, 960);
//initialize columns and rows to create a grid
cols = width/videoScale;
rows = height/videoScale;
//just double checking that the math is working out
// println(cols);
// println(rows);

//getting the video started
video = new Capture(this, width, height, 24);
video.start();

// Create an empty image the same size as the video
prevFrame = createImage(video.width, video.height, RGB);
//create another image to which pixelation will be applied
golFrame = createImage(video.width, video.height, RGB);

//declare the board, our 2D array
board = new int[cols] [rows];
}

void draw() {

// Capture video
if (video.available()) {
// Save previous frame for motion detection
// Before we read the new frame, we always save the previous frame for comparison
prevFrame.copy(video, 0, 0, video.width, video.height, 0, 0, video.width, video.height);
prevFrame.updatePixels();
video.read();
}

//load the pixels so that we can manipulate them
loadPixels();
video.loadPixels();
prevFrame.loadPixels();
golFrame.loadPixels();

// Begin loop to walk through every pixel (every column, every row)
for (int x = 0; x < video.width; x ++ ) {
for (int y = 0; y < video.height; y ++ ) {

int loc = x + y*video.width; // Step 1, determine the 1D pixel location
color current = video.pixels[loc]; // Step 2, determine the current color of each pixel
color previous = prevFrame.pixels[loc]; // Step 3, determine the previous color of each pixel

// Step 4, compare previous color to current color for each pixel
float r1 = red(current);
float g1 = green(current);
float b1 = blue(current);
float r2 = red(previous);
float g2 = green(previous);
float b2 = blue(previous);

//find the difference between current and previous frames for each pixel
float diff = dist(r1, g1, b1, r2, g2, b2);

//Evaluate how different the pixels are.
// What we want to do is take the “answer” from the “how different” question and use that as input in the GOL board.
//We can put the information directly into the 2D array board (instead of using the answer to effect another variable which, in turn, effects the board).

//if the difference is greater than the threshold and the pixel is divisible by 8 (as in, it’s part of the larger grid) — this acts as a gate
//then put a 1 in that spot of the 2D board array (1 = birth)
//basically: if motion is detected, turn the cell black
if (diff > threshold && loc%8==0 && y%8==0) {
board[x/videoScale][y/videoScale]=1;

//otherwise, if the difference is less than the threshold and the pixel is divisible by 8,
//and if the cell does not equal 1 (as in, if it equals 0, which means that it’s dead)
//then keep a 0 in that spot of the 2D board
//basically: if no motion is detected, and a cell is dead, keep it dead (white)
}
else if (diff <= threshold && loc%8==0 && y%8==0) {
if (board[x/videoScale][y/videoScale]!= 1) {
board[x/videoScale][y/videoScale]=0;
}
}
}
}

//call the generate function in GOL
gol.generate();
//call the display function in GOL
gol.display();
saveFrame(“img_####.jpg”);
//saveFrame(“line-######.png”);
} //end of draw

//game of life from nature of code, modified for interactivity, modified rules

class GOL {
//this line for pixelation
//needs to equal int videoScale in the main page of code
int w = 8;
//int w = 16;
//int w = 4;

GOL() {
//nothing goes in the constructor because we’re declaring cols = width/w, rows=height/w, and board=new int[cols][rows] in setup()
//in our main page of code
//because we’re using those variables in the video input and manipulation, too.

//we also aren’t calling initialize here because we only want initialization when there’s movement
}

//we acually never call initialize but we’ll leave it in just in case we want to effect the state with a mouse press or something
void initialize() {
//initialize each cell of the board with a random state: 0 or 1
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
board[x][y] = 0; //initialize each cell with 0 or 1 (a random number between 0 and 2)
}
}
} //end initialize

//process to create a new generation (one for each frame of animation)
void generate() {
//2-dimensional array for the new board
int[][] next = new int[cols][rows];

for (int x = 1; x < cols-1; x++) {
for (int y = 1; y < rows-1; y++) {

//keep track of the neighbors – create a counter that finds the total live neighbors
int neighbors = 0;
//add up all of the neighbors using nested for loops
for (int i = -1; i<= 1; i++) {
for (int j = -1; j<=1; j++) {
neighbors = neighbors + board[x+i][y+j];
}
}
//the above loops count the center cell, which we don’t want to include
//so we need to subtract it after the loop is finished
neighbors = neighbors – board[x][y];

//////original rules////////
//write the rules of death/birth/stasis:
//death
if ((board [x][y] == 1) && (neighbors < 2)) {
next[x][y] = 0;
}
else if ((board[x][y] == 1) && (neighbors > 3)) {
next[x][y] = 0;
}
// //birth
else if ((board[x][y] == 0) && (neighbors == 3)) {
next[x][y] = 1;
}
// //stasis, next = current
else {
next[x][y] = board[x][y];
}

//////reset to original rules by pressing space//////
//write the rules of death/birth/stasis:
if(key == ‘r’ || key == ‘R’) {
//death
if ((board [x][y] == 1) && (neighbors < 2)) {
next[x][y] = 0;
}
else if ((board[x][y] == 1) && (neighbors > 3)) {
next[x][y] = 0;
}
// //birth
else if ((board[x][y] == 0) && (neighbors == 3)) {
next[x][y] = 1;
}
// //stasis, next = current
else {
next[x][y] = board[x][y];
}
}

/////////new rules — NYC version/////////
if (key == ‘n’ || key == ‘N’) { // n = NYC!
//death
if ((board [x][y] == 1) && (neighbors < 2)) {
next[x][y] = 0;
} //increase overcrowing threshold — allow more neighbors
//this turns the motion into a negative space
else if ((board[x][y] == 1) && (neighbors > 4)) {
next[x][y] = 0;
}
//birth
else if ((board[x][y] == 0) && (neighbors == 3)) {
next[x][y] = 1;
}
//stasis, next = current
else {
next[x][y] = board[x][y];
}
}

///////new rules — spontaneous generation – if no neighbors then born! == lightning effect/////
if (key == ‘s’ || key == ‘S’) { //s = spontaneous!
if ((board [x][y] == 1) && (neighbors < 2)) {
next[x][y] = 0;
}
else if ((board[x][y] == 1) && (neighbors > 3)) {
next[x][y] = 0;
}
//birth
else if ((board[x][y] == 0) && (neighbors == 0)) {
next[x][y] = 1;
}
//stasis, next = current
else {
next[x][y] = board[x][y];
}
}
}
}

//replace the board
board = next;
} //end of generate()

//display the board as a grid:
void display() {
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
if ((board[i][j] == 1)) {
fill(0);
}
else {
fill(255);
}

//include grid:
stroke(0);
//no grid:
//noStroke();
rect(i*w, j*w, w, w);
}
}
} //end of display()
}//end of GOL class

Leave a Reply