Skip to content

Commit

Permalink
Added function explanations for grid, cell, and related classes
Browse files Browse the repository at this point in the history
  • Loading branch information
LuuKhang committed Mar 9, 2017
1 parent 2a99026 commit d96be23
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package com.wakaleo.gameoflife.domain;

/**
* Wakaleo Consulting - John Ferguson Smart
* Game of life, demonstration application for Jenkins: The Definitive Guide
*
* Cell.java
* Cell object class, contains information on individual cells (positions) in the grid
*/
public enum Cell {
// Symbols to represent cell status
LIVE_CELL("*"), DEAD_CELL(".");

private String symbol;
Expand All @@ -9,11 +17,14 @@ private Cell(final String initialSymbol) {
this.symbol = initialSymbol;
}

// Override function for easy printing of cell's symbol
// Functions identically to default getter ( getSymbol() )
@Override
public String toString() {
return symbol;
}

// Function used for creating a cell given a string
static Cell fromSymbol(final String symbol) {
Cell cellRepresentedBySymbol = null;
for (Cell cell : Cell.values()) {
Expand All @@ -25,6 +36,7 @@ static Cell fromSymbol(final String symbol) {
return cellRepresentedBySymbol;
}

// Default getter
public String getSymbol() {
return symbol;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,73 @@
import static com.wakaleo.gameoflife.domain.Cell.DEAD_CELL;
import static com.wakaleo.gameoflife.domain.Cell.LIVE_CELL;

/**
* Wakaleo Consulting - John Ferguson Smart
* Game of life, demonstration application for Jenkins: The Definitive Guide
*
* Grid.java
* Grid object class, containing information on a collection of cells
*/
public class Grid {

private static final int DEFAULT_ROW_COUNT = 3;
private static final int DEFAULT_COLUMN_COUNT = 3;

private Cell[][] cells;


// Helper classes with functions to access cell information
private GridReader gridReader = new GridReader();
private GridWriter gridWriter = new GridWriter();

public Grid(final String gridContents) {
this.cells = makeCellArrayFrom(gridContents);
}

// Default constructor, called on "NEW GAME" button click
public Grid() {
this.cells = anArrayOfDeadCells(DEFAULT_ROW_COUNT,
DEFAULT_COLUMN_COUNT);
this.cells = anArrayOfDeadCells(DEFAULT_ROW_COUNT, DEFAULT_COLUMN_COUNT);
}

// Create blank grid of given size, called on "Go" button click
public Grid(final int rows, final int columns) {
this.cells = anArrayOfDeadCells(rows, columns);
}
// Create grid given cell layout, called on "Next Generation" button click
public Grid(final String gridContents) {
this.cells = makeCellArrayFrom(gridContents);
}

// Convert input string of symbols into 2D array of cell objects
private Cell[][] makeCellArrayFrom(final String gridContents) {
return gridReader.loadFrom(gridContents);
}

// Helper function, populates grid with dead cells
private Cell[][] anArrayOfDeadCells(final int rows, final int columns) {
Cell[][] deadCells = new Cell[rows][columns];
Cell[][] deadCells = new Cell[rows][columns]; // First create an empty 2D array of cells
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
deadCells[i][j] = DEAD_CELL;
deadCells[i][j] = DEAD_CELL; // Then set each one's status to DEAD
}
}
return deadCells;
}

private Cell[][] makeCellArrayFrom(final String gridContents) {
return gridReader.loadFrom(gridContents);
}

// Override function for easy printing of entire grids
@Override
public String toString() {
return gridWriter.convertToString(cells);
}

// Calculates the number of neighbours with LIVE status, called when creating the game-of-life's next step
public int getLiveNeighboursAt(final int x, final int y) {
int liveNeighbourCount = 0;
// "neighbouring cells" are positions adjacent horizontally, vertically, and diagonally
for (int xPosition = x - 1; xPosition <= x + 1; xPosition++) {
for (int yPosition = y - 1; yPosition <= y + 1; yPosition++) {
if (!cellIsCentralCell(xPosition, yPosition, x, y)) {
liveNeighbourCount += countLiveNeighboursInCell(xPosition, yPosition);
if (!cellIsCentralCell(xPosition, yPosition, x, y)) { // Cell does not count itself as a neighbour
liveNeighbourCount += countLiveNeighboursInCell(xPosition, yPosition); // Increment counter if LIVE
}
}
}
return liveNeighbourCount;
}

// Helper function, returns 1 if the cell at the given coordinate is LIVE, else 0
private int countLiveNeighboursInCell(final int x, final int y) {
if (cellIsOutsideBorders(x, y)) {
return 0;
Expand All @@ -68,7 +80,7 @@ private int countLiveNeighboursInCell(final int x, final int y) {
return 0;
}
}

// Helper function, validates if given coordinate is within the grid size
private boolean cellIsOutsideBorders(final int x, final int y) {
return (y < 0 || y > getMaxRow()) || (x < 0 || x > getMaxColumn());
}
Expand All @@ -81,6 +93,9 @@ private int getMaxColumn() {
return cells[0].length - 1;
}

// Makes sure cell does not count itself as a neighbour
// In a simple example, creating a new validation function may not be necessary
// However, if validation later becomes complex, a seperate function is good practice
private boolean cellIsCentralCell(final int x, final int y,
final int centerX, final int centerY) {
return (x == centerX) && (y == centerY);
Expand All @@ -102,6 +117,7 @@ public void setCellAt(final int x, final int y, final Cell cell) {
cells[y][x] = cell;
}

// Returns entire grid as 2D array of cell objects
public Cell[][] getContents() {
Cell[][] contentCopy = new Cell[getHeight()][getWidth()];
for (int row = 0; row < getHeight(); row++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,45 @@
import java.util.ArrayList;
import java.util.List;

/**
* Wakaleo Consulting - John Ferguson Smart
* Game of life, demonstration application for Jenkins: The Definitive Guide
*
* GridReader.java
* Class for converting inputted string of state symbols into array of cell objects
*/
public class GridReader {

private static final String NEW_LINE = System.getProperty("line.separator");

// Returns array of cell objects created from inputted string
public Cell[][] loadFrom(final String gridContents) {
List<Cell[]> rows = new ArrayList<Cell[]>();
String[] rowValues = splitIntoRows(gridContents);

String[] rowValues = splitIntoRows(gridContents); // First separate total content into different rows
for (String row : rowValues) {
Cell[] cellsInRow = splitIntoCells(row);
rows.add(cellsInRow);
Cell[] cellsInRow = splitIntoCells(row); // Then separate each row into different cells
rows.add(cellsInRow); // Save the cells of a row into a list
}

// Convert list into 2D array of new cell objects
return (Cell[][]) rows.toArray(new Cell[0][0]);
}

// Helper function, converts series of symbols into array of Cell objects
private Cell[] splitIntoCells(final String row) {
// TODO: ugly code
char[] cellSymbols = row.trim().toCharArray();
char[] cellSymbols = row.trim().toCharArray(); // First convert string into array of state symbols
List<Cell> cellsInRow = new ArrayList<Cell>();
for (char cellSymbol : cellSymbols) {
Cell cell = Cell.fromSymbol(Character.toString(cellSymbol));
for (char cellSymbol : cellSymbols) { // Then for each symbol
Cell cell = Cell.fromSymbol(Character.toString(cellSymbol)); // Create a new cell object
if (cell == null) {
throw new IllegalArgumentException();
}
cellsInRow.add(cell);
cellsInRow.add(cell); // And save it to return
}
return cellsInRow.toArray(new Cell[0]);
}

// Helper function, converts input grid into array of string symbols
private String[] splitIntoRows(final String gridContents) {
return gridContents.split(NEW_LINE);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
package com.wakaleo.gameoflife.domain;

/**
* Wakaleo Consulting - John Ferguson Smart
* Game of life, demonstration application for Jenkins: The Definitive Guide
*
* GridWriter.java
* Class for easy printing/showing of the grid contents
*/
public class GridWriter {

private static final String LINE_SEPARATOR = System.getProperty("line.separator");

// Functions returns the status of the cells in the grid as a string, for printing
public String convertToString(final Cell[][] gridContents) {
StringBuffer printedGrid = new StringBuffer();
for (Cell[] row : gridContents) {
for (Cell[] row : gridContents) {
for (Cell cell : row) {
printedGrid.append(cell.toString());
printedGrid.append(cell.toString()); // Save the cell's status for printing
}
// TODO: This simply masks the problem: why empty rows being passed?

// Add seperator to denote the next row
// Note: does not add seperator for empty rows
if (row.length > 0) {
printedGrid.append(LINE_SEPARATOR);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import static com.wakaleo.gameoflife.domain.Cell.LIVE_CELL;
import static com.wakaleo.gameoflife.domain.Cell.DEAD_CELL;

/**
/**
* Wakaleo Consulting - John Ferguson Smart
* Game of life, demonstration application for Jenkins: The Definitive Guide
*
* Universe.java
* A universe is a succession of grids over time.
* Each new grid is generated from the previous one using the rules of the Game Of Life.
*/
Expand All @@ -13,76 +17,84 @@ public class Universe {

private Grid currentGridContent;

// Default constructor, called on "NEW GAME" button click
public Universe() {
currentGridContent = new Grid();
}

// Create blank grid of given size, called on "Go" button click
public Universe(final int rows, final int columns) {
currentGridContent = new Grid(rows, columns);
}

// Create grid given cell layout, called on "Next Generation" button click
public Universe(final String initialGridContents) {
currentGridContent = new Grid(initialGridContents);
}

// Returns the input string, functions as the Universe class' print
public static String seededWith(final String gridContents) {
return gridContents;
}

// Calculates the game-of-life's next iteration, called on "Next Generation" button click
public void spawnsANewGeneration() {
createNextGeneration();
}

public void createNextGeneration() {

StringBuffer nextGenerationContent = new StringBuffer();

int maxRow = currentGridContent.getWidth();
int maxColumn = currentGridContent.getHeight();
// TODO: simplify this code
for (int y = 0; y < maxRow; y++) {
for (int x = 0; x < maxColumn; x++) {

// Iterates over entire grid
for (int y = 0; y < maxRow; y++) { // From top to bottom
for (int x = 0; x < maxColumn; x++) { // From left to right
Cell currentCell = currentGridContent.getCellAt(x, y);
int neighbourCount = currentGridContent.getLiveNeighboursAt(x, y);
int neighbourCount = currentGridContent.getLiveNeighboursAt(x, y); // getLiveNeighboursAt() defined in Grid.java
Cell nextCell = null;
if (currentCell == Cell.LIVE_CELL) {

// Based on the rules of game-of-life, calculate next state
if (currentCell == Cell.LIVE_CELL) { // If cell is currently LIVE
if ((neighbourCount == 2) || (neighbourCount == 3)) {
nextCell = LIVE_CELL;
nextCell = LIVE_CELL; // Stay LIVE if 2 or 3 neighbours are LIVE
} else {
nextCell = DEAD_CELL;
nextCell = DEAD_CELL; // Else, become DEAD due to underpopulation or overcrowding
}
} else {
} else { // If cell is currently DEAD
if (neighbourCount == 3) {
nextCell = LIVE_CELL;
nextCell = LIVE_CELL; // Become LIVE if 3 neighbours are LIVE
} else {
nextCell = DEAD_CELL;
nextCell = DEAD_CELL; // Else, stay DEAD
}
}
nextGenerationContent.append(nextCell);
}
nextGenerationContent.append(NEW_LINE);
}
nextGenerationContent.append(NEW_LINE);

// Sets the finalized grid of the game-of-life's next step
currentGridContent = new Grid(nextGenerationContent.toString());
}

// Return the status of every cell as a string
public String getGrid() {
return currentGridContent.toString();
}

// Return the cell objects in the grid as a 2D array
public Cell[][] getCells() {
return currentGridContent.getContents();
}

// Set cell at given coordinate to LIVE
public void setLiveCellAt(final int row, final int column) {
this.currentGridContent.setCellAt(column, row, LIVE_CELL);
}
// Set cell at given coordinate to DEAD
public void setDeadCellAt(final int row, final int column) {
this.currentGridContent.setCellAt(column, row, DEAD_CELL);
}

// Return the cell object at the given coordinate
public Cell getCellAt(final int row, final int column) {
return currentGridContent.getCellAt(column, row);
}

public void setDeadCellAt(final int row, final int column) {
this.currentGridContent.setCellAt(column, row, DEAD_CELL);
}
}

0 comments on commit d96be23

Please sign in to comment.