Game of life, pydroid v.2

import pygame
import pickle
import os
import copy

# — Configuration —
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
CELL_SIZE = 10
GRID_WIDTH = SCREEN_WIDTH // CELL_SIZE
GRID_HEIGHT = (SCREEN_HEIGHT – 100) // CELL_SIZE # Reserve space for buttons

# Colors
COLOR_WHITE = (255, 255, 255)
COLOR_BLACK = (0, 0, 0)
COLOR_GRAY = (40, 40, 40)
COLOR_GREEN = (0, 255, 0)
COLOR_BLUE = (30, 144, 255)
COLOR_RED = (255, 69, 0)

# — Game of Life Logic (using standard lists) —
def create_grid(width, height):
    “””Creates a 2D list to represent the grid.”””
    return [[0 for _ in range(height)] for _ in range(width)]

def next_generation(grid):
    “””Computes the next generation of the grid based on the rules.”””
    width = len(grid)
    height = len(grid[0])
    new_grid = copy.deepcopy(grid) # Use deepcopy for lists of lists

    for x in range(width):
        for y in range(height):
            # Count live neighbors, handling wrapping at the edges (toroidal array)
            total = 0
            for i in range(-1, 2):
                for j in range(-1, 2):
                    if i == 0 and j == 0:
                        continue
                    # Calculate wrapped coordinates
                    nx, ny = (x + i) % width, (y + j) % height
                    total += grid[nx][ny]

            # Apply Conway’s rules
            if grid[x][y] == 1:
                if (total < 2) or (total > 3):
                    new_grid[x][y] = 0
            else:
                if total == 3:
                    new_grid[x][y] = 1
    return new_grid

# — Classic Patterns —
def get_patterns():
    “””Returns a dictionary of classic Game of Life patterns.”””
    patterns = {
        “Gosper Glider Gun”: [
            (5, 1), (5, 2), (6, 1), (6, 2), (5, 11), (6, 11), (7, 11),
            (4, 12), (8, 12), (3, 13), (9, 13), (3, 14), (9, 14), (6, 15),
            (4, 16), (8, 16), (5, 17), (6, 17), (7, 17), (6, 18),
            (3, 21), (4, 21), (5, 21), (3, 22), (4, 22), (5, 22),
            (2, 23), (6, 23), (1, 25), (2, 25), (6, 25), (7, 25),
            (3, 35), (4, 35), (3, 36), (4, 36)
        ],
        “Glider”: [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)],
        “Pulsar”: [
            (2,4),(3,4),(4,4),(8,4),(9,4),(10,4), (2,5),(7,5),(9,5),(14,5),
            (2,6),(7,6),(9,6),(14,6), (4,7),(5,7),(6,7),(10,7),(11,7),(12,7),
            (4,9),(5,9),(6,9),(10,9),(11,9),(12,9), (2,10),(7,10),(9,10),(14,10),
            (2,11),(7,11),(9,11),(14,11), (2,12),(3,12),(4,12),(8,12),(9,12),(10,12)
        ],
        “Clear”: []
    }
    return patterns

def place_pattern(grid, pattern_name, offset_x=10, offset_y=5):
    “””Places a pattern onto the grid.”””
    patterns = get_patterns()
    pattern = patterns.get(pattern_name)
    width = len(grid)
    height = len(grid[0])
   
    # Clear grid before placing pattern
    for x in range(width):
        for y in range(height):
            grid[x][y] = 0
           
    if pattern:
        for pos in pattern:
            # Ensure pattern fits on screen
            if 0 <= pos[0] + offset_x < width and 0 <= pos[1] + offset_y < height:
                grid[pos[0] + offset_x][pos[1] + offset_y] = 1

# — Pygame Setup —
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption(“Conway’s Game of Life”)
clock = pygame.time.Clock()
font = pygame.font.SysFont(“sans”, 20)

# — Button Class —
class Button:
    def __init__(self, x, y, width, height, text, color):
        self.rect = pygame.Rect(x, y, width, height)
        self.text = text
        self.color = color

    def draw(self, surface):
        pygame.draw.rect(surface, self.color, self.rect)
        pygame.draw.rect(surface, COLOR_WHITE, self.rect, 2)
        text_surf = font.render(self.text, True, COLOR_WHITE)
        text_rect = text_surf.get_rect(center=self.rect.center)
        surface.blit(text_surf, text_rect)

    def is_clicked(self, pos):
        return self.rect.collidepoint(pos)

# — UI Elements —
buttons = {
    “play_pause”: Button(10, SCREEN_HEIGHT – 80, 100, 30, “Play”, COLOR_BLUE),
    “step”: Button(120, SCREEN_HEIGHT – 80, 70, 30, “Step”, COLOR_BLUE),
    “save”: Button(10, SCREEN_HEIGHT – 40, 70, 30, “Save”, COLOR_BLUE),
    “load”: Button(90, SCREEN_HEIGHT – 40, 70, 30, “Load”, COLOR_BLUE),
}
pattern_buttons = {}
pattern_names = list(get_patterns().keys())
x_offset = 200
for i, name in enumerate(pattern_names):
    pattern_buttons[name] = Button(x_offset + (i * 120), SCREEN_HEIGHT – 80, 110, 30, name, COLOR_GRAY)

# — Main Game Loop —
def main():
    grid = create_grid(GRID_WIDTH, GRID_HEIGHT)
    running = True
    paused = True
    drawing = False
    speed = 10  # Generations per second

    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT: running = False
            if event.type == pygame.KEYDOWN and event.key == pygame.K_AC_BACK: running = False

            if event.type == pygame.MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
                on_button = any(btn.is_clicked(pos) for btn in list(buttons.values()) + list(pattern_buttons.values()))

                if on_button:
                    # Handle UI buttons
                    if buttons[“play_pause”].is_clicked(pos):
                        paused = not paused
                        buttons[“play_pause”].text = “Play” if paused else “Pause”
                        buttons[“play_pause”].color = COLOR_BLUE if paused else COLOR_RED
                    elif buttons[“step”].is_clicked(pos) and paused:
                        grid = next_generation(grid)
                    elif buttons[“save”].is_clicked(pos):
                        try:
                            with open(“life_save.dat”, “wb”) as f:
                                pickle.dump(grid, f)
                            print(“Grid saved!”)
                        except Exception as e:
                            print(f”Error saving file: {e}”)
                    elif buttons[“load”].is_clicked(pos):
                        try:
                            if os.path.exists(“life_save.dat”):
                                with open(“life_save.dat”, “rb”) as f:
                                    grid = pickle.load(f)
                                print(“Grid loaded!”)
                        except Exception as e:
                            print(f”Error loading file: {e}”)
                   
                    for name, btn in pattern_buttons.items():
                        if btn.is_clicked(pos):
                            place_pattern(grid, name)
                else:
                    # Handle drawing on the grid
                    drawing = True
                    x, y = pos[0] // CELL_SIZE, pos[1] // CELL_SIZE
                    if 0 <= x < GRID_WIDTH and 0 <= y < GRID_HEIGHT:
                        grid[x][y] = 1 – grid[x][y] # Toggle cell

            if event.type == pygame.MOUSEBUTTONUP:
                drawing = False

            if event.type == pygame.MOUSEMOTION and drawing:
                pos = pygame.mouse.get_pos()
                x, y = pos[0] // CELL_SIZE, pos[1] // CELL_SIZE
                if 0 <= x < GRID_WIDTH and 0 <= y < GRID_HEIGHT:
                    grid[x][y] = 1 # Draw live cells

        if not paused:
            grid = next_generation(grid)

        # — Drawing —
        screen.fill(COLOR_BLACK)
        for x in range(GRID_WIDTH):
            for y in range(GRID_HEIGHT):
                if grid[x][y] == 1:
                    pygame.draw.rect(screen, COLOR_GREEN, (x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE, CELL_SIZE))
                pygame.draw.rect(screen, COLOR_GRAY, (x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE, CELL_SIZE), 1)

        pygame.draw.rect(screen, (50,50,50), (0, SCREEN_HEIGHT – 100, SCREEN_WIDTH, 100))
        for btn in buttons.values():
            btn.draw(screen)
        for btn in pattern_buttons.values():
            btn.draw(screen)
       
        pygame.display.flip()
        clock.tick(speed)

    pygame.quit()

if __name__ == ‘__main__’:
    main()

Leave a Reply