Conway’s Game of Life#
This version of Conway’s Game of Life speeds everything up by using controlling a cell’s visibility through its alpha value, and handing the drawing logic off to the graphics card.
Grid-based games can take a while to render the program uses classic raster-based graphics. Every cell has to be re-drawn every single frame. If the cells are complex at all, that adds to the rendering time.
In this program, we create all cells in the grid to begin with. (This does causes the program to pause a while at start-up.)
After the sprites are created, we turn the cells on and off by their alpha value. We can update the entire grid by simply sending a list of alpha values to the graphics card. This significantly improves drawing time.
1"""
2Conway's Game of Life
3
4This code shows how to set up sprites in a grid, and then use their
5'alpha' value to quickly turn them on and off.
6
7After installing the "arcade" package version 2.4.4+, this program can be run by
8typing:
9python -m arcade.examples.conway_alpha
10"""
11from __future__ import annotations
12
13import arcade
14import random
15
16# Set how many rows and columns we will have
17ROW_COUNT = 70
18COLUMN_COUNT = 128
19
20# This sets the WIDTH and HEIGHT of each grid location
21CELL_WIDTH = 15
22CELL_HEIGHT = 15
23
24# This sets the margin between each cell
25# and on the edges of the screen.
26CELL_MARGIN = 0
27
28# Do the math to figure out our screen dimensions
29SCREEN_WIDTH = (CELL_WIDTH + CELL_MARGIN) * COLUMN_COUNT + CELL_MARGIN
30SCREEN_HEIGHT = (CELL_HEIGHT + CELL_MARGIN) * ROW_COUNT + CELL_MARGIN
31SCREEN_TITLE = "Conway's Game of Life"
32
33# Colors and alpha values
34ALIVE_COLOR = arcade.color.BISTRE
35BACKGROUND_COLOR = arcade.color.ANTIQUE_WHITE
36ALPHA_ON = 255
37ALPHA_OFF = 0
38
39
40def create_grids():
41 """
42 Create a 2D and 1D grid of sprites. We use the 1D SpriteList for drawing,
43 and the 2D list for accessing via grid. Both lists point to the same set of
44 sprites.
45 """
46 # One dimensional list of all sprites in the two-dimensional sprite list
47 grid_sprites_one_dim = arcade.SpriteList()
48
49 # This will be a two-dimensional grid of sprites to mirror the two
50 # dimensional grid of numbers. This points to the SAME sprites that are
51 # in grid_sprite_list, just in a 2d manner.
52 grid_sprites_two_dim = []
53
54 # Create a list of sprites to represent each grid location
55 for row in range(ROW_COUNT):
56 grid_sprites_two_dim.append([])
57
58 for column in range(COLUMN_COUNT):
59
60 # Make the sprite as a soft circle
61 sprite = arcade.SpriteCircle(CELL_WIDTH // 2, ALIVE_COLOR, soft=True)
62
63 # Position the sprite
64 x = column * (CELL_WIDTH + CELL_MARGIN) + (CELL_WIDTH / 2 + CELL_MARGIN)
65 y = row * (CELL_HEIGHT + CELL_MARGIN) + (CELL_HEIGHT / 2 + CELL_MARGIN)
66 sprite.center_x = x
67 sprite.center_y = y
68
69 # Add the sprite to both lists
70 grid_sprites_one_dim.append(sprite)
71 grid_sprites_two_dim[row].append(sprite)
72
73 return grid_sprites_one_dim, grid_sprites_two_dim
74
75
76def randomize_grid(grid: arcade.SpriteList):
77 """ Randomize the grid to alive/dead """
78 for cell in grid:
79 pick = random.randrange(2)
80 if pick:
81 cell.alpha = ALPHA_ON
82 else:
83 cell.alpha = ALPHA_OFF
84
85
86class MyGame(arcade.Window):
87 """
88 Main application class.
89 """
90
91 def __init__(self, width: int, height: int, title: str):
92 """
93 Set up the application.
94 """
95 super().__init__(width, height, title)
96
97 self.background_color = BACKGROUND_COLOR
98
99 # We need two layers. One holds the current state of our grid, the other
100 # holds the next frame's state. We flip back and forth between the two.
101 grid_sprites_one_dim1, grid_sprites_two_dim1 = create_grids()
102 grid_sprites_one_dim2, grid_sprites_two_dim2 = create_grids()
103
104 self.layers_grid_sprites_one_dim = [grid_sprites_one_dim1, grid_sprites_one_dim2]
105 self.layers_grid_sprites_two_dim = [grid_sprites_two_dim1, grid_sprites_two_dim2]
106
107 self.cur_layer = 0
108 randomize_grid(self.layers_grid_sprites_one_dim[0])
109
110 def on_draw(self):
111 """ Render the screen. """
112 # Clear all pixels in the window
113 self.clear()
114 self.layers_grid_sprites_one_dim[0].draw()
115
116 def on_update(self, delta_time: float):
117 """ Update the grid """
118
119 # Flip layers
120 if self.cur_layer == 0:
121 layer1 = self.layers_grid_sprites_two_dim[0]
122 layer2 = self.layers_grid_sprites_two_dim[1]
123 self.cur_layer = 1
124 else:
125 layer1 = self.layers_grid_sprites_two_dim[1]
126 layer2 = self.layers_grid_sprites_two_dim[0]
127 self.cur_layer = 0
128
129 # Count the neighbors that are alive
130 for row in range(ROW_COUNT):
131 for column in range(COLUMN_COUNT):
132 live_neighbors = 0
133 # -1 -1
134 if row > 0 and column > 0 \
135 and layer1[row - 1][column - 1].alpha == ALPHA_ON:
136 live_neighbors += 1
137 # -1 0
138 if row > 0 and layer1[row - 1][column].alpha == ALPHA_ON:
139 live_neighbors += 1
140 # -1 +1
141 if row > 0 and column < COLUMN_COUNT - 1\
142 and layer1[row - 1][column + 1].alpha == ALPHA_ON:
143 live_neighbors += 1
144 # 0 +1
145 if column < COLUMN_COUNT - 1 \
146 and layer1[row][column + 1].alpha == ALPHA_ON:
147 live_neighbors += 1
148 # +1 +1
149 if row < ROW_COUNT - 1 \
150 and column < COLUMN_COUNT - 1 \
151 and layer1[row + 1][column + 1].alpha == ALPHA_ON:
152 live_neighbors += 1
153 # +1 0
154 if row < ROW_COUNT - 1 and layer1[row + 1][column].alpha == ALPHA_ON:
155 live_neighbors += 1
156 # +1 -1
157 if row < ROW_COUNT - 1 and column > 0 \
158 and layer1[row + 1][column - 1].alpha == ALPHA_ON:
159 live_neighbors += 1
160 # 0 -1
161 if column > 0 and layer1[row][column - 1].alpha == ALPHA_ON:
162 live_neighbors += 1
163
164 """
165 Implement Conway's game of life rules
166
167 Any live cell with two or three live neighbours survives.
168 Any dead cell with three live neighbours becomes a live cell.
169 All other live cells die in the next generation. Similarly, all other dead cells stay dead.
170 """
171 if layer1[row][column].alpha == ALPHA_ON and (live_neighbors == 2 or live_neighbors == 3):
172 if layer2[row][column].alpha == ALPHA_OFF:
173 layer2[row][column].alpha = ALPHA_ON
174 elif layer1[row][column].alpha == ALPHA_OFF and live_neighbors == 3:
175 if layer2[row][column].alpha == ALPHA_OFF:
176 layer2[row][column].alpha = ALPHA_ON
177 else:
178 if layer2[row][column].alpha == ALPHA_ON:
179 layer2[row][column].alpha = ALPHA_OFF
180
181
182def main():
183 """ Main function - starting point to the program """
184 window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
185 window.center_window()
186 arcade.run()
187
188
189if __name__ == "__main__":
190 main()