
Prompt
Write a Python program that implements Conway's Game of Life. The program should include a grid of cells that evolve according to the standard rules (a live cell with 2 or 3 live neighbors stays alive, a dead cell with exactly 3 live neighbors becomes alive, otherwise the cell dies). Allow the user to specify the grid size and initial configuration, and display the grid's evolution over time, either in the console or with a simple graphical interface. Include comments to explain the code.
Below is a self-contained, well-commented Python program that runs Conway’s Game of Life in the console.
The user can
1) choose the grid size,
2) decide whether to start from a random pattern or to enter live-cell coordinates manually, and
3) pick the simulation speed.
Copy the code into a file named life.py (or any name you prefer) and run with Python 3.
```python
#!/usr/bin/env python3
"""
Conway's Game of Life – console version
---------------------------------------
Author : (your name)
Date : (today)
Rules
-----
1. Any live cell with 2 or 3 live neighbours survives.
2. Any dead cell with exactly 3 live neighbours becomes a live cell.
3. All other live cells die in the next generation; all other dead cells stay dead.
The grid edges are NOT wrapped (no torus). Feel free to change `count_neighbors`
if you prefer wrapping behaviour.
"""
import os
import random
import time
from typing import List
# ---------- Helper functions -------------------------------------------------
def clear_screen() -> None:
"""Clear the terminal window (Windows, macOS, Linux)."""
os.system('cls' if os.name == 'nt' else 'clear')
def create_grid(rows: int, cols: int, randomize: bool = False,
density: float = 0.2) -> List[List[int]]:
"""
Create a 2-D grid (rows x cols).
If randomize is True, each cell has `density` probability of being alive.
Alive == 1, Dead == 0.
"""
if randomize:
return [[1 if random.random() < density else 0 for _ in range(cols)]
for _ in range(rows)]
else:
# Initially all dead
return [[0 for _ in range(cols)] for _ in range(rows)]
def display_grid(grid: List[List[int]], generation: int) -> None:
"""
Print the grid to the console using simple ASCII art.
'█' (full block) : alive
' ' (space) : dead
"""
clear_screen()
print(f"Conway's Game of Life – Generation {generation}")
print('-' * (len(grid[0]) + 2))
for row in grid:
print('|' + ''.join('█' if cell else ' ' for cell in row) + '|')
print('-' * (len(grid[0]) + 2))
print("Ctrl-C to quit.")
def count_neighbors(grid: List[List[int]], r: int, c: int) -> int:
"""Count live neighbours of cell (r, c) without wrapping the edges."""
rows, cols = len(grid), len(grid[0])
count = 0
for dr in (-1, 0, 1):
for dc in (-1, 0, 1):
if dr == 0 and dc == 0:
continue # Skip the cell itself
nr, nc = r + dr, c + dc # neighbour row/col
if 0 <= nr < rows and 0 <= nc < cols:
count += grid[nr][nc]
return count
def next_generation(grid: List[List[int]]) -> List[List[int]]:
"""Return the grid that results from applying the Game of Life rules once."""
rows, cols = len(grid), len(grid[0])
new_grid = [[0 for _ in range(cols)] for _ in range(rows)]
for r in range(rows):
for c in range(cols):
live_neighbors = count_neighbors(grid, r, c)
if grid[r][c] == 1: # Cell is alive
new_grid[r][c] = 1 if live_neighbors in (2, 3) else 0
else: # Cell is dead
new_grid[r][c] = 1 if live_neighbors == 3 else 0
return new_grid
def get_user_configuration(grid: List[List[int]]) -> None:
"""
Allow the user to turn on cells manually.
Input format: a list of coordinates separated by spaces, e.g.:
1,1 1,2 1,3 (creates the classic Blinker)
Coordinates are zero-based (row,col). Empty input finishes.
"""
print("\nEnter coordinates of live cells, e.g. 1,1 1,2 1,3")
print("Leave the line empty to start the simulation.")
coords = input("Coordinates > ").strip()
if not coords:
return
rows, cols = len(grid), len(grid[0])
for pair in coords.split():
try:
r_str, c_str = pair.split(',')
r, c = int(r_str), int(c_str)
if 0 <= r < rows and 0 <= c < cols:
grid[r][c] = 1
else:
print(f"Warning: ({r},{c}) is out of bounds and was ignored.")
except ValueError:
print(f"Warning: '{pair}' is not in 'row,col' format – ignored.")
# ---------- Main program -----------------------------------------------------
def main() -> None:
print("Conway's Game of Life – console edition\n")
# 1. Ask for grid size
while True:
try:
rows = int(input("Number of rows > "))
cols = int(input("Number of columns > "))
if rows <= 0 or cols <= 0:
raise ValueError
break
except ValueError:
print("Please enter positive integers for both rows and columns.\n")
# 2. Choose initialization method
print("\nInitial configuration:")
print(" 1 – Random pattern")
print(" 2 – Enter live-cell coordinates manually")
choice = input("Your choice [1/2] > ").strip() or '1'
if choice == '1':
while True:
try:
density = float(input("Probability that a cell starts alive (0-1, default 0.2) > ") or 0.2)
if not (0.0 <= density <= 1.0):
raise ValueError
break
except ValueError:
print("Enter a number between 0 and 1.")
grid = create_grid(rows, cols, randomize=True, density=density)
else:
grid = create_grid(rows, cols, randomize=False)
get_user_configuration(grid)
# 3. Ask for simulation speed
try:
delay = float(input("Delay between generations in seconds (default 0.2) > ") or 0.2)
except ValueError:
delay = 0.2
# 4. Run the simulation
generation = 0
try:
while True:
display_grid(grid, generation)
time.sleep(delay)
grid = next_generation(grid)
generation += 1
except KeyboardInterrupt:
print("\nSimulation stopped by user. Goodbye!")
if __name__ == "__main__":
main()
```
How to run
----------
1. Save the snippet as life.py
2. `$ python3 life.py`
3. Follow the prompts: give the grid size, choose random or manual setup, pick a delay, and watch the evolution.
4. Press Ctrl-C at any time to exit.
Extending the program
---------------------
• Change `count_neighbors` so the edges wrap (torus) by replacing the boundary test with modulo arithmetic.
• Add color using the `colorama` package or switch to a GUI toolkit such as `tkinter` or `pygame` for a graphical version.
• Save/load patterns from files to build a pattern library.Of course! Here is a comprehensive Python implementation of Conway's Game of Life.
This solution provides two versions:
1. **A Console-Based Version:** This is the primary solution. It's simple, runs anywhere Python is installed, and clearly demonstrates the core logic of the game.
2. **A Simple GUI Version (using `pygame`):** This is provided as an optional, more visually appealing alternative. It requires the `pygame` library to be installed.
---
### 1. Console-Based Implementation
This version is great for understanding the algorithm. It prints the grid to the terminal and uses a simple loop to show the evolution.
#### Features:
* User-defined grid size.
* User can choose between a random starting pattern or a pre-defined "Glider" pattern.
* Clear console output that refreshes for each generation.
* Well-commented code explaining each part of the process.
#### How to Run:
1. Save the code as a Python file (e.g., `game_of_life_console.py`).
2. Run from your terminal: `python game_of_life_console.py`
3. Follow the prompts to set the grid size and initial pattern.
4. Press `Ctrl+C` to stop the simulation.
#### Code: `game_of_life_console.py`
```python
import random
import os
import time
# --- Constants for Cell Representation ---
LIVE_CELL = '■' # Character for a live cell
DEAD_CELL = ' ' # Character for a dead cell
def create_grid(rows, cols):
"""Creates a new grid (list of lists) with all cells initialized to dead."""
return [[0 for _ in range(cols)] for _ in range(rows)]
def initialize_grid_random(grid, density=0.2):
"""
Initializes the grid with a random pattern of live cells.
Args:
grid (list of lists): The game grid.
density (float): The probability (0.0 to 1.0) that a cell will be alive.
"""
rows = len(grid)
cols = len(grid[0])
for r in range(rows):
for c in range(cols):
if random.random() < density:
grid[r][c] = 1 # 1 represents a live cell
def initialize_grid_glider(grid):
"""Initializes the grid with a classic 'Glider' pattern."""
# Ensure the grid is large enough to hold the glider
if len(grid) < 5 or len(grid[0]) < 5:
print("Grid is too small for a Glider. Please use a grid of at least 5x5.")
return
# Glider pattern at the top-left corner
# . X .
# . . X
# X X X
glider_pattern = [(1, 2), (2, 3), (3, 1), (3, 2), (3, 3)]
for r, c in glider_pattern:
grid[r][c] = 1
def print_grid(grid, generation):
"""Prints the grid to the console."""
# Clear the console screen
# 'cls' for Windows, 'clear' for Linux/macOS
os.system('cls' if os.name == 'nt' else 'clear')
print(f"Conway's Game of Life - Generation: {generation}")
# Print top border
print('+' + '-' * len(grid[0]) + '+')
for row in grid:
# Print each row with borders
print('|', end='')
for cell in row:
print(LIVE_CELL if cell == 1 else DEAD_CELL, end='')
print('|')
# Print bottom border
print('+' + '-' * len(grid[0]) + '+')
print("Press Ctrl+C to exit.")
def count_live_neighbors(grid, r, c):
"""
Counts the number of live neighbors for a given cell at (r, c).
The grid is considered finite (no wrap-around).
"""
rows = len(grid)
cols = len(grid[0])
live_neighbors = 0
# Iterate through the 8 neighboring positions
for i in range(-1, 2):
for j in range(-1, 2):
# Skip the cell itself
if i == 0 and j == 0:
continue
# Calculate neighbor coordinates
neighbor_r, neighbor_c = r + i, c + j
# Check if the neighbor is within the grid boundaries
if 0 <= neighbor_r < rows and 0 <= neighbor_c < cols:
# If the neighbor is alive, increment the count
if grid[neighbor_r][neighbor_c] == 1:
live_neighbors += 1
return live_neighbors
def update_grid(grid):
"""
Applies the rules of the Game of Life to create the next generation of the grid.
"""
rows = len(grid)
cols = len(grid[0])
# Create a new grid to store the next state, as changes must be simultaneous
new_grid = create_grid(rows, cols)
for r in range(rows):
for c in range(cols):
# Get the number of live neighbors for the current cell
live_neighbors = count_live_neighbors(grid, r, c)
cell_state = grid[r][c]
# Rule 1: A live cell with 2 or 3 live neighbors survives.
if cell_state == 1 and (live_neighbors == 2 or live_neighbors == 3):
new_grid[r][c] = 1
# Rule 2: A dead cell with exactly 3 live neighbors becomes a live cell.
elif cell_state == 0 and live_neighbors == 3:
new_grid[r][c] = 1
# Rule 3: All other live cells die, and all other dead cells stay dead.
# This is the default state of new_grid, so no 'else' is needed.
return new_grid
def main():
"""Main function to run the Game of Life simulation."""
try:
# --- User Input ---
width = int(input("Enter grid width: "))
height = int(input("Enter grid height: "))
choice = input("Choose initial pattern ('random' or 'glider'): ").lower()
# --- Initialization ---
grid = create_grid(height, width)
if choice == 'glider':
initialize_grid_glider(grid)
else:
density = float(input("Enter random density (e.g., 0.2 for 20%): "))
initialize_grid_random(grid, density)
generation = 0
# --- Main Game Loop ---
while True:
print_grid(grid, generation)
grid = update_grid(grid)
generation += 1
time.sleep(0.1) # Pause between generations
except ValueError:
print("Invalid input. Please enter integers for size and a float for density.")
except KeyboardInterrupt:
print("\nSimulation stopped. Goodbye!")
if __name__ == "__main__":
main()
```
---
### 2. GUI Implementation (Optional, using `pygame`)
This version provides a much better visual experience. It draws the grid in a window and updates it smoothly.
#### Prerequisites:
You must have the `pygame` library installed. If you don't, open your terminal or command prompt and run:
`pip install pygame`
#### How to Run:
1. Save the code as a Python file (e.g., `game_of_life_gui.py`).
2. Run from your terminal: `python game_of_life_gui.py`
3. A window will appear displaying the simulation.
4. Close the window to stop the program.
#### Code: `game_of_life_gui.py`
This code reuses the core logic functions (`create_grid`, `count_live_neighbors`, `update_grid`) from the console version, demonstrating how a display layer can be separated from the game logic.
```python
import pygame
import random
import sys
# --- Re-using Core Logic Functions (can be imported from the console version) ---
def create_grid(rows, cols):
"""Creates a new grid (list of lists) with all cells initialized to dead."""
return [[0 for _ in range(cols)] for _ in range(rows)]
def initialize_grid_random(grid, density=0.2):
"""Initializes the grid with a random pattern of live cells."""
rows = len(grid)
cols = len(grid[0])
for r in range(rows):
for c in range(cols):
if random.random() < density:
grid[r][c] = 1
def count_live_neighbors(grid, r, c):
"""Counts the number of live neighbors for a given cell at (r, c)."""
rows = len(grid)
cols = len(grid[0])
live_neighbors = 0
for i in range(-1, 2):
for j in range(-1, 2):
if i == 0 and j == 0:
continue
neighbor_r, neighbor_c = r + i, c + j
if 0 <= neighbor_r < rows and 0 <= neighbor_c < cols:
if grid[neighbor_r][neighbor_c] == 1:
live_neighbors += 1
return live_neighbors
def update_grid(grid):
"""Applies the rules of the Game of Life to create the next generation."""
rows = len(grid)
cols = len(grid[0])
new_grid = create_grid(rows, cols)
for r in range(rows):
for c in range(cols):
live_neighbors = count_live_neighbors(grid, r, c)
cell_state = grid[r][c]
if cell_state == 1 and (live_neighbors == 2 or live_neighbors == 3):
new_grid[r][c] = 1
elif cell_state == 0 and live_neighbors == 3:
new_grid[r][c] = 1
return new_grid
# --- Pygame-Specific Code ---
# --- Constants ---
GRID_WIDTH = 80 # Number of cells horizontally
GRID_HEIGHT = 60 # Number of cells vertically
CELL_SIZE = 10 # Size of each cell in pixels
# Calculate window size based on grid and cell size
WINDOW_WIDTH = GRID_WIDTH * CELL_SIZE
WINDOW_HEIGHT = GRID_HEIGHT * CELL_SIZE
# --- Colors ---
COLOR_WHITE = (255, 255, 255) # Live cells
COLOR_BLACK = (0, 0, 0) # Background
COLOR_GRAY = (40, 40, 40) # Grid lines
def draw_grid(screen, grid):
"""Draws the entire grid on the pygame screen."""
for r in range(GRID_HEIGHT):
for c in range(GRID_WIDTH):
# Define the rectangle for the cell
rect = pygame.Rect(c * CELL_SIZE, r * CELL_SIZE, CELL_SIZE, CELL_SIZE)
# Draw the cell filled with white if alive, black if dead
if grid[r][c] == 1:
pygame.draw.rect(screen, COLOR_WHITE, rect)
else:
pygame.draw.rect(screen, COLOR_BLACK, rect)
# Draw a gray border around the cell for the grid effect
pygame.draw.rect(screen, COLOR_GRAY, rect, 1)
def main_gui():
"""Main function to run the Pygame-based Game of Life."""
pygame.init()
# Set up the display
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("Conway's Game of Life")
# Set up the clock to control the frame rate
clock = pygame.time.Clock()
# Initialize the grid
grid = create_grid(GRID_HEIGHT, GRID_WIDTH)
initialize_grid_random(grid, density=0.3)
# --- Main Game Loop ---
running = True
while running:
# Event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# --- Game Logic ---
grid = update_grid(grid)
# --- Drawing ---
screen.fill(COLOR_BLACK) # Clear the screen
draw_grid(screen, grid) # Draw the current state of the grid
# Update the display
pygame.display.flip()
# Limit the frame rate (e.g., 10 generations per second)
clock.tick(10)
pygame.quit()
sys.exit()
if __name__ == "__main__":
# To run the GUI version, call main_gui()
# You can add a command-line argument parser to choose between console and GUI
main_gui()
```Certainly! Below is a commented Python program that implements **Conway's Game of Life**. The user can specify the grid size and initial configuration, and watch the simulation evolve in the console.
---
## Features
- **Grid Size Specification:** Users enter the width and height.
- **Initial Configuration:**
- Users can specify live cell positions.
- Or pick a random initialization.
- **Console Display**: The grid is printed each generation, and user can set delay speed.
- **Clean Comments**: For easy understanding and modification.
---
```python
import time
import os
import random
def clear_screen():
# Clear the console screen (cross-platform)
os.system("cls" if os.name == "nt" else "clear")
def print_grid(grid):
# Print the grid to the console
for row in grid:
print(' '.join('█' if cell else ' ' for cell in row))
def count_neighbors(grid, x, y, width, height):
# Count the number of live neighbors for the cell at (y, x)
count = 0
for dy in [-1, 0, 1]:
for dx in [-1, 0, 1]:
if dy == 0 and dx == 0:
continue
ny, nx = y + dy, x + dx
if 0 <= ny < height and 0 <= nx < width:
count += grid[ny][nx]
return count
def next_generation(grid, width, height):
# Generate the next grid state based on Game of Life rules
new_grid = [[0 for _ in range(width)] for _ in range(height)]
for y in range(height):
for x in range(width):
live_neighbors = count_neighbors(grid, x, y, width, height)
if grid[y][x] == 1:
# Rule 1 and 2: Survive with 2 or 3 neighbors
new_grid[y][x] = 1 if live_neighbors in [2, 3] else 0
else:
# Rule 4: Dead cell with exactly 3 neighbors becomes live
new_grid[y][x] = 1 if live_neighbors == 3 else 0
return new_grid
def get_initial_grid(width, height):
# Get the initial configuration from the user
print("Choose initial configuration:")
print("1. Manually enter coordinates of live cells")
print("2. Random initial configuration")
choice = input("Enter 1 or 2: ")
grid = [[0 for _ in range(width)] for _ in range(height)]
if choice == "1":
print("Enter live cell coordinates as 'x y'. Type 'done' when finished:")
print(f"Coordinates in the range: x=0..{width-1}, y=0..{height-1}")
while True:
s = input("Cell (x y) or 'done': ")
if s.lower() == "done":
break
try:
x_str, y_str = s.strip().split()
x, y = int(x_str), int(y_str)
if 0 <= x < width and 0 <= y < height:
grid[y][x] = 1
else:
print("Coordinates out of range.")
except:
print("Invalid input.")
else:
density = input("Enter initial live cell density (0.0 - 1.0): ")
try:
density = float(density)
except:
density = 0.2
for y in range(height):
for x in range(width):
grid[y][x] = 1 if random.random() < density else 0
return grid
def main():
print("=== Conway's Game of Life ===")
# Get grid size
try:
width = int(input("Grid width: "))
height = int(input("Grid height: "))
except:
print("Invalid/input. Defaulting to 20x10.")
width = 20
height = 10
# Get initial state
grid = get_initial_grid(width, height)
# Get delay between generations
delay = input("Time between generations in seconds (e.g., 0.5): ")
try:
delay = float(delay)
except:
delay = 0.5
generations = input("Number of generations to run (or leave blank for infinite): ")
try:
generations = int(generations)
except:
generations = None
gen = 0
try:
while generations is None or gen < generations:
clear_screen()
print(f"Generation {gen}")
print_grid(grid)
grid = next_generation(grid, width, height)
time.sleep(delay)
gen += 1
except KeyboardInterrupt:
print("\nSimulation stopped by user.")
if __name__ == "__main__":
main()
```
---
## How to Use
1. **Run the script.**
2. Specify grid size.
3. Choose initial configuration (manual or random).
4. Enter delay per generation and number of generations (blank = infinite).
5. Watch the evolution in your console.
---
**If you want a simple graphical implementation instead (e.g., using `pygame`), just ask!**# Conway's Game of Life in Python
```python
import time
import random
import os
import argparse
class GameOfLife:
"""Implementation of Conway's Game of Life with console display"""
def __init__(self, width=50, height=50, randomize=True):
"""
Initialize the game grid
Args:
width (int): Number of columns in the grid
height (int): Number of rows in the grid
randomize (bool): Whether to randomize the initial state
"""
self.width = width
self.height = height
self.grid = [[False for _ in range(width)] for _ in range(height)]
if randomize:
self.randomize_grid()
def randomize_grid(self):
"""Fill the grid with random live cells"""
for row in range(self.height):
for col in range(self.width):
self.grid[row][col] = random.random() > 0.7
def set_cell(self, row, col, state):
"""Set the state of a specific cell"""
if 0 <= row < self.height and 0 <= col < self.width:
self.grid[row][col] = state
def count_live_neighbors(self, row, col):
"""Count the number of live neighbors around a cell"""
count = 0
for r in range(row-1, row+2):
for c in range(col-1, col+2):
# Skip the cell itself and check boundaries
if (r == row and c == col) or r < 0 or r >= self.height or c < 0 or c >= self.width:
continue
if self.grid[r][c]:
count += 1
return count
def next_generation(self):
"""Compute the next generation based on current state"""
new_grid = [[False for _ in range(self.width)] for _ in range(self.height)]
for row in range(self.height):
for col in range(self.width):
neighbors = self.count_live_neighbors(row, col)
# Apply Conway's rules
if self.grid[row][col]: # Cell is alive
new_grid[row][col] = neighbors in (2, 3)
else: # Cell is dead
new_grid[row][col] = neighbors == 3
self.grid = new_grid
def display(self):
"""Print the current grid state to console with live cells as ■ and dead as spaces"""
os.system('cls' if os.name == 'nt' else 'clear') # Clear console
output = []
for row in self.grid:
line = ''.join(['■' if cell else ' ' for cell in row])
output.append(line)
print('\n'.join(output))
print(f"Grid: {self.width}x{self.height}")
def run_simulation(self, generations=100, delay=0.1):
"""Run the simulation for specified number of generations"""
for _ in range(generations):
self.display()
self.next_generation()
time.sleep(delay)
def glider_generator(life):
"""Initialize a glider pattern in the top-left corner"""
glider = [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
for row, col in glider:
life.set_cell(row, col, True)
def pulsar_generator(life):
"""Initialize a pulsar pattern near the center"""
rows = [5, 10, 12, 17]
cols = [7, 9, 14, 16]
positions = [(r, c) for r in rows for c in cols]
# Add horizontal bars
positions.extend([(2, c) for c in range(7, 10)])
positions.extend([(2, c) for c in range(14, 17)])
positions.extend([(7, c) for c in range(5, 12)])
positions.extend([(12, c) for c in range(5, 12)])
positions.extend([(17, c) for c in range(7, 10)])
positions.extend([(17, c) for c in range(14, 17)])
# Add vertical bars
positions.extend([(r, 2) for r in range(7, 10)])
positions.extend([(r, 2) for r in range(14, 17)])
positions.extend([(r, 7) for r in range(5, 12)])
positions.extend([(r, 12) for r in range(5, 12)])
positions.extend([(r, 17) for r in range(7, 10)])
positions.extend([(r, 17) for r in range(14, 17)])
for row, col in positions:
if row < life.height and col < life.width:
life.set_cell(row, col, True)
def main():
"""Main function to parse arguments and run the simulation"""
parser = argparse.ArgumentParser(description="Conway's Game of Life")
parser.add_argument('--width', type=int, default=50, help="Grid width")
parser.add_argument('--height', type=int, default=50, help="Grid height")
parser.add_argument('--generations', type=int, default=100, help="Number of generations")
parser.add_argument('--delay', type=float, default=0.1, help="Delay between generations")
parser.add_argument('--pattern', choices=['random', 'glider', 'pulsar'], default='random',
help="Initial pattern")
args = parser.parse_args()
# Initialize the game
life = GameOfLife(args.width, args.height, randomize=False)
# Set initial pattern
if args.pattern == 'glider':
glider_generator(life)
elif args.pattern == 'pulsar':
pulsar_generator(life)
else:
life.randomize_grid()
# Run the simulation
try:
life.run_simulation(args.generations, args.delay)
except KeyboardInterrupt:
print("\nSimulation stopped by user.")
if __name__ == "__main__":
main()
```
## How to Use This Program
1. **Basic Usage**: Run the program with default settings (50x50 random grid)
```bash
python game_of_life.py
```
2. **Customize Settings**:
```bash
python game_of_life.py --width 40 --height 30 --generations 200 --delay 0.05
```
3. **Choose Pattern**:
```bash
# Use the glider pattern
python game_of_life.py --pattern glider
# Use the pulsar pattern
python game_of_life.py --pattern pulsar
```
## Features
1. **Interactive Simulation**: Watch the evolution of cells in your terminal
2. **Customizable Grid**: Set your preferred width and height
3. **Initial Patterns**: Choose between random, glider, or pulsar configurations
4. **Adjustable Speed**: Control the delay between generations
The program demonstrates fundamental Conway's Game of Life rules:
- Any live cell with fewer than two live neighbors dies (underpopulation)
- Any live cell with two or three live neighbors lives on
- Any live cell with more than three live neighbors dies (overpopulation)
- Any dead cell with exactly three live neighbors becomes a live cell (reproduction)## Conway's Game of Life Implementation in Python
### Overview
The following Python program implements Conway's Game of Life. It allows users to specify the grid size and initial configuration, and displays the grid's evolution over time using a simple graphical interface with Pygame.
### Requirements
- Python 3.x
- Pygame library (`pip install pygame`)
### Code
```python
import pygame
import numpy as np
# Window size
WIDTH, HEIGHT = 800, 600
# Cell size
CELL_SIZE = 10
# Colors
ALIVE_COLOR = (255, 255, 255)
DEAD_COLOR = (0, 0, 0)
class GameOfLife:
def __init__(self, grid_size):
"""
Initialize the Game of Life with a grid of specified size.
:param grid_size: Tuple (rows, cols) representing the grid size.
"""
self.rows, self.cols = grid_size
self.grid = np.random.choice([0,1], size=(self.rows, self.cols), p=[0.5, 0.5])
def count_neighbors(self, row, col):
"""
Count the number of alive neighbors for a given cell.
:param row: Row index of the cell.
:param col: Column index of the cell.
:return: Number of alive neighbors.
"""
count = 0
for r in range(max(0, row-1), min(self.rows, row+2)):
for c in range(max(0, col-1), min(self.cols, col+2)):
count += self.grid[r, c]
count -= self.grid[row, col]
return count
def next_generation(self):
"""
Compute the next generation of the grid based on the Game of Life rules.
"""
new_grid = np.copy(self.grid)
for row in range(self.rows):
for col in range(self.cols):
alive_neighbors = self.count_neighbors(row, col)
if self.grid[row, col] == 1 and (alive_neighbors < 2 or alive_neighbors > 3):
new_grid[row, col] = 0
elif self.grid[row, col] == 0 and alive_neighbors == 3:
new_grid[row, col] = 1
self.grid = new_grid
def draw(self, screen):
"""
Draw the current grid on the Pygame screen.
:param screen: Pygame screen object.
"""
screen.fill(DEAD_COLOR)
for row in range(self.rows):
for col in range(self.cols):
if self.grid[row, col] == 1:
pygame.draw.rect(screen, ALIVE_COLOR, (col*CELL_SIZE, row*CELL_SIZE, CELL_SIZE, CELL_SIZE))
pygame.display.update()
def main():
# Initialize Pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
# Get grid size from user
rows = int(input("Enter number of rows: "))
cols = int(input("Enter number of columns: "))
# Adjust cell size based on grid size
global CELL_SIZE
CELL_SIZE = min(WIDTH // cols, HEIGHT // rows)
# Create Game of Life instance
game = GameOfLife((rows, cols))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
game.draw(screen)
game.next_generation()
clock.tick(60)
pygame.quit()
if __name__ == "__main__":
main()
```
### Usage
1. Run the program.
2. Enter the number of rows and columns when prompted.
3. The Game of Life will be displayed in a Pygame window, evolving over time.
4. Close the window to exit the program.
### Notes
- The grid is initialized with random values.
- The `count_neighbors` method counts the number of alive neighbors for a given cell.
- The `next_generation` method computes the next generation of the grid based on the Game of Life rules.
- The `draw` method draws the current grid on the Pygame screen.
- The `main` function initializes Pygame, gets the grid size from the user, and runs the Game of Life loop.# Conway's Game of Life in Python
```python
import time
import os
import random
def initialize_grid(rows, cols, pattern=None):
"""Initialize the grid based on user preferences
Args:
rows (int): Number of rows in the grid
cols (int): Number of columns in the grid
pattern (str): Optional - specifies the initial pattern
Returns:
list: A 2D list representing the grid
"""
grid = [[0 for _ in range(cols)] for _ in range(rows)]
if pattern:
# Handle predefined patterns
if pattern.lower() == 'random':
# Fill grid randomly with ~25% living cells
for i in range(rows):
for j in range(cols):
if random.random() < 0.25:
grid[i][j] = 1
elif pattern.lower() == 'glider':
# Place a glider pattern near center
center_row, center_col = rows // 2, cols // 2
points = [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
for dr, dc in points:
r = (center_row + dr) % rows
c = (center_col + dc) % cols
grid[r][c] = 1
elif pattern.lower() == 'blinker':
# Place a blinker pattern near center
center_row, center_col = rows // 2, cols // 2
points = [(0, 0), (1, 0), (2, 0)]
for dr, dc in points:
r = (center_row + dr) % rows
c = (center_col + dc) % cols
grid[r][c] = 1
else:
# Manual grid setup
print("\nEnter coordinates for living cells (row column).")
print("Type 'done' when finished.")
while True:
coords = input("Enter cell coordinates (or 'done'): ")
if coords.lower() == 'done':
break
try:
r, c = map(int, coords.split())
if 0 <= r < rows and 0 <= c < cols:
grid[r][c] = 1
else:
print(f"Coordinates out of range (0-{rows-1} for rows, 0-{cols-1} for columns)")
except ValueError:
print("Invalid input. Please enter two integers separated by a space.")
return grid
def print_grid(grid):
"""Print the grid to the console with visual representations
Args:
grid (list): The 2D grid to display
"""
for row in grid:
# Represent living cells as ■ and dead cells as .
print(' '.join(['■' if cell else '.' for cell in row]))
def count_neighbors(grid, row, col):
"""Count the number of living neighbors around a cell
Args:
grid (list): The 2D grid
row (int): Row index of the cell
col (int): Column index of the cell
Returns:
int: Number of living neighbors
"""
rows = len(grid)
cols = len(grid[0])
count = 0
# Check all 8 surrounding neighbors
for dr in (-1, 0, 1):
for dc in (-1, 0, 1):
if dr == 0 and dc == 0:
continue # Skip the cell itself
# Handle grid wrapping (toroidal grid)
r = (row + dr) % rows
c = (col + dc) % cols
count += grid[r][c]
return count
def update_grid(grid):
"""Compute the next generation based on Conway's rules
Args:
grid (list): Current grid state
Returns:
list: Updated grid state for the next generation
"""
rows = len(grid)
cols = len(grid[0])
new_grid = [[0 for _ in range(cols)] for _ in range(rows)]
for i in range(rows):
for j in range(cols):
neighbors = count_neighbors(grid, i, j)
# Apply Conway's Game of Life rules:
# 1. Living cell with 2 or 3 neighbors survives
if grid[i][j] == 1 and neighbors in (2, 3):
new_grid[i][j] = 1
# 2. Dead cell with exactly 3 neighbors becomes alive
elif grid[i][j] == 0 and neighbors == 3:
new_grid[i][j] = 1
# 3. All other cells die or remain dead
return new_grid
def clear_screen():
"""Clear the console screen in a cross-platform way"""
os.system('cls' if os.name == 'nt' else 'clear')
def main():
"""Main function to run Conway's Game of Life"""
print("=" * 50)
print("Conway's Game of Life Simulation")
print("=" * 50)
# Get grid dimensions
rows = int(input("\nEnter number of rows: "))
cols = int(input("Enter number of columns: "))
# Pattern selection
print("\nChoose initialization method:")
print("1: Manual input")
print("2: Predefined pattern")
choice = input("Enter your choice (1 or 2): ")
pattern = None
if choice == '2':
print("\nAvailable patterns:")
print("- Random: Random starting pattern")
print("- Glider: A moving pattern")
print("- Blinker: An oscillating pattern")
pattern = input("Enter pattern name: ")
# Initialize grid based on user choices
grid = initialize_grid(rows, cols, pattern)
# Get simulation parameters
generations = int(input("\nHow many generations to simulate? "))
delay = float(input("Seconds between generations? "))
# Controls for simulation
print("\nControls:")
print("Press any key to pause/resume")
print("Press 'q' to quit")
print("=" * 50)
input("\nPress Enter to start simulation...")
# Simulation loop
pause = False
for gen in range(generations):
clear_screen()
print(f"Generation: {gen+1}/{generations}")
print_grid(grid)
# Handle user input
try:
# Check for pause/exit commands without blocking
import termios
import sys
import select
# Configure non-blocking input
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
new_settings = termios.tcgetattr(fd)
new_settings[3] &= ~termios.ICANON
termios.tcsetattr(fd, termios.TCSAFLUSH, new_settings)
# Check for input
rlist, _, _ = select.select([sys.stdin], [], [], 0)
if rlist:
key = sys.stdin.read(1).lower()
if key == 'q':
break
else:
pause = not pause
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
except ImportError:
# If termios isn't available, skip input handling
pass
# Normal evolution flow
if not pause:
grid = update_grid(grid)
time.sleep(delay)
else:
input("\nPaused. Press Enter to resume...")
pause = False
print("\nSimulation complete!")
if __name__ == "__main__":
main()
```
## Features & Explanation
### Core Functionality
1. **Grid Representation**:
- Uses a 2D list to represent cells (0 = dead, 1 = alive)
- Implements a toroidal grid wrapping around edges
2. **Conway's Rules**:
- Survival: Live cells with 2-3 neighbors stay alive
- Reproduction: Dead cells with exactly 3 neighbors become alive
- Death: All other situations result in death
3. **Simulation**:
- Processes generations sequentially
- Visualizes each generation with pause/continue control
### User Interaction
1. **Grid Configuration**:
- Sets grid dimensions (rows × columns)
- Chooses between manual input or pre-defined patterns
2. **Pattern Options**:
- Random: Distributes live cells randomly
- Glider: Famous moving pattern
- Blinker: Simple oscillator
- Manual: User-specified live cell coordinates
3. **Simulation Control**:
- Sets number of generations to simulate
- Adjustable delay between generations
- Pause/resume with any key input
- Quit with 'q' during simulation
### Enhanced Console Experience
- Clear screen between generations
- Visual symbols (■ for live cells, . for dead)
- Generation counter
- Handling for cross-platform input (Linux, macOS, Windows)
- Proper error handling for invalid inputs
### Technical Implementation
1. `initialize_grid()`: Sets up starting configuration
2. `print_grid()`: Visualizes grid with ASCII symbols
3. `count_neighbors()`: Calculates neighbor count for each cell
4. `update_grid()`: Applies Game of Life rules to compute next state
5. `clear_screen()`: Provides platform-specific screen clearing
6. `main()`: Orchestrates user input and simulation flow
To run:
1. Save to `conway.py`
2. Execute with `python3 conway.py`
3. Follow on-screen prompts to configure simulationI'll create a comprehensive implementation of Conway's Game of Life with both console and graphical interfaces. Here's the complete program:
```python
import time
import random
import os
import sys
try:
import tkinter as tk
from tkinter import messagebox, simpledialog
GUI_AVAILABLE = True
except ImportError:
GUI_AVAILABLE = False
print("GUI not available. Running in console mode only.")
class GameOfLife:
"""
Conway's Game of Life implementation
Rules:
1. Any live cell with 2 or 3 live neighbors survives
2. Any dead cell with exactly 3 live neighbors becomes a live cell
3. All other live cells die in the next generation
4. All other dead cells stay dead
"""
def __init__(self, width, height):
"""
Initialize the game grid
Args:
width (int): Width of the grid
height (int): Height of the grid
"""
self.width = width
self.height = height
self.grid = [[0 for _ in range(width)] for _ in range(height)]
self.generation = 0
def set_cell(self, x, y, state):
"""
Set the state of a specific cell
Args:
x (int): X coordinate
y (int): Y coordinate
state (int): 1 for alive, 0 for dead
"""
if 0 <= x < self.width and 0 <= y < self.height:
self.grid[y][x] = state
def get_cell(self, x, y):
"""
Get the state of a specific cell
Args:
x (int): X coordinate
y (int): Y coordinate
Returns:
int: 1 if alive, 0 if dead or out of bounds
"""
if 0 <= x < self.width and 0 <= y < self.height:
return self.grid[y][x]
return 0
def count_neighbors(self, x, y):
"""
Count the number of live neighbors for a given cell
Args:
x (int): X coordinate
y (int): Y coordinate
Returns:
int: Number of live neighbors (0-8)
"""
count = 0
# Check all 8 neighboring cells
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if dx == 0 and dy == 0:
continue # Skip the cell itself
count += self.get_cell(x + dx, y + dy)
return count
def next_generation(self):
"""
Calculate and update to the next generation based on Conway's rules
"""
# Create a new grid for the next generation
new_grid = [[0 for _ in range(self.width)] for _ in range(self.height)]
# Apply Conway's rules to each cell
for y in range(self.height):
for x in range(self.width):
neighbors = self.count_neighbors(x, y)
current_cell = self.get_cell(x, y)
# Apply the rules
if current_cell == 1: # Cell is alive
if neighbors == 2 or neighbors == 3:
new_grid[y][x] = 1 # Cell survives
else:
new_grid[y][x] = 0 # Cell dies
else: # Cell is dead
if neighbors == 3:
new_grid[y][x] = 1 # Cell becomes alive
else:
new_grid[y][x] = 0 # Cell stays dead
# Update the grid and increment generation counter
self.grid = new_grid
self.generation += 1
def clear_grid(self):
"""Clear all cells in the grid"""
self.grid = [[0 for _ in range(self.width)] for _ in range(self.height)]
self.generation = 0
def randomize_grid(self, probability=0.3):
"""
Randomly populate the grid
Args:
probability (float): Probability of a cell being alive (0.0 to 1.0)
"""
for y in range(self.height):
for x in range(self.width):
self.grid[y][x] = 1 if random.random() < probability else 0
self.generation = 0
def load_pattern(self, pattern_name):
"""
Load a predefined pattern into the grid
Args:
pattern_name (str): Name of the pattern to load
"""
self.clear_grid()
patterns = {
'glider': [(1, 0), (2, 1), (0, 2), (1, 2), (2, 2)],
'blinker': [(1, 0), (1, 1), (1, 2)],
'block': [(0, 0), (0, 1), (1, 0), (1, 1)],
'beacon': [(0, 0), (0, 1), (1, 0), (2, 3), (3, 2), (3, 3)],
'toad': [(1, 1), (2, 1), (3, 1), (0, 2), (1, 2), (2, 2)]
}
if pattern_name in patterns:
# Center the pattern in the grid
offset_x = self.width // 2
offset_y = self.height // 2
for x, y in patterns[pattern_name]:
self.set_cell(offset_x + x, offset_y + y, 1)
class ConsoleInterface:
"""Console-based interface for the Game of Life"""
def __init__(self):
self.game = None
self.running = False
def clear_screen(self):
"""Clear the console screen"""
os.system('cls' if os.name == 'nt' else 'clear')
def display_grid(self):
"""Display the current grid state in the console"""
self.clear_screen()
print(f"Conway's Game of Life - Generation: {self.game.generation}")
print("=" * (self.game.width * 2 + 1))
for y in range(self.game.height):
row = ""
for x in range(self.game.width):
if self.game.get_cell(x, y) == 1:
row += "██" # Alive cell
else:
row += " " # Dead cell
print(row)
print("=" * (self.game.width * 2 + 1))
print("Press Ctrl+C to stop")
def get_grid_size(self):
"""Get grid dimensions from user input"""
try:
width = int(input("Enter grid width (10-50): "))
height = int(input("Enter grid height (10-30): "))
# Validate input
width = max(10, min(50, width))
height = max(10, min(30, height))
return width, height
except ValueError:
print("Invalid input. Using default size 20x15.")
return 20, 15
def setup_initial_configuration(self):
"""Setup the initial configuration of the grid"""
print("\nChoose initial configuration:")
print("1. Random")
print("2. Glider")
print("3. Blinker")
print("4. Block")
print("5. Beacon")
print("6. Toad")
print("7. Empty (manual setup)")
try:
choice = int(input("Enter choice (1-7): "))
if choice == 1:
density = float(input("Enter density (0.1-0.5): "))
density = max(0.1, min(0.5, density))
self.game.randomize_grid(density)
elif choice == 2:
self.game.load_pattern('glider')
elif choice == 3:
self.game.load_pattern('blinker')
elif choice == 4:
self.game.load_pattern('block')
elif choice == 5:
self.game.load_pattern('beacon')
elif choice == 6:
self.game.load_pattern('toad')
elif choice == 7:
self.manual_setup()
else:
print("Invalid choice. Using random configuration.")
self.game.randomize_grid(0.3)
except ValueError:
print("Invalid input. Using random configuration.")
self.game.randomize_grid(0.3)
def manual_setup(self):
"""Allow manual cell placement"""
print("Manual setup mode:")
print("Enter coordinates as 'x y' (0-based indexing)")
print("Enter 'done' when finished")
while True:
try:
user_input = input("Enter coordinates: ").strip()
if user_input.lower() == 'done':
break
x, y = map(int, user_input.split())
self.game.set_cell(x, y, 1)
print(f"Cell at ({x}, {y}) set to alive")
except (ValueError, IndexError):
print("Invalid input. Use format 'x y' or 'done'")
def run(self):
"""Run the console interface"""
print("Conway's Game of Life - Console Version")
print("=====================================")
# Get grid size
width, height = self.get_grid_size()
self.game = GameOfLife(width, height)
# Setup initial configuration
self.setup_initial_configuration()
# Get simulation speed
try:
delay = float(input("Enter delay between generations (0.1-2.0 seconds): "))
delay = max(0.1, min(2.0, delay))
except ValueError:
delay = 0.5
# Run simulation
self.running = True
try:
while self.running:
self.display_grid()
time.sleep(delay)
self.game.next_generation()
except KeyboardInterrupt:
print("\nSimulation stopped by user.")
class GUIInterface:
"""Graphical interface for the Game of Life using Tkinter"""
def __init__(self):
if not GUI_AVAILABLE:
raise ImportError("GUI libraries not available")
self.root = tk.Tk()
self.root.title("Conway's Game of Life")
self.root.geometry("800x600")
self.game = None
self.canvas = None
self.cell_size = 10
self.running = False
self.speed = 200 # milliseconds between generations
self.setup_gui()
def setup_gui(self):
"""Setup the GUI components"""
# Control frame
control_frame = tk.Frame(self.root)
control_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
# Buttons
tk.Button(control_frame, text="New Grid", command=self.new_grid).pack(side=tk.LEFT, padx=2)
tk.Button(control_frame, text="Random", command=self.randomize).pack(side=tk.LEFT, padx=2)
tk.Button(control_frame, text="Clear", command=self.clear).pack(side=tk.LEFT, padx=2)
tk.Button(control_frame, text="Start", command=self.start_simulation).pack(side=tk.LEFT, padx=2)
tk.Button(control_frame, text="Stop", command=self.stop_simulation).pack(side=tk.LEFT, padx=2)
tk.Button(control_frame, text="Step", command=self.step).pack(side=tk.LEFT, padx=2)
# Pattern selection
pattern_frame = tk.Frame(self.root)
pattern_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2)
tk.Label(pattern_frame, text="Patterns:").pack(side=tk.LEFT)
patterns = ['glider', 'blinker', 'block', 'beacon', 'toad']
for pattern in patterns:
tk.Button(pattern_frame, text=pattern.capitalize(),
command=lambda p=pattern: self.load_pattern(p)).pack(side=tk.LEFT, padx=2)
# Speed control
speed_frame = tk.Frame(self.root)
speed_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2)
tk.Label(speed_frame, text="Speed:").pack(side=tk.LEFT)
self.speed_scale = tk.Scale(speed_frame, from_=50, to=1000, orient=tk.HORIZONTAL,
command=self.update_speed)
self.speed_scale.set(self.speed)
self.speed_scale.pack(side=tk.LEFT, fill=tk.X, expand=True)
# Generation counter
self.generation_label = tk.Label(self.root, text="Generation: 0")
self.generation_label.pack(side=tk.TOP)
# Canvas for the grid
self.canvas = tk.Canvas(self.root, bg='white')
self.canvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5)
self.canvas.bind("<Button-1>", self.canvas_click)
self.canvas.bind("<B1-Motion>", self.canvas_drag)
# Initialize with default grid
self.new_grid()
def new_grid(self):
"""Create a new grid with user-specified dimensions"""
width = simpledialog.askinteger("Grid Width", "Enter grid width:",
initialvalue=40, minvalue=10, maxvalue=100)
if width is None:
return
height = simpledialog.askinteger("Grid Height", "Enter grid height:",
initialvalue=30, minvalue=10, maxvalue=80)
if height is None:
return
self.game = GameOfLife(width, height)
self.update_cell_size()
self.draw_grid()
def update_cell_size(self):
"""Calculate appropriate cell size based on canvas and grid dimensions"""
if self.game is None:
return
canvas_width = self.canvas.winfo_width()
canvas_height = self.canvas.winfo_height()
if canvas_width <= 1 or canvas_height <= 1:
canvas_width, canvas_height = 600, 400
max_cell_width = canvas_width // self.game.width
max_cell_height = canvas_height // self.game.height
self.cell_size = max(3, min(max_cell_width, max_cell_height, 20))
def draw_grid(self):
"""Draw the current grid state on the canvas"""
if self.game is None:
return
self.canvas.delete("all")
self.update_cell_size()
for y in range(self.game.height):
for x in range(self.game.width):
x1 = x * self.cell_size
y1 = y * self.cell_size
x2 = x1 + self.cell_size
y2 = y1 + self.cell_size
if self.game.get_cell(x, y) == 1:
# Alive cell - black
self.canvas.create_rectangle(x1, y1, x2, y2,
fill='black', outline='gray')
else:
# Dead cell - white with border
self.canvas.create_rectangle(x1, y1, x2, y2,
fill='white', outline='gray')
self.generation_label.config(text=f"Generation: {self.game.generation}")
def canvas_click(self, event):
"""Handle mouse clicks on the canvas to toggle cells"""
if self.game is None or self.running:
return
x = event.x // self.cell_size
y = event.y // self.cell_size
if 0 <= x < self.game.width and 0 <= y < self.game.height:
current_state = self.game.get_cell(x, y)
self.game.set_cell(x, y, 1 - current_state)
self.draw_grid()
def canvas_drag(self, event):
"""Handle mouse dragging to paint cells"""
self.canvas_click(event)
def randomize(self):
"""Randomize the grid"""
if self.game is None:
return
density = simpledialog.askfloat("Random Density",
"Enter cell density (0.0-1.0):",
initialvalue=0.3, minvalue=0.0, maxvalue=1.0)
if density is not None:
self.game.randomize_grid(density)
self.draw_grid()
def clear(self):
"""Clear the grid"""
if self.game is None:
return
self.game.clear_grid()
self.draw_grid()
def load_pattern(self, pattern_name):
"""Load a predefined pattern"""
if self.game is None:
return
self.game.load_pattern(pattern_name)
self.draw_grid()
def start_simulation(self):
"""Start the simulation"""
if self.game is None:
return
self.running = True
self.simulate_step()
def stop_simulation(self):
"""Stop the simulation"""
self.running = False
def step(self):
"""Advance one generation"""
if self.game is None:
return
self.game.next_generation()
self.draw_grid()
def simulate_step(self):
"""Perform one simulation step and schedule the next one"""
if self.running and self.game is not None:
self.game.next_generation()
self.draw_grid()
self.root.after(self.speed, self.simulate_step)
def update_speed(self, value):
"""Update simulation speed"""
self.speed = int(value)
def run(self):
"""Start the GUI application"""
self.root.mainloop()
def main():
"""Main function to choose between console and GUI interface"""
print("Conway's Game of Life")
print("====================")
if GUI_AVAILABLE:
print("Choose interface:")
print("1. Console")
print("2. GUI (Graphical)")
try:
choice = input("Enter choice (1 or 2): ").strip()
if choice == "1":
interface = ConsoleInterface()
interface.run()
elif choice == "2":
interface = GUIInterface()
interface.run()
else:
print("Invalid choice. Starting GUI interface.")
interface = GUIInterface()
interface.run()
except (ValueError, KeyboardInterrupt):
print("Starting console interface.")
interface = ConsoleInterface()
interface.run()
else:
print("GUI not available. Starting console interface.")
interface = ConsoleInterface()
interface.run()
if __name__ == "__main__":
main()
```
This comprehensive implementation includes:
## Features:
1. **Core Game Logic**:
- Proper implementation of Conway's Game of Life rules
- Grid management with boundary checking
- Generation tracking
2. **Console Interface**:
- Text-based grid display using Unicode characters
- User input for grid size and initial configuration
- Multiple pattern options
- Manual cell placement
- Adjustable simulation speed
3. **GUI Interface** (if Tkinter is available):
- Interactive grid where you can click to toggle cells
- Control buttons for simulation management
- Predefined pattern loading
- Speed control slider
- Real-time generation counter
4. **Pattern Library**:
- Glider (moves across the grid)
- Blinker (oscillates)
- Block (static)
- Beacon (oscillates)
- Toad (oscillates)
5. **User Options**:
- Custom grid sizes
- Random initialization with adjustable density
- Manual cell placement
- Pattern loading
- Speed control
## How to Use:
1. **Run the program**: `python game_of_life.py`
2. **Choose interface**: Console or GUI (if available)
3. **Set grid size**: Specify width and height
4. **Initialize**: Choose random, pattern, or manual setup
5. **Run simulation**: Watch the evolution of the cellular automaton
The program handles invalid inputs gracefully and provides clear instructions throughout the user interaction process.