Skip to content
Snippets Groups Projects
Commit 570b5834 authored by Goik Martin's avatar Goik Martin
Browse files

Reworked CellState, static images

parent 638abb16
No related branches found
No related tags found
No related merge requests found
import greenfoot.Actor;
import greenfoot.GreenfootImage;
/**
* Implementing a single cell. Each cell has its own state information
* and a reference to its surrounding neighbors,
* Implementing a single cell. Each cell has its own live or death state information
* and a reference to an array containing its surrounding neighbours,
*
*/
public class Cell extends Actor {
CellState state, previousState;
final GreenfootImage myBody;
final int myBodySize;
Cell[] neighbors;
CellState currentState, previousState;
Cell[] neighbours; // Will be initialized in by LifeWorld.getCellsNeighbours
/**
* Setting the current (C) cell's neighbors (N). A cell may have
* a maximum of eight , five or just three neighbors depending on its
* relative position to the world's boundary:
*
* ......... _________ _____
* ...NNN... ...NCN... |CN...
* ...NCN... ...NNN... |NN...
* ...NNN... ......... |.....
* .........
*
* @param neighbors The cell's set of neighbors
*/
public void setNeighbors(Cell[] neighbors) {
this.neighbors = neighbors;
}
/**
* Setting the current cell's array of neighbours.
*
* @param neighbors The cell's set of neighbours
*/
public void setNeighbors(Cell[] neighbors) {
this.neighbours = neighbors;
}
/**
* Create the cell body's image and initialize state.
*
* @param cellSize The cell's size in pixel units,
* @param intialState Indicating {@link CellState#ALIVE} or {@link CellState#DEAD}
*/
public Cell(int cellSize, CellState intialState) {
super();
state = previousState = intialState;
myBody = new GreenfootImage(cellSize, cellSize);
setImage(myBody);
myBodySize = cellSize - 2;
displayState();
}
/**
* Create the cell body's image and initialize state.
*
* @param cellSize The cell's size in pixel units,
* @param intialState Indicating {@link CellState#ALIVE} or {@link CellState#DEAD}
*/
public Cell(int cellSize, CellState intialState) {
super();
currentState = previousState = intialState;
setImage(currentState.image);
}
/**
* On first execution, find and save neighboring cell references. After that,
* apply cell rule and show cell state.
*/
public void act() {
applyRule();
displayState();
}
/**
* On first execution, find and save neighboring cell references. After that,
* apply cell rule and show cell state.
*/
public void act() {
applyRule();
setImage(currentState.image);
}
/**
* Save the previous state in preparation for the next time step.
*/
public void savePreviousState() {
previousState = state;
}
/**
* Save the previous state in preparation for the next time step.
*/
public void saveCurrentToPreviousState() {
previousState = currentState;
}
/**
* Set the state of the cell.
* @param state See {@link #getState()}}
*/
public void setState(final CellState state) {
this.state = state;
displayState();
}
/**
* Count neighbouring cells currently living.
*
* @return The number of living cells among the set of neighbors
* according to {@link #setNeighbors(Cell[])}
*/
public int getLivingNeighborCount() {
int sum = 0;
for (final Cell neighbor: neighbours) {
if (CellState.ALIVE == neighbor.previousState) {
sum ++;
}
}
return sum;
}
/**
* Get the state of the cell.
* @return Either {@link CellState#ALIVE} or {@link CellState#DEAD}.
*/
public CellState getState() {
return state;
}
/**
* Apply the Game of Life rules to the current cell's neighbouring state.
*/
public void applyRule() {
final int livingNeighborsCount = getLivingNeighborCount();
/**
* Flip the cell's state.
*/
public void flipState() {
setState(state.getInverseState());
}
switch(previousState) {
/**
* Count neighboring cells currently living.
*
* @return The number of living cells among the set of neighbors
* according to {@link #setNeighbors(Cell[])}
*/
public int getLivingNeighborCount() {
int sum = 0;
for (final Cell neighbor: neighbors) {
if (CellState.ALIVE == neighbor.previousState) {
sum ++;
}
}
return sum;
}
case ALIVE:
if (livingNeighborsCount < 2 || // Dying on account of under-population
3 < livingNeighborsCount) { // Dying on account of over-population
currentState = CellState.DEAD;
}
break;
/**
* Apply the Game of Life rules to the current cell situation.
*/
public void applyRule() {
final int livingNeighborsCount = getLivingNeighborCount();
if (CellState.ALIVE == previousState) { // Living Cell
if (livingNeighborsCount < 2 || // Dying on account of under-population
3 < livingNeighborsCount) { // Dying on account of overcrowding
state = CellState.DEAD;
} else { // two or three living neighbors
state = CellState.ALIVE;
case DEAD:
if (3 == livingNeighborsCount) { // Born by reproduction
currentState = CellState.ALIVE;
}
break;
}
} else { // cell is dead
if (3 == livingNeighborsCount) { // Born by reproduction
state = CellState.ALIVE;
} else { // remain dead
state = CellState.DEAD;
}
}
}
/**
* Draw the cell's body based on its state.
*/
private void displayState() {
myBody.setColor(state.color);
myBody.fillRect(1, 1, myBodySize + 1, myBodySize + 1);
}
}
}
import java.awt.Color;
import greenfoot.GreenfootImage;
/**
* A cell's state: Either alive or dead
*
*/
public enum CellState {
/**
* Cell is alive.
*/
ALIVE(Color.black, "Cell is in 'alive' state") ,
/**
* Cell is dead.
*/
DEAD(Color.white, "Cell is in 'dead' state");
/**
* The cell's color when rendered.
*/
public final Color color;
/** Cell is alive. */
ALIVE(LifeWorld.imageAlive, "Cell is in 'alive' state") ,
/** Cell is dead. */
DEAD(LifeWorld.imageDead, "Cell is in 'dead' state");
/** Image corresponding to the given state. */
public final GreenfootImage image;
private final String description;
private CellState inverseState;
static { // "Class" constructor. You may want to read the
ALIVE.inverseState = DEAD; // "Using Initialization Blocks" subsection within the
DEAD.inverseState = ALIVE; // "Initializing Data Members" section of Ivor Horton's
} // Java introduction.
CellState(final Color color, final String description) {
this.color = color;
this.description = description;
CellState(GreenfootImage image, final String description) {
this.image = image;
this.description = description;
}
@Override
public String toString() {
return description;
}
/**
* @return {@link #ALIVE} if current value is {@link #DEAD},
* {@link #DEAD} if current value is {@link #ALIVE}
*/
public CellState getInverseState() {
return inverseState;
}
}
......@@ -11,167 +11,188 @@ import java.awt.Color;
*/
public class LifeWorld extends World {
private static int initialLivingPercentage = 25;
private final GreenfootImage gridImage;
private static final int width = 50;
private static final int height = 50;
private static final int cellSize = 10;
private final Cell[] cells;
/**
* The probability for a newly born cell
* to be in 'living' ( See {@link Cell#setState(CellState)}) state.
*
* @param initialLivingPercentage A value from [0, 100] ranging from
* 'all cells dead' to 'all cells alive'
*/
public static void setInitialLivingPercentage(int initialLivingPercentage) {
LifeWorld.initialLivingPercentage = initialLivingPercentage;
}
/**
* Constructor for objects of class Grid. Calls methods to draw the grid
* lines, to create the array of randomly initialized cells .
*
*/
public LifeWorld() {
super(width, height, cellSize);
gridImage = getBackground();
drawGrid();
cells = createCells(); // Create an initialize array all cells.
// Having created all cells we may now determine each cell's neighbors.
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
final Cell centerCell = cells[getCellIndex(x, y)];
centerCell.setNeighbors(getNeighboringCells(x, y));
}
}
}
/**
* Tell each cell to save its current state in preparation of the game's
* next step. According to {@link World#act()} this happens prior to
* calling any actor's {@link Actor#act()} methods methods.
*/
public void act() {
for (final Cell c : cells) {
c.savePreviousState();
private static int initialLivingPercentage = 25;
private static final int
width = 50,
height = 50;
public static final int cellSize = 10; // Minimum of 3 pixel in width here yields a (cellSize-2)x(cellSize-2) pixel visible cell
static public GreenfootImage
imageAlive = new GreenfootImage(cellSize, cellSize)
,imageDead = new GreenfootImage(cellSize, cellSize);
// "Class" constructor initializing static fields. You may want to read Ivor Horton's
// "Using Initialization Blocks" / "Initializing Data Members" subsection.
static {
imageDead.setColor(Color.WHITE);
imageDead.fillRect(1, 1, cellSize - 1, cellSize - 1);
imageAlive.setColor(Color.black);
imageAlive.fillRect(1, 1, cellSize - 1, cellSize - 1);
}
}
/**
* Draw the cell grid's lines.
*/
private void drawGrid() {
gridImage.setColor(Color.red);
final int endX = cellSize * width,
endY = cellSize * height;
for (int x = 0; x < endX; x += cellSize) { // Drawing vertical grid lines. Diagram
gridImage.drawLine(x, 0, x, endY); // is being scaled by cellSize:
} //
// 0 1 2 ...(width - 1)
// 0 | | | ... |
// 1 | | | ... |
// 2 | | | ... |
// ... ... ... ...
// (height-1) | | | ... |
for (int y = 0; y < endY; y += cellSize) { // Drawing horizontal grid lines. Diagram
gridImage.drawLine(0, y, endX, y); // is being scaled by cellSize:
} //
// 0 1 2 ...(width - 1)
// 0 __________..._____
// 1 __________..._____
// 2 __________..._____
// .....................
// (height-1) __________..._____
}
/**
* Turn (x|y) coordinates into linear cell array index
* values ranging from 0 to (width * height - 1).
*
* Consider a simplified example of an array having width = 4 and
* height = 3:
*
* {(0|0) (1|0) (2|0) (3|0)} --> Linear array Index values {0, 1, 2, 3}
* {(0|1) (1|1) (2|1) (4|1)} --> Linear array Index values {4, 5, 6, 7}
* {(0|2) (1|2) (2|2) (4|2)} --> Linear array Index values {8, 9, 10, 11}
*
* @param x horizontal position in cell coordinates
* @param y vertical position in cell coordinates
* @return The linear array index.
*/
private int getCellIndex(int x, int y) {
return x + y * width;
}
/** Create the array of cells and randomly turn some of them on
* according to {@link #setInitialLivingPercentage(int)}.
*/
private Cell[] createCells() {
final Cell [] cellsToReturn = // Variable name has to be different from LifeWorld.cells
new Cell[width * height]; // in order to initialize final variable.
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
final Cell c = new Cell(cellSize, // Create a cell having a fixed probability
getRandomLifeOrDeadState()); // to be either dead or alive.
cellsToReturn[getCellIndex(x, y)] = c; // A separate array allows for easier access than via
// Greenfoot's own "world" object accounting mechanism.
addObject(c, x, y); // Add to Greenfoot world.
private final GreenfootImage gridImage;
private final Cell[] cells;
/**
* The probability for a newly born cell
* to be in 'living' ( See {@link Cell#setState(CellState)}) state.
*
* @param initialLivingPercentage A value from [0, 100] ranging from
* 'all cells dead' to 'all cells alive'
*/
public static void setInitialLivingPercentage(int initialLivingPercentage) {
LifeWorld.initialLivingPercentage = initialLivingPercentage;
}
/**
* Constructor for objects of class Grid. Calls methods to draw the grid
* lines, to create the array of randomly initialized cells .
*
*/
public LifeWorld() {
super(width, height, cellSize);
gridImage = getBackground();
drawGrid();
cells = createCells(); // Create an initialize array all cells.
// Having created all cells we may now determine each cell's neighbors.
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
final Cell centerCell = cells[getCellIndex(x, y)];
centerCell.setNeighbors(getCellsNeighbours(x, y));
}
}
}
/**
* Tell each cell to save its current state in preparation of the game's
* next step. According to {@link World#act()} this happens prior to
* calling any actor's {@link Actor#act()} methods methods.
*/
public void act() {
for (final Cell c : cells) {
c.saveCurrentToPreviousState();
}
}
return cellsToReturn; // Will be assigned to variable cells
}
/**
* Find the set of all neighbors (N) of a given cell (C). A cell may have
* a maximum of eight , five or just three neighbors depending on its
* relative position to the world's boundary:
*
* ......... _________ _____
* ...NNN... ...NCN... |CN...
* ...NCN... ...NNN... |NN...
* ...NNN... ......... |.....
* .........
*
* @param x The given cell's x-position
* @param y The given cell's y-position
* @return The array of all neighboring cells.
*/
private Cell[] getNeighboringCells(int x, int y) {
final int
xMin = Math.max(0, x - 1), // Limited by left border
xMax = Math.min(getWidth() - 1, x + 1), // Limited by right border
yMin = Math.max(0, y - 1), // Limited by top border
yMax = Math.min(getHeight() - 1, y + 1);// Limited by bottom border
final Cell[] neighbors = new Cell[(xMax - xMin + 1) * (yMax - yMin + 1) - 1];
int neighborIndexCount = 0;
for (int dx = xMin; dx <= xMax; dx++) {
for (int dy = yMin; dy <= yMax; dy++) {
if (dx != x || dy != y) {// Exclude the center cell itself
neighbors[neighborIndexCount++] = cells[getCellIndex(dx, dy)];
}
}
/**
* Draw the cell grid's lines.
*/
private void drawGrid() {
gridImage.setColor(Color.red);
final int endX = cellSize * width,
endY = cellSize * height;
for (int x = 0; x < endX; x += cellSize) { // Drawing vertical grid lines. Diagram
gridImage.drawLine(x, 0, x, endY); // is being scaled by cellSize:
} //
// 0 1 2 ...(width - 1)
// 0 | | | ... |
// 1 | | | ... |
// 2 | | | ... |
// ... ... ... ...
// (height-1) | | | ... |
for (int y = 0; y < endY; y += cellSize) { // Drawing horizontal grid lines. Diagram
gridImage.drawLine(0, y, endX, y); // is being scaled by cellSize:
} //
// 0 1 2 ...(width - 1)
// 0 __________..._____
// 1 __________..._____
// 2 __________..._____
// .....................
// (height-1) __________..._____
}
/**
* Turn (x|y) coordinates into linear cell array index
* values ranging from 0 to (width * height - 1).
*
* Consider a simplified example of an array having width = 4 and
* height = 3:
*
* {(0|0) (1|0) (2|0) (3|0)} --> Linear array Index values {0, 1, 2, 3}
* {(0|1) (1|1) (2|1) (4|1)} --> Linear array Index values {4, 5, 6, 7}
* {(0|2) (1|2) (2|2) (4|2)} --> Linear array Index values {8, 9, 10, 11}
*
* @param x horizontal position in cell coordinates
* @param y vertical position in cell coordinates
* @return The linear array index.
*/
private int getCellIndex(int x, int y) {
return x + y * width;
}
/** Create the array of cells and randomly turn some of them on
* according to {@link #setInitialLivingPercentage(int)}.
*/
private Cell[] createCells() {
final Cell [] cellsToReturn = // Variable name has to be different from LifeWorld.cells
new Cell[width * height]; // in order to initialize final variable.
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
final Cell c = new Cell(cellSize, // Create a cell having a fixed probability
getRandomLifeOrDeadState()); // to be either dead or alive.
cellsToReturn[getCellIndex(x, y)] = c; // A separate array allows for easier access than via
// Greenfoot's own "world" object accounting mechanism.
addObject(c, x, y); // Add to Greenfoot world.
}
}
}
return neighbors;
}
/** Randomly return the two states {@link CellState#ALIVE} and {@link CellState#DEAD}. The
* corresponding probabilities depend on the value of {@link #setInitialLivingPercentage(int)}.
*
* @return Either {@link CellState#ALIVE} or {@link CellState#DEAD}.
*/
public static CellState getRandomLifeOrDeadState() {
return Greenfoot.getRandomNumber(100) < initialLivingPercentage ?
CellState.ALIVE : CellState.DEAD;
}
return cellsToReturn; // Will be assigned to variable cells
}
/**
* Find the set of all neighbors (n) of a given cell (c). A cell may have
* a maximum of eight , five or just three neighbors depending on its
* relative position to the world's boundary:
*
* Middle: Edge: Corner:
* 8 Neigh- 6 Neigh- 3 Neigh-
* bours bours bours
* ......... _________ _____
* ...nnn... ...ncn... |cn... 3 other corner positions left / right / bottom
* ...ncn... ...nnn... |nn... and 3 more edge positions upper right /
* ...nnn... ......... |..... lower left / lower right omitted). So we have
* ......... ......... |..... 9 different world boundary related situations.
*
* @param x The given cell's x-position
* @param y The given cell's y-position
* @return The array of all neighboring cells.
*/
private Cell[] getCellsNeighbours(final int x, final int y) {
// Rectangular limits corresponding to the 9 different boundary
// related situations being described in the above Javadoc.
//
final int
xMin = Math.max( 0, x - 1), // Possibly limited by left border
xMax = Math.min( getWidth() - 1, x + 1), // Possibly limited by right border
yMin = Math.max(0 , y - 1), // Possibly limited by top border
yMax = Math.min(getHeight() - 1, y + 1); // Possibly limited by bottom border
final Cell[] neighbors = new Cell[(xMax - xMin + 1) * (yMax - yMin + 1) - 1];
int neighborIndexCount = 0;
for (int dx = xMin; dx <= xMax; dx++) {
for (int dy = yMin; dy <= yMax; dy++) {
if (dx != x || dy != y) {// Exclude center cell
neighbors[neighborIndexCount++] = cells[getCellIndex(dx, dy)];
}
}
}
return neighbors;
}
/** Randomly return the two states {@link CellState#ALIVE} and {@link CellState#DEAD}. The
* corresponding probabilities depend on the value of {@link #setInitialLivingPercentage(int)}.
*
* @return Either {@link CellState#ALIVE} or {@link CellState#DEAD}.
*/
public static CellState getRandomLifeOrDeadState() {
return Greenfoot.getRandomNumber(100) < initialLivingPercentage ?
CellState.ALIVE : CellState.DEAD;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment