diff --git a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Cell.java b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Cell.java index 47e1ca870c..ae8cd3cb3f 100644 --- a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Cell.java +++ b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Cell.java @@ -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; @@ -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()) { @@ -25,6 +36,7 @@ static Cell fromSymbol(final String symbol) { return cellRepresentedBySymbol; } + // Default getter public String getSymbol() { return symbol; } diff --git a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Grid.java b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Grid.java index 88f7a1f374..e0078ca087 100644 --- a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Grid.java +++ b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Grid.java @@ -3,6 +3,13 @@ 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; @@ -10,54 +17,59 @@ public class Grid { 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; @@ -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()); } @@ -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); @@ -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++) { diff --git a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/GridReader.java b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/GridReader.java index 6564bd80f6..240ef5dd07 100644 --- a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/GridReader.java +++ b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/GridReader.java @@ -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 rows = new ArrayList(); - 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 cellsInRow = new ArrayList(); - 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); } diff --git a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/GridWriter.java b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/GridWriter.java index 3691d0a734..2b7505d56f 100644 --- a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/GridWriter.java +++ b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/GridWriter.java @@ -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); } diff --git a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Universe.java b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Universe.java index 79affc236a..3928fff39d 100644 --- a/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Universe.java +++ b/gameoflife-core/src/main/java/com/wakaleo/gameoflife/domain/Universe.java @@ -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. */ @@ -13,49 +17,52 @@ 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); @@ -63,26 +70,31 @@ public void createNextGeneration() { 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); - } }