
Game of Life
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.
Drag to resize
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() ```
Drag to resize
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!**
Drag to resize
# 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)
Drag to resize
## 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.
Drag to resize
# 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 simulation
Drag to resize
I'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.
Drag to resize