This assignment will lead you into
the world of so called “Cellular Automata”. It's a big word for a small idea,
which, however is extremely powerful. I will not give a general introduction, but
a specific example – wireworld. The most famous example for cellular automata
is
This assignment will show the
benefits of combining different data structures, here combining an array with a
list. We store the data in both data structures, and, depending on the task, we
access the more appropriate data structure.
THIS IS A BONUS ASSIGNMENT. IT HAS
ITS CHALLENGING MOMENTS.
Introduction to Cellular Automata
and Wireworld:
Imagine a grid of cells (or call
it a 2 dimensional array, a matrix, a bunch of boxes arranged in a rectangle).
Each of the cells can be in a so called state (or call it “can have a value
from a set of values”, “can have a color from a set of colors”). In the
following picture, The states are represented by colors. In wireworld, there
are 4 possible states for each cell, which I call “background” (black), “head”
(red), “tail” (blue), “wire” (white). Wire World is a simulation of an electric
board, hence this image might remind you of an electric board with wires:
Again, we have single cells on a
grid, each cell can have a state. The number of states is limited (here: 4
states). We are already close to the definition of cellular automata. The only
thing missing is a set of rules to change the state of cells. In cellular
automata, you create “generations of cell-grids”, the transition from one
generation to the next is based on certain rules. These rules govern the change
of state of a single cell, depending on the states of neighboring cells.
Wire World has an impressively
simple set of rules:
·
if a cell is in state 0
(background), it will stay in state 0 (background)
·
if a cell is in state 1 (head), it
will change to state 2 (tail)
·
if a cell is in state 2 (tail), it
will change to state 3 (wire)
·
the interesting one: if a cell is
in state 3 (wire), it will change to head if there are 1 or 2 of the 8
neighbors in state “head”, otherwise it will change stay in state wire.
So the cellular automaton “Wire
World” does the following:
Look at a 2-dimensional array.
This is called the n-th generation. Create a new 2D array, the (n+1)
generation. For every cell in of the n-th generation, apply the rules above,
and store the result in the corresponding cell in the (n+1) generation. After
this, display the new generation, and proceed from generation (n+1) to (n+2)
and so on. Here are some images from Wire Word that will give you an idea of
what's happening (left to right: generation 0, generation1, generation 2,
generation 3).
If you look at this figure, it
seems that the read head and the blue tail are moving (and splitting). This is
an interesting illusion. What happened from generation 0 to generation 1 is the
following: The blue cell turned white (according to the rule “tale becomes
wire”), the red cell turned blue (according to the rule “head becomes tail”),
and the white cell right of the red one turned red (according to the rule “wire
with one or two heads as neighbors becomes head”). In short, only single cells
were switched, simulating a movement on the grid along the wire.
Note that you have an impression
of something complex going on, involving the entire board. However, the rules are
all defined for single cells. This is the nature of cellular automata. There is
no central force ruling all, but the cells themselves act depending on a small
neighborhood, causing an entire system to be able to behave in a complex way.
Compare this to schools of fish, swarms of birds etc. ! (<= not part of the
programming assignment.)
To give you an even better idea of
wireworld, here is an executable java file, that you can start and play around
with. Unfortunately, I will only give you the .jar --- since that's the program
you should write (at least parts of it) ! Please play around with this program.
You can get more information about Wire World in the internet, google it
(that’s part of the assignment).
Click here to play with Wire World.
Here is your task: Program Wire
World. You will be given a graphical grid-input editor. You have to program the
actual logic of the game, and an output grid that shows the iterated
generations.
You only need to program a simple
GUI, which, once the logic has started to work, shows the output. No return to
the editor has to be provided (in contrast, the .jar file above does provide
such a return to the editor, which demands for a slightly more difficult code).
Don't panic, you will be given
some support. This is a project, which might look big at first, but if you
split up the programming task into small pieces, you will see that it's
entirely doable.
Multiple Data Structures
Wire world
basically iterates through a 2D array, looks at 8 neighbors of each cell, and
determines a new value for the cell. However, background cells, which are the
most frequent ones, do never change. That means, going through the array,
there's a lot of idling: "if cell == 0 go to the next". In average,
this idling comes into play for 80% of the cells. We can increase the
performance by storing the indices (row, column) of non-background cells in a
JAVA LinkedList. Traversing this list reaches the non-background cells only,
having the data still stored in an array makes it easy to look at their
neighbors (question: if we would NOT keep the array, but only use the linked
list, what would be the order of magnitude of the program then? Would we gain
speed?).
Using the list
together with an array does not change the order of magnitude of the algorithm
(it's always O(n)), but reduces the constant. The program will be, in average,
5 times faster (20% of non-background cells). This is important if you have
really big grids (10000x10000) and you want a real-time simulation.
To
make this program appear as simple as it actually is, let’s break it down into
tasks. These tasks will also define a basic skeleton that suggest classes,
methods etc.
(A)
For this, create a new Netbeans
Project, and create a CellArray class that first has to provide the basic data
structure: a 2D array (please call it currentGeneration). Size it reasonably,
e.g. 64x64
(B) Import the editor jar-file. The TA will tell you how. After
that is done, you can just call a method which will (1) open the graphical
editor, and (2) on exit, will copy its edited 2D array into the output array
that is provided by you.
(C) Write the GUI:
a.
Extend your class from JPanel
b. Create a frame
c.
Add yourself to the frame
d.
Override the paintComponent(…)
method. The paintComponent method has to paint the grid. In order to do so,
traverse the 2D array, look which state each cell has (0,1,2,3), define a color
accordingly, and draw a filled rectangle at the corresponding position.
Example: let’s say your loop is at a position row=13, column = 27, and your
graphical squares have an edge-length of SIZE pixels. Check what’s the state of
myArray[13][27]. Set the drawing color accordingly (…if state=… the
g.setColor(…) …). The graphical coordinates of the box depend on the index of the
cell you are examining. The coordinates are: x = column*size, here 27*SIZE; y =
row*SIZE, here 13*size.
e.
TEST your code so far: call the
input editor, edit some input grid, click “DONE”, and draw your own board. The
board should show exactly the edited array, of course!
(D)
You are half way through already,
would you believe that? Go and get a coffee.
(E) Now implement the game logic. In order to do so, you have to
traverse through the grid, look at the states of the cells (you did the same for
the graphics already, you might want to copy some lines from there), and
implement the rules based on the state of each cell. You have to be careful,
there’s one tricky thing here: the new generation MUST be a new array, you can
not copy the changes into the original array. So: create a “nextGeneration” 2D
array. Traverse through the currentGeneration array, apply the rules, store the
result in the newGeneration array. Then, when that’s done, make the
currentGeneration array the newGeneration array (currentGeneration =
newGeneration, i.e. re-reference. Do NOT copy. Too slow!). Visualize,
re-compute the next generation and YOU ARE DONE. Yes, it is that simple. As
usual, there are some little details, so let’s get down to them:
a.
The newGeneration array has to be
of the same size as the currentGeneration array.
b. Since in the wire-rule, you have to check for neighbors, you
do not want to traverse the entire currentArray, but leave a margin of one cell
to all sides. That prevents out of boundary errors. So your loop should be from
1 to NUMBEROFCOLUMNS-2 for columns, and from 1 to NUMBEROFROWS for rows. Please
notice, it’s from 1, not from 0!
c.
First implement the simple rules,
which are the background, the head and the tail rule. There’s no neighborhood
check. It’s a simple “if tail in currentGeneration Array, then make it wire in
nextGeneration array.” Etc.
d.
Test your logic with this subset
of rules. The next generation should look like the previous one, yet with
“dying” heads and tails. They won’t move, since the “wire” rule is still
missing.
e.
Implement the “wire” rule. For
this one, you have to check the neighbors of your current cell. Write a simple
method “public int checkNeighbors(int column, int row), that takes as input the
indices of the currently examined cell, and check the neighbors, which are the
cells with index: [column-1][row-1] for top left neighbor, [column][row-1] for
top neighbor etc. You have to check 8 neighbors. Check if they are “head”. If
so, increase a counter (which at the beginning of your method is initialized to
0). When you are done with 8 neighbors, return the counter value.
f.
Test your program again. Now that
all is implemented, it should behave like the example .jar file.
(F) So far, you have implemented the game based on an array only.
Now extend it to use an additional linked list: when you read in the array from
the editor, create a linked list of the indices of foreground cells. Change the
nested for loop to an iteration through this list.
(G)
You are done. Be proud.
Here
is the jar file for the editor: WireWorld.jar
How
to use it: copy wireworld.jar to your project folder. In netbeans, right click
on your projects, select 'properties', select 'libraries'. That opens a window
to import libraries. Press the button "add Jar file". Select the
'wireworld.jar' file.
After
you did these steps, you have to add " import wireworld.*; " in your
code. The class wireworld.jar contains a class "BoardInput", which is
the input-editor. Just instantiate an object of this class, passing your 2D
array to its constructor (BoardInput b = new BoardInput(board)). That will show
the editor. When the editor finishes, its result is stored in the array
"board" that you passed to it.
This
is a BONUS assignment. You are only allowed to work on it, if you finished the
Shape Abstraction assignment! It is NOT a replacement assignment for the shape
abstraction. In short: if you do NOT hand in the shape abstraction, this
assignment is invalid!
Deadline is Sunday, 2/26.
Points: implementation of wire
world with array only: 5 points. Using the list: 10 points.
Good luck!