Write a Java program that implements Conway’s Game of Life, a simple cellular au
ID: 3762682 • Letter: W
Question
Write a Java program that implements Conway’s Game of Life, a simple cellular automaton. See for example:
http://www.bitstorm.org/gameoflife/
Our version has a 10 x 10 grid, numbered like this:
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
The grid is represented by a 10 x 10 2dimensional integer array. If the grid point (i, j) is “populated”, the array element [i][j] contains 1; otherwise it contains 0. Elements along the edges, i == 0 or 9, or j == 0 or 9, are always unpopulated.
When we display the grid, a populated cell is indicated by a ‘#’; an unpopulated cell is indicated by a space.
What your program should do:
• Prompt the user to enter a list of (i,j) pairs, both nonnegative integers (stop when a negative integer is read for either i or j)
Prompt the user to enter the number of time steps
Initialize the grid based on the (i,j) pairs entered by the user (set the
corresponding array elements to 1)
Display the initial state of the grid (call the displayGrid() method)
For each time step,
update the grid according to Conway’s rules (call the updateGrid() method)
display the grid (call the displayGrid() method)
We follow Conway’s standard rules for updating the cells, as follows. A “neighbor” is a vertically, horizontally, or diagonally adjacent cell that is populated. A cell has anywhere from 0 to 8 neighbors.
For a cell that is “populated”, if the cell has <= 1 neighbors, or >= 4 neighbors, it dies (becomes 0). Otherwise, it survives (remains 1).
For a cell that is not populated, if the cell has exactly 3 neighbors, it becomes populated (becomes 1).
Cells on the edge always remain unpopulated (0). The displayGrid() method has prototype:
It displays the borders of the grid (see sample runs below), and prints the 10 x 10 grid of cells. Populated cells are indicated with a ‘#’ sign, unpopulated cells with a space.
The updateGrid() method has prototype:
mat is the 2dimensional array that contains the current state of the grid. The method counts the neighbors in each cell of the grid, and updates the cells in the grid according to Conway’s rules.
You can obviously enter arbitrary initial conditions to test your program. Some fun examples adapted from the bitstorm.org site:
Glider: initial populated cells at (3, 4) (4, 2) (4, 4) (5, 3) (5, 4)
5 cell row: initial populated cells at (6,4) (6,5) (6,6) (6,7) (6,8)
Small explosion: initial populated cells at (4, 5) (4,6) (5,4) (5,5) (5, 7) (6, 5) (6,6)
Note that these will not behave exactly the same as the bitstorm.org display when the populated cells run into the boundary conditions.
Sample runs:
libra% java Life
Please enter list of (i,j) pairs for populated cells
(negative i or j to quit): 6 4 6 5 6 6 6 7 6 8 -1 -1
Enter number of time steps: 5
Initial grid:
0123456789
0
1
2
3
4
5
##### 6
7
8
9
Time step 1
0123456789
0
1
2
3
4
### 5
### 6
### 7
8
9
Time step 2
0123456789
0
1
2
3
# 4
# # 5
# # 6
# # 7
# 8
9
Time step 3
0123456789
0
1
2
3
# 4
### 5
## ## 6
### 7
# 8
9
Time step 4
0123456789
0
1
2
3
### 4
# # 5
# # 6
# # 7
### 8
9
Time step 5
0123456789
0
1
2
# 3
### 4
# # # 5
### ## 6
# # # 7
### 8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
Explanation / Answer
class Cell
{
public Point Position { get; private set; }
public Rectangle Bounds { get; private set; }
public bool IsAlive { get; set; }
public Cell(Point position)
{
Position = position;
Bounds = new Rectangle(Position.X * Game1.CellSize, Position.Y * Game1.CellSize, Game1.CellSize, Game1.CellSize);
IsAlive = false;
}
public void Update(MouseState mouseState)
{
if (Bounds.Contains(new Point(mouseState.X, mouseState.Y)))
{
// Make cells come alive with left-click, or kill them with right-click.
if (mouseState.LeftButton == ButtonState.Pressed)
IsAlive = true;
else if (mouseState.RightButton == ButtonState.Pressed)
IsAlive = false;
}
}
public void Draw(SpriteBatch spriteBatch)
{
if (IsAlive)
spriteBatch.Draw(Game1.Pixel, Bounds, Color.Black);
// Don't draw anything if it's dead, since the default background color is white.
}
}
class Grid
{
public Point Size { get; private set; }
private Cell[,] cells;
public Grid()
{
Size = new Point(Game1.CellsX, Game1.CellsY);
cells = new Cell[Size.X, Size.Y];
for (int i = 0; i < Size.X; i++)
for (int j = 0; j < Size.Y; j++)
cells[i, j] = new Cell(new Point(i, j));
}
public void Update(GameTime gameTime)
{
(...)
// Loop through every cell on the grid.
for (int i = 0; i < Size.X; i++)
{
for (int j = 0; j < Size.Y; j++)
{
// Check the cell's current state, and count its living neighbors.
bool living = cells[i, j].IsAlive;
int count = GetLivingNeighbors(i, j);
bool result = false;
// Apply the rules and set the next state.
if (living && count < 2)
result = false;
if (living && (count == 2 || count == 3))
result = true;
if (living && count > 3)
result = false;
if (!living && count == 3)
result = true;
cells[i, j].IsAlive = result;
}
}
}
(...)
}
public int GetLivingNeighbors(int x, int y)
{
int count = 0;
// Check cell on the right.
if (x != Size.X - 1)
if (cells[x + 1, y].IsAlive)
count++;
// Check cell on the bottom right.
if (x != Size.X - 1 && y != Size.Y - 1)
if (cells[x + 1, y + 1].IsAlive)
count++;
// Check cell on the bottom.
if (y != Size.Y - 1)
if (cells[x, y + 1].IsAlive)
count++;
// Check cell on the bottom left.
if (x != 0 && y != Size.Y - 1)
if (cells[x - 1, y + 1].IsAlive)
count++;
// Check cell on the left.
if (x != 0)
if (cells[x - 1, y].IsAlive)
count++;
// Check cell on the top left.
if (x != 0 && y != 0)
if (cells[x - 1, y - 1].IsAlive)
count++;
// Check cell on the top.
if (y != 0)
if (cells[x, y - 1].IsAlive)
count++;
// Check cell on the top right.
if (x != Size.X - 1 && y != 0)
if (cells[x + 1, y - 1].IsAlive)
count++;
return count;
}
public void Draw(SpriteBatch spriteBatch)
{
foreach (Cell cell in cells)
cell.Draw(spriteBatch);
// Draw vertical gridlines.
for (int i = 0; i < Size.X; i++)
spriteBatch.Draw(Game1.Pixel, new Rectangle(i * Game1.CellSize - 1, 0, 1, Size.Y * Game1.CellSize), Color.DarkGray);
// Draw horizontal gridlines.
for (int j = 0; j < Size.Y; j++)
spriteBatch.Draw(Game1.Pixel, new Rectangle(0, j * Game1.CellSize - 1, Size.X * Game1.CellSize, 1), Color.DarkGray);
}
protected override void Update(GameTime gameTime)
{
keyboardState = Keyboard.GetState();
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// Toggle pause when spacebar is pressed.
if (keyboardState.IsKeyDown(Keys.Space) && lastKeyboardState.IsKeyUp(Keys.Space))
Paused = !Paused;
// Clear the screen if backspace is pressed.
if (keyboardState.IsKeyDown(Keys.Back) && lastKeyboardState.IsKeyUp(Keys.Back))
grid.Clear();
base.Update(gameTime);
grid.Update(gameTime);
lastKeyboardState = keyboardState;
}
protected override void Draw(GameTime gameTime)
{
if (Paused)
GraphicsDevice.Clear(Color.Red);
else
GraphicsDevice.Clear(Color.White);
spriteBatch.Begin();
if (Paused)
{
string paused = "Paused";
spriteBatch.DrawString(Font, paused, ScreenSize / 2, Color.Gray, 0f, Font.MeasureString(paused) / 2, 1f, SpriteEffects.None, 0f);
}
grid.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
public void Update(GameTime gameTime)
{
(...)
updateTimer += gameTime.ElapsedGameTime;
if (updateTimer.TotalMilliseconds > 1000f / Game1.UPS)
{
updateTimer = TimeSpan.Zero;
(...) // Update the cells and apply the rules.
}
}
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.