1056 words
5 minutes
Mini Game of Life - Stanford's CS106B Modified First Assignment
2025-08-08

The Game of Life, devised by mathematician John Conway, is a cellular automaton that models a zero-player game. The simulation evolves deterministically based on an initial grid configuration and a set of predefined rules. The game takes place on a two-dimensional grid where each cell is either alive or dead, and the state of the grid updates over discrete time steps, referred to as generations or ticks. The evolution of each cell depends solely on the number of its living neighbors, making it an excellent example of how simple local rules can produce complex global behavior.

In this programming assignment, you will implement a simplified version of the Game of Life using C++. The focus will be on fundamental programming concepts, including file input/output, loops, conditional statements, and manipulation of two-dimensional arrays. This project provides an opportunity to explore rule-based systems and their applications in computational simulations.

Learning Objectives#

By completing this assignment, you will:

  • Develop proficiency in working with two-dimensional arrays in C++ to represent the game grid.
  • Strengthen skills in using loops and conditional statements to implement game logic.
  • Gain experience with file input/output to read structured grid data from a file.
  • Understand the mechanics of a rule-based cellular automaton and its implementation.
  • Explore how local interactions between cells can lead to emergent global patterns in a simulation.

Game Rules#

The Game of Life operates on a grid where each cell is in one of two states:

  • Alive, represented by the character X.
  • Dead, represented by the character -.

Each cell has up to eight neighbors, defined as the cells directly adjacent in the horizontal, vertical, and diagonal directions. The state of each cell in the next generation is determined by the following rules, based on the number of living neighbors:

Number of Living NeighborsCurrent Cell StateResulting State
0–1AliveDies
2–3AliveLives
>3AliveDies
Exactly 3DeadBecomes alive

In this simplified version, the grid does not wrap around at the edges, meaning cells on the boundaries (top, bottom, left, or right) will have fewer than eight neighbors.

Input Format#

Your program must read the initial grid configuration from a text file. The file format is as follows:

  • The first line contains two integers: <rowCount> <colCount>, representing the number of rows and columns in the grid.
  • The subsequent lines represent the grid rows, with each character being either:
    • X for an alive cell.
    • - for a dead cell.

Example Input File (grid.txt)#

5 5
-----
--X--
--X--
--X--
-----

In this example, the grid is 5 rows by 5 columns, with three alive cells forming a vertical line in the center column.

Program Requirements#

Your program must implement the following functionality:

  1. Read the initial grid from a file (e.g., grid.txt) and store it in memory.
  2. Display the grid in the console, showing the current state of all cells.
  3. Handle user input to control the simulation:
    • Enter t to advance the simulation by one generation (tick).
    • Enter q to quit the program.
  4. Simulate the next generation by applying the rules described above to update the grid.
  5. Print the updated grid after each tick, maintaining the same format as the input.

Implementation Notes#

To simplify development, consider the following approach:

  • Begin by hardcoding a sample grid directly in your program to test the simulation logic before implementing file input.
  • Use a two-dimensional array (e.g., std::vector<std::vector<char>> or a C-style array) to represent the grid.
  • Create a helper function, such as int countLivingNeighbors(int row, int col), to calculate the number of living neighbors for a given cell.
  • To avoid modifying the grid while computing the next state, use a second grid to store the updated state, then copy it to the original grid after each tick.
  • Ensure boundary checks when accessing neighbor cells to avoid out-of-bounds errors, as edge cells have fewer neighbors.

Sample Program Output#

Initial Grid#

-----
--X--
--X--
--X--
-----

After One Tick#

-----
-----
-XXX-
-----
-----

This example demonstrates a “blinker” pattern, where a vertical line of three alive cells evolves into a horizontal line of three alive cells.

Testing Scenarios#

To ensure your implementation is robust, test it with the following scenarios:

  • An empty grid (all cells dead) to verify that no cells become alive without exactly three neighbors.
  • A small pattern, such as the blinker shown above, to confirm correct application of the rules.
  • Grids with cells on the edges to ensure proper handling of boundary conditions (no wrapping).
  • Larger grids (e.g., 10x10) with random initial configurations to observe more complex behavior.

Implementation Tips#

  • Grid Representation: Use a std::vector<std::vector<char>> for dynamic sizing or a fixed-size C-style array if the grid dimensions are known to be small.
  • Neighbor Counting: Implement a function to count living neighbors by checking all eight adjacent positions, ensuring to validate that each position is within the grid boundaries.
  • State Updates: Use a temporary grid to compute the next generation, as updating the grid in-place could affect neighbor counts for subsequent cells in the same tick.
  • File I/O: Use std::ifstream to read the input file, parsing the first line for dimensions and subsequent lines for the grid data.
  • Modular Design: Break the program into functions for reading the grid, printing the grid, counting neighbors, and updating the grid to improve readability and maintainability.

Extension Ideas (Optional)#

For those seeking additional challenges, consider implementing one or more of the following:

  • Allow the user to specify a different input file at runtime via command-line arguments or console input.
  • Implement edge wrapping, where the grid connects at the top/bottom and left/right edges, so all cells have exactly eight neighbors.
  • Add an option to simulate multiple ticks automatically, with a short delay between each generation for animation-like behavior.
  • Enhance console output with ANSI escape codes to display alive and dead cells in different colors (e.g., green for alive, red for dead).

Deliverables#

Your submission should include:

  1. Source Code: A complete C++ source file (e.g., game_of_life.cpp) containing the implementation of the Game of Life.
  2. Test Files: At least two test input files (e.g., grid1.txt, grid2.txt) with different grid configurations to demonstrate functionality.
  3. README: A text file (README.md or README.txt) with:
    • Instructions for compiling and running the program.
    • A brief description of how to use the program, including the commands (t for tick, q for quit).
    • Any additional notes about your implementation or optional features.

Additional Notes#

  • Ensure your program handles invalid input gracefully, such as malformed files or incorrect grid characters.
  • Test thoroughly to confirm that the simulation correctly follows the rules for all possible neighbor counts.
  • Keep your code well-documented with comments explaining key functions and logic.
  • If you implement optional features, clearly document them in the README and explain how to use them.

This assignment provides a practical introduction to computational simulations and C++ programming. By implementing the Game of Life, you will gain hands-on experience with core programming constructs while exploring an iconic model of emergent behavior.

Mini Game of Life - Stanford's CS106B Modified First Assignment
https://frqblog.vercel.app/posts/cs106bassignment1-modified/
Author
frqblog
Published at
2025-08-08
License
CC BY-NC-SA 4.0