From 377ace5e292c61178b6498bbcedd078ee08dcaf9 Mon Sep 17 00:00:00 2001 From: Tom Weber Date: Sat, 12 Nov 2022 22:30:14 +0100 Subject: [PATCH] added PyPlot Animation, changed board starte to numpy instead of python list --- README.md | 4 ++- anime.py | 79 +++++++++++++++++++++++++++++++++++++++++++++ main.py | 84 +++++++++++++++++------------------------------- requirements.txt | 4 +++ 4 files changed, 116 insertions(+), 55 deletions(-) create mode 100644 anime.py diff --git a/README.md b/README.md index 10b0efc..45d73a3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # Conway's Game of Life -Rules: https://en.wikipedia.org/wiki/Conway's_Game_of_Life \ No newline at end of file +Rules: https://en.wikipedia.org/wiki/Conway\'s_Game_of_Life + +This is a game that relies on simple rules but can spawn complex behaviour. \ No newline at end of file diff --git a/anime.py b/anime.py new file mode 100644 index 0000000..6484bbc --- /dev/null +++ b/anime.py @@ -0,0 +1,79 @@ +import matplotlib.animation as animation +import matplotlib.pyplot as plt +#import plotly.graph_objects as go +import plotly.express as px +import numpy as np + + +class PlotlyAnimation(object): + def __init__(self, state, steps): + self.state = state + self.steps = steps + self.frames = np.expand_dims(self.state.board, axis=0) + self.frame_gen() + self.animation = self.animation_gen() + + def frame_gen(self): + for _ in range(self.steps): + self.frames = np.concatenate((self.frames, np.expand_dims(self.state.step(), axis=0)), axis=0) + return self.frames + + def animation_gen(self): + fig = px.imshow(self.frames, animation_frame=0) + fig.layout.updatemenus[0].buttons[0].args[1]['frame']['duration'] = 30 + fig.layout.updatemenus[0].buttons[0].args[1]['transition']['duration'] = 5 + fig.layout.updatemenus[0].showactive = True + fig.layout.sliders[0].visible = False + fig.layout.coloraxis.showscale = False + fig.layout.xaxis.showticklabels = False + fig.layout.yaxis.showticklabels = False + return fig + + def show(self): + self.animation.show() + + +class PltAnimation(object): + def __init__(self, state, steps=None): + + self.state = state + self.steps = steps + self.fig, self.ax = plt.subplots() + self.ax = self.ax.matshow(self.state.board) + + def frame_yield(self): + while True: + self.state.step() + yield self.state.board + + def frame_gen(self): + frames = [] + for i in range(self.steps): + self.state.step() + [].append(self.state.board) + return self.ax, frames + + def init(self): + return self.state.board + + def update(self, data): + self.ax.set_data(data) + return self.ax + + def animate(self): + if self.steps is None: + return animation.FuncAnimation( + self.fig, + self.update, + self.frame_yield, + init_func=self.init, + interval=100, + ) + else: + return animation.FuncAnimation( + self.fig, + self.update, + self.frame_gen, + init_func=self.init, + interval=100, + ) diff --git a/main.py b/main.py index bb3186f..8418956 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,11 @@ +#!/usr/bin/env python + import random import copy import matplotlib.pyplot as plt -import matplotlib.animation as animation +import numpy as np +from anime import PltAnimation +from anime import PlotlyAnimation class State(object): @@ -9,17 +13,16 @@ class State(object): # Coords: tuple of ints or list of tuple of ints self.width = width self.depth = depth - - self.board = [[0 for i in range(width)] for j in range(depth)] + self.board = np.array([[0 for i in range(width)] for j in range(depth)]) if coords is None: pass elif isinstance(coords, tuple): x, y = coords - self.board[x][y] = 1 + self.board[x, y] = 1 elif isinstance(coords, list): for pair in coords: x, y = pair - self.board[x][y] = 1 + self.board[x, y] = 1 elif coords == "random": self.randomBoard() else: @@ -31,9 +34,9 @@ class State(object): for idx, i in enumerate(self.board): for jdx, j in enumerate(i): if random.randint(1, 2) == 1: - self.board[idx][jdx] = 1 + self.board[idx, jdx] = 1 else: - self.board[idx][jdx] = 0 + self.board[idx, jdx] = 0 def terminal_render(self): print("-" * (self.width + 2)) @@ -54,84 +57,57 @@ class State(object): for rowidx, row in enumerate(self.board): for elemidx, elem in enumerate(row): live_neighbours = self.checkNeighbours(boardcopy, rowidx, elemidx) - if self.board[rowidx][elemidx] == 1: + if self.board[rowidx, elemidx] == 1: if live_neighbours == 2 or live_neighbours == 3: pass else: - self.board[rowidx][elemidx] = 0 + self.board[rowidx, elemidx] = 0 else: if live_neighbours == 3: - self.board[rowidx][elemidx] = 1 + self.board[rowidx, elemidx] = 1 + return self.board def checkNeighbours(self, board, rowindex, columnindex, neighbourhood="moore"): alive = 0 if rowindex != 0: - if board[rowindex - 1][columnindex] == 1: + if board[rowindex - 1, columnindex] == 1: alive += 1 if rowindex < self.depth - 1: - if board[rowindex + 1][columnindex] == 1: + if board[rowindex + 1, columnindex] == 1: alive += 1 if columnindex != 0: - if board[rowindex][columnindex - 1] == 1: + if board[rowindex, columnindex - 1] == 1: alive += 1 if columnindex < self.width - 1: - if board[rowindex][columnindex + 1] == 1: + if board[rowindex, columnindex + 1] == 1: alive += 1 if neighbourhood == "moore": if rowindex != 0 and columnindex != 0: - if board[rowindex - 1][columnindex - 1] == 1: + if board[rowindex - 1, columnindex - 1] == 1: alive += 1 if rowindex < self.depth - 1 and columnindex < self.width - 1: - if board[rowindex + 1][columnindex + 1] == 1: + if board[rowindex + 1, columnindex + 1] == 1: alive += 1 if rowindex != 0 and columnindex < self.width - 1: - if board[rowindex - 1][columnindex + 1] == 1: + if board[rowindex - 1, columnindex + 1] == 1: alive += 1 if rowindex < self.depth - 1 and columnindex != 0: - if board[rowindex + 1][columnindex - 1] == 1: + if board[rowindex + 1, columnindex - 1] == 1: alive += 1 return alive -class BoardAnimation(object): - def __init__(self, state, steps=None): - - self.state = state - self.steps = steps - self.fig, self.ax = plt.subplots() - self.ax = self.ax.matshow(self.state.board) - - def frame_yield(self): - while True: - self.state.step() - yield self.state.board - - def frame_gen(self): - frames = [] - for i in range(self.steps): - self.state.step() - [].append(self.state.board) - return self.ax, frames - - def init(self): - return self.state.board - - def update(self, data): - self.ax.set_data(data) - return self.ax - - def animate(self): - return animation.FuncAnimation( - self.fig, self.update, self.frame_yield, init_func=self.init, interval=100 - ) def main(): - state = State(20, 20, [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)]) - animation = BoardAnimation(state) - ani = animation.animate() - plt.show() - + # state = State(20, 20, [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)]) + + state = State(100, 100, "random") + #animation = PltAnimation(state) + #ani = animation.animate() + #plt.show() + animation = PlotlyAnimation(state, 100) + animation.show() if __name__ == "__main__": main() diff --git a/requirements.txt b/requirements.txt index 03abb3d..b1da47c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,11 @@ kiwisolver==1.4.4 matplotlib==3.6.0 numpy==1.23.3 packaging==21.3 +pandas==1.5.1 Pillow==9.2.0 +plotly==5.11.0 pyparsing==3.0.9 python-dateutil==2.8.2 +pytz==2022.6 six==1.16.0 +tenacity==8.1.0