def __init__(self, rows, cols, setup=None, loop=None, hexagonal=False):
        screen_res = self.__get_square_screen_size__()
        self.rows, self.cols = rows, cols
        self.hexagonal = hexagonal
        self.__generation_count__ = 0
        self.__grid_width__ = int(screen_res[0] * WIDTH_RATIO)
        self.__grid_height__ = int(screen_res[1] * HEIGHT_RATIO)
        self.__paused__ = False
        self.__game_paused__ = True
        self.__render_cells__ = {}

        self.theme = Theme(
            {
                "font": "Lucida Grande",
                "font_size": int((FONT_SIZE_NORMAL / 1280) * int(screen_res[0])),
                "font_size_small": int(FONT_SIZE_SMALL / 1280) * int(screen_res[0]),
                "gui_color": [255, 255, 255, 255],
                "disabled_color": [160, 160, 160, 255],
                "text_color": [255, 255, 255, 255],
                "highlight_color": [255, 255, 255, 64],
                "button": {
                    "down": {
                        "highlight": {
                            "image": {
                                "source": "button-highlight_2.png",
                                "frame": [8, 6, 2, 2],
                                "padding": [18, 18, 8, 6],
                            }
                        },
                        "image": {"source": "button-down_2.png", "frame": [6, 6, 3, 3], "padding": [12, 12, 4, 2]},
                        "text_color": [0, 0, 0, 255],
                    },
                    "up": {
                        "highlight": {
                            "image": {
                                "source": "button-highlight_2.png",
                                "frame": [8, 6, 2, 2],
                                "padding": [18, 18, 8, 6],
                            }
                        },
                        "image": {"source": "button_2.png", "frame": [6, 6, 3, 3], "padding": [12, 12, 4, 2]},
                    },
                },
            },
            resources_path="assets/",
        )

        super(LifeWindow, self).__init__(self.__grid_width__, int((1 / GRID_RATIO) * self.__grid_height__))
        self.set_caption("Conway's Game of Life")

        self.setup = setup
        if self.setup != None:
            self.setup(self)

        self.loop = loop

        def on_press_play(is_pressed):
            self.__game_paused__ = not self.__game_paused__
            if self.__game_paused__ == False:
                pyglet.clock.schedule_once(self.on_logic, SPEED)

        def on_press_reset(is_pressed):
            pyglet.clock.schedule_once(self.__reset_button__, RESET_BUTTON_PAUSE, self.reset)
            self.clear_grid()
            self.__generation_count__ = 0
            if self.setup != None:
                self.setup(self)

        def on_press_step(is_pressed):
            pyglet.clock.schedule_once(self.__reset_button__, RESET_BUTTON_PAUSE, self.step)
            self.on_logic()

        self.play = HighlightedButton("Pause", on_press=on_press_play)
        self.play._is_pressed = True
        self.reset = HighlightedButton("Reset", on_press=on_press_reset)
        self.step = HighlightedButton("Step", on_press=on_press_step)
        self.generation = Label(str(self.__generation_count__))
        self.button_container = Container(content=[self.play, self.reset, self.step, self.generation])
        self.batch = pyglet.graphics.Batch()

        Manager(self.button_container, window=self, theme=self.theme, batch=self.batch)

        pyglet.clock.schedule_interval(self.on_draw, 1 / FRAMERATE)

        pyglet.app.run()
    def __init__(self, rows, cols, setup=None, loop=None, hexagonal = False):
        screen_res = self.__get_square_screen_size__()
        self.rows, self.cols = rows, cols
        self.hexagonal = hexagonal
        self.__generation_count__ = 0
        self.__grid_width__ = int(screen_res[0] * WIDTH_RATIO)
        self.__grid_height__ = int(screen_res[1] * HEIGHT_RATIO)
        self.__paused__ = False
        self.__game_paused__ = True
        self.__render_cells__ = {}
        
        self.theme = Theme({
                  "font": "Lucida Grande",
                  "font_size": int((FONT_SIZE_NORMAL / 1280) * int(screen_res[0])),
                  "font_size_small": int(FONT_SIZE_SMALL / 1280) * int(screen_res[0]),
                  "gui_color": [255, 255, 255, 255],
                  "disabled_color": [160, 160, 160, 255],
                  "text_color": [255, 255, 255, 255],
                  "highlight_color": [255, 255, 255, 64],
                  "button": {
                      "down": {
                          "highlight": {
                              "image": {
                                  "source": "button-highlight_2.png",
                                  "frame": [8, 6, 2, 2],
                                  "padding": [18, 18, 8, 6]
                              }
                          },
                          "image": {
                              "source": "button-down_2.png",
                              "frame": [6, 6, 3, 3],
                              "padding": [12, 12, 4, 2]
                          },
                          "text_color": [0, 0, 0, 255]
                      },
                      "up": {
                          "highlight": {
                              "image": {
                                  "source": "button-highlight_2.png",
                                  "frame": [8, 6, 2, 2],
                                  "padding": [18, 18, 8, 6]
                              }
                          },
                          "image": {
                              "source": "button_2.png",
                              "frame": [6, 6, 3, 3],
                              "padding": [12, 12, 4, 2]
                          }
                      }
                  }}, resources_path='assets/')

        super(LifeWindow, self).__init__(
            self.__grid_width__, int((1/GRID_RATIO) * self.__grid_height__))
        self.set_caption("Conway\'s Game of Life")

        self.setup = setup
        if self.setup != None:
            self.setup(self)

        self.loop = loop

        def on_press_play(is_pressed):
            self.__game_paused__ = not self.__game_paused__
            if self.__game_paused__ == False:
                pyglet.clock.schedule_once(self.on_logic, SPEED)


        def on_press_reset(is_pressed):
            pyglet.clock.schedule_once(self.__reset_button__, RESET_BUTTON_PAUSE, self.reset)
            self.clear_grid()
            self.__generation_count__ = 0
            if self.setup != None:
                self.setup(self)


        def on_press_step(is_pressed):
            pyglet.clock.schedule_once(self.__reset_button__, RESET_BUTTON_PAUSE, self.step)
            self.on_logic()

        self.play = HighlightedButton('Pause', on_press = on_press_play)
        self.play._is_pressed = True
        self.reset = HighlightedButton('Reset', on_press = on_press_reset)
        self.step = HighlightedButton('Step', on_press = on_press_step)
        self.generation = Label(str(self.__generation_count__))
        self.button_container = Container(content=[self.play, self.reset, self.step, self.generation])
        self.batch = pyglet.graphics.Batch()

        Manager(self.button_container, window = self, theme = self.theme, batch = self.batch)

        pyglet.clock.schedule_interval(self.on_draw, 1 / FRAMERATE)

        pyglet.app.run()
class LifeWindow(pyglet.window.Window):
    def __init__(self, rows, cols, setup=None, loop=None, hexagonal=False):
        screen_res = self.__get_square_screen_size__()
        self.rows, self.cols = rows, cols
        self.hexagonal = hexagonal
        self.__generation_count__ = 0
        self.__grid_width__ = int(screen_res[0] * WIDTH_RATIO)
        self.__grid_height__ = int(screen_res[1] * HEIGHT_RATIO)
        self.__paused__ = False
        self.__game_paused__ = True
        self.__render_cells__ = {}

        self.theme = Theme(
            {
                "font": "Lucida Grande",
                "font_size": int((FONT_SIZE_NORMAL / 1280) * int(screen_res[0])),
                "font_size_small": int(FONT_SIZE_SMALL / 1280) * int(screen_res[0]),
                "gui_color": [255, 255, 255, 255],
                "disabled_color": [160, 160, 160, 255],
                "text_color": [255, 255, 255, 255],
                "highlight_color": [255, 255, 255, 64],
                "button": {
                    "down": {
                        "highlight": {
                            "image": {
                                "source": "button-highlight_2.png",
                                "frame": [8, 6, 2, 2],
                                "padding": [18, 18, 8, 6],
                            }
                        },
                        "image": {"source": "button-down_2.png", "frame": [6, 6, 3, 3], "padding": [12, 12, 4, 2]},
                        "text_color": [0, 0, 0, 255],
                    },
                    "up": {
                        "highlight": {
                            "image": {
                                "source": "button-highlight_2.png",
                                "frame": [8, 6, 2, 2],
                                "padding": [18, 18, 8, 6],
                            }
                        },
                        "image": {"source": "button_2.png", "frame": [6, 6, 3, 3], "padding": [12, 12, 4, 2]},
                    },
                },
            },
            resources_path="assets/",
        )

        super(LifeWindow, self).__init__(self.__grid_width__, int((1 / GRID_RATIO) * self.__grid_height__))
        self.set_caption("Conway's Game of Life")

        self.setup = setup
        if self.setup != None:
            self.setup(self)

        self.loop = loop

        def on_press_play(is_pressed):
            self.__game_paused__ = not self.__game_paused__
            if self.__game_paused__ == False:
                pyglet.clock.schedule_once(self.on_logic, SPEED)

        def on_press_reset(is_pressed):
            pyglet.clock.schedule_once(self.__reset_button__, RESET_BUTTON_PAUSE, self.reset)
            self.clear_grid()
            self.__generation_count__ = 0
            if self.setup != None:
                self.setup(self)

        def on_press_step(is_pressed):
            pyglet.clock.schedule_once(self.__reset_button__, RESET_BUTTON_PAUSE, self.step)
            self.on_logic()

        self.play = HighlightedButton("Pause", on_press=on_press_play)
        self.play._is_pressed = True
        self.reset = HighlightedButton("Reset", on_press=on_press_reset)
        self.step = HighlightedButton("Step", on_press=on_press_step)
        self.generation = Label(str(self.__generation_count__))
        self.button_container = Container(content=[self.play, self.reset, self.step, self.generation])
        self.batch = pyglet.graphics.Batch()

        Manager(self.button_container, window=self, theme=self.theme, batch=self.batch)

        pyglet.clock.schedule_interval(self.on_draw, 1 / FRAMERATE)

        pyglet.app.run()

    def on_logic(self, dt=SPEED):
        if self.loop != None:
            self.loop(self)
        self.__generation_count__ += 1
        if not self.__game_paused__:
            pyglet.clock.schedule_once(self.on_logic, SPEED)

    def on_draw(self, dt=1 / FRAMERATE):
        if not self.__paused__:
            glClearColor(VALUE_COLORS[0][0], VALUE_COLORS[0][1], VALUE_COLORS[0][2], 1.0)
            glLineWidth(LINE_THICKNESS)
            self.clear()
            self.__render_batch__ = pyglet.graphics.Batch()
            for cell in self.__render_cells__:
                new_val = self.__render_cells__[cell] % len(VALUE_COLORS)
                self.__draw_cell__(cell[0], cell[1], VALUE_COLORS[new_val])
            self.__draw_grid__((0.12, 0.59, 0.95))
            self.__draw_toolbar__()
            self.__render_all__()
            self.batch.draw()

    def create_cell(self, row=-1, col=-1, x=-1, y=-1, value=1):
        """Creates a cell at the specified row and column OR 
        (exclusive OR) at the position specified by (x, y).

        If the cell at the specified position is already alive,
        this function does nothing.


        Example:

        lifewindow.create_cell(row = 1, col = 2)
        lifewindow.create_cell(x = 2, y = 1)

        Both of the examples above create a cell at the same
        location.

        """
        self.__exec_cell__(lambda p: self.__render_cells__.update({p: value}), row, col, x, y)

    def check_cell(self, row=-1, col=-1, x=-1, y=-1):
        """Determines whether the cell at the specified row and
        column OR (exclusive OR) at the position specified by (x, y)
        is alive.

        Returns True if a cell is found at the specified position
        otherwise False.

        Example:

        lifewindow.check_cell(row = 1, col = 2)
        lifewindow.check_cell(x = 2, y = 1)

        Both of the examples above check for a cell at the same
        location.

        """
        return self.__exec_cell__(lambda c: c in self.__render_cells__, row, col, x, y)

    def kill_cell(self, row=-1, col=-1, x=-1, y=-1):
        """Kills a cell at the specified row and column OR 
        (exclusive OR) at the position specified by (x, y).

        If there is no living cell at the specified position,
        this function does nothing.


        Example:

        lifewindow.kill_cell(row = 1, col = 2)
        lifewindow.kill_cell(x = 2, y = 1)

        Both of the examples above kill a cell at the same
        location.

        """

        def get_rid_of_cell(p):
            if p in self.__render_cells__:
                del self.__render_cells__[p]

        self.__exec_cell__(lambda p: get_rid_of_cell(p), row, col, x, y)

    def clear_grid(self):
        """Kills all the cells on the grid."""

        self.__render_cells__ = {}

    def __draw_toolbar__(self):
        bottom_toolbar = self.__grid_height__
        top_toolbar = self.height
        padding = 9
        padded_bottom = bottom_toolbar + padding
        padded_top = top_toolbar - padding
        padded_left = padding
        padded_right = self.width - padding

        width = (padded_right - padded_left - padding * 2) // NUM_BUTTONS

        # left, top, right, bottom
        button_boundaries = lambda n: (
            n * (width) + padded_left + padding,
            padded_top - padding,
            (n + 1) * (width) + padded_left - padding,
            padded_bottom + padding,
        )

        self.__draw_rectangle__(0, top_toolbar, self.width, bottom_toolbar, (1.0, 1.0, 1.0))
        self.__draw_rectangle__(padded_left, padded_top, padded_right, padded_bottom, (0.506, 0.78, 0.518))

        def render_buttons(buttons):
            for button_index in range(len(buttons)):
                bound = button_boundaries(button_index)
                self.__draw_rectangle__(bound[0], bound[1], bound[2], bound[3], (1.0, 1.0, 1.0))

                buttons[button_index].set_position(bound[0], bound[3])
                buttons[button_index].width = bound[2] - bound[0]
                buttons[button_index].height = bound[1] - bound[3]

        render_buttons([self.play, self.reset, self.step])
        self.generation.set_text(str(self.__generation_count__))

    def __reset_button__(self, dt, button):
        self.reset._is_pressed = False
        self.reset.reload()

    def __exec_cell__(self, func, row=-1, col=-1, x=-1, y=-1):
        bad_arguments = lambda a, b, c, d: True if a < 0 or a > c or b < 0 or b > d else False
        if row == -1 and col == -1 and x == -1 and y == -1:
            raise ValueError("Please specify a position")
        elif (row == -1 and col == -1) or (x == -1 and y == -1):
            if row == -1 and col == -1:
                if bad_arguments(y, x, self.rows, self.cols):
                    raise ValueError("Position {} was out of range".format((x, y)))
                return func((y, x))
            else:
                if bad_arguments(row, col, self.rows, self.cols):
                    raise ValueError("Position {} was out of range".format((row, col)))
                return func((row, col))
        else:
            raise ValueError("Either give the position in (x, y) OR (row, col) but NOT both")

    def __draw_grid__(self, color):
        if not self.hexagonal:
            for i in range(0, self.__grid_width__, int(self.__grid_width__ / self.cols)):
                self.__draw_line__(i, 0, i, self.__grid_height__, color, LINE_THICKNESS)
            for j in range(0, self.__grid_height__, int(self.__grid_height__ / self.rows)):
                self.__draw_line__(0, j, self.__grid_width__, j, color, LINE_THICKNESS)
        else:
            # Number of radii in every row of the grid, both per row and per column
            num_radii_cols, num_radii_rows = self.cols + int(self.cols / 2), self.rows + int(self.rows / 2)
            # Length of each radius, both per row and per column
            radius_cols, radius_rows = self.__grid_width__ / num_radii_cols, self.__grid_height__ / num_radii_rows
            for r in range(0, num_radii_rows + 1):
                c = (r % 2) * 1.5
                while c < num_radii_cols:
                    self.__draw_line__(
                        int(c * radius_cols),
                        r * radius_rows,
                        int((c + 1) * radius_cols),
                        r * radius_rows,
                        color,
                        LINE_THICKNESS,
                    )
                    self.__draw_line__(
                        int(c * radius_cols),
                        r * radius_rows,
                        int((c - 0.5) * radius_cols),
                        (r - 1) * radius_rows,
                        color,
                        LINE_THICKNESS,
                    )
                    self.__draw_line__(
                        int((c + 1) * radius_cols),
                        r * radius_rows,
                        int((c + 1.5) * radius_cols),
                        (r - 1) * radius_rows,
                        color,
                        LINE_THICKNESS,
                    )
                    c += 3

    def __draw_cell__(self, row, col, color):
        if not self.hexagonal:
            dx, dy = (self.__grid_width__ // self.cols), (self.__grid_height__ // self.rows)
            x1, y1, x2, y2 = dx * col, dy * row, dx * (col + 1), dy * (row + 1)
            self.__draw_rectangle__(x1, y1, x2, y2, color)
        else:
            num_radii_cols, num_radii_rows = self.cols + int(self.cols / 2), self.rows + int(self.rows / 2)
            radius_cols, radius_rows = self.__grid_width__ / num_radii_cols, self.__grid_height__ / num_radii_rows
            center_x, center_y = None, None

            # Don't ask how it works. Ugly math.
            if col % 2 == 0:
                center_x, center_y = radius_cols * (col * 1.5 + 0.5), radius_rows * ((row + 0.5) * 2)
            else:
                center_x, center_y = radius_cols * (col * 1.5 + 0.5), radius_rows * row * 2
            self.__draw_hexagon__(center_x, center_y, radius_cols, radius_rows, color)

    def __draw_rectangle__(self, x1, y1, x2, y2, color):
        self.__render_batch__.add(4, GL_QUADS, None, ("v2f", (x1, y1, x1, y2, x2, y2, x2, y1)), ("c3f", color * 4))

    def __draw_hexagon__(self, x1, y1, radius_x, radius_y, color):
        vertices = [None] * 12
        # Six points around a circle is a regular hexagon
        for i in range(0, len(vertices), 2):
            vertices[i] = x1 + radius_x * math.cos(i / 12.0 * (2 * math.pi))
            vertices[i + 1] = y1 + radius_y * math.sin(i / 12.0 * (2 * math.pi))
        # TODO: Respect render order
        pyglet.graphics.draw(6, GL_POLYGON, ("v2f", vertices), ("c3f", color * 6))

    def __draw_pixel__(self, x, y, color):
        self.__render_batch__.add(1, GL_POINTS, None, ("v2f", (x, y)), ("c3B", (color[0], color[1], color[2])))

    def __draw_line__(self, x1, y1, x2, y2, color, width=LINE_THICKNESS):
        if width != LINE_THICKNESS:
            raise RuntimeError("All lines must be the same thickness for batch mode")
        self.__render_batch__.add(2, pyglet.gl.GL_LINES, None, ("v2f", (x1, y1, x2, y2)), ("c3f", color * 2))

    def __render_all__(self):
        self.__render_batch__.draw()

    def __draw_error__(self, error_string):
        self.paused = True
        print(error_string)

    def __get_screen_size__(self):
        screen = pyglet.window.Platform().get_default_display().get_default_screen()
        return (screen.width, screen.height)

    def __get_square_screen_size__(self):
        size = self.__get_screen_size__()
        s = min(size[0], size[1])
        return (s, s)
class LifeWindow(pyglet.window.Window):


    def __init__(self, rows, cols, setup=None, loop=None, hexagonal = False):
        screen_res = self.__get_square_screen_size__()
        self.rows, self.cols = rows, cols
        self.hexagonal = hexagonal
        self.__generation_count__ = 0
        self.__grid_width__ = int(screen_res[0] * WIDTH_RATIO)
        self.__grid_height__ = int(screen_res[1] * HEIGHT_RATIO)
        self.__paused__ = False
        self.__game_paused__ = True
        self.__render_cells__ = {}
        
        self.theme = Theme({
                  "font": "Lucida Grande",
                  "font_size": int((FONT_SIZE_NORMAL / 1280) * int(screen_res[0])),
                  "font_size_small": int(FONT_SIZE_SMALL / 1280) * int(screen_res[0]),
                  "gui_color": [255, 255, 255, 255],
                  "disabled_color": [160, 160, 160, 255],
                  "text_color": [255, 255, 255, 255],
                  "highlight_color": [255, 255, 255, 64],
                  "button": {
                      "down": {
                          "highlight": {
                              "image": {
                                  "source": "button-highlight_2.png",
                                  "frame": [8, 6, 2, 2],
                                  "padding": [18, 18, 8, 6]
                              }
                          },
                          "image": {
                              "source": "button-down_2.png",
                              "frame": [6, 6, 3, 3],
                              "padding": [12, 12, 4, 2]
                          },
                          "text_color": [0, 0, 0, 255]
                      },
                      "up": {
                          "highlight": {
                              "image": {
                                  "source": "button-highlight_2.png",
                                  "frame": [8, 6, 2, 2],
                                  "padding": [18, 18, 8, 6]
                              }
                          },
                          "image": {
                              "source": "button_2.png",
                              "frame": [6, 6, 3, 3],
                              "padding": [12, 12, 4, 2]
                          }
                      }
                  }}, resources_path='assets/')

        super(LifeWindow, self).__init__(
            self.__grid_width__, int((1/GRID_RATIO) * self.__grid_height__))
        self.set_caption("Conway\'s Game of Life")

        self.setup = setup
        if self.setup != None:
            self.setup(self)

        self.loop = loop

        def on_press_play(is_pressed):
            self.__game_paused__ = not self.__game_paused__
            if self.__game_paused__ == False:
                pyglet.clock.schedule_once(self.on_logic, SPEED)


        def on_press_reset(is_pressed):
            pyglet.clock.schedule_once(self.__reset_button__, RESET_BUTTON_PAUSE, self.reset)
            self.clear_grid()
            self.__generation_count__ = 0
            if self.setup != None:
                self.setup(self)


        def on_press_step(is_pressed):
            pyglet.clock.schedule_once(self.__reset_button__, RESET_BUTTON_PAUSE, self.step)
            self.on_logic()

        self.play = HighlightedButton('Pause', on_press = on_press_play)
        self.play._is_pressed = True
        self.reset = HighlightedButton('Reset', on_press = on_press_reset)
        self.step = HighlightedButton('Step', on_press = on_press_step)
        self.generation = Label(str(self.__generation_count__))
        self.button_container = Container(content=[self.play, self.reset, self.step, self.generation])
        self.batch = pyglet.graphics.Batch()

        Manager(self.button_container, window = self, theme = self.theme, batch = self.batch)

        pyglet.clock.schedule_interval(self.on_draw, 1 / FRAMERATE)

        pyglet.app.run()


    def on_logic(self, dt = SPEED):
        if self.loop != None:
            self.loop(self)
        self.__generation_count__ += 1
        if not self.__game_paused__:
            pyglet.clock.schedule_once(self.on_logic, SPEED)


    def on_draw(self, dt = 1 / FRAMERATE):
        if not self.__paused__:
            glClearColor(VALUE_COLORS[0][0], VALUE_COLORS[0][1], VALUE_COLORS[0][2], 1.0)
            glLineWidth(LINE_THICKNESS)
            self.clear()
            self.__render_batch__ = pyglet.graphics.Batch()
            for cell in self.__render_cells__:
                new_val = self.__render_cells__[cell] % len(VALUE_COLORS)
                self.__draw_cell__(cell[0], cell[1], VALUE_COLORS[new_val])
            self.__draw_grid__((.12, 0.59, 0.95))
            self.__draw_toolbar__()
            self.__render_all__()
            self.batch.draw()


    def create_cell(self, row = -1, col = -1, x = -1, y = -1, value = 1):
        """Creates a cell at the specified row and column OR 
        (exclusive OR) at the position specified by (x, y).

        If the cell at the specified position is already alive,
        this function does nothing.


        Example:

        lifewindow.create_cell(row = 1, col = 2)
        lifewindow.create_cell(x = 2, y = 1)

        Both of the examples above create a cell at the same
        location.

        """
        self.__exec_cell__(lambda p: self.__render_cells__.update({p: value}),
            row, col, x, y)


    def check_cell(self, row = -1, col = -1, x = -1, y = -1):
        """Determines whether the cell at the specified row and
        column OR (exclusive OR) at the position specified by (x, y)
        is alive.

        Returns True if a cell is found at the specified position
        otherwise False.

        Example:

        lifewindow.check_cell(row = 1, col = 2)
        lifewindow.check_cell(x = 2, y = 1)

        Both of the examples above check for a cell at the same
        location.

        """
        return self.__exec_cell__(lambda c: c in self.__render_cells__, \
            row, col, x, y)


    def kill_cell(self, row = -1, col = -1, x = -1, y = -1):
        """Kills a cell at the specified row and column OR 
        (exclusive OR) at the position specified by (x, y).

        If there is no living cell at the specified position,
        this function does nothing.


        Example:

        lifewindow.kill_cell(row = 1, col = 2)
        lifewindow.kill_cell(x = 2, y = 1)

        Both of the examples above kill a cell at the same
        location.

        """
        def get_rid_of_cell(p):
            if p in self.__render_cells__:
                del self.__render_cells__[p]


        self.__exec_cell__(lambda p: get_rid_of_cell(p), row, col, x, y)


    def clear_grid(self):
        """Kills all the cells on the grid."""

        self.__render_cells__ = {}


    def __draw_toolbar__(self):
        bottom_toolbar = self.__grid_height__
        top_toolbar = self.height
        padding = 9
        padded_bottom = bottom_toolbar + padding
        padded_top = top_toolbar - padding
        padded_left = padding
        padded_right = self.width - padding

        width = (padded_right - padded_left - padding * 2) // NUM_BUTTONS

        # left, top, right, bottom
        button_boundaries = lambda n: (n * (width) + padded_left + padding, \
            padded_top - padding, (n + 1) * (width) + padded_left - padding, \
            padded_bottom + padding)

        self.__draw_rectangle__(0, top_toolbar, self.width, bottom_toolbar, \
            (1., 1., 1.))
        self.__draw_rectangle__(padded_left, padded_top, padded_right, \
            padded_bottom, (0.506, 0.78, 0.518))


        def render_buttons(buttons):
            for button_index in range(len(buttons)):
                bound = button_boundaries(button_index)
                self.__draw_rectangle__(bound[0], bound[1], bound[2], bound[3], \
                    (1., 1., 1.))

                buttons[button_index].set_position(bound[0], bound[3])
                buttons[button_index].width = bound[2] - bound[0]
                buttons[button_index].height = bound[1] - bound[3]

        render_buttons([self.play, self.reset, self.step])
        self.generation.set_text(str(self.__generation_count__))


    def __reset_button__(self, dt, button):
        self.reset._is_pressed = False
        self.reset.reload()

    def __exec_cell__(self, func, row = -1, col = -1, x = -1, y = -1):
        bad_arguments = lambda a, b, c, d: True if a < 0 or a > c or b < 0 or b > d else False
        if row == -1 and col == -1 and x == -1 and y == -1:
            raise ValueError("Please specify a position")
        elif (row == -1 and col == -1) or (x == -1 and y == -1):
            if row == -1 and col == -1:
                if bad_arguments(y, x, self.rows, self.cols):
                    raise ValueError('Position {} was out of range'.format((x, y)))
                return func((y, x))
            else:
                if bad_arguments(row, col, self.rows, self.cols):
                    raise ValueError('Position {} was out of range'.format((row, col)))
                return func((row, col))
        else:
            raise ValueError('Either give the position in (x, y) OR (row, col) but NOT both')


    def __draw_grid__(self, color):
        if not self.hexagonal:
            for i in range(0, self.__grid_width__, int(self.__grid_width__ / self.cols)):
                self.__draw_line__(i, 0, i, self.__grid_height__, color, LINE_THICKNESS)
            for j in range(0, self.__grid_height__, int(self.__grid_height__ / self.rows)):
                self.__draw_line__(0, j, self.__grid_width__, j, color, LINE_THICKNESS)
        else:
            # Number of radii in every row of the grid, both per row and per column
            num_radii_cols, num_radii_rows = self.cols + int(self.cols / 2), self.rows + int(self.rows / 2)
            # Length of each radius, both per row and per column
            radius_cols, radius_rows = self.__grid_width__ / num_radii_cols, self.__grid_height__ / num_radii_rows
            for r in range(0, num_radii_rows + 1):
                c = (r % 2) * 1.5
                while c < num_radii_cols:
                    self.__draw_line__(int(c * radius_cols), r * radius_rows, int((c + 1) * radius_cols), r * radius_rows,
                        color, LINE_THICKNESS)
                    self.__draw_line__(int(c * radius_cols), r * radius_rows, int((c - 0.5) * radius_cols), (r - 1) * radius_rows,
                        color, LINE_THICKNESS)
                    self.__draw_line__(int((c + 1) * radius_cols), r * radius_rows, int((c + 1.5) * radius_cols), (r - 1) * radius_rows,
                        color, LINE_THICKNESS)
                    c += 3


    def __draw_cell__(self, row, col, color):
        if not self.hexagonal:
            dx, dy = (self.__grid_width__ // self.cols), (self.__grid_height__ // self.rows)
            x1, y1, x2, y2 = dx * col, dy * row, \
                 dx * (col + 1), dy * (row + 1)
            self.__draw_rectangle__(x1, y1, x2, y2, color)
        else:
            num_radii_cols, num_radii_rows = self.cols + int(self.cols / 2), self.rows + int(self.rows / 2)
            radius_cols, radius_rows = self.__grid_width__ / num_radii_cols, self.__grid_height__ / num_radii_rows
            center_x, center_y = None, None

            # Don't ask how it works. Ugly math.
            if (col % 2 == 0):
                center_x, center_y = radius_cols * (col * 1.5 + 0.5), radius_rows * ((row + 0.5) * 2)
            else:
                center_x, center_y = radius_cols * (col * 1.5 + 0.5), radius_rows * row * 2
            self.__draw_hexagon__(center_x, center_y, radius_cols, radius_rows, color)
 

    def __draw_rectangle__(self, x1, y1, x2, y2, color):
        self.__render_batch__.add(4, GL_QUADS, None,
            ('v2f', (x1, y1, x1, y2, x2, y2, x2, y1)),
            ('c3f', color * 4))


    def __draw_hexagon__(self, x1, y1, radius_x, radius_y, color):
        vertices = [None] * 12
        # Six points around a circle is a regular hexagon
        for i in range(0, len(vertices), 2):
            vertices[i] = x1 + radius_x * math.cos(i / 12.0 * (2 * math.pi))
            vertices[i + 1] = y1 + radius_y * math.sin(i / 12.0 * (2 * math.pi))
        # TODO: Respect render order
        pyglet.graphics.draw(6, GL_POLYGON, ('v2f', vertices), ('c3f', color * 6))


    def __draw_pixel__(self, x, y, color):
        self.__render_batch__.add(1, GL_POINTS, None,
            ('v2f', (x, y)),
            ('c3B', (color[0], color[1], color[2])))


    def __draw_line__(self, x1, y1, x2, y2, color, width = LINE_THICKNESS):
        if width != LINE_THICKNESS:
            raise RuntimeError("All lines must be the same thickness for batch mode")
        self.__render_batch__.add(2, pyglet.gl.GL_LINES, None,
            ('v2f', (x1, y1, x2, y2)), ('c3f', color * 2))


    def __render_all__(self):
        self.__render_batch__.draw()


    def __draw_error__(self, error_string):
        self.paused = True
        print(error_string)


    def __get_screen_size__(self):
        screen = pyglet.window.Platform().get_default_display(). \
            get_default_screen()
        return (screen.width, screen.height)


    def __get_square_screen_size__(self):
        size = self.__get_screen_size__()
        s = min(size[0], size[1])
        return (s, s)