diff --git a/Sd1/P/life/V3/src/main/java/Cell.java b/Sd1/P/life/V3/src/main/java/Cell.java index 5f52a138701b2e23556c68a8b100e7b36e30cc6b..ca34b0a0424d59370d0f9a3e98d4bdad44c016a5 100755 --- a/Sd1/P/life/V3/src/main/java/Cell.java +++ b/Sd1/P/life/V3/src/main/java/Cell.java @@ -1,133 +1,88 @@ 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); - } + } } diff --git a/Sd1/P/life/V3/src/main/java/CellState.java b/Sd1/P/life/V3/src/main/java/CellState.java index c22addcf2ae7b375b1faa6b9f65bcb614ddc89ac..0f2f0fd779c41973b582e25e7f1d29842d6225d1 100644 --- a/Sd1/P/life/V3/src/main/java/CellState.java +++ b/Sd1/P/life/V3/src/main/java/CellState.java @@ -1,46 +1,29 @@ -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; - } } diff --git a/Sd1/P/life/V3/src/main/java/LifeWorld.java b/Sd1/P/life/V3/src/main/java/LifeWorld.java index e5a43eb9c63fd1fa0b45725c623efbae7b0be533..09664f4f3a9126df1dec74b4cdf54e8af0b75ba7 100755 --- a/Sd1/P/life/V3/src/main/java/LifeWorld.java +++ b/Sd1/P/life/V3/src/main/java/LifeWorld.java @@ -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; + } }