CS 161 - Intro to Computer Science
Lab 10: Conway’s Game of Life
Conway’s Game of Life is not just a fun curiosity (although it may have started as one). It has applications in biology (modeling evolutionary and life processes), physics (Modeling Reaction-Diffusion Systems), gaming, and artificial intelligence.
In this lab, you will implement Conway’s Game of Life, a classic example of a cellular automaton. The Game of Life simulates a grid-based “universe” of cells, where each cell can be either alive or inactive. The state of each cell evolves over time based on a simple set of rules that consider the states of neighboring cells. Despite its simplicity, the Game of Life can produce surprisingly complex and beautiful patterns, making it a powerful tool for exploring how complexity can emerge from simple rules.
Fun fact: The flooring in our CS student lounge inspired by “glider patterns” that emerge from playing Game of Life using hexagonal tiles instead of square tiles.

Student Outcomes
- Experience with 2D arrays
- Practice with nested loops for traversing 2D arrays
Required Files
The following file(s) have been provided for this project.
Part 1: Cell Class
The game is played on a square grid of cells. Each cell can either be alive or inactive, so this should be a very simple class to write.
-
Create a new class
Cell, which has a single instance variable that represents whether it is alive or inactive (abooleanought to work). -
Write two constructors:
-
The default constructor has a 50%-50% chance of setting the state to inactive or alive. The
Randomclass will again be useful here. It has a method callednextBoolean()that will randomly returntrueorfalsewith 50%-50% probability.Recall that, to use the random number generator, you must first import it at the top of your file:
1
import java.util.Random;
Then to use it, you need to instantiate a copy:
1 2
Random rng = new Random(); // now you can call rng.nextBoolean() to return a boolean value!
-
A second constructor should allow users to input the initial value for the Cell’s living state, instead of being randomly assigned.
-
Write a getter method named
state()that takes no inputs and returns the living state of yourCell. -
You can test your code from within the Code Pad:
1 2 3 4 5 6 7
Cell c = new Cell(); c.state() > false (boolean) <--- your result may vary given 50-50 probability Cell anotherCell = new Cell(true); anotherCell.state() > true (boolean)
-
After the Cell class has been implemented, the compile error in
Lifeshould disappear.
-
Part 2: Life Class
- Instance Variables: Because the Game of Life needs store a grid of Cell objects, you will need a 2D array of
Cells. You should name this instance variableboard. To declare a 2D array as an instance variable, you can use:1
private data-type[][] array-name;
- Constructor: Fill in the constructor, which inputs the height and width of the
board. Instantiate yourboardusing these row and column dimensions. Recall that, you can use the syntax:1
array-name = new data-type[number-of-rows][number-of-columns];
Now after you’ve instantiated the
board2D array, callfillSpaceShip(). Let’s test this out. Go back out to the project window, and instantiate aLifeFrameobject. Do you see the following “spaceships?”
-
Complete the
isAlive()method, which inputs therowandcolcoordinates of the board, and returns whether theCellat that location is alive. Now you need to be careful here.rowandcolcould be given illegal values and out of bounds of the boundaries of the board. Returnfalsewhen eitehrroworcolare out of bounds. Otherwise, access the cell at position[row][col]and return its living status. - Next, write the
fillRandom()method. This method should fill your board with randomCells(that is randomly alive or inactive). Hey, yournew Cell()default constructor already does that!- Now you need to go back to modify your constructor to call this method after it instantiates the empty 2D array.
- Right-click on
LifeFrameand instantiate a new object. In the window that appears, you should see the three spaceships, but now hit the"Random"button to create a random community of cells. You may hit the"Random"button repeatedly to test whetherfillRandom()is generating random cell communities. None of the other buttons should be working at this point. Close the window and move on to the next step.
Part 3: Counting Neighbors
We will now implement the methods that determine if a Cell should live or die. To do this, one of the key methods we need is a way to count the number of live neighbors for any given Cell at position row and col.
-
Complete the
countLivingNeighbors()method, which inputs aCell’s position at x and y, and returns the its number of living neighboringCellobjects. Previously, I had you simply return 0 in this method so that we could test the code. Remove that line of code. Now, for aCellobject located at locationboard[row][col], its 8 neighboringCellsare located at:row-1andcol-1row-1andcolrow-1andcol+1rowandcol-1rowandcol+1row+1andcol-1row+1andcolrow+1andcol+1
Recall that you can ask an individual Cell to see if it’s alive or not, by using the
isAlive()method that you wrote earlier. Therefore, you can write something like:1 2 3
if (isAlive(x-1, y-1)) { // we've got a live one to the north-west! }
- It’s important to test the
countLivingNeighbors()method, because everything from here on will rely on it. Right click on theTestNeighborsclass, and choose to runtestCountNeighbors(). Here’s what it does. It creates an internal Life board filled with the 3 spaceships. Then for each Cell at[row][col], it counts the number of neighbors and if there’s more than 0, it prints the count to the terminal. If yourcountLivingNeighbors()method is working properly, you should get the following results:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
Neighbors at [0][0]: 2 Neighbors at [0][1]: 1 Neighbors at [0][2]: 1 Neighbors at [1][0]: 3 Neighbors at [1][1]: 5 Neighbors at [1][2]: 3 Neighbors at [1][3]: 1 Neighbors at [2][0]: 2 Neighbors at [2][1]: 3 Neighbors at [2][2]: 1 Neighbors at [2][3]: 1 Neighbors at [3][0]: 2 Neighbors at [3][1]: 3 Neighbors at [3][2]: 2 Neighbors at [3][3]: 1 Neighbors at [10][11]: 1 Neighbors at [10][12]: 1 Neighbors at [10][13]: 1 Neighbors at [11][10]: 1 Neighbors at [11][11]: 2 Neighbors at [11][12]: 1 Neighbors at [11][13]: 1 Neighbors at [12][10]: 2 Neighbors at [12][11]: 3 Neighbors at [12][12]: 5 Neighbors at [12][13]: 3 Neighbors at [12][14]: 1 Neighbors at [13][10]: 2 Neighbors at [13][11]: 2 Neighbors at [13][12]: 3 Neighbors at [13][13]: 1 Neighbors at [13][14]: 1 Neighbors at [14][10]: 1 Neighbors at [14][11]: 2 Neighbors at [14][12]: 3 Neighbors at [14][13]: 2 Neighbors at [14][14]: 1 Neighbors at [21][22]: 1 Neighbors at [21][23]: 1 Neighbors at [21][24]: 1 Neighbors at [22][22]: 1 Neighbors at [22][23]: 1 Neighbors at [22][24]: 2 Neighbors at [23][21]: 1 Neighbors at [23][22]: 3 Neighbors at [23][23]: 5 Neighbors at [23][24]: 3 Neighbors at [24][21]: 1 Neighbors at [24][22]: 1 Neighbors at [24][23]: 3 Neighbors at [24][24]: 2
-
Compare your test output to mine, and if things don’t match up, there’s likely a bug somewhere in either
countLivingNeighbors()or insideisAlive(). -
Now complete the
nextGeneration()method that updates the state of theboardby:-
First, declare and instantiate a local 2D array of Cells. It should be the same dimensions as your current
boardinstance variable. You can call this temporary 2D arraynextGenBoard. -
Iterate (zig-zag) through all cells in your
boardarray: for each individual Cell atboard[row][col], call thecountLivingNeighbors()method on it to determine whether the cell should be living or inactive in the “next generation.” The rules are:- Any living cell with fewer than two living neighbors dies in the next generation (due to underpopulation).
- Any living cell with more than three living neighbors dies in the next generation (due to overcrowding).
- By inference, any living cell with exactly two or three living neighbors stays alive.
- Any inactive cell with exactly three living neighbors becomes alive in the next generation (slightly awkward reproduction).
-
Record the new living status of this Cell by updating its corresponding position in
nextGenBoard. To do this, you simply have to create anew Cell(...)assign it to thenextGenBoard[row][col]. -
When you’re done, and the whole
nextGenBoardhas been populated, simply re-assignboardto point tonextGenBoard. This replaces your board with the new one!
-
-
Okay, if you did this properly, everything should be working! Hit the
"Next"button to see a single generation (every time you hit the"Next"button, yournextGeneration()method is called.) You could also use the"Start"and"Stop"buttons to run through generations continuously to see your board evolve! Try running the game multiple times (or hitting"Random"to reset the board). Does everything die out? Or does it keep going for a long time? Does it eventually settle into a steady state? Or alternate between two closely related states? - Although it’s random, if your board stabilizes after only 4-5 generations, something is probably a bit off in your
nextGeneration()code. Our results consistently either never converges to a steady state, or takes dozens of generations to settle.
Part 4: Gliders, Spaceships, and Oscillators — Oh my!
There are known patterns that produce interesting results over time.
- Instead of calling
fillRandom(), write a another method calledfillMyPattern(), and only activate certain cells to your liking!- Instead of calling
fillRandom()inside the Life constructor, callfillMyPattern().
- Instead of calling
-
For instance, one class of patterns you might try are known as Gliders, perpetually move across the screen.
-
Explore other patterns, including spaceships, oscillators, and other gliders.
- Resizing the grid: You may want to make your board larger than 25x25 to really appreciate their lifecycle though!
- To do this, change
DEFAULT_SIZEconstant in the Life class to something large, say 200. - Then, in LifePanel, change the
BOARD_WIDTHandBOARD_HEIGHTinstance variables to 200 as well.
- To do this, change
- Changing the colors: For fun, you can also change the color of the cells inside
LifePanel. Look towards the bottom:1
g.setColor(Color.BLACK); // try Color.RED, Color.BLUE, Color.YELLOW, Color.MAGENTA, ...
Grading
1
2
3
This assignment will be graded out of 2 points, provided that:
- You were in attendance and on-time.
- Completed all required methods.
Submitting Your Assignment
Follow these instructions to submit your work. You may submit as often as you’d like before the deadline. I will grade the most recent copy.
-
Navigate to our course page on Canvas and click on the assignment to which you are submitting. Click on “Submit Assignment.”
-
Upload all files ending in
.javafrom your project folder. -
Click “Submit Assignment” again to upload it.
Credits
Written by David Chiu, Phil Howard, Joel Ross.
Lab Attendance Policies
Attendance is required for lab. Unexcused absence = no credit even if you turned in the lab. Unexcused tardiness = half credit.