From a0d126de8b9fcf6bbc56851e841452af2dc1ffc3 Mon Sep 17 00:00:00 2001 From: mtoce Date: Mon, 27 Jul 2020 19:53:02 -0400 Subject: [PATCH 01/10] finished initial states, random states, clear states, and grid set up --- pygameoflife/__init__.py | 0 pygameoflife/__main__.py | 6 ++ pygameoflife/pygameoflife.py | 140 +++++++++++++++++++++++++++++++++++ venv/README.md | 0 venv/setup.py | 0 5 files changed, 146 insertions(+) create mode 100644 pygameoflife/__init__.py create mode 100644 pygameoflife/__main__.py create mode 100644 pygameoflife/pygameoflife.py create mode 100644 venv/README.md create mode 100644 venv/setup.py diff --git a/pygameoflife/__init__.py b/pygameoflife/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pygameoflife/__main__.py b/pygameoflife/__main__.py new file mode 100644 index 000000000..9da2711a6 --- /dev/null +++ b/pygameoflife/__main__.py @@ -0,0 +1,6 @@ +# Main function for pygameoflife.py + +# from pygameoflife import GameOfLife + +# game = GameOfLife() +# game.run() \ No newline at end of file diff --git a/pygameoflife/pygameoflife.py b/pygameoflife/pygameoflife.py new file mode 100644 index 000000000..01e6aa0eb --- /dev/null +++ b/pygameoflife/pygameoflife.py @@ -0,0 +1,140 @@ +# import pygame +import sys +import time +import random + +import pygame + +# colors +black = 0,0,0 +red = 255, 0, 0 +cyan = 0, 255, 255 + +# vars +BOARD_SIZE = WIDTH, HEIGHT = 640, 480 +CELL_SIZE = 10 +DEAD_COLOR = black +ALIVE_COLOR = cyan + +class GameOfLife(): + def __init__(self): + pygame.init() + self.screen = pygame.display.set_mode(BOARD_SIZE) + self.clear_screen() + self.cell_size = 10 + self.cell_radius = self.cell_size / 2 + pygame.display.flip() + + self.init_grids() + + def init_grids(self): + """ + Function for the initial grid + """ + self.num_cols = int(WIDTH / CELL_SIZE) + self.num_rows = int(HEIGHT / CELL_SIZE) + print("Columns: %d\nRows: %d" %(self.num_cols, self.num_rows)) + + # self.grids = [[[0] * self.num_rows] * self.num_cols, + # [[0] * self.num_rows] * self.num_cols] + self.grids = [] + rows = [] + row_num = 0 + for row_num in range(self.num_rows): + list_of_columns = [0] * self.num_cols + rows.append(list_of_columns) + + print(rows) + print(len(rows)) + self.active_grid = 0 + self.grids.append(rows) + self.set_grid() + + + def set_grid(self, value=None): + """ + Creates random grid of alive / dead cells + set_grid(0): all cells dead + set_grid(1): all cells alive + set_grid(): random cell_state grid + """ + for row in range(self.num_rows): + for col in range(self.num_cols): + self.grids[self.active_grid][row][col] = random.choice([0, 1]) + + def draw_grid(self): + """ + Draws circles for alive cells. + """ + # clear the screen + self.clear_screen + # draw the grid going col by col and row by row + for col in range(self.num_cols): + for row in range(self.num_rows): + # if grid value is 1 (alive), set it to that color + if self.grids[self.active_grid][row][col] == 1: + color = ALIVE_COLOR + # else if grid value is 0 (dead), set it to that color + else: + color = DEAD_COLOR + pygame.draw.circle(self.screen, + color, + (int(col * self.cell_size + self.cell_radius), + int(row * self.cell_size + self.cell_radius)), + int(CELL_SIZE / 2), + 0) + # flip to save display state + pygame.display.flip() + + + def clear_screen(self): + """ + Clears our screen to default color: currently -- black + """ + self.screen.fill(DEAD_COLOR) + self.init_grids() + + def user_events(self): + """ + Handles user events (user presses keys indicated below): + + "s": Toggle Game Pause + "r": Create Random Grid of Alive / Dead Cells + "q": Quit the Game of Life + """ + for event in pygame.event.get(): + # if event is keypress of "s" then toggle game pause + # if event is keypress of "r" then set grid + # if event is keypress of "q" then quit + if event.type == pygame.QUIT: sys.exit() + + self.screen.fill(DEAD_COLOR) + + # blit to draw + #screen.blit(ball, ballrect) + + # flip to push into memory + # pygame.display.flip() + + def generation_next(self): + """ + Inspect the crrent active generation. + Update the inactive grid to store next generation. + Swap out the active grid. + """ + self.set_grid(None) + + def run(self): + """Main loop, runs game of life""" + while True: + # Handle events, update the generation, and draw the grid. + self.user_events() + + # time_checking? + self.generation_next() + self.draw_grid() + time.sleep(0.5) + +if __name__ == "__main__": + game = GameOfLife() + game.run() \ No newline at end of file diff --git a/venv/README.md b/venv/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/venv/setup.py b/venv/setup.py new file mode 100644 index 000000000..e69de29bb From cb861c14c3bc06d538a42bef5e42cec9f7fed517 Mon Sep 17 00:00:00 2001 From: mtoce Date: Tue, 28 Jul 2020 16:19:57 -0400 Subject: [PATCH 02/10] FPS capped between gens (currently at 10) --- pygameoflife/pygameoflife.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/pygameoflife/pygameoflife.py b/pygameoflife/pygameoflife.py index 01e6aa0eb..9e885bf7d 100644 --- a/pygameoflife/pygameoflife.py +++ b/pygameoflife/pygameoflife.py @@ -2,7 +2,7 @@ import sys import time import random - +import datetime as dt import pygame # colors @@ -11,6 +11,7 @@ cyan = 0, 255, 255 # vars +MAX_FPS = 10 BOARD_SIZE = WIDTH, HEIGHT = 640, 480 CELL_SIZE = 10 DEAD_COLOR = black @@ -21,10 +22,10 @@ def __init__(self): pygame.init() self.screen = pygame.display.set_mode(BOARD_SIZE) self.clear_screen() + pygame.display.flip() self.cell_size = 10 self.cell_radius = self.cell_size / 2 - pygame.display.flip() - + self.last_gen_completed = 0 self.init_grids() def init_grids(self): @@ -35,11 +36,10 @@ def init_grids(self): self.num_rows = int(HEIGHT / CELL_SIZE) print("Columns: %d\nRows: %d" %(self.num_cols, self.num_rows)) - # self.grids = [[[0] * self.num_rows] * self.num_cols, - # [[0] * self.num_rows] * self.num_cols] self.grids = [] rows = [] - row_num = 0 + + # create the grid for row_num in range(self.num_rows): list_of_columns = [0] * self.num_cols rows.append(list_of_columns) @@ -124,8 +124,18 @@ def generation_next(self): """ self.set_grid(None) + def cap_frame_rate(self): + desired_time_between_gens_ms = (1.0 / MAX_FPS) * 1000.0 + now = pygame.time.get_ticks() + time_since_last_gen_ms = now - self.last_gen_completed + time_to_sleep = desired_time_between_gens_ms - time_since_last_gen_ms + if time_to_sleep > 0: + pygame.time.delay(int(time_to_sleep)) + self.last_gen_completed = now + def run(self): """Main loop, runs game of life""" + # desired_time_between_gens_ms = (1.0 / MAX_FPS) * 1000000.0 while True: # Handle events, update the generation, and draw the grid. self.user_events() @@ -133,7 +143,8 @@ def run(self): # time_checking? self.generation_next() self.draw_grid() - time.sleep(0.5) + self.cap_frame_rate() + if __name__ == "__main__": game = GameOfLife() From 36efbc4ab42b318fc204de18d8f01f972854f736 Mon Sep 17 00:00:00 2001 From: mtoce Date: Tue, 28 Jul 2020 16:28:59 -0400 Subject: [PATCH 03/10] added user input for FPS cap --- pygameoflife/pygameoflife.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pygameoflife/pygameoflife.py b/pygameoflife/pygameoflife.py index 9e885bf7d..2c4c3ecce 100644 --- a/pygameoflife/pygameoflife.py +++ b/pygameoflife/pygameoflife.py @@ -124,11 +124,16 @@ def generation_next(self): """ self.set_grid(None) - def cap_frame_rate(self): + def cap_frame_rate(self, MAX_FPS=10): + """ + Caps the frame rate at a user specified value + """ desired_time_between_gens_ms = (1.0 / MAX_FPS) * 1000.0 now = pygame.time.get_ticks() time_since_last_gen_ms = now - self.last_gen_completed time_to_sleep = desired_time_between_gens_ms - time_since_last_gen_ms + + # If program needs to wait, delay from time since last gen to a desired FPS cap if time_to_sleep > 0: pygame.time.delay(int(time_to_sleep)) self.last_gen_completed = now @@ -136,15 +141,13 @@ def cap_frame_rate(self): def run(self): """Main loop, runs game of life""" # desired_time_between_gens_ms = (1.0 / MAX_FPS) * 1000000.0 + FPS_MAX = input("How many FPS should each generation be? ") while True: # Handle events, update the generation, and draw the grid. self.user_events() - - # time_checking? self.generation_next() self.draw_grid() - self.cap_frame_rate() - + self.cap_frame_rate(float(FPS_MAX)) if __name__ == "__main__": game = GameOfLife() From 218d3076973fc76c4272d8f7a1db785b146b8581 Mon Sep 17 00:00:00 2001 From: mtoce Date: Wed, 29 Jul 2020 23:25:14 -0400 Subject: [PATCH 04/10] Added rules, need to add specific grid states (glider gun) --- pygameoflife/{pygameoflife.py => pygame_2.py} | 135 +++++++---- pygameoflife/pygame_of_life.py | 225 ++++++++++++++++++ 2 files changed, 318 insertions(+), 42 deletions(-) rename pygameoflife/{pygameoflife.py => pygame_2.py} (57%) create mode 100644 pygameoflife/pygame_of_life.py diff --git a/pygameoflife/pygameoflife.py b/pygameoflife/pygame_2.py similarity index 57% rename from pygameoflife/pygameoflife.py rename to pygameoflife/pygame_2.py index 2c4c3ecce..1b3450c38 100644 --- a/pygameoflife/pygameoflife.py +++ b/pygameoflife/pygame_2.py @@ -6,12 +6,11 @@ import pygame # colors -black = 0,0,0 +black = 0, 0, 0 red = 255, 0, 0 cyan = 0, 255, 255 # vars -MAX_FPS = 10 BOARD_SIZE = WIDTH, HEIGHT = 640, 480 CELL_SIZE = 10 DEAD_COLOR = black @@ -19,37 +18,38 @@ class GameOfLife(): def __init__(self): + # self.fps = float(input("How many FPS should each generation be? ")) + self.fps = 8.0 pygame.init() self.screen = pygame.display.set_mode(BOARD_SIZE) self.clear_screen() pygame.display.flip() self.cell_size = 10 self.cell_radius = self.cell_size / 2 + self.num_cols = int(WIDTH / CELL_SIZE) + self.num_rows = int(HEIGHT / CELL_SIZE) self.last_gen_completed = 0 + self.active_grid = 0 + self.grids = [] self.init_grids() def init_grids(self): """ Function for the initial grid """ - self.num_cols = int(WIDTH / CELL_SIZE) - self.num_rows = int(HEIGHT / CELL_SIZE) - print("Columns: %d\nRows: %d" %(self.num_cols, self.num_rows)) - - self.grids = [] - rows = [] - - # create the grid - for row_num in range(self.num_rows): - list_of_columns = [0] * self.num_cols - rows.append(list_of_columns) - print(rows) - print(len(rows)) - self.active_grid = 0 - self.grids.append(rows) + def create_grid(): + rows = [] + # create the grid: 2-D array of zeroes + for row_num in range(self.num_rows): + list_of_columns = [0] * self.num_cols + rows.append(list_of_columns) + return rows + + self.grids.append(create_grid()) + self.grids.append(create_grid()) + self.set_grid() - def set_grid(self, value=None): """ @@ -60,7 +60,11 @@ def set_grid(self, value=None): """ for row in range(self.num_rows): for col in range(self.num_cols): - self.grids[self.active_grid][row][col] = random.choice([0, 1]) + if value is None: + cell_value = random.randint(0, 1) + else: + cell_value = value + self.grids[self.active_grid][row][col] = cell_value def draw_grid(self): """ @@ -72,6 +76,8 @@ def draw_grid(self): for col in range(self.num_cols): for row in range(self.num_rows): # if grid value is 1 (alive), set it to that color + # print(self.grids[1]) + # print(self.active_grid) if self.grids[self.active_grid][row][col] == 1: color = ALIVE_COLOR # else if grid value is 0 (dead), set it to that color @@ -86,49 +92,73 @@ def draw_grid(self): # flip to save display state pygame.display.flip() + def inactive_grid(self): + return (self.active_grid + 1) % 2 def clear_screen(self): """ Clears our screen to default color: currently -- black """ self.screen.fill(DEAD_COLOR) - self.init_grids() - - def user_events(self): + + def get_cell(self, row_index, col_index): + try: + cell_value = self.grids[self.active_grid][row][col] + except: + cell_value = 0 + return cell_value + def check_cell_neighbors(self, row_index, col_index): """ - Handles user events (user presses keys indicated below): - - "s": Toggle Game Pause - "r": Create Random Grid of Alive / Dead Cells - "q": Quit the Game of Life + implement rules: 1-overpopulated, 2-underpopulated, 3-birth, 4-death """ - for event in pygame.event.get(): - # if event is keypress of "s" then toggle game pause - # if event is keypress of "r" then set grid - # if event is keypress of "q" then quit - if event.type == pygame.QUIT: sys.exit() - - self.screen.fill(DEAD_COLOR) + # Get the number of alive cells surrounding current cell - # blit to draw - #screen.blit(ball, ballrect) + # Check total alive of 8 cell neighbors: + # 3 Above - Left to Right + num_alive_neighbors = 0 + num_alive_neighbors += self.get_cell(row_index - 1, col_index - 1) + num_alive_neighbors += self.get_cell(row_index - 1, col_index - 1) + num_alive_neighbors += self.get_cell(row_index - 1, col_index - 1) + num_alive_neighbors += self.get_cell(row_index, col_index - 1) + num_alive_neighbors += self.get_cell(row_index, col_index + 1) + num_alive_neighbors += self.get_cell(row_index + 1, col_index - 1) + num_alive_neighbors += self.get_cell(row_index + 1, col_index - 1) + num_alive_neighbors += self.get_cell(row_index + 1, col_index - 1) - # flip to push into memory - # pygame.display.flip() + # # Cell Birth + if self.grids[self.active_grid][row_index][col_index] == 0 and num_alive_neighbors == 3: + return 1 + elif num_alive_neighbors > 4: + return 0 + elif num_alive_neighbors < 3: + return 0 + elif (num_alive_neighbors == 3 or num_alive_neighbors == 4) and self.grids[self.active_grid][row_index][col_index] == 1: + return 1 + else: + return 0 def generation_next(self): """ Inspect the crrent active generation. Update the inactive grid to store next generation. Swap out the active grid. """ - self.set_grid(None) + for row in range(self.num_rows): + for col in range(self.num_cols): + next_gen_state = self.check_cell_neighbors(row, col) + # set inactive grid future cell state + self.grids[self.inactive_grid()][row][col] = next_gen_state + + # update the inactive grid to store the next generation + self.active_grid = self.inactive_grid() + + # self.set_grid() def cap_frame_rate(self, MAX_FPS=10): """ Caps the frame rate at a user specified value """ - desired_time_between_gens_ms = (1.0 / MAX_FPS) * 1000.0 + desired_time_between_gens_ms = (1.0 / self.fps) * 1000.0 now = pygame.time.get_ticks() time_since_last_gen_ms = now - self.last_gen_completed time_to_sleep = desired_time_between_gens_ms - time_since_last_gen_ms @@ -138,16 +168,37 @@ def cap_frame_rate(self, MAX_FPS=10): pygame.time.delay(int(time_to_sleep)) self.last_gen_completed = now + def user_events(self): + """ + Handles user events (user presses keys indicated below): + + "s": Toggle Game Pause + "r": Create Random Grid of Alive / Dead Cells + "q": Quit the Game of Life + """ + for event in pygame.event.get(): + # if event is keypress of "s" then toggle game pause + # if event is keypress of "r" then set grid + # if event is keypress of "q" then quit + if event.type == pygame.QUIT: sys.exit() + + self.screen.fill(DEAD_COLOR) + + # blit to draw + #screen.blit(ball, ballrect) + + # flip to push into memory + # pygame.display.flip() def run(self): """Main loop, runs game of life""" # desired_time_between_gens_ms = (1.0 / MAX_FPS) * 1000000.0 - FPS_MAX = input("How many FPS should each generation be? ") + # FPS_MAX = input("How many FPS should each generation be? ") while True: # Handle events, update the generation, and draw the grid. self.user_events() self.generation_next() self.draw_grid() - self.cap_frame_rate(float(FPS_MAX)) + self.cap_frame_rate(self.fps) if __name__ == "__main__": game = GameOfLife() diff --git a/pygameoflife/pygame_of_life.py b/pygameoflife/pygame_of_life.py new file mode 100644 index 000000000..52fe4d922 --- /dev/null +++ b/pygameoflife/pygame_of_life.py @@ -0,0 +1,225 @@ +import random +import sys +import pygame + + +class LifeGame: + + def __init__(self, screen_width=800, screen_height=600, cell_size=10, alive_color=(0, 255, 255), + dead_color=(0, 0, 0), max_fps=10): + """ + Initialize grid, set default game state, initialize screen + :param screen_width: Game window width + :param screen_height: Game window height + :param cell_size: Diameter of circles. + :param alive_color: RGB tuple e.g. (255,255,255) for cells + :param dead_color: RGB tuple e.g. (255,255,255) + :param max_fps: Framerate cap to limit game speed + """ + pygame.init() + self.screen_width = screen_width + self.screen_height = screen_height + self.cell_size = cell_size + self.alive_color = alive_color + self.dead_color = dead_color + + self.screen = pygame.display.set_mode((self.screen_width, self.screen_height)) + self.clear_screen() + pygame.display.flip() + + self.max_fps = max_fps + + self.active_grid = 0 + self.num_cols = int(self.screen_width / self.cell_size) + self.num_rows = int(self.screen_height / self.cell_size) + self.grids = [] + self.init_grids() + self.set_grid() + + self.paused = False + self.game_over = False + + def init_grids(self): + """ + Create and stores the default active and inactive grid + :return: None + """ + def create_grid(): + """ + Generate an empty 2 grid + :return: + """ + rows = [] + for row_num in range(self.num_rows): + list_of_columns = [0] * self.num_cols + rows.append(list_of_columns) + return rows + self.grids.append(create_grid()) + self.grids.append(create_grid()) + + def set_grid(self, value=None, grid=0): + """ + Set an entire grid at once. Set to a single value or random 0/1. + Examples: + set_grid(0) # all dead + set_grid(1) # all alive + set_grid() # random + set_grid(None) # random + :param grid: Index of grid, for active/inactive (0 or 1) + :param value: Value to set the cell to (0 or 1) + :return: + """ + for r in range(self.num_rows): + for c in range(self.num_cols): + if value is None: + cell_value = random.randint(0, 1) + else: + cell_value = value + self.grids[grid][r][c] = cell_value + + def draw_grid(self): + """ + Given the grid and cell states, draw the cells on the screen + :return: + """ + self.clear_screen() + for c in range(self.num_cols): + for r in range(self.num_rows): + if self.grids[self.active_grid][r][c] == 1: + color = self.alive_color + else: + color = self.dead_color + pygame.draw.circle(self.screen, + color, + (int(c * self.cell_size + (self.cell_size / 2)), + int(r * self.cell_size + (self.cell_size / 2))), + int(self.cell_size / 2), + 0) + pygame.display.flip() + + def clear_screen(self): + """ + Fill whole screen with dead color + :return: + """ + self.screen.fill(self.dead_color) + + def get_cell(self, row_num, col_num): + """ + Get the alive/dead (0/1) state of a specific cell in active grid + :param row_num: + :param col_num: + :return: 0 or 1 depending on state of cell. Defaults to 0 (dead) + """ + try: + cell_value = self.grids[self.active_grid][row_num][col_num] + except: + cell_value = 0 + return cell_value + + def check_cell_neighbors(self, row_index, col_index): + """ + Get the number of alive neighbor cells, and determine the state of the cell + for the next generation. Determine whether it lives, dies, survives, or is born. + :param row_index: Row number of cell to check + :param col_index: Column number of cell to check + :return: The state the cell should be in next generation (0 or 1) + """ + num_alive_neighbors = 0 + num_alive_neighbors += self.get_cell(row_index - 1, col_index - 1) + num_alive_neighbors += self.get_cell(row_index - 1, col_index) + num_alive_neighbors += self.get_cell(row_index - 1, col_index + 1) + num_alive_neighbors += self.get_cell(row_index, col_index - 1) + num_alive_neighbors += self.get_cell(row_index, col_index + 1) + num_alive_neighbors += self.get_cell(row_index + 1, col_index - 1) + num_alive_neighbors += self.get_cell(row_index + 1, col_index) + num_alive_neighbors += self.get_cell(row_index + 1, col_index + 1) + + # Rules for life and death + if self.grids[self.active_grid][row_index][col_index] == 1: # alive + if num_alive_neighbors > 3: # Overpopulation + return 0 + if num_alive_neighbors < 2: # Underpopulation + return 0 + if num_alive_neighbors == 2 or num_alive_neighbors == 3: + return 1 + elif self.grids[self.active_grid][row_index][col_index] == 0: # dead + if num_alive_neighbors == 3: + return 1 # come to life + + return self.grids[self.active_grid][row_index][col_index] + + def update_generation(self): + """ + Inspect current generation state, prepare next generation + :return: + """ + self.set_grid(0, self.inactive_grid()) + for r in range(self.num_rows - 1): + for c in range(self.num_cols - 1): + next_gen_state = self.check_cell_neighbors(r, c) + self.grids[self.inactive_grid()][r][c] = next_gen_state + self.active_grid = self.inactive_grid() + + def inactive_grid(self): + """ + Simple helper function to get the index of the inactive grid + If active grid is 0 will return 1 and vice-versa. + :return: + """ + return (self.active_grid + 1) % 2 + + def handle_events(self): + """ + Handle any keypresses + s - start/stop (pause) the game + q - quit + r - randomize grid + :return: + """ + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN: + print("key pressed") + if event.unicode == 's': + print("Toggling pause.") + if self.paused: + self.paused = False + else: + self.paused = True + elif event.unicode == 'r': + print("Randomizing grid.") + self.active_grid = 0 + self.set_grid(None, self.active_grid) # randomize + self.set_grid(0, self.inactive_grid()) # set to 0 + self.draw_grid() + elif event.unicode == 'q': + print("Exiting.") + self.game_over = True + if event.type == pygame.QUIT: + sys.exit() + + def run(self): + """ + Kick off the game and loop forever until quit state + :return: + """ + + clock = pygame.time.Clock() + + while True: + if self.game_over: + return + + self.handle_events() + + if not self.paused: + self.update_generation() + self.draw_grid() + + clock.tick(self.max_fps) + +if __name__ == "__main__": + """ + Launch a game of life + """ + LifeGame().run() \ No newline at end of file From 5f5eeffb687f382ee094b18f1ca11fececdf0042 Mon Sep 17 00:00:00 2001 From: mtoce Date: Thu, 30 Jul 2020 16:53:06 -0400 Subject: [PATCH 05/10] Added jump X gens and skip 1 gen --- README.md | 12 ++--- pygameoflife/mouse_events.py | 22 +++++++++ pygameoflife/pygame_of_life.py | 85 ++++++++++++++++++++++++++++++---- 3 files changed, 105 insertions(+), 14 deletions(-) create mode 100644 pygameoflife/mouse_events.py diff --git a/README.md b/README.md index b4dcd6e71..99aa90c43 100644 --- a/README.md +++ b/README.md @@ -122,16 +122,16 @@ Implement at least 3 of the following features: * Create a few sample cell configurations that users can load and run * Add an option that creates a random cell configuration that users can - run -* Add additional cell properties, like color or size, and incorporate + run 1 +* Add additional cell properties, like color or size, and incorporate 2 them into your visualization -* Allow users to specify the speed of the simulation +* Allow users to specify the speed of the simulation 3 * Provide functionality to manually step through the simulation one - generation at a time, as opposed to animating automatically -* Allow users to change the dimension of the grid being displayed + generation at a time, as opposed to animating automatically 4 +* Allow users to change the dimension of the grid being displayed 5 * Given a specific generation, calculate the configuration of cells at that point in time, and jump to that state, bypassing animation (i.e. - skip ahead _n_ generations). + skip ahead _n_ generations). 6 * If you have an idea for a custom feature on this list, run it by your TL or instructor diff --git a/pygameoflife/mouse_events.py b/pygameoflife/mouse_events.py new file mode 100644 index 000000000..4a67d6c04 --- /dev/null +++ b/pygameoflife/mouse_events.py @@ -0,0 +1,22 @@ +def mouse_events(): + LEFT = 1 + RIGHT = 3 + + running = 1 + screen = pygame.display.set_mode((320, 200)) + + while running: + event = pygame.event.poll() + if event.type == pygame.QUIT: + running = 0 + elif event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT: + print "You pressed the left mouse button at (%d, %d)" % event.pos + elif event.type == pygame.MOUSEBUTTONUP and event.button == LEFT: + print "You released the left mouse button at (%d, %d)" % event.pos + elif event.type == pygame.MOUSEBUTTONDOWN and event.button == RIGHT: + print "You pressed the right mouse button at (%d, %d)" % event.pos + elif event.type == pygame.MOUSEBUTTONUP and event.button == RIGHT: + print "You released the right mouse button at (%d, %d)" % event.pos + + screen.fill((0, 0, 0)) + pygame.display.flip() \ No newline at end of file diff --git a/pygameoflife/pygame_of_life.py b/pygameoflife/pygame_of_life.py index 52fe4d922..b4c647f2c 100644 --- a/pygameoflife/pygame_of_life.py +++ b/pygameoflife/pygame_of_life.py @@ -1,9 +1,9 @@ import random import sys import pygame +import numpy as np - -class LifeGame: +class GameOfLife: def __init__(self, screen_width=800, screen_height=600, cell_size=10, alive_color=(0, 255, 255), dead_color=(0, 0, 0), max_fps=10): @@ -27,7 +27,9 @@ def __init__(self, screen_width=800, screen_height=600, cell_size=10, alive_colo self.clear_screen() pygame.display.flip() - self.max_fps = max_fps + # FPS gen-to-gen (allow for user input?) + # self.max_fps = max_fps + self.max_fps = float(input("Enter your desired FPS between generations (sugested 1-10) ")) self.active_grid = 0 self.num_cols = int(self.screen_width / self.cell_size) @@ -97,12 +99,44 @@ def draw_grid(self): 0) pygame.display.flip() + # def glider_gun(self): + # glider_gun =[ + # [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,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,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,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], + # [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], + # [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,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,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,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] + # ] + # self.screen_width = 700 + # self.screen_height = 500 + # self.cell_size = 10 + # self.init_grids() + # padding_row = 10 + # padding_col = 10 + # row = 0 + # col = 0 + # for row in range(0, 9): + # for col in range(0, 36): + # # overwrite current cell state with those in glider_gun variable + # print("CC: ", self.grids[self.active_grid][row + padding_row][col + padding_col]) + # print("GG: ", glider_gun[row][col]) + # self.grids[self.active_grid][row + padding_row][col + padding_col] = glider_gun[row][col] + # # X = np.zeros((int(self.screen_width/self.cell_size), int(self.screen_height/self.cell_size))) + # # X = np.array(X) + # # X[1:10,1:37] = glider_gun + # pygame.display.flip() + def clear_screen(self): """ Fill whole screen with dead color :return: """ self.screen.fill(self.dead_color) + pygame.display.flip() def get_cell(self, row_num, col_num): """ @@ -172,29 +206,64 @@ def inactive_grid(self): def handle_events(self): """ Handle any keypresses - s - start/stop (pause) the game + p - pause the game q - quit r - randomize grid + + #TODO + s - change size of screen + f - change fps max between gens + g - initialize glider gun input + i - interact with screen with mouse events (click to turn cells alive) :return: """ for event in pygame.event.get(): if event.type == pygame.KEYDOWN: print("key pressed") - if event.unicode == 's': + if event.unicode == 'p': # toggle pause print("Toggling pause.") if self.paused: self.paused = False else: self.paused = True - elif event.unicode == 'r': + elif event.unicode == 'c': # clear screen (set all dead) + print("Clearing screen.") + self.clear_screen() + elif event.unicode == 'r': # randomize grid print("Randomizing grid.") self.active_grid = 0 self.set_grid(None, self.active_grid) # randomize self.set_grid(0, self.inactive_grid()) # set to 0 self.draw_grid() - elif event.unicode == 'q': + elif event.unicode == 'q': # quit print("Exiting.") self.game_over = True + elif event.unicode == 'n': # next generation + print("Stepping forward 1 generation.") + self.update_generation() + self.draw_grid() + elif event.unicode == 'j': # jump forward x generations + skip_step = int(input("Enter # of gens to skip ahead. ")) + # print(f"Jumping forward {skip_step} generations. ") + i = 0 + last_skip = skip_step - 1 + for i in range(0, skip_step): + if i != last_skip: + self.update_generation() + else: + self.update_generation() + self.draw_grid() + # elif event.unicode == 's': + # # change size of the screen in init + # elif event.unicode == 'g': + # # initialize glider grid + # print("Cosper Glider-Gun grid.") + # self.active_grid = self.glider_gun() + # # self.set_grid(None, self.active_grid) # randomize + # # self.set_grid(0, self.inactive_grid()) # set to 0 + # self.draw_grid() + # elif event.unicode == 'i': + # # interact via mouse events (turn cells off and on with : and R-click) if event.type == pygame.QUIT: sys.exit() @@ -222,4 +291,4 @@ def run(self): """ Launch a game of life """ - LifeGame().run() \ No newline at end of file + GameOfLife().run() \ No newline at end of file From 09ba17ccb65c625ffb3f6704156abfc72396f60c Mon Sep 17 00:00:00 2001 From: mtoce Date: Fri, 31 Jul 2020 12:39:59 -0400 Subject: [PATCH 06/10] generation counter added --- pygameoflife/extra_code.py | 56 +++++ pygameoflife/mouse_events.py | 22 -- pygameoflife/pygame_2.py | 205 ------------------ .../{pygame_of_life.py => pygameoflife.py} | 64 ++---- 4 files changed, 80 insertions(+), 267 deletions(-) create mode 100644 pygameoflife/extra_code.py delete mode 100644 pygameoflife/mouse_events.py delete mode 100644 pygameoflife/pygame_2.py rename pygameoflife/{pygame_of_life.py => pygameoflife.py} (82%) diff --git a/pygameoflife/extra_code.py b/pygameoflife/extra_code.py new file mode 100644 index 000000000..59195a092 --- /dev/null +++ b/pygameoflife/extra_code.py @@ -0,0 +1,56 @@ +# def mouse_events(): +# LEFT = 1 +# RIGHT = 3 + +# running = 1 +# screen = pygame.display.set_mode((320, 200)) + +# while running: +# event = pygame.event.poll() +# if event.type == pygame.QUIT: +# running = 0 +# elif event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT: +# print "You pressed the left mouse button at (%d, %d)" % event.pos +# elif event.type == pygame.MOUSEBUTTONUP and event.button == LEFT: +# print "You released the left mouse button at (%d, %d)" % event.pos +# elif event.type == pygame.MOUSEBUTTONDOWN and event.button == RIGHT: +# print "You pressed the right mouse button at (%d, %d)" % event.pos +# elif event.type == pygame.MOUSEBUTTONUP and event.button == RIGHT: +# print "You released the right mouse button at (%d, %d)" % event.pos + +# screen.fill((0, 0, 0)) +# pygame.display.flip() + +# --------------------------------------------------------------------------------------- + +# def glider_gun(self): +# glider_gun =[ +# [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,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,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,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], +# [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], +# [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,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,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,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] +# ] +# self.screen_width = 700 +# self.screen_height = 500 +# self.cell_size = 10 +# self.init_grids() +# padding_row = 10 +# padding_col = 10 +# row = 0 +# col = 0 +# for row in range(0, 9): +# for col in range(0, 36): +# # overwrite current cell state with those in glider_gun variable +# print("CC: ", self.grids[self.active_grid][row + padding_row][col + padding_col]) +# print("GG: ", glider_gun[row][col]) +# self.grids[self.active_grid][row + padding_row][col + padding_col] = glider_gun[row][col] +# # X = np.zeros((int(self.screen_width/self.cell_size), int(self.screen_height/self.cell_size))) +# # X = np.array(X) +# # X[1:10,1:37] = glider_gun +# pygame.display.flip() + \ No newline at end of file diff --git a/pygameoflife/mouse_events.py b/pygameoflife/mouse_events.py deleted file mode 100644 index 4a67d6c04..000000000 --- a/pygameoflife/mouse_events.py +++ /dev/null @@ -1,22 +0,0 @@ -def mouse_events(): - LEFT = 1 - RIGHT = 3 - - running = 1 - screen = pygame.display.set_mode((320, 200)) - - while running: - event = pygame.event.poll() - if event.type == pygame.QUIT: - running = 0 - elif event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT: - print "You pressed the left mouse button at (%d, %d)" % event.pos - elif event.type == pygame.MOUSEBUTTONUP and event.button == LEFT: - print "You released the left mouse button at (%d, %d)" % event.pos - elif event.type == pygame.MOUSEBUTTONDOWN and event.button == RIGHT: - print "You pressed the right mouse button at (%d, %d)" % event.pos - elif event.type == pygame.MOUSEBUTTONUP and event.button == RIGHT: - print "You released the right mouse button at (%d, %d)" % event.pos - - screen.fill((0, 0, 0)) - pygame.display.flip() \ No newline at end of file diff --git a/pygameoflife/pygame_2.py b/pygameoflife/pygame_2.py deleted file mode 100644 index 1b3450c38..000000000 --- a/pygameoflife/pygame_2.py +++ /dev/null @@ -1,205 +0,0 @@ -# import pygame -import sys -import time -import random -import datetime as dt -import pygame - -# colors -black = 0, 0, 0 -red = 255, 0, 0 -cyan = 0, 255, 255 - -# vars -BOARD_SIZE = WIDTH, HEIGHT = 640, 480 -CELL_SIZE = 10 -DEAD_COLOR = black -ALIVE_COLOR = cyan - -class GameOfLife(): - def __init__(self): - # self.fps = float(input("How many FPS should each generation be? ")) - self.fps = 8.0 - pygame.init() - self.screen = pygame.display.set_mode(BOARD_SIZE) - self.clear_screen() - pygame.display.flip() - self.cell_size = 10 - self.cell_radius = self.cell_size / 2 - self.num_cols = int(WIDTH / CELL_SIZE) - self.num_rows = int(HEIGHT / CELL_SIZE) - self.last_gen_completed = 0 - self.active_grid = 0 - self.grids = [] - self.init_grids() - - def init_grids(self): - """ - Function for the initial grid - """ - - def create_grid(): - rows = [] - # create the grid: 2-D array of zeroes - for row_num in range(self.num_rows): - list_of_columns = [0] * self.num_cols - rows.append(list_of_columns) - return rows - - self.grids.append(create_grid()) - self.grids.append(create_grid()) - - self.set_grid() - - def set_grid(self, value=None): - """ - Creates random grid of alive / dead cells - set_grid(0): all cells dead - set_grid(1): all cells alive - set_grid(): random cell_state grid - """ - for row in range(self.num_rows): - for col in range(self.num_cols): - if value is None: - cell_value = random.randint(0, 1) - else: - cell_value = value - self.grids[self.active_grid][row][col] = cell_value - - def draw_grid(self): - """ - Draws circles for alive cells. - """ - # clear the screen - self.clear_screen - # draw the grid going col by col and row by row - for col in range(self.num_cols): - for row in range(self.num_rows): - # if grid value is 1 (alive), set it to that color - # print(self.grids[1]) - # print(self.active_grid) - if self.grids[self.active_grid][row][col] == 1: - color = ALIVE_COLOR - # else if grid value is 0 (dead), set it to that color - else: - color = DEAD_COLOR - pygame.draw.circle(self.screen, - color, - (int(col * self.cell_size + self.cell_radius), - int(row * self.cell_size + self.cell_radius)), - int(CELL_SIZE / 2), - 0) - # flip to save display state - pygame.display.flip() - - def inactive_grid(self): - return (self.active_grid + 1) % 2 - - def clear_screen(self): - """ - Clears our screen to default color: currently -- black - """ - self.screen.fill(DEAD_COLOR) - - def get_cell(self, row_index, col_index): - try: - cell_value = self.grids[self.active_grid][row][col] - except: - cell_value = 0 - return cell_value - def check_cell_neighbors(self, row_index, col_index): - """ - implement rules: 1-overpopulated, 2-underpopulated, 3-birth, 4-death - """ - # Get the number of alive cells surrounding current cell - - # Check total alive of 8 cell neighbors: - # 3 Above - Left to Right - num_alive_neighbors = 0 - num_alive_neighbors += self.get_cell(row_index - 1, col_index - 1) - num_alive_neighbors += self.get_cell(row_index - 1, col_index - 1) - num_alive_neighbors += self.get_cell(row_index - 1, col_index - 1) - num_alive_neighbors += self.get_cell(row_index, col_index - 1) - num_alive_neighbors += self.get_cell(row_index, col_index + 1) - num_alive_neighbors += self.get_cell(row_index + 1, col_index - 1) - num_alive_neighbors += self.get_cell(row_index + 1, col_index - 1) - num_alive_neighbors += self.get_cell(row_index + 1, col_index - 1) - - - # # Cell Birth - if self.grids[self.active_grid][row_index][col_index] == 0 and num_alive_neighbors == 3: - return 1 - elif num_alive_neighbors > 4: - return 0 - elif num_alive_neighbors < 3: - return 0 - elif (num_alive_neighbors == 3 or num_alive_neighbors == 4) and self.grids[self.active_grid][row_index][col_index] == 1: - return 1 - else: - return 0 - def generation_next(self): - """ - Inspect the crrent active generation. - Update the inactive grid to store next generation. - Swap out the active grid. - """ - for row in range(self.num_rows): - for col in range(self.num_cols): - next_gen_state = self.check_cell_neighbors(row, col) - # set inactive grid future cell state - self.grids[self.inactive_grid()][row][col] = next_gen_state - - # update the inactive grid to store the next generation - self.active_grid = self.inactive_grid() - - # self.set_grid() - - def cap_frame_rate(self, MAX_FPS=10): - """ - Caps the frame rate at a user specified value - """ - desired_time_between_gens_ms = (1.0 / self.fps) * 1000.0 - now = pygame.time.get_ticks() - time_since_last_gen_ms = now - self.last_gen_completed - time_to_sleep = desired_time_between_gens_ms - time_since_last_gen_ms - - # If program needs to wait, delay from time since last gen to a desired FPS cap - if time_to_sleep > 0: - pygame.time.delay(int(time_to_sleep)) - self.last_gen_completed = now - - def user_events(self): - """ - Handles user events (user presses keys indicated below): - - "s": Toggle Game Pause - "r": Create Random Grid of Alive / Dead Cells - "q": Quit the Game of Life - """ - for event in pygame.event.get(): - # if event is keypress of "s" then toggle game pause - # if event is keypress of "r" then set grid - # if event is keypress of "q" then quit - if event.type == pygame.QUIT: sys.exit() - - self.screen.fill(DEAD_COLOR) - - # blit to draw - #screen.blit(ball, ballrect) - - # flip to push into memory - # pygame.display.flip() - def run(self): - """Main loop, runs game of life""" - # desired_time_between_gens_ms = (1.0 / MAX_FPS) * 1000000.0 - # FPS_MAX = input("How many FPS should each generation be? ") - while True: - # Handle events, update the generation, and draw the grid. - self.user_events() - self.generation_next() - self.draw_grid() - self.cap_frame_rate(self.fps) - -if __name__ == "__main__": - game = GameOfLife() - game.run() \ No newline at end of file diff --git a/pygameoflife/pygame_of_life.py b/pygameoflife/pygameoflife.py similarity index 82% rename from pygameoflife/pygame_of_life.py rename to pygameoflife/pygameoflife.py index b4c647f2c..9af75bb91 100644 --- a/pygameoflife/pygame_of_life.py +++ b/pygameoflife/pygameoflife.py @@ -17,6 +17,11 @@ def __init__(self, screen_width=800, screen_height=600, cell_size=10, alive_colo :param max_fps: Framerate cap to limit game speed """ pygame.init() + # self.max_fps = float(input("Enter your desired FPS between generations (sugested 1-10) ")) + # self.screen_width = int(float(input("Enter your desired Screen Width in pixels. "))) + # self.screen_height = int(float(input("Enter your desired Screen Height in pixels. "))) + # self.cell_size = int(float(input("Enter your desired cell size. "))) + self.max_fps = max_fps self.screen_width = screen_width self.screen_height = screen_height self.cell_size = cell_size @@ -29,8 +34,7 @@ def __init__(self, screen_width=800, screen_height=600, cell_size=10, alive_colo # FPS gen-to-gen (allow for user input?) # self.max_fps = max_fps - self.max_fps = float(input("Enter your desired FPS between generations (sugested 1-10) ")) - + self.active_grid = 0 self.num_cols = int(self.screen_width / self.cell_size) self.num_rows = int(self.screen_height / self.cell_size) @@ -40,6 +44,7 @@ def __init__(self, screen_width=800, screen_height=600, cell_size=10, alive_colo self.paused = False self.game_over = False + self.generation_counter = 0 def init_grids(self): """ @@ -79,12 +84,20 @@ def set_grid(self, value=None, grid=0): cell_value = value self.grids[grid][r][c] = cell_value + pygame.display.update() + + def display_generation(self): + """ + Displays the current generation at in a caption at the top of the window. + """ + pygame.display.set_caption("Current Generation: %s" %(self.generation_counter)) + def draw_grid(self): """ Given the grid and cell states, draw the cells on the screen :return: """ - self.clear_screen() + #self.clear_screen() for c in range(self.num_cols): for r in range(self.num_rows): if self.grids[self.active_grid][r][c] == 1: @@ -97,39 +110,9 @@ def draw_grid(self): int(r * self.cell_size + (self.cell_size / 2))), int(self.cell_size / 2), 0) + self.display_generation() pygame.display.flip() - # def glider_gun(self): - # glider_gun =[ - # [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,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,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,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], - # [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], - # [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,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,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,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - # ] - # self.screen_width = 700 - # self.screen_height = 500 - # self.cell_size = 10 - # self.init_grids() - # padding_row = 10 - # padding_col = 10 - # row = 0 - # col = 0 - # for row in range(0, 9): - # for col in range(0, 36): - # # overwrite current cell state with those in glider_gun variable - # print("CC: ", self.grids[self.active_grid][row + padding_row][col + padding_col]) - # print("GG: ", glider_gun[row][col]) - # self.grids[self.active_grid][row + padding_row][col + padding_col] = glider_gun[row][col] - # # X = np.zeros((int(self.screen_width/self.cell_size), int(self.screen_height/self.cell_size))) - # # X = np.array(X) - # # X[1:10,1:37] = glider_gun - # pygame.display.flip() - def clear_screen(self): """ Fill whole screen with dead color @@ -170,14 +153,14 @@ def check_cell_neighbors(self, row_index, col_index): num_alive_neighbors += self.get_cell(row_index + 1, col_index + 1) # Rules for life and death - if self.grids[self.active_grid][row_index][col_index] == 1: # alive - if num_alive_neighbors > 3: # Overpopulation + if self.grids[self.active_grid][row_index][col_index] == 1: # if alive + if num_alive_neighbors > 3: # Overpopulation, dies return 0 - if num_alive_neighbors < 2: # Underpopulation + if num_alive_neighbors < 2: # Underpopulation, stays dead return 0 - if num_alive_neighbors == 2 or num_alive_neighbors == 3: + if num_alive_neighbors == 2 or num_alive_neighbors == 3: # perfect population, come to life return 1 - elif self.grids[self.active_grid][row_index][col_index] == 0: # dead + elif self.grids[self.active_grid][row_index][col_index] == 0: # if dead if num_alive_neighbors == 3: return 1 # come to life @@ -241,6 +224,7 @@ def handle_events(self): elif event.unicode == 'n': # next generation print("Stepping forward 1 generation.") self.update_generation() + self.generation_counter += 1 self.draw_grid() elif event.unicode == 'j': # jump forward x generations skip_step = int(input("Enter # of gens to skip ahead. ")) @@ -274,7 +258,6 @@ def run(self): """ clock = pygame.time.Clock() - while True: if self.game_over: return @@ -283,6 +266,7 @@ def run(self): if not self.paused: self.update_generation() + self.generation_counter += 1 self.draw_grid() clock.tick(self.max_fps) From f5ca5bbf87dd3fbe17446625b027c14a449257a7 Mon Sep 17 00:00:00 2001 From: mtoce Date: Fri, 31 Jul 2020 13:05:46 -0400 Subject: [PATCH 07/10] finished mouse to toggle cells alive/dead --- pygameoflife/extra_code.py | 56 ------------------------------------ pygameoflife/pygameoflife.py | 24 +++++++++++++++- 2 files changed, 23 insertions(+), 57 deletions(-) delete mode 100644 pygameoflife/extra_code.py diff --git a/pygameoflife/extra_code.py b/pygameoflife/extra_code.py deleted file mode 100644 index 59195a092..000000000 --- a/pygameoflife/extra_code.py +++ /dev/null @@ -1,56 +0,0 @@ -# def mouse_events(): -# LEFT = 1 -# RIGHT = 3 - -# running = 1 -# screen = pygame.display.set_mode((320, 200)) - -# while running: -# event = pygame.event.poll() -# if event.type == pygame.QUIT: -# running = 0 -# elif event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT: -# print "You pressed the left mouse button at (%d, %d)" % event.pos -# elif event.type == pygame.MOUSEBUTTONUP and event.button == LEFT: -# print "You released the left mouse button at (%d, %d)" % event.pos -# elif event.type == pygame.MOUSEBUTTONDOWN and event.button == RIGHT: -# print "You pressed the right mouse button at (%d, %d)" % event.pos -# elif event.type == pygame.MOUSEBUTTONUP and event.button == RIGHT: -# print "You released the right mouse button at (%d, %d)" % event.pos - -# screen.fill((0, 0, 0)) -# pygame.display.flip() - -# --------------------------------------------------------------------------------------- - -# def glider_gun(self): -# glider_gun =[ -# [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,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,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,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], -# [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], -# [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,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,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,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] -# ] -# self.screen_width = 700 -# self.screen_height = 500 -# self.cell_size = 10 -# self.init_grids() -# padding_row = 10 -# padding_col = 10 -# row = 0 -# col = 0 -# for row in range(0, 9): -# for col in range(0, 36): -# # overwrite current cell state with those in glider_gun variable -# print("CC: ", self.grids[self.active_grid][row + padding_row][col + padding_col]) -# print("GG: ", glider_gun[row][col]) -# self.grids[self.active_grid][row + padding_row][col + padding_col] = glider_gun[row][col] -# # X = np.zeros((int(self.screen_width/self.cell_size), int(self.screen_height/self.cell_size))) -# # X = np.array(X) -# # X[1:10,1:37] = glider_gun -# pygame.display.flip() - \ No newline at end of file diff --git a/pygameoflife/pygameoflife.py b/pygameoflife/pygameoflife.py index 9af75bb91..8cffb0cec 100644 --- a/pygameoflife/pygameoflife.py +++ b/pygameoflife/pygameoflife.py @@ -5,7 +5,7 @@ class GameOfLife: - def __init__(self, screen_width=800, screen_height=600, cell_size=10, alive_color=(0, 255, 255), + def __init__(self, screen_width=1000, screen_height=800, cell_size=30, alive_color=(0, 255, 255), dead_color=(0, 0, 0), max_fps=10): """ Initialize grid, set default game state, initialize screen @@ -201,6 +201,28 @@ def handle_events(self): :return: """ for event in pygame.event.get(): + if self.paused: + if event.type == pygame.MOUSEBUTTONDOWN: + if(event.button==1): + mousepos_x, mousepos_y = event.pos + c, r = (int((mousepos_x - self.cell_size / 2) // self.cell_size), + int((mousepos_y - self.cell_size / 2) // self.cell_size)) + #print(event.pos, '->', (r, c)) # Show result. + if r in range(self.num_rows) and c in range(self.num_cols): + # Toggle state of cell: active <--> inactive + if self.grids[self.active_grid][r][c] == 1: + self.grids[self.active_grid][r][c] = 0 + color = self.dead_color + else: + self.grids[self.active_grid][r][c] = 1 + color = self.alive_color + # Redraw cell in its new color. + posn = (int(c * self.cell_size + self.cell_size / 2), + int(r * self.cell_size + self.cell_size / 2)) + #print(' posn:', posn) + pygame.draw.circle(self.screen, color, posn, int(self.cell_size / 2), 0) + + pygame.display.flip() if event.type == pygame.KEYDOWN: print("key pressed") if event.unicode == 'p': # toggle pause From 448b990106fee0a4bd15f07e423188bc9bdfbaaf Mon Sep 17 00:00:00 2001 From: mtoce Date: Fri, 31 Jul 2020 13:16:51 -0400 Subject: [PATCH 08/10] added README.md with basical outline of conways rules --- pygameoflife/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 pygameoflife/README.md diff --git a/pygameoflife/README.md b/pygameoflife/README.md new file mode 100644 index 000000000..d74eb1612 --- /dev/null +++ b/pygameoflife/README.md @@ -0,0 +1,8 @@ + +# Conway's Rules of Life + +1. Any live cell with two or three live neighbours survives. + +2. Any dead cell with three live neighbours becomes a live cell. + +3. All other live cells die in the next generation. Similarly, all other dead cells stay dead. \ No newline at end of file From 205e9cabab4af0a637ae3ff277b86599ede249f0 Mon Sep 17 00:00:00 2001 From: mtoce Date: Fri, 31 Jul 2020 13:34:53 -0400 Subject: [PATCH 09/10] Added user events section to README.md --- pygameoflife/README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pygameoflife/README.md b/pygameoflife/README.md index d74eb1612..3a171ca2d 100644 --- a/pygameoflife/README.md +++ b/pygameoflife/README.md @@ -1,8 +1,28 @@ # Conway's Rules of Life +Conway's Game of Life is a solitary game where a user interacts with a grid of cells. These cells will survive under specific conditions and die in other specific conditions. These conditions are outlined below: + 1. Any live cell with two or three live neighbours survives. 2. Any dead cell with three live neighbours becomes a live cell. -3. All other live cells die in the next generation. Similarly, all other dead cells stay dead. \ No newline at end of file +3. All other live cells die in the next generation. Similarly, all other dead cells stay dead. + +## My Version + +The version of the game I created initially randomized the grid cells to one of two binary states (alive or dead). The current generation displays as a caption at the top of the window. The user is able to interact with the game through the use of the keyboard and mouse, outlined in the next section. + +## User Events + +The user can interact with the game by doing the following keypresses on the keyboard: + +Key Press | Action | Explanation +----------|--------|------------ +p | pause | Toggle pause for game state. +c | clear | Clears the screen, setting all cells to dead. +r | randomize | Randomizes the grid, setting all cells to either alive or dead. +q | quit | Exits the Game. +n | next | Jump 1 generation forward. +j | jump | Jump X generations forward. Specified with user input in terminal +mouse-click | toggle-value | If the game is paused, user can toggle cells alive and dead by clicking on them. \ No newline at end of file From 947fcf73e5503eb29ab886a7081a0efae7c0af1e Mon Sep 17 00:00:00 2001 From: mtoce Date: Fri, 31 Jul 2020 13:55:58 -0400 Subject: [PATCH 10/10] Added helper function to show user events --- pygameoflife/README.md | 3 ++- pygameoflife/pygameoflife.py | 34 ++++++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/pygameoflife/README.md b/pygameoflife/README.md index 3a171ca2d..0bb29327f 100644 --- a/pygameoflife/README.md +++ b/pygameoflife/README.md @@ -22,7 +22,8 @@ Key Press | Action | Explanation p | pause | Toggle pause for game state. c | clear | Clears the screen, setting all cells to dead. r | randomize | Randomizes the grid, setting all cells to either alive or dead. +h | help | Displays helper functions listed here inside the terminal window. q | quit | Exits the Game. n | next | Jump 1 generation forward. j | jump | Jump X generations forward. Specified with user input in terminal -mouse-click | toggle-value | If the game is paused, user can toggle cells alive and dead by clicking on them. \ No newline at end of file +mouse-click | toggle-value | If the game is paused, user can toggle cells alive and dead by clicking on them. diff --git a/pygameoflife/pygameoflife.py b/pygameoflife/pygameoflife.py index 8cffb0cec..72ad58081 100644 --- a/pygameoflife/pygameoflife.py +++ b/pygameoflife/pygameoflife.py @@ -186,19 +186,30 @@ def inactive_grid(self): """ return (self.active_grid + 1) % 2 + def help(self): + print( + """ + p (pause) - Toggle pause for game state. + c (clear) - Clears the screen, setting all cells to dead. + r (randomize) - Randomizes the grid, setting all cells to either alive or dead. + h (help) - Displays helper functions listed here inside the terminal window. + q (quit) - Exits the Game. + n (next) - Jump 1 generation forward. + j (jump) - Jump X generations forward. Specified with user input in terminal + mouse-click (toggle-value) - If the game is paused, user can toggle cells alive and dead by clicking on them. + """) + def handle_events(self): """ Handle any keypresses - p - pause the game - q - quit - r - randomize grid - - #TODO - s - change size of screen - f - change fps max between gens - g - initialize glider gun input - i - interact with screen with mouse events (click to turn cells alive) - :return: + p (pause) - Toggle pause for game state. + c (clear) - Clears the screen, setting all cells to dead. + r (randomize) - Randomizes the grid, setting all cells to either alive or dead. + h (help) - Displays helper functions listed here inside the terminal window. + q (quit) - Exits the Game. + n (next) - Jump 1 generation forward. + j (jump) - Jump X generations forward. Specified with user input in terminal + mouse-click (toggle-value) - If the game is paused, user can toggle cells alive and dead by clicking on them. """ for event in pygame.event.get(): if self.paused: @@ -243,6 +254,9 @@ def handle_events(self): elif event.unicode == 'q': # quit print("Exiting.") self.game_over = True + elif event.unicode == 'h': + print('User Events Help Below:') + self.help() elif event.unicode == 'n': # next generation print("Stepping forward 1 generation.") self.update_generation()