Created
April 11, 2021 02:01
-
-
Save asher-pembroke/8a6a488ca44a78beecb379b4ed88f1b0 to your computer and use it in GitHub Desktop.
pixel puzzler/conway game of life/plotly dash demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import dash | |
| import dash_core_components as dcc | |
| import dash_html_components as html | |
| from dash.dependencies import Input, Output, State | |
| from dash.exceptions import PreventUpdate | |
| import dash_daq as daq | |
| # from plotly.offline import iplot | |
| import dash_extendable_graph as deg | |
| import plotly.graph_objs as go | |
| import numpy as np | |
| N_default = 4 | |
| z_default = np.zeros((N_default, N_default)) | |
| colorscale= [ | |
| [0, 'rgb(250, 250, 250)'], #0 | |
| [1., 'rgb(0, 0, 0)'], #1 | |
| ] | |
| external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] | |
| theme = { | |
| 'dark': False, | |
| 'detail': '#007439', | |
| 'primary': '#00EA64', | |
| 'secondary': '#6E6E6E' | |
| } | |
| app = dash.Dash(__name__, external_stylesheets=external_stylesheets) | |
| trace = go.Heatmap(z=z_default, text = [], showscale=False, hoverinfo='text', colorscale=colorscale, xgap=1, ygap=1, zmin=0, zmax=1) | |
| layout = go.Layout({'xaxis': { | |
| # 'range': [0.2, 1], | |
| 'showgrid': False, # thin lines in the background | |
| 'zeroline': False, # thick line at x=0 | |
| 'visible': False, # numbers below | |
| 'fixedrange' : True, | |
| }, | |
| 'yaxis': { | |
| # 'range': [0.2, 1], | |
| 'showgrid': False, # thin lines in the background | |
| 'zeroline': False, # thick line at x=0 | |
| 'visible': False, # numbers below | |
| 'fixedrange' : True, | |
| 'scaleanchor' : 'x', | |
| 'scaleratio' : 1, | |
| }, | |
| # (yaxis=dict(scaleanchor="x", scaleratio=1) | |
| }) | |
| fig = go.Figure([trace], layout=layout) | |
| app.layout = html.Div([ | |
| html.Div([ | |
| html.Div(html.Button('Randomize', id='randomize', n_clicks=0), className='three columns'), | |
| daq.BooleanSwitch(id='edit-play', on=True, className='three columns'), | |
| dcc.Slider( | |
| id='N', | |
| min=1, | |
| max=100, | |
| step=1, | |
| value=4, | |
| className='three columns', | |
| updatemode='drag', | |
| ), | |
| html.Div(html.Button('Conway', id='conway', n_clicks=0), className='three columns'), | |
| ], className = 'row'), | |
| deg.ExtendableGraph(id='game-board', | |
| # dcc.Graph(id='game-board', | |
| # animate=True, | |
| className='row', | |
| figure=fig, | |
| style={'width': '90vh', 'height': '90vh', 'maxWidth': '650px', 'margin':'0 auto'}, | |
| config={'displayModeBar': False}), | |
| html.Div(id='debug-click'), | |
| html.Div(id='debug-fig'), | |
| dcc.Interval( | |
| id='interval_extendablegraph_update', | |
| interval=1000, | |
| n_intervals=0, | |
| max_intervals=-1), | |
| ]) | |
| @app.callback( | |
| Output('game-board', 'figure'), | |
| Input('game-board', 'clickData'), | |
| Input('N', 'value'), | |
| Input('conway', 'n_clicks'), | |
| Input('randomize', 'n_clicks'), | |
| State('edit-play', 'on'), | |
| State('game-board', 'figure')) | |
| def update_figure(click_data, n, conway_button, randomize_button, edit_mode, game_board): | |
| ctx = dash.callback_context | |
| z = np.array(game_board['data'][0]['z']) | |
| if not ctx.triggered: | |
| button_id = 'No clicks yet' | |
| else: | |
| button_id = ctx.triggered[0]['prop_id'].split('.')[0] | |
| if button_id == 'conway': | |
| game_board['data'][0]['z'] = conway(z) | |
| return game_board | |
| elif button_id == 'randomize': | |
| game_board['data'][0]['z'] = 1*(np.random.random((n,n))>.75) | |
| return game_board | |
| if n != len(z): | |
| if n > len(z): | |
| z = np.pad(z, ((0,n-len(z)),(0,n-len(z))), mode='constant', constant_values=0) | |
| elif n < len(z): | |
| z = z[:n, :n] | |
| game_board['data'][0]['z'] = z | |
| return game_board | |
| if click_data is None: | |
| raise PreventUpdate | |
| else: | |
| click = click_data['points'][0] | |
| j = min(click['x'], n-1) | |
| i = min(click['y'], n-1) | |
| z[i,j] = 1-z[i,j] | |
| if edit_mode: | |
| pass | |
| else: | |
| for row in [i-1, i+1]: | |
| if (row >= 0) & (row < n): | |
| z[row, j] = 1-z[row, j] | |
| for col in [j-1, j+1]: | |
| if (col >= 0) & (col < n): | |
| z[i, col] = 1-z[i, col] | |
| game_board['data'][0]['z'] = z | |
| return game_board | |
| def conway(X): | |
| """borows heavily from | |
| # http://jakevdp.github.io/blog/2013/08/07/conways-game-of-life/ | |
| """ | |
| """Game of life step using generator expressions""" | |
| nbrs_count = sum(np.roll(np.roll(X, i, 0), j, 1) | |
| for i in (-1, 0, 1) for j in (-1, 0, 1) | |
| if (i != 0 or j != 0)) | |
| return 1*((nbrs_count == 3) | (X & (nbrs_count == 2))) | |
| # @app.callback( | |
| # Output('debug-click', 'children'), | |
| # Input('game-board', 'clickData'),) | |
| # def update_debug(click_data): | |
| # return '' | |
| @app.callback( | |
| Output('debug-fig', 'children'), | |
| Input('game-board', 'figure'),) | |
| def update_debug(fig): | |
| z_data = np.array(fig['data'][0]['z']) | |
| if (z_data.max() == 0) | (z_data.min() == 1): | |
| return html.Div('Success!', style=dict(color='green', textAlign='center')) | |
| return html.Div(html.Br()) | |
| if __name__ == '__main__': | |
| app.run_server(debug=True, host='0.0.0.0') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment