Compare commits

..

1 Commits

Author SHA1 Message Date
Tom Weber 220f0e7ffa test commit
2 years ago

1
.gitignore vendored

@ -6,7 +6,6 @@ __pycache__/
# C extensions # C extensions
*.so *.so
.idea/
# Distribution / packaging # Distribution / packaging
.Python .Python
build/ build/

@ -1,29 +0,0 @@
pipeline:
standardize:
image: python:${TAG}-buster
commands:
- python -m pip install --upgrade pip
# - python -m pip install -r requirements.txt
- python -m pip install pylint flake8 mypy>=0.971
- python -m flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# - mypy --strict serial_sphinx/
# - python -m pylint -f parseable serial_sphinx/*.py
build:
image: python:${TAG}-buster
commands:
- ls
- python -m venv venv
- /bin/bash -c "source venv/bin/activate"
- python -m pip install --upgrade pip pytest
# - python -m pip install -r requirements.txt
- python -m pip install .
- python -m pytest
matrix:
TAG:
- 3.9
- 3.10

@ -1,19 +0,0 @@
Copyright (c) 2022 Tom Weber
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,7 +1,8 @@
# Conway's Game of Life # Conway's Game of Life
[![status-badge](https://ci.weber.codes/api/badges/tom/gameoflife/status.svg)](https://ci.weber.codes/tom/gameoflife)
Rules: https://en.wikipedia.org/wiki/Conway\'s_Game_of_Life 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. This is a game that relies on simple rules but can spawn complex behaviour.
A simple python implementation of Conway's Game of Life.

@ -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,
)

@ -1,7 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
import copy import copy
import matplotlib.pyplot as plt
import numpy as np import numpy as np
from conway.anime import PltAnimation
from conway.anime import PlotlyAnimation from conway.anime import PlotlyAnimation
@ -30,7 +32,6 @@ class State(object):
def randomBoard(self): def randomBoard(self):
self.board = np.random.binomial(1, 0.5, (self.width, self.depth)) self.board = np.random.binomial(1, 0.5, (self.width, self.depth))
return self.board return self.board
def terminal_render(self): def terminal_render(self):
print("-" * (self.width + 2)) print("-" * (self.width + 2))
for i in self.board: for i in self.board:
@ -41,6 +42,10 @@ class State(object):
) )
print("-" * (self.width + 2)) print("-" * (self.width + 2))
def plt_render(self):
plt.imshow(self.board)
plt.show()
def step(self): def step(self):
boardcopy = copy.deepcopy(self.board) boardcopy = copy.deepcopy(self.board)
for rowidx, row in enumerate(self.board): for rowidx, row in enumerate(self.board):
@ -85,31 +90,21 @@ class State(object):
alive += 1 alive += 1
return alive return alive
def get_anime(size=100, steps=100, start="random"): def get_anime(size=100, steps=100, start="random"):
state = State(size, size, start) state = State(size, size, start)
anime = PlotlyAnimation(state, steps) anime = PlotlyAnimation(state, steps)
return anime.animation return anime.animation
def get_starting_board(size, start="random"):
# TODO: implement other starting options like e.g. glider
if start == "random":
return State(size, size, "random")
if start == "glider":
return State(size, size, [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)])
if start == "acorn":
return State(size, size, coords=[(20+5, 20+3), (20+5, 20+4), (20+3, 20+4), (20+4, 20+6), (20+5, 20+7), (20+5, 20+8), (20+5, 20+9)])
else:
return State(size, size, None)
def get(size = 50, steps = 200, start="random"):
board = get_starting_board(size, start)
return PlotlyAnimation(board, steps).animation
def main(): def main():
anime = get(50, 200, "acorn") # state = State(20, 20, [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)])
anime.show()
state = State(100, 100, "random")
#animation = PltAnimation(state)
#ani = animation.animate()
#plt.show()
animation = PlotlyAnimation(state, 100)
animation.show()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

@ -1,31 +0,0 @@
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "conway"
version = "0.0.1"
authors = [
{ name="Tom Weber", email="tom@weber.codes" },
]
description = "A game of life implementation"
readme = "README.md"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"numpy>=1.23.3",
"plotly>=5.11.0",
"pandas>=1.5.1"
]
[project.urls]
"repository" = "https://git.weber.codes/tom/gameoflife"
[tool.pytest.ini_options]
addopts = [
"--import-mode=importlib",
]

@ -0,0 +1,15 @@
contourpy==1.0.5
cycler==0.11.0
fonttools==4.37.4
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

@ -0,0 +1,19 @@
from setuptools import setup
setup(
name='conway',
version='0.1.0',
description="A conway's game of life implementation",
url='https://git.weber.codes/tom/conwaysgameoflife',
author='Tom Weber',
author_email='mail@weber.codes',
license='BSD 2-clause',
packages=['conway'],
install_requires=['wheel',
'numpy',
'pandas',
'matplotlib',
'plotly'
],
)

@ -1,29 +0,0 @@
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, color_continuous_scale=[[0, "white"], [1, "black"]])
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()

@ -1,12 +0,0 @@
import pytest
import conway.anime as anime
import conway.gameoflife as gol
class TestPlotlyAnimation:
state = gol.State(10, 10, "random")
steps = 10
plotly_anime = anime.PlotlyAnimation(state, steps)
def test_frame_gen(self):
assert self.plotly_anime.frames.shape == (11,10,10)
assert len(self.plotly_anime.frame_gen()) == 2 * self.steps + 1

@ -1,17 +0,0 @@
import pytest
import numpy as np
import conway.gameoflife as gol
class TestState:
def test_neighbours(self):
state = gol.State(10, 10)
n1 = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]])
assert state.checkNeighbours(n1, 1, 1) == 8
assert state.checkNeighbours(n1, 1, 1, neighbourhood="neumann") == 4
n2 = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
assert state.checkNeighbours(n2, 1, 1) == 0
assert state.checkNeighbours(n2, 1, 1, neighbourhood="neumann") == 0
n3 = np.array([[1, 0, 1], [0, 1, 0], [1, 0, 1]])
assert state.checkNeighbours(n3, 1, 1, neighbourhood="moore") == 4
assert state.checkNeighbours(n3, 1, 1, neighbourhood="neumann") == 0
Loading…
Cancel
Save