Skip to content

Instantly share code, notes, and snippets.

@FreedomGrenade
Last active April 17, 2016 12:10
Show Gist options
  • Save FreedomGrenade/6e80612226cd593c4566c08609758d99 to your computer and use it in GitHub Desktop.
Save FreedomGrenade/6e80612226cd593c4566c08609758d99 to your computer and use it in GitHub Desktop.
Game of life 5x5
// 5x5 Game of life, by Freedom_Grenade
// s save grid state / selection as image
// ctrl + left click = paste input
// '.' load input
// shift rightclick = select
int[] vals;
int[] oldvals;
int[] sums;
long max;
int[][] livedie;
long alive, dead;
int wd, ht; // these are all the same number
int gsize; //
float xpos, ypos; // viewing position
float gscale, vscale; // scale grid, view scale
boolean pause; // pause by user
boolean drawpause = false; // pause for drawing
boolean shiftDown = false; // mouse is erasing vs drawing
boolean increment = false; // increment by 1
boolean info ; // display info
int[] countHistory; //
int countpos = 0; // counthistory position
int count = 0; // current count
boolean fivebyfive; // 5x5 vs 3x3 kernal??
int iteration = 0; // current iteration (from start)
StringBuilder inputRule;
boolean binaryInput;
String tm;
boolean gridon = true;
int[] pasteArray;
boolean pasteVis;
int pasteWidth;
int pasteHeight;
float rscale;
int sx0, sy0, sx1, sy1;
int firstXsel, firstYsel, nextXsel, nextYsel;
boolean showselection;
public void setup() {
size(1600, 800, JAVA2D);
///////////////////////////////////////////////////////
//// MODIFY THIS /////////////////////////////////////
gsize = 800; // max grid size, lower if running too slow
vscale = 10.0; // initial zoom
fivebyfive = false; // false = 3x3
alive = 12; // table for if cell is alive
dead = 8; // table for if cell is dead
// binary : 0b1000 instead of 8
// Standard CGOL :
// fivebyfive = false;
// alive = 12 or 0b1100;
// dead = 8 or 0b1000;
textFont(createFont("Monospaced", 20)); // dunno if this will work on everycomp
info = false; // true = info panel on start
binaryInput = true; // start with binary input, false = decimal input
pause = true; // start paused
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
inputRule = new StringBuilder();
max = fivebyfive?1<<25:1<<8;
ht = gsize;
wd = gsize;
noStroke();
gscale = (float)1.0/gsize; // for scaling grid of values
vals = new int[wd * ht]; // holds values
oldvals = new int[vals.length];
sums = new int[vals.length]; // holds sums :3
livedie = new int[2][25];
countHistory = new int[width]; // record count for each frame
gen(alive, dead); // generate live/die tables
tm = hour()+"_"+minute();
sx0 = 0;
sx1 = 0;
sy0 = 0;
sy1 = 0;
}
public void draw() {
background(0);
fill(255);
stroke(0);
rscale = 1.0/(vscale*gscale*width);
pushMatrix(); // scale+translate view
translate(width/2, height/2);
translate(-xpos, -ypos);
scale(vscale*width);
noFill(); // grid border
stroke(255);
strokeWeight(gscale);
rect(-0.5, -0.5, 1, 1);
noStroke(); // draw/update grid
if (!pause && !drawpause || increment) calc();
display();
popMatrix();
if (info) dispInfo(); // info screen
//fill(255);
//noStroke();
//ellipse(mouseX, mouseY, 10, 10);
//saveFrame("/data/"+tm+"/########.jpg");
}
public void dispInfo() { // display info
textSize(20);
noStroke();
fill(0, 200);
rect(0, 0, width, 280);
fill(255);
textAlign(LEFT);
//stroke(255);
text("SPACE = PAUSE", 20, 20);
text("'n' = increment", 20, 40);
text("'x' = clear", 20, 60);
text("'c' = circle (random)", 20, 80);
text("'f' = fill (random)", 20, 100);
text("L,R,U,D,rightMouse = move", 20, 120);
text("leftMouse = draw on", 20, 140);
text("leftMouse + shift = draw off (erase)", 20, 160);
text("scroll mouse = zoom", 20, 180);
text("'i' = info on/off", 20, 200);
text("'g' = grid on/off", 20, 220);
text("'1','0',Backspace to modify, 'd' or 'a' to enter new Rule", 20, 240);
textSize(15);
if (binaryInput) {
text("01234567890123456789012345 BINARY input : Press b to switch to decimal", 20, 255);
} else {
text("DECIMAL input : Press b to switch to binary", 20, 255);
}
fill(255, 255, 0);
text(inputRule.toString(), 20, 270);
textAlign(RIGHT);
textSize(20);
fill(192, 255, 192);
if (pause||drawpause) text("PAUSED", 20, 0, width-20, 30);
text("RULE: " + alive + ":" +dead, 20, 20, width-20, 30);
text("Total ON : "+count, 20, 40, width-20, 30);
text((fivebyfive?"5x5":"3x3") + " Neighbourhood", 20, 60, width-20, 30);
float scaleCount = height*0.25/(gsize*gsize); // Display count history
stroke(0, 0, 255);
strokeWeight(3);
for (int i = 0; i < width; i++) {
int countind = (i + countpos)%width;
float countV = countHistory[countind] * scaleCount;
float countVn = countHistory[(countind+1)%width] * scaleCount;
line(i, height-countV, i+1, height-countVn);
}
strokeWeight(1);
}
public void keyPressed() { // uh don't look at this
if (key == CODED) {
if (keyCode == LEFT) xpos-=2; // arrow mod position
if (keyCode == RIGHT) xpos+=2;
if (keyCode == DOWN) ypos+=2;
if (keyCode == UP) ypos-=2;
if (keyCode == SHIFT) shiftDown = true; //
if (keyCode == CONTROL) pasteVis = true;
} else {
if (key == 'f' || key == 'F') rand(1, 4); // 'fill'
if (key == 'c' || key == 'C') randCircle(wd*ht/10, 0.5);// circle
if (key == 'x' || key == 'X') erase(); // clear
if (key == ' ' ) pause = !pause; // pause/unpause
if (key == 'g' || key == 'G') gridon = !gridon;
if (key == 't' || key == 'T') {
pause = true;
fivebyfive = !fivebyfive;
max = fivebyfive?1<<25:1<<8;
gen(alive, dead);
}
if (key == 'b' || key == 'B') { // binary/decimal input mode
info = true;
binaryInput = !binaryInput;
inputRule.setLength(0);
}
if (key == 'i' || key == 'I') {
info = !info; // show/hide info
}
if (key == 'n' || key == 'N') { // pause and increment
pause = true;
increment = true;
}
if (key == 's' || key == 'S') { // save
saveSelection();
}
if (binaryInput ) { // 1,0's only
if ((key == '1' || key == '0') && inputRule.length() <= 25 ) {
info = true;
inputRule.append(key);
}
} else { // numbers only
int kv = (int)key - (int)'0';
if ((kv >= 0 && kv <= 9) && inputRule.length() <= 8) {
info = true;
inputRule.append(key);
}
}
if ((key == BACKSPACE || key == DELETE) && inputRule.length() > 0) {
inputRule.deleteCharAt(inputRule.length() - 1);
}
if ((key == 'a' || key == 'A') && inputRule.length() > 0) { // set alive cell table
if (binaryInput) {
alive = lfromsb(inputRule);
} else {
alive = Long.parseLong(inputRule.toString());
}
pause = true;
inputRule.setLength(0);
gen(alive, dead);
}
if ((key == 'd' || key == 'D') && inputRule.length() > 0) { // set dead cell table
if (binaryInput) {
dead = lfromsb(inputRule);
} else {
dead = Long.parseLong(inputRule.toString());
}
pause = true;
inputRule.setLength(0);
gen(alive, dead);
}
if (key == '.') {
selectInput("Select file", "fileselect");
}
}
}
public void saveSelection() {
int x0 = 0;
int y0 = 0;
int x1 = gsize - 1;
int y1 = gsize - 1;
if (showselection) {
x0 = sx0;
y0 = sy0;
x1 = sx1;
y1 = sy1;
}
int awd = x1 - x0 + 1;
int aht = y1 - y0 + 1;
PImage img = createImage(awd, aht, RGB);
img.loadPixels();
for (int y = y0; y <= y1; y++) {
int indexP = (y-y0)*awd;
int indexG = y*gsize;
for (int x = x0; x <= x1; x++) {
img.pixels[x-x0 + indexP] = color(vals[x + indexG]*255);
}
}
img.updatePixels();
String time = year() + "-" + month() + "-" + day() + "_" + hour() + "-" + minute() + "-" + second() + "_"; // uhg
img.save(time + (fivebyfive?"(5x5)_":"(3x3)_") + "RULE_A-" + alive+ "_D-"+dead+"_Iter-"+iteration+".png");
}
public void fileselect(File fselect) {
if (fselect == null) return;
PImage img;
try {
img = loadImage(fselect.getAbsoluteFile().toString());
}
catch (Exception e) {
println("Not an image or something");
return;
}
pasteArray = new int[img.width * img.height];
pasteWidth = img.width;
pasteHeight = img.height;
img.loadPixels();
for (int i = 0; i < pasteArray.length; i++) {
if ((img.pixels[i]&255) > 127) pasteArray[i] = 1;
}
println("Loaded image");
}
public long lfromsb(StringBuilder s) { // get a long from a 'binary' string
long result = 0;
if (s.length() < 1) return result;
for (int i = 0; i < s.length(); i++) {
result+= ((s.charAt(i)=='1'?1:0)<<i);
}
return result;
}
public void keyReleased() {
if (key == CODED) {
if (keyCode == SHIFT) shiftDown = false;
if (keyCode == CONTROL) pasteVis = false;
}
}
public void mouseWheel(MouseEvent m) {
if (m.getCount() < 0) { // zoom/scale
vscale*=1.1;
xpos*=1.1;
ypos*=1.1;
} else {
vscale*=0.9;
xpos*=0.9;
ypos*=0.9;
}
}
public void mousePressed() { // cause mouseDrag isn't mousePressed
if (pasteVis) {
if (mouseButton == LEFT) pasteImage();
} else {
if (mouseButton == LEFT) drawAtmouse();
}
if (mouseButton == RIGHT && shiftDown) {
sx0 = mtog(mouseX, xpos, width);
sy0 = mtog(mouseY, ypos, height);
showselection = false;
}
}
public void mouseReleased() { // turn off drawpause when done drawing
if (mouseButton == LEFT) drawpause = false;
}
public void mouseDragged() {
if (mouseButton == RIGHT) {
if (!shiftDown) {
xpos+=(pmouseX-mouseX); // move by mouse
ypos+=(pmouseY-mouseY);
} else {
sx1 = mtog(mouseX, xpos, width);
sy1 = mtog(mouseY, ypos, height);
showselection = true;
}
} else {
drawpause = true; // draw/erase
drawAtmouse();
}
}
public void drawAtmouse() { // convert mouse pos, to grid pos
int xgrid = mtog(mouseX, xpos, width);
int ygrid = mtog(mouseY, ypos, height);
xgrid = constrain(xgrid, 0, gsize-1);
ygrid = constrain(ygrid, 0, gsize-1);
int index = xgrid + ygrid*gsize;
vals[index] = shiftDown?0:1;
}
public int mtog(int m, float pos, int wh) {
return (int)( (m - wh/2 + pos)*rscale + gsize/2 );
}
public void erase() { // clear the board
for (int i = 0; i < vals.length; i++) vals[i] = 0;
}
public void rand(int n, int m) { // randomly set values to 1, n/m chance
for (int i = 0; i < vals.length; i++) vals[i] = (random(m) <= n)?1:0;
}
public void randCircle(int n, float r) { // random circle
float rad = abs(r);
if (rad >= 0.9) rad = 0.9; // radius no larger than 90% of smallest half width/height
rad*=min(wd, ht)/2.0;
for (int i = 0; i < vals.length; i++) vals[i] = 0; // zero
for (int i = 0; i < n; i++) {
float a = i*TWO_PI/n;
float d = sqrt(random(1))*rad; //evenly distributed distance
float x = cos(a)*d + wd/2;
float y = sin(a)*d + ht/2;
vals[(int)x + (int)y*wd] = 1;
}
}
public void gen(long live, long die) { // convert long to 'binary' array
if (live >= max) live = live % max;
if (die >= max) die = die % max;
alive = live;
dead = die;
for (int i = 0; i < 25; i++) {
livedie[0][i] = (int)((die>>i)&1);
livedie[1][i] = (int)((live>>i)&1);
}
}
public void display() {
int x0 = max(mtog(-1, xpos, width), 0); // topleft of visible grid
int y0 = max(mtog(-1, ypos, height), 0);
int x1 = min(mtog(width+1, xpos, width), gsize-1); // bottomright of visible grid
int y1 = min(mtog(height+1, ypos, height), gsize-1);
int mx = mtog(mouseX, xpos, width); // grid coordinate of mouse
int my = mtog(mouseY, ypos, height);
boolean drawGrid = gridon && vscale > 2;
strokeWeight(gscale*0.001);
for (int y = y0; y <= y1; y++) {
int yindex = y*wd;
float yg = y*gscale - 0.5;
if (drawGrid) {
stroke(255);
line(-0.5, yg, 0.5, yg); // horizontal grid
}
for (int x= x0; x <= x1; x++) {
int pCell = -1;
int index = yindex+x;
float xg = x*gscale - 0.5;
if (y == y0 && drawGrid) { // vert grid
stroke(255);
line(xg, -0.5, xg, 0.5);
}
noStroke();
fill(255);
if (pasteVis) {
// -1 = out of bounds, 0 or 1 = state
pCell = getPasteCellAt(x-mx, y-my); // current cell - mouse cell location = current paste Array location
if (pCell == 1) fill(0, 0, 255);
if (pCell == 0) fill(0, 0, 64);
}
if (vals[index] == 1 || pCell != -1) {
if (pCell==-1 && oldvals[index] != vals[index]) fill(255,0,0);
rect(xg, yg, gscale, gscale); // scaled so the grid is -0.5 to 0.5
}
if (showselection && (frameCount + x + y) % 10 < 5) {
if ((x == sx0 || x == sx1) && y >= sy0 && y <= sy1 || (y == sy0 || y == sy1) && x >= sx0 && x <= sx1) {
//fill(((frameCount + x + y)%10)*25);
fill(255);
rect(xg, yg, gscale, gscale);
}
}
}
}
}
public int getPasteCellAt(int x, int y) {
if (pasteArray == null) return -1;
if (x < 0 || x >= pasteWidth || y < 0 || y >= pasteHeight) return -1;
return pasteArray[x + y * pasteWidth];
}
public void pasteImage() {
int mx = mtog(mouseX, xpos, width);
int my = mtog(mouseY, ypos, height);
int x0 = max(mx, 0); // topleft of overlap
int y0 = max(my, 0);
int x1 = min(mx + pasteWidth, gsize - 1);
int y1 = min(my + pasteHeight, gsize - 1);
for (int y = y0; y <= y1; y++) {
int gyindex = y*gsize;
for (int x = x0; x <= x1; x++) {
int pCell = getPasteCellAt(x - mx, y - my);
if (pCell != -1) { // shouldn't be -1 since we are only going over the overlap
vals[gyindex + x] = pCell;
}
}
}
//println((y1-y0) + ":" + (x1-x0));
}
public void calc() { // drw = true, draw while updating
setSums(vals, wd, ht, sums);
count = 0;
for (int y = 0; y < ht; y++) {
int index = y*wd;
for (int x = 0; x < wd; x++) {
int vi = vals[index];
int sum = 0;
if (fivebyfive) {
sum = getSum(x-3, y-3, x+2, y+2, wd, ht, sums); // 5x5
} else {
sum = getSum(x-2, y-2, x+1, y+1, wd, ht, sums); // 3x3
}
oldvals[index] = vals[index];
int indv = livedie[vi][sum-vi];
vals[index] = indv;
count+=vals[index];
index++;
}
}
countpos++;
if (countpos >= width) countpos = 0; // position to write to array
countHistory[countpos] = count;
increment = false;
if (!pause) iteration++;
}
// Based on fast box blur
//
public int getSum(int x0, int y0_, int x1_, int y1_, int wd, int ht, int[] sums) {
// [abcd] [abcd] [abcd] [abcd] [a c ] [a c ] [a c ] [a c ] [ ] sum = a -c -b +d
// [abcd] [abcd] [abcd] [abcd] [a c ] [a c ] [a c ] [a c ] [ ]
// [ab ] [ab ] [ab ] [ab ] [a ] [a ] [a ] [a ] [ ]
// [ab ] [ab ] [ab ] [ab ] [a ] [a ] [a ]*[a ]*[ ]
// [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
int y0 = wd*y0_;
int x1 = (x1_ >= wd)? wd-1 : x1_ ;
int y1 = wd*( (y1_ >= ht)? ht-1 : y1_ );
int cond = ((x0>= 0)?1:0) + ((y0>=0)?2:0);
if (cond == 1) return sums[x1+y1] - sums[x0 + y1]; // x0 < 0, sum would be 0
if (cond == 2) return sums[x1+y1] - sums[x1 + y0]; // y0 < 0, sum would be 0
if (cond == 3) return sums[x1+y1] - sums[x1 + y0] - sums[x0 + y1] + sums[x0 + y0]; // sum - top - left + overlapped subtraction of left, right
return sums[x1+y1]; // x0,y0 < 0, sums would be 0
}
// every 'cell' is the sum of all cells to the left and up of it
public void setSums(int[] values, int wd, int ht, int[] sums) {
for (int y = 0; y < ht; y++) {
int index = y*wd;
int linesum = 0;
for (int x = 0; x < wd; x++) { //
linesum+=values[index]; // s s s s s s . . . . .
if (y > 0) { // s s s s s S . . . . .
sums[index] = sums[index-wd] + linesum;//S+L// l l l l l(L). . . . .
} else { // . . . . . . . . . . .
sums[index] = linesum; //L //
}
index++;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment