Follow-up to: Game Of Life implemented with for-loops and a boolean-array
As proposed by palacsint I went on and rewrote my implementation of the Game Of Life and extracted everything into two classes:
PetriDish
- The parent which holds the array of cells
- Sets the array up and initializes each cell
- Starts the evolution
Cell
- Stores it's current value
- Holds references to it's neighbors
The main cell array is still one-dimensional because I find it convenient to access all cells easily with a simple foreach loop. The outermost cells are a dead border and never change there state, mostly to ease handling of the array.
It works like this:
- Initiate the PetriDish
- Set the array
- Add cells into the array
- Initialize all cells
- The cell fetches it's neighbors from the parent PetriDish, except if it is a border-cell, then it does nothing.
- The PetriDish kicks off the evolution
- The cell checks it's neighbors via two static methods,
countAliveNeighbors
andcheckSurvival
- It stores that value in a second variable
- The cell checks it's neighbors via two static methods,
- The PetriDish ends the evolution
- The cell overrides it's old value with the new one from the above cycle
This has a few ugly quirks:
- Handling of the border-cells feels a little bit odd, but while I think about it I could remove them completely
- It's necessary to save the new value of the cell in another variable, there for makes the evolution a two-step process
Okay, enough talk, here's the code:
public class PetriDish {
private Cell[] cells;
private int width;
private int height;
private long generation;
private long duration;
public Cell[] getCells() {
return cells;
}
public Cell getCell(int x, int y) {
return cells[y * width + x];
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public long getGeneration() {
return generation;
}
public long getDuration() {
return duration;
}
public PetriDish(int width, int height) {
this.width = width;
this.height = height;
this.cells = new Cell[width * height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
cells[y * width + x] = new Cell(this, x, y,
x == 0 || y == 0 || x == width - 1 || y == height - 1);
}
}
for (Cell cell : cells) {
cell.prepare();
}
}
public void doEvolution() {
long start = System.currentTimeMillis();
for (Cell cell : cells) {
cell.startEvolution();
}
for (Cell cell : cells) {
cell.finishEvolution();
}
generation++;
duration = System.currentTimeMillis() - start;
}
}
public class Cell {
private PetriDish parent;
private int x;
private int y;
private boolean value;
private boolean nextValue;
private boolean isBorderCell;
private List<Cell> neighbors;
public boolean getValue() {
return value;
}
public void setValue(boolean value) {
if (!isBorderCell) {
this.value = value;
}
}
public Cell(PetriDish parent, int x, int y, boolean isBorderCell) {
this.parent = parent;
this.x = x;
this.y = y;
this.isBorderCell = isBorderCell;
this.neighbors = new ArrayList<Cell>();
}
public void prepare() {
if (!isBorderCell) {
for (int neighborX = x - 1; neighborX <= x + 1; neighborX++) {
for (int neighborY = y - 1; neighborY <= y + 1; neighborY++) {
if (neighborX != x || neighborY != y) {
neighbors.add(parent.getCell(neighborX, neighborY));
}
}
}
}
}
public void startEvolution() {
nextValue = checkSurvival(value, countAliveNeighbors(neighbors));
}
public void finishEvolution() {
value = nextValue;
}
private static int countAliveNeighbors(Iterable<Cell> neighbors) {
int neighborsAlive = 0;
for (Cell neighbor : neighbors) {
if (neighbor.value) {
neighborsAlive++;
}
}
return neighborsAlive;
}
private static boolean checkSurvival(boolean isAlive, int neighborsAlive) {
switch (neighborsAlive) {
case 0:
case 1:
return false;
case 2:
return isAlive;
case 3:
return true;
default:
return false;
}
}
}
The project can be found at GitHub and needs Slick for visualization and input.