Last active
April 17, 2016 12:10
-
-
Save FreedomGrenade/6e80612226cd593c4566c08609758d99 to your computer and use it in GitHub Desktop.
Game of life 5x5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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