We are about to switch to a new forum software. Until then we have removed the registration on this forum.
tl;dr : I reset the variable to store the neighbours in everytime I checked a new neighbour.
I recently found processing and thought the Game of Life would be an easy first project. But when I tried going from neighbourCount only, to saving neighbours in order - in an Array - I stopped getting neighbourCounts bigger than one.
I know it is not needed to store the specific configuration of neighbours to make the Game of Life work, but I want to expand on the idea once I get this working. Why am I not getting the right neighbourCount? I know it's a lot, but I put in as many comments as possible to make it easier on the eyes.
int interval = 1000; // time between iterations
int lastRecordedTime = 0; // timer
int cellSize = 5; // size of one Cell
int counterLocal; // variable for storing neighbourCount
int [] neighbourhood = {0,0,0,0,0,0,0,0}; // Array for storing neighbourhood configuration
int [][] cells; // 2D Cells Array
int [][] cellsBuffer; // 2D Buffer Array to work on while leaving cells[][] unharmed
float prob = 30; // probability of Cell being alive at setup
color alive = color(0, 0, 0); // Alive color = black
color dead = color(255, 255, 255); // Dead color = white
void setup() {
size (200, 200);
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++) { // Randomize cells[][]
for ( int y = 0; y < height/cellSize; y++) {
float state = random(100);
if (state > prob) {
state = 0;
} else {
state = 1;
}
cells[x][y] = int(state);
}
}
background(0);
} // end of setup()
void draw() { // Loop through every cell in cells[][] and draw them according to their state
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) {
iteration();
lastRecordedTime = millis();
}
} // end of draw()
void iteration() {
for (int x = 0; x < width/cellSize; x++) { // copy cells Array to cellsBuffer
for (int y = 0; y < height/cellSize; y++) {
cellsBuffer[x][y] = cells[x][y];
}
}
for (int x = 0; x < width/cellSize; x++) { // go through every cell
for (int y = 0; y < height/cellSize; y++) {
String neighbourCode = "0,0,0,0,0,0,0,0"; // string used to encode neighbour positions
for (int xx=x-1; xx<=x+1; xx++) { // go through all neighbours of current Cell
for (int yy=y-1; yy<=y+1; yy++) {
if (((xx>=0)&&(xx<width/cellSize))&&((yy>=0)&&(yy<height/cellSize))) { // Make sure you are not out of bounds
//if (!((xx==x)&&(yy==y))) { // Make sure to to check against self (not needed)
int[] codeArray = int(split(neighbourCode, ",")); // Split string into Array
if ((x - xx == 1) && (y - yy == 1)) { //top left //1. Check which neighbour you are (if, else if, else if...)
if (cellsBuffer[xx][yy] == 1){
codeArray[0] = 1; //2. if alive, set codeArray at corresponding index to 1
}
}
else if ((x - xx == 0) && (y - yy == 1)) { //top mid
if (cellsBuffer[xx][yy] == 1){
codeArray[1] = 1;
}
}
else if ((x - xx == -1) && (y - yy == 1)) { //top right
if (cellsBuffer[xx][yy] == 1){
codeArray[2] = 1;
}
}
else if ((x - xx == 1) && (y - yy == 0)) { //mid left
if (cellsBuffer[xx][yy] == 1){
codeArray[3] = 1;
}
}
else if ((x - xx == -1) && (y - yy == 0)) { //mid right
if (cellsBuffer[xx][yy] == 1){
codeArray[4] = 1;
}
}
else if ((x - xx == 1) && (y - yy == -1)) { //bot left
if (cellsBuffer[xx][yy] == 1){
codeArray[5] = 1;
}
}
else if ((x - xx == 0) && (y - yy == -1)) { //bot mid
if (cellsBuffer[xx][yy] == 1){
codeArray[6] = 1;
}
}
else if ((x - xx == -1) && (y - yy == -1)) { //bot right
if (cellsBuffer[xx][yy] == 1){
codeArray[7] = 1;
}
} // finished making codeArray
for (int n = 0; n < codeArray.length; n++) { // copy codeArray to public Array
neighbourhood[n] = codeArray[n];
}
int neighbourCount = 0;
for (int neighbourIndex = 0; neighbourIndex < codeArray.length; neighbourIndex++) { // Converting neighbourCode to neighbourCount
if (codeArray[neighbourIndex] == 1) {
neighbourCount = neighbourCount + 1;
}
} // end of Conversion
counterLocal = neighbourCount; // copy neighbourCount to public int
//} // end of self check (not needed)
} // end of out of Bounds check
} // end of for (yy++) loop
} // end of for (xx++) loop
if (cellsBuffer[x][y] == 1) { // applying ruleset for the game of life and writing to cells Array
if (counterLocal < 2 || counterLocal > 3) {
cells [x][y] = 0;
}
} else {
if (counterLocal == 3) {
cells[x][y] = 1;
}
}
} // end of for (y++) loop
} // end of for (x++) loop
} //end of iteration ()
If you're still reading this, you are my hero.
Answers
To make clear what I want: Check neighbourhood of each cell and record them, so their "location data" (relative to current cell) gets preserved until I am done applying the rules.
Right now I am trying to save them in codeArray[]
I calculate the index at which I save the neighbour in codeArray by comparing x and y of the current cell to xx and yy (those are the coordinates of the neighbour I am currently looking at).
So for a cell which has the 4 neighbours in the cardinal directions alive and every other is dead I would hope the array looks like this: {0,1,0,1,1,0,1,0} Also this was heavily inspired by: https://processing.org/examples/gameoflife.html
I haven't reviewed this code yet, but a common error in GoL implementations is to alter the matrix as you read it. You can't do that. You need to take one pass and calculate all the neighbor counts, then a second pass and update the matrix. Are you already doing this, or are you editing cells while you are reading / updating your cellBuffer? That's a first guess at a problem to check for.
Have not programmed much last month and a half but this idea sparked my interest to get back into it.
From the PM's it sounds like what you're trying to do could also be described as an "elementary CA", but with 9 bits and in 2D rather than just the 3 bits ontop?
This is a bit crude and might contain misstakes but a start
EDIT: changed code as I found the rule corresponding to game of life
@jeremydouglass
Ok let me work through this out loud to understand it myself:
Copy cells[][] to cellsBuffer[][] (lines 63ff)
Loop through cellsBuffer[][]
for each cell in Buffer: (lines 72 - 80)
Saving my cells[][] is the only time I write to cellsBuffer[][], so I am never reading and writing to the same array in the same step. I strongly suspect the problem is with the actual counting of the neighbours and/or the int array I try to save them to... (lines 75+83ff)
Thanks for the suggestion
@prince_polka
Thank you so much for taking your time, there's a lot in there to check out. I've had trouble with the pixels array on my first try, so I started over and replaced it with my own 2D array, after watching The Coding Train videos on CA...
Also: you can do return in a void method? (your code line39) Or only in setup?
no problem,
A method has to return whatever type it starts with, void means nothing and thus can't return anything.
made a sketch to find the rule for game of life for the 9-bit automata above
turns out to be
95269658970504075026401947768164943776577911284651056525821775275694548745963441068740035536685992072438984633721408802547302109256447217920
@prince_polka That's nice, but I think you are skipping every other frame, even though you're doing the calculations correctly, from just looking at the pictures.
To be honest, your calculations are a bit beyond me.. I sort of understand how you get neighbours positions with the booleans. I don't quite get
I know you're converting from boolean to int, but not how the state interacts with the BigInteger
(oh and a small thing: int swap,xyz=200; is that the same as int swap=200; int xyz=200; ?
I haven't noticed the skipping and I'm aware my code is cryptic.
the one i call swap I at least intended to swap back and forth each frame between 0-1 to get a ping pong buffering kind of thing going.
xyz is the dimensions
one thing I'm wondering is if you have any ideas for an interface to enter alternative rules into the system?
Ah it's starting to make sense now. At least I got a lot of material now to continue my google adventures. No need to apologize for cryptic code, have you looked at my hot mess?
Still have too many ideas for interfaces, I'll have to familiarize myself with libraries first I think, making buttons and stuff seems like a hassle. One thing I'm sure I want is a feature to make a random ruleset with a variable ratio of (configurations that result in death)/(configurations that result in life) Quickly swap out rulesets maybe
I'm also interested in comparing different methods of doing the same thing; so your code really helps a lot, thanks. wrapping my head around arrays, lists, bit manipulation.. honestly I'm just having fun with it.
@HolyMoley -- yes, from your description it sounds like you are using the buffer correctly. My first guess was probably wrong.
These are the lines that jump out at me:
...should that
else
be there inelse if
?Those elses make it look like you are only registering one neighbor per neighborhood, then skipping to the end of the conditions. You probably want to test each condition independently with a set of eight simple
if
blocks...?I fixed the else ifs to ifs and I finally figured it out... It would help if I don't make a new Array every time I go to the next neighbour. Took the initialization of the Array out of the xx,yy loop and it works now. What a silly mistake... This is my interation() now:
Why do you need the 3x3 array?
@koogs converting the 9 states of current cell in the 2D cells[][] Array, that define the neighbourhood, into a 1D codeArray[]. Compare codeArray to rules and update cells accordingly. I could make the rules use a 2D array as well, but then I'd have to convert down eventually..I think.
looking at the n'th bit, counted "backwards".
Maybe the rightshift is superfluos to do this,
rule.shiftRight(state).testBit(1)rule.testBit(state)havent tested it yetreturn rule.testBit(state+1);
worksI actuallt barelly looked at your actual code to be honest.
I think your original code is redundant because you run xx and yy over all neighbours but then you check with if which neighbour you are looking at.
I got rid of the for xx and for yy loop and put the neighbour check in a sub function
checkNeighbours
:so the rules are more than just a count?
my optimisation for this whole bit (original post)
would be to just to use a counter. set it to 0 outside the xx, yy loops and then the loop body becomes
that's it. no need for all those checks. the loops in lines 77 and 78 iterate over the neighbours in the correct order, you just need to remember the position. (this array also includes the cell being checked, whereas you omit it)
but if i was coding the classic game of life i'd do it completely differently - loop over all the current pixels and iff the pixel is set then increment all the neighbours in the array for the next step. much fewer comparisons, no overlaps. and you only have to process the black squares.
@koogs Yes! I was scratching my head for so long, thinking about how to convert the relative coordinates to an index, when I could've just counted them.
This will be perfect for the new and clean project, where I use objects and make things pretty. Thanks a lot!
implemented interface to change rules
EDIT: and grayscale