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.
conway_alpha.py
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"""
11import arcade
12import random
13
14# Set how many rows and columns we will have
15ROW_COUNT = 70
16COLUMN_COUNT = 128
17
18# This sets the WIDTH and HEIGHT of each grid location
19CELL_WIDTH = 15
20CELL_HEIGHT = 15
21
22# This sets the margin between each cell
23# and on the edges of the screen.
24CELL_MARGIN = 0
25
26# Do the math to figure out our screen dimensions
27WINDOW_WIDTH = (CELL_WIDTH + CELL_MARGIN) * COLUMN_COUNT + CELL_MARGIN
28WINDOW_HEIGHT = (CELL_HEIGHT + CELL_MARGIN) * ROW_COUNT + CELL_MARGIN
29WINDOW_TITLE = "Conway's Game of Life"
30
31# Colors and alpha values
32ALIVE_COLOR = arcade.color.BISTRE
33BACKGROUND_COLOR = arcade.color.ANTIQUE_WHITE
34ALPHA_ON = 255
35ALPHA_OFF = 0
36
37
38def create_grids():
39 """
40 Create a 2D and 1D grid of sprites. We use the 1D SpriteList for drawing,
41 and the 2D list for accessing via grid. Both lists point to the same set of
42 sprites.
43 """
44 # One dimensional list of all sprites in the two-dimensional sprite list
45 grid_sprites_one_dim = arcade.SpriteList()
46
47 # This will be a two-dimensional grid of sprites to mirror the two
48 # dimensional grid of numbers. This points to the SAME sprites that are
49 # in grid_sprite_list, just in a 2d manner.
50 grid_sprites_two_dim = []
51
52 # Create a list of sprites to represent each grid location
53 for row in range(ROW_COUNT):
54 grid_sprites_two_dim.append([])
55
56 for column in range(COLUMN_COUNT):
57
58 # Make the sprite as a soft circle
59 sprite = arcade.SpriteCircle(CELL_WIDTH // 2, ALIVE_COLOR, soft=True)
60
61 # Position the sprite
62 x = column * (CELL_WIDTH + CELL_MARGIN) + (CELL_WIDTH / 2 + CELL_MARGIN)
63 y = row * (CELL_HEIGHT + CELL_MARGIN) + (CELL_HEIGHT / 2 + CELL_MARGIN)
64 sprite.center_x = x
65 sprite.center_y = y
66
67 # Add the sprite to both lists
68 grid_sprites_one_dim.append(sprite)
69 grid_sprites_two_dim[row].append(sprite)
70
71 return grid_sprites_one_dim, grid_sprites_two_dim
72
73
74def randomize_grid(grid: arcade.SpriteList):
75 """ Randomize the grid to alive/dead """
76 for cell in grid:
77 pick = random.randrange(2)
78 if pick:
79 cell.alpha = ALPHA_ON
80 else:
81 cell.alpha = ALPHA_OFF
82
83
84class GameView(arcade.View):
85 """
86 Main application class.
87 """
88
89 def __init__(self):
90 """
91 Set up the application.
92 """
93 super().__init__()
94
95 self.background_color = BACKGROUND_COLOR
96
97 # We need two layers. One holds the current state of our grid, the other
98 # holds the next frame's state. We flip back and forth between the two.
99 grid_sprites_one_dim1, grid_sprites_two_dim1 = create_grids()
100 grid_sprites_one_dim2, grid_sprites_two_dim2 = create_grids()
101
102 self.layers_grid_sprites_one_dim = [grid_sprites_one_dim1, grid_sprites_one_dim2]
103 self.layers_grid_sprites_two_dim = [grid_sprites_two_dim1, grid_sprites_two_dim2]
104
105 self.cur_layer = 0
106 randomize_grid(self.layers_grid_sprites_one_dim[0])
107
108 def reset(self):
109 """ Reset the grid """
110 randomize_grid(self.layers_grid_sprites_one_dim[0])
111
112 def on_draw(self):
113 """ Render the screen. """
114 # Clear all pixels in the window
115 self.clear()
116 self.layers_grid_sprites_one_dim[0].draw()
117
118 def on_key_press(self, symbol: int, modifiers: int):
119 """ Handle key press events """
120 if symbol == arcade.key.SPACE:
121 self.reset()
122 elif symbol == arcade.key.ESCAPE:
123 self.window.close()
124
125 def on_update(self, delta_time: float):
126 """ Update the grid """
127
128 # Flip layers
129 if self.cur_layer == 0:
130 layer1 = self.layers_grid_sprites_two_dim[0]
131 layer2 = self.layers_grid_sprites_two_dim[1]
132 self.cur_layer = 1
133 else:
134 layer1 = self.layers_grid_sprites_two_dim[1]
135 layer2 = self.layers_grid_sprites_two_dim[0]
136 self.cur_layer = 0
137
138 # Count the neighbors that are alive
139 for row in range(ROW_COUNT):
140 for column in range(COLUMN_COUNT):
141 live_neighbors = 0
142 # -1 -1
143 if row > 0 and column > 0 \
144 and layer1[row - 1][column - 1].alpha == ALPHA_ON:
145 live_neighbors += 1
146 # -1 0
147 if row > 0 and layer1[row - 1][column].alpha == ALPHA_ON:
148 live_neighbors += 1
149 # -1 +1
150 if row > 0 and column < COLUMN_COUNT - 1\
151 and layer1[row - 1][column + 1].alpha == ALPHA_ON:
152 live_neighbors += 1
153 # 0 +1
154 if column < COLUMN_COUNT - 1 \
155 and layer1[row][column + 1].alpha == ALPHA_ON:
156 live_neighbors += 1
157 # +1 +1
158 if row < ROW_COUNT - 1 \
159 and column < COLUMN_COUNT - 1 \
160 and layer1[row + 1][column + 1].alpha == ALPHA_ON:
161 live_neighbors += 1
162 # +1 0
163 if row < ROW_COUNT - 1 and layer1[row + 1][column].alpha == ALPHA_ON:
164 live_neighbors += 1
165 # +1 -1
166 if row < ROW_COUNT - 1 and column > 0 \
167 and layer1[row + 1][column - 1].alpha == ALPHA_ON:
168 live_neighbors += 1
169 # 0 -1
170 if column > 0 and layer1[row][column - 1].alpha == ALPHA_ON:
171 live_neighbors += 1
172
173 """
174 Implement Conway's game of life rules
175
176 Any live cell with two or three live neighbours survives.
177 Any dead cell with three live neighbours becomes a live cell.
178 All other live cells die in the next generation. Similarly,
179 all other dead cells stay dead.
180 """
181 # Shortcut the cell sprites
182 l1_sprite = layer1[row][column]
183 l2_sprite = layer2[row][column]
184
185 if l1_sprite.alpha == ALPHA_ON and (live_neighbors == 2 or live_neighbors == 3):
186 if l2_sprite.alpha == ALPHA_OFF:
187 l2_sprite.alpha = ALPHA_ON
188 elif l1_sprite.alpha == ALPHA_OFF and live_neighbors == 3:
189 if l2_sprite.alpha == ALPHA_OFF:
190 l2_sprite.alpha = ALPHA_ON
191 else:
192 if l2_sprite.alpha == ALPHA_ON:
193 l2_sprite.alpha = ALPHA_OFF
194
195
196def main():
197 """ Main function """
198 # Create a window class. This is what actually shows up on screen
199 window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
200 window.center_window()
201
202 # Create and setup the GameView
203 game = GameView()
204
205 # Show GameView on screen
206 window.show_view(game)
207
208 # Start the arcade game loop
209 arcade.run()
210
211
212if __name__ == "__main__":
213 main()