Conway’s Game of Life Object oriented implementation in Java

I have designed Conway’s Game of Life in Java, the solution follows Object Oriented design and paradigm, please review and let me know the feedback

Class Cell

Cell has one property alive and behavior to update alive status

public class Cell {
    private boolean alive;

    public Cell( boolean alive) {
        this.alive = alive;
    }

    public void updateStatus(int aliveNeighboursCount) {
        if (alive && aliveNeighboursCount > 3) {
            alive = false;
        } else if (alive && aliveNeighboursCount < 2) {
            alive = false;
        } else if (aliveNeighboursCount == 3 && !alive) {
            alive = true;
        }
    }

    public boolean isAlive() {
        return this.alive;
    }
}

Class Grid

getAliveNeighboursCount method finds the alive neighbor of every cell and returns the count

nextCycle creates new cells with their alive status based neighbours count and replaces the current cells of the Grid

public class Grid {
    private Cell[][] cells;

    public Grid(int size, Randomizer randomizer) {
        this.cells = randomizer.loadCells(size);
    }


    private boolean isOutOfBound(int maxSize, int i, int j) {
        return (i < 0 || i == maxSize) || (j < 0 || j == maxSize);
    }


    int getAliveNeighboursCount(int xPos, int yPos) {
        int aliveNeighboursCount = 0;
        for (int i = xPos - 1; i <= xPos + 1; i++) {
            for (int j = yPos - 1; j <= yPos + 1; j++) {
                if (isOutOfBound(cells.length, i, j) || (i == xPos && j == yPos)) {
                    continue;
                }
                aliveNeighboursCount += cells[i][j].isAlive() ? 1 : 0;
            }
        }
        return aliveNeighboursCount;
    }


    public void nextCycle() {
        Cell[][] newCells = new Cell[this.cells.length][this.cells[0].length];
        for (int i = 0; i < newCells.length; i++) {
            for (int j = 0; j < newCells[0].length; j++) {
                Cell cell = new Cell(false);
                cell.updateStatus(getAliveNeighboursCount(i, j));
                newCells[i][j] = cell;
            }
        }
        this.cells = newCells;
    }

    @Override
    public String toString() {
        StringBuilder gridString = new StringBuilder();
        for (int i = 0; i < cells.length; i++) {
            for (int j = 0; j < cells[0].length; j++) {
                gridString.append(cells[i][j].isAlive() ? "*\t" : "-\t");
            }
            gridString.append("\n");
        }
        return gridString.toString();
    }

}

Class Randomizer

Grid takes Randomizer object to randomize cells based on the biased random number

import java.util.Random;

public class Randomizer {

    private final int aliveCellsForEveryTenCell;

    public Randomizer(int aliveCellsForEveryTenCell) {
        this.aliveCellsForEveryTenCell = aliveCellsForEveryTenCell;
    }

    private boolean getNext() {
        return new Random().nextInt(10) <= aliveCellsForEveryTenCell;
    }

    public Cell[][] loadCells(int size) {
        Cell[][] cells = new Cell[size][size];
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < cells[0].length; j++) {
                cells[i][j] = new Cell( getNext());
            }
        }
        return cells;
    }
}

Class Action

This class is to execute the Code

public class Action {
    public static void main(String[] args) throws InterruptedException {
        Randomizer randomizer = new Randomizer(4);
        Grid grid = new Grid(25, randomizer);
        System.out.println(grid);

        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            grid.nextCycle();
            System.out.println(grid);
        }
    }
}

Answer

It’s hard to judge a design when there is no information about what you want from it. If you wanted classes and methods, well you succeeded, but that’s about it. But if you, for example, wanted extensibility and robustness, then it’s a whole different thing. You added the “interview-questions” tag. Can you tell us what your interviewer was looking for?

Grid

For me the Grid class is supposed to encapsulate and protect the array. That is broken in your design as the array is created by Randomizer. There is very little point in trying to verify the bounds of the array in Grid because Grid doesn’t even know the size of the array it has. There is no guarantee that Randomizer provides what it is asked to provide because a programmer can subclass Randomizer and return data that is not compatible to what Grid expects. You could add error checking to ensure the array returned by Randomizer has correct dimensions, but that’s “treating the symptoms, not the disease.” A better approach would be for Grid to initialize the array itself and ask Randomizer to place the live/dead value to the cells created by Grid using setter methods.

Randomizer

Conway’s game of life is about creating seeds that provide interesting generations. You should define the Randomizer as an interface, called something like Seeder, and make Grid accept the interface instead of some concrete implementation. Then you can implement different seeders, like your RandomSeeder.

Cell

It has no meaning outside the Grid class. It seems to only exist as a container for the game rules, and even the rules have been split between the Grid and the Cell (grid being responsible for counting the neighbors and cell making the decision based on that count). Maybe it exists only for the sake of having a class?

Attribution
Source : Link , Question Author : HariHaravelan , Answer Author : TorbenPutkonen

Leave a Comment