-
Notifications
You must be signed in to change notification settings - Fork 0
/
gameOfLife_v2.py
166 lines (138 loc) · 8.1 KB
/
gameOfLife_v2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# ---------------------------------------------------------------------------
# SEGA97
# Simulation of Conway's Game of Life in pygame
# ---------------------------------------------------------------------------
# V2: Updated to user Nunpy array operations
# ---------------------------------------------------------------------------
# Possible speedups:
# TODO: Instead of redrawing the entire grid every frame, only update the cells that have changed since the last frame
# TODO: Rather than iterating over every cell in the grid every frame, only update cells that are near living cells
# TODO: Parallelize computations?
import pygame
import numpy as np
# import win32gui
# import win32con
# Initialize pygame font for displaying generation information
pygame.font.init()
# Define colors for the cells and background
col_about_to_die = (255, 0, 0) #red
col_alive = (255, 255, 215)
col_background = (10, 10, 40)
col_grid = (30, 30, 60)
# Initialize a font for displaying generation information
myfont = pygame.font.SysFont("monospace", 16)
# Function to update the game state and draw cells
def update(surface, cur, sz, gen):
# Create a new array for the next generation
nxt = np.zeros_like(cur)
# Use Numpy array operations for neighbor counting and updating
neighbor_count = (
np.roll(cur, (-1, -1), axis=(0, 1)) + np.roll(cur, (-1, 0), axis=(0, 1)) +
np.roll(cur, (-1, 1), axis=(0, 1)) + np.roll(cur, (0, -1), axis=(0, 1)) +
np.roll(cur, (0, 1), axis=(0, 1)) + np.roll(cur, (1, -1), axis=(0, 1)) +
np.roll(cur, (1, 0), axis=(0, 1)) + np.roll(cur, (1, 1), axis=(0, 1))
)
# Apply Conway's Game of Life rules using Numpy broadcasting
nxt[(cur == 1) & ((neighbor_count < 2) | (neighbor_count > 3))] = 0
nxt[(cur == 1) & ((neighbor_count == 2) | (neighbor_count == 3))] = 1
nxt[(cur == 0) & (neighbor_count == 3)] = 1
# Update cell colors directly using Numpy operations
alive_indices = np.argwhere(cur == 1)
dead_indices = np.argwhere(cur == 0)
# Convert indices to tuples for set_at function
alive_indices = [(idx[1] * sz, idx[0] * sz) for idx in alive_indices]
dead_indices = [(idx[1] * sz, idx[0] * sz) for idx in dead_indices]
# Fill the surface with the background color
surface.fill(col_background)
# Draw alive and dead cells with their respective color
for pos in alive_indices:
pygame.draw.rect(surface, col_alive, pygame.Rect(pos[0], pos[1], sz, sz))
for pos in dead_indices:
pygame.draw.rect(surface, col_background, pygame.Rect(pos[0], pos[1], sz, sz))
# Apply color change for cells about to die
about_to_die_indices = np.argwhere((cur == 1) & ((neighbor_count < 2) | (neighbor_count > 3)))
about_to_die_indices = [(idx[1] * sz, idx[0] * sz) for idx in about_to_die_indices]
for pos in about_to_die_indices:
pygame.draw.rect(surface, col_about_to_die, pygame.Rect(pos[0], pos[1], sz, sz))
# Render and display generation information
gentext = myfont.render("Generation: {0}".format(gen), 1, (255, 255, 255))
surface.blit(gentext, (0, cur.shape[0] * sz - 16))
# Return new array
return nxt
# Initialize the game grid with a given x and y size and an optional pattern
def init(dimx, dimy, pattern):
cells = np.zeros((dimy, dimx))
cells[:pattern.shape[0], :pattern.shape[1]] = pattern
return cells
# Initialize the game grid with gliders pattern
def init_gliders(dimx, dimy, pattern):
cells = np.zeros((dimy, dimx))
# Define patterns for one or two Gosper's glider guns
if pattern == '1':
# A single Gosper's glider gun creating gliders
pattern = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0],
[1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]])
elif pattern == '2':
# Two Gosper's glider gun creating gliders
pattern = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
[1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1],
[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0]])
# Position the pattern in the game grid
pos = (3,3) # Position where the pattern starts
cells[pos[0]:pos[0]+pattern.shape[0], pos[1]:pos[1]+pattern.shape[1]] = pattern
return cells
# Main function to run the game
def main(dimx, dimy, cellsize, glider_count, pattern):
# Initialize game state
pygame.init()
surface = pygame.display.set_mode((dimx * cellsize, dimy * cellsize))
pygame.display.set_caption("Py Game of Life")
# Initialize based on user input
if glider_count == '1':
cells = init_gliders(dimx, dimy, pattern)
elif glider_count == '2':
cells = init_gliders(dimx, dimy, pattern)
else:
cells = init(dimx, dimy, pattern)
gen = 0
clock = pygame.time.Clock()
running = True
# Game loop
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
cells = update(surface, cells, cellsize, gen)
pygame.display.update()
gen += 1
# clock.tick(10) # Limit frame rate to 10 FPS
# Get user input to determine the game setup
gliders = input("Run a Gosper's Glider Gun, creating gliders? (Yes, No): ").lower()
if gliders == 'yes':
glider_count = input("1 or 2 Gliders? (1, 2): ")
if glider_count == '1' or glider_count == '2':
main(120, 90, 8, glider_count)
else:
print("Invalid input for glider count.")
elif gliders == 'no':
n = int(input("Enter world width: "))
m = int(input("Enter world height: "))
s = int(input("Enter cell size: "))
c = float(input("Determine chance of each cell starting with a life (0:1): "))
pattern = np.random.choice([0, 1], size=(n, m), p=[1 - c, c])
main(n, m, s, None, pattern)
else:
print("Invalid input.")