# 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:
10"""
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
27SCREEN_WIDTH = (CELL_WIDTH + CELL_MARGIN) * COLUMN_COUNT + CELL_MARGIN
28SCREEN_HEIGHT = (CELL_HEIGHT + CELL_MARGIN) * ROW_COUNT + CELL_MARGIN
29SCREEN_TITLE = "Conway's Game of Life"
30
31# Colors and alpha values
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
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
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
85    """
86    Main application class.
87    """
88
89    def __init__(self, width: int, height: int, title: str):
90        """
91        Set up the application.
92        """
93        super().__init__(width, height, title)
94
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)
107
108    def on_draw(self):
109        """ Render the screen. """
110
111        # This command has to happen before we start drawing
113        self.layers_grid_sprites_one_dim.draw()
114        self.layers_grid_sprites_one_dim.vbo_buf = None
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
122            layer2 = self.layers_grid_sprites_two_dim
123            self.cur_layer = 1
124        else:
125            layer1 = self.layers_grid_sprites_two_dim
126            layer2 = self.layers_grid_sprites_two_dim
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 and layer1[row - 1][column - 1].alpha == ALPHA_ON:
135                    live_neighbors += 1
136                # -1  0
137                if row > 0 and layer1[row - 1][column].alpha == ALPHA_ON:
138                    live_neighbors += 1
139                # -1 +1
140                if row > 0 and column < COLUMN_COUNT - 1 and layer1[row - 1][column + 1].alpha == ALPHA_ON:
141                    live_neighbors += 1
142                #  0 +1
143                if column < COLUMN_COUNT - 1 and layer1[row][column + 1].alpha == ALPHA_ON:
144                    live_neighbors += 1
145                # +1 +1
146                if row < ROW_COUNT - 1 and column < COLUMN_COUNT - 1 and layer1[row + 1][column + 1].alpha == ALPHA_ON:
147                    live_neighbors += 1
148                # +1  0
149                if row < ROW_COUNT - 1 and layer1[row + 1][column].alpha == ALPHA_ON:
150                    live_neighbors += 1
151                # +1 -1
152                if row < ROW_COUNT - 1 and column > 0 and layer1[row + 1][column - 1].alpha == ALPHA_ON:
153                    live_neighbors += 1
154                #  0 -1
155                if column > 0 and layer1[row][column - 1].alpha == ALPHA_ON:
156                    live_neighbors += 1
157
158                """
159                Implement Conway's game of life rules
160
161                Any live cell with two or three live neighbours survives.
162                Any dead cell with three live neighbours becomes a live cell.
163                All other live cells die in the next generation. Similarly, all other dead cells stay dead.
164                """
165                if layer1[row][column].alpha == ALPHA_ON and (live_neighbors == 2 or live_neighbors == 3):
166                    if layer2[row][column].alpha == ALPHA_OFF:
167                        layer2[row][column].alpha = ALPHA_ON
168                elif layer1[row][column].alpha == ALPHA_OFF and live_neighbors == 3:
169                    if layer2[row][column].alpha == ALPHA_OFF:
170                        layer2[row][column].alpha = ALPHA_ON
171                else:
172                    if layer2[row][column].alpha == ALPHA_ON:
173                        layer2[row][column].alpha = ALPHA_OFF
174
175
176def main():
177    """ Main method - starting point to the program """
178    window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
179    window.center_window()