In [1]:
import bqplot
import numpy as np
import pandas as pd
import ipywidgets as widgets
import pprint
In [10]:
# input coordinates and class, function to modify board state
def shape_grid(num_shapes, shape_function, size, inp, **interact_params):
sc_x = bqplot.scales.LinearScale(min=0, max=size)
sc_y = bqplot.scales.LinearScale(min=0, max=size)
ax_x = bqplot.axes.Axis(scale=sc_x)
ax_y = bqplot.axes.Axis(scale=sc_y, tick_format='0.2f', orientation='vertical')
ax_x.visible = False
ax_y.visible = False
lst_scatter_plts = []
for i in range(num_shapes):
lst_scatter_plts.append(
bqplot.marks.Scatter(
scales={'x': sc_x, 'y': sc_y}, ))
# layout = widgets.Layout(kw=dict(min_width='400px'))
fig = bqplot.Figure(marks=lst_scatter_plts, axis=[ax_x,ax_y], min_aspect_ratio=1,
max_aspect_ratio=1, fig_margin={'top':10, 'bottom':60, 'left':60, 'right':60})
fig.layout.height = '600px'
fig.layout.width = '600px'
def wrapped(**interact_params):
nonlocal inp
lst_scatter_plts = []
output = shape_function(size, inp, **interact_params)
marks = fig.marks
# keep track of which dictionaries in new output have been added so that we can add remaining in the end
added = []
empty_old_points = []
for old_points in marks:
old_point_match = False
for dictionary in output:
if dictionary['options']['marker'] == old_points.marker and dictionary['options']['color'] == old_points.colors[0]:
old_point_match = True
added.append(dictionary)
options = dictionary['options']
coords = dictionary['coords']
old_points.x = list(zip(*coords))[0]
old_points.y = list(zip(*coords))[1]
old_points.colors=[options['color']]
old_points.marker=options['marker']
if not old_point_match:
old_points.x = []
old_points.y =[]
empty_old_points.append(old_points)
for dictionary in output:
if dictionary not in added:
old_points = empty_old_points.pop()
options = dictionary['options']
coords = dictionary['coords']
old_points.x = list(zip(*coords))[0]
old_points.y = list(zip(*coords))[1]
old_points.colors=[options['color']]
old_points.marker=options['marker']
added.append(dictionary)
# fig.marks = fig.marks + [(bqplot.Lines(x=np.arange(5), y=np.arange(5), scales={'x': sc_x, 'y': sc_y},
# colors=[GOLDENROD]))]
inp = output
for marks in fig.marks:
marks.default_size = 120
display_widgets = widgets.interactive(wrapped, **interact_params)
display(display_widgets)
display(fig)
In [11]:
DARK_BLUE = '#475A77'
GOLDENROD = '#FEC62C'
BRICK_RED = '#B22222'
In [12]:
def cute_shape(size, plotting_info, time, ratio):
# build attribute grid where if there is no shape it is a 0, and dict of attribute where there is a shape
shape_color_mapping = {'circle': GOLDENROD, 'triangle-up': DARK_BLUE}
attribute_grid = [[0] * size for _ in range(size)]
for coords_and_options in plotting_info:
coords = coords_and_options['coords']
options = coords_and_options['options']
for i,j in coords:
attribute_grid[size-j-1][i] = options.copy()
# check if the neighbor is satisfied
def neighbors_are_similar(x,y):
attribute = attribute_grid[x][y]
similar_count = different_count = 0
for i in range(max(0, x-1), min(size, x+2)):
for j in range(max(0, y-1), min(size, y+2)):
if attribute_grid[i][j] != 0 and (i != x or j != y):
if attribute['marker'] == attribute_grid[i][j]['marker']:
similar_count += 1
else:
different_count += 1
return different_count+similar_count == 0 or similar_count/(different_count+similar_count) > ratio
# set the happiness colors for all shapes
if time ==0:
for i in range(size):
for j in range(size):
options = attribute_grid[i][j]
if options != 0:
if not neighbors_are_similar(i, j):
attribute_grid[i][j]['color'] = BRICK_RED
else:
attribute_grid[i][j]['color'] = shape_color_mapping[attribute_grid[i][j]['marker']]
# for visualizing the board changing
if time !=0 and time < 1000:
#find the unhappy shape
unhappy_shape_coord = None
unhappy_shape = None
for i in range(size):
if unhappy_shape:
break
for j in range(size):
if attribute_grid[i][j] != 0 and attribute_grid[i][j]['color'] == BRICK_RED:
unhappy_shape = attribute_grid[i][j]
unhappy_shape_coord = [i,j]
displaced = True
if unhappy_shape:
while displaced:
new_coord = [np.random.randint(0, size), np.random.randint(0, size)]
if attribute_grid[new_coord[0]][new_coord[1]] == 0:
attribute_grid[unhappy_shape_coord[0]][unhappy_shape_coord[1]] = 0
attribute_grid[new_coord[0]][new_coord[1]] = unhappy_shape
displaced = False
for i in range(size):
for j in range(size):
options = attribute_grid[i][j]
if options != 0:
if not neighbors_are_similar(i, j):
attribute_grid[i][j]['color'] = BRICK_RED
else:
attribute_grid[i][j]['color'] = shape_color_mapping[attribute_grid[i][j]['marker']]
# else:
# for i in range(size):
# for j in range(size):
# options = attribute_grid[i][j]
# if options != 0:
# attribute_grid[i][j]['color'] = shape_color_mapping[attribute_grid[i][j]['marker']]
# change grid to output format
merged_options_coordinates = []
for i in range(size):
for j in range(size):
options = attribute_grid[i][j]
if options != 0:
added = False
for dictionary in merged_options_coordinates:
if dictionary['options'] == options:
dictionary['coords'].append([j, size-i-1])
added = True
break
if not added:
merged_options_coordinates.append({'options':options, 'coords':[[j, size-i-1]]})
return merged_options_coordinates
In [13]:
import itertools
In [14]:
np.random.seed(43)
coordinates = list(set(itertools.combinations(np.append(np.arange(15),np.arange(15)), 2)))
np.random.shuffle(coordinates)
inp= [{'options': {'color':'blue', 'marker':'triangle-up'}, 'coords':coordinates[0:40]},
{'options': {'color':'blue', 'marker':'circle'}, 'coords':coordinates[40:80]}]
In [15]:
#widgets.Play(value=0, max=100)
In [16]:
shape_grid(4, cute_shape, 15, inp, time=widgets.Play(value=0, max=100), ratio=widgets.FloatSlider(value=0.3, min=0, max=1))
In [ ]: