In this practice we will create an implementation of John Conway’s Game of Life cellular automaton. We will use multi-threading to make it faster.
Download these classes:
A Point.java class for storing points on an infinite 2D plane.
A Main.java that contains the main function. It sets up a glider and displays its process.
Resources:
Exercises:
Create new classes GameOfLife
and GOLThread
. The latter extends Thread
. An object of GameOfLife
should use, and keep record of, a number of threads (GOLThread
).
Create the method setThreadCount
that sets the number of threads. This number is taken as parameter. First, it calls the method exiting
(see below) on each thread, and it waits for the threads to terminate using join
. The method exiting
signals the thread that it should terminate. Second, setThreadCount
starts the given number of threads.
Make sure that only one thread can execute setThreadCount
at a time using synchronization.
Add a method start
which sets the number of threads to one.
Add a field to the GOLThread
that acts as a work queue. It is a sequence Point
objects.
Create a method that adds a Point
to the queue.
Add flags exiting
and pointsProcessed
. Create methods which set these flags.
Add a reference to an object of GameOfLife
that created this GOLThread
. This should be received in the constructor.
Also, add the method getEnv
to GameOfLife
which returns the current environment.
Override the run
method. It should process points from the queue until the flag exiting
is set. When the queue is empty and exiting
is not set, it should sleep for 100ms. We will implement processing of a Point
later.
Create a class Environment
. It records living cells and how many surrounding cell each place has. That is, an environment is a two-dimensional space of cells and empty spaces.
Constructor of GameOfLife
should take an Environment
as argument.
Write the method next
that creates a new evironment. In the new environment, cells with 2 or 3 neighbour cells remain living, and at empty places with 3 neighbour cells a new living cell should be created.
Create a function step
in GameOfLife
. This should distribute points among the threads. After all living cell was put into one of the threads queues, it should notify each GOLThread
that the points have been processed.
Make sure that only one thread can execute step
at a time using synchronization.
Change method run
in GOLThread
. When all points are processed, it should notify its GameOfLife
creator. The method step
in GameOfLife
should only continue if all threads notified it (use the CountDownLatch
). After that step
can replace the environment with the result of calling next
on the current environment.
In Environment
, the number of surrounding cells of each place should be stored in AtomicInteger
s.
Create the method increment
that receives a Point
, and increments the number of surrounding cells associated to that Point
by one. Beware that increment
will be used by multiple threads at the same time. Multiple threads should not modify the same collection without proper locking mechanism!
The run
in GOLThread
should call the method increment
on each neighbour of a cell that was put in the queue.
Extra exercise. Make GameOfLife
AutoClosable
.