patternjavaMinorCanonical
Game of Life (Java)
Viewed 0 times
lifegamejava
Problem
This is a simple implementation of Conway's Game of Life in Java. The algorithm I use is inefficient however. Every Cell's neighbors (alive or dead) are checked on every iteration.
```
import java.util.Scanner;
public class GameOfLife {
public final static int BOARD_HEIGHT = 20;
public static Cell[][] board = new Cell[BOARD_HEIGHT][BOARD_HEIGHT];
public static Cell[][] lastIteration = new Cell[BOARD_HEIGHT][BOARD_HEIGHT];
public static final int TIME_BETWEEN_ITERATIONS_MS = 250;
public static final char DEAD_CELL_SYMBOL = '□';
public static final char ALIVE_CELL_SYMBOL = '■';
private enum Cell {
DEAD, ALIVE
}
public static void printBoard(Cell[][] board) {
for (int i = 0; i = 0 && y + j >= 0 && x + i 3 && boardArg[x][y] == Cell.ALIVE)
return Cell.DEAD;
else if (boardArg[x][y] == Cell.DEAD && liveNeighbours == 3)
return Cell.ALIVE;
else
return Cell.DEAD;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < BOARD_HEIGHT; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) {
board[i][j] = Cell.DEAD;
}
}
Scanner reader = new Scanner(System.in);
int numberOfLiveCells = reader.nextInt();
for (int i = 0; i < numberOfLiveCells; i++) {
int x = reader.nextInt();
int y = reader.nextInt();
board[x][y] = Cell.ALIVE;
}
reader.close();
printBoard(board);
while (true) {
for (int i = 0; i < BOARD_HEIGHT; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) {
lastIteration[i][j] = board[i][j];
}
}
for (int i = 0; i < BOARD_HEIGHT; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) {
board[i][j] = changeCell(i, j, lastIteration);
}
}
pr
```
import java.util.Scanner;
public class GameOfLife {
public final static int BOARD_HEIGHT = 20;
public static Cell[][] board = new Cell[BOARD_HEIGHT][BOARD_HEIGHT];
public static Cell[][] lastIteration = new Cell[BOARD_HEIGHT][BOARD_HEIGHT];
public static final int TIME_BETWEEN_ITERATIONS_MS = 250;
public static final char DEAD_CELL_SYMBOL = '□';
public static final char ALIVE_CELL_SYMBOL = '■';
private enum Cell {
DEAD, ALIVE
}
public static void printBoard(Cell[][] board) {
for (int i = 0; i = 0 && y + j >= 0 && x + i 3 && boardArg[x][y] == Cell.ALIVE)
return Cell.DEAD;
else if (boardArg[x][y] == Cell.DEAD && liveNeighbours == 3)
return Cell.ALIVE;
else
return Cell.DEAD;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < BOARD_HEIGHT; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) {
board[i][j] = Cell.DEAD;
}
}
Scanner reader = new Scanner(System.in);
int numberOfLiveCells = reader.nextInt();
for (int i = 0; i < numberOfLiveCells; i++) {
int x = reader.nextInt();
int y = reader.nextInt();
board[x][y] = Cell.ALIVE;
}
reader.close();
printBoard(board);
while (true) {
for (int i = 0; i < BOARD_HEIGHT; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) {
lastIteration[i][j] = board[i][j];
}
}
for (int i = 0; i < BOARD_HEIGHT; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) {
board[i][j] = changeCell(i, j, lastIteration);
}
}
pr
Solution
Unicode - nice
I see you directly use Unicode. I like that.
Incorrect solution
Your program is actually NOT an implementation of Game of Life, because your universe is finite. The universe size is limited to 20×20, which is very tiny. A good implementation of Game of Life would feature a Universe of size 2³²×2³² which is still finite but feels infinite for practical purposes. Think of a different data structure. Instead of managing the entire space-time continuum of the Game of Life universe, manage only the matter. Stop caring about dead cells, care about life cells only.
Test
Your program lacks testing, which I can see from the next point.
Do not use
You have
Use the power of
In this case you could give
In general,
You could even create an abstract method in
Separate the construction of output from the actual output
Method
Consider separating these two things. Create one method which constructs the output, maybe using a
Use the same format for input as for output
Currently, you use coordinates for input and a Unicode-Art representation of the universe for output.
That means the program's output cannot be fed into the program as input again.
Imagine how a test could look like if the output can be fed back to the program as input.
JUnit Example
Gherkin / Cucumber Example
Long
The
The method which loops for the generations should then also be split.
Use braces as an opportunity to extract.
Links
Here's what I consider beautiful implementations of Game of Life:
I see you directly use Unicode. I like that.
Incorrect solution
Your program is actually NOT an implementation of Game of Life, because your universe is finite. The universe size is limited to 20×20, which is very tiny. A good implementation of Game of Life would feature a Universe of size 2³²×2³² which is still finite but feels infinite for practical purposes. Think of a different data structure. Instead of managing the entire space-time continuum of the Game of Life universe, manage only the matter. Stop caring about dead cells, care about life cells only.
Test
Your program lacks testing, which I can see from the next point.
Do not use
static for mutable fieldsYou have
static variables which are not final, which means that your program has only one shared mutable state. Holding mutable state is one of the things for which OO languages like Java provide objects.Use the power of
enumenums aren't mere enumarations of constants. In other languages they are. In Java, enums are special classes, which describe a predefined, finite, immutable set of objects (as opposed to a normal class which describes a set of objects which is not predefined, infinite and mutable).In this case you could give
enum Cell a toString() method which would return the cell symbol. Then you would not need a switch in printBoard().In general,
switch statements (and if statements that are switch statements in disguise) indicate a lack of OO-design. Often, they can be replaced with polymorphism.You could even create an abstract method in
Cell which generates the next cell based on the number of life neighbors and implemented it differently for alive and dead cells.Separate the construction of output from the actual output
Method
printBoard() is doing two things and thus difficult to test (and a bit inefficient):- It constructs the output.
- It prints the output.
Consider separating these two things. Create one method which constructs the output, maybe using a
StringBuilder. And the other method would only print the output. It will give you two benefits:- The program will be faster (which probably is no concern here).
- The program will be easier to test.
Use the same format for input as for output
Currently, you use coordinates for input and a Unicode-Art representation of the universe for output.
That means the program's output cannot be fed into the program as input again.
Imagine how a test could look like if the output can be fed back to the program as input.
JUnit Example
@Test
public void testBlinker() {
final Universe blinkerFrame1 = Universe.parse(".*\n.*\n.*");
final Universe blinkerFrame2 = Universe.parse("...\n***");
assertEquals(blinkerFrame2, blinkerFrame1.iterate());
assertEquals(blinkerFrame1, blinkerFrame2.iterate());
}Gherkin / Cucumber Example
Given the following Universe:
"""
.*.
.*.
.*.
"""
When iterating it once,
Then it MUST be equal to this:
"""
...
***
...
"""Long
main() methodThe
main() method is quite long. You could split it into a method that reads the Universe from the input, and a method which loops for the generations.The method which loops for the generations should then also be split.
Use braces as an opportunity to extract.
Links
Here's what I consider beautiful implementations of Game of Life:
- Java: https://github.com/nelkinda/gameoflife-java
- Kotlin: https://github.com/nelkinda/gameoflife-kotlin
Code Snippets
@Test
public void testBlinker() {
final Universe blinkerFrame1 = Universe.parse(".*\n.*\n.*");
final Universe blinkerFrame2 = Universe.parse("...\n***");
assertEquals(blinkerFrame2, blinkerFrame1.iterate());
assertEquals(blinkerFrame1, blinkerFrame2.iterate());
}Given the following Universe:
"""
.*.
.*.
.*.
"""
When iterating it once,
Then it MUST be equal to this:
"""
...
***
...
"""Context
StackExchange Code Review Q#151461, answer score: 6
Revisions (0)
No revisions yet.