- 3.1k
- 12
- 23
Your program is actually NOT an implementation of Game of Life, because your universe is finite. The universe size is limited to ×ばつ20, which is very tiny. A good implementation of Game of Life would feature a Universe of size ×ばつ232 which is still finite but feels infinite for practical purposes. Think of a differendifferent 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
public void testBlinker() {
final Universe blinkerFrame1 = Universe.parse(".*\n.*\n.*");
final Universe blinkerFrame2 = Universe.pasreparse("...\n***");
assertEquals(blinkerFrame2, blinkerFrame1.iterate());
assertEquals(blinkerFrame1, blinkerFrame2.iterate());
}
Use braces as an opportunity to extract.
Links
Here's what I consider beautiful implementations of Game of Life:
Your program is actually NOT an implementation of Game of Life, because your universe is finite. The universe size is limited to ×ばつ20, which is very tiny. A good implementation of Game of Life would feature a Universe of size ×ばつ232 which is still finite but feels infinite for practical purposes. Think of a differen 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
public void testBlinker() {
final Universe blinkerFrame1 = Universe.parse(".*\n.*\n.*");
final Universe blinkerFrame2 = Universe.pasre("...\n***");
assertEquals(blinkerFrame2, blinkerFrame1.iterate());
assertEquals(blinkerFrame1, blinkerFrame2.iterate());
}
Use braces as an opportunity to extract.
Your program is actually NOT an implementation of Game of Life, because your universe is finite. The universe size is limited to ×ばつ20, which is very tiny. A good implementation of Game of Life would feature a Universe of size ×ばつ232 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
public void testBlinker() {
final Universe blinkerFrame1 = Universe.parse(".*\n.*\n.*");
final Universe blinkerFrame2 = Universe.parse("...\n***");
assertEquals(blinkerFrame2, blinkerFrame1.iterate());
assertEquals(blinkerFrame1, blinkerFrame2.iterate());
}
Use braces as an opportunity to extract.
Links
Here's what I consider beautiful implementations of Game of Life:
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, which is very tiny. A good implementation of Game of Life would feature a Universe of size ×ばつ232 which is still finite but feels infinite for practical purposes. Think of a differen 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 fields
You 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 enum
enum
s 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.pasre("...\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()
method
The 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.