Last active
May 6, 2024 11:58
-
-
Save jul/37f418aefd27b69bb213fe1e2a563bfd to your computer and use it in GitHub Desktop.
experiment #1543 making a cellular automata on hexagonal network of stuff falling (black) with obstacles (red)
This file contains 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 PySimpleGUI as sg | |
from operator import itemgetter | |
### uitliser ipython -i argv[0] pour être en interactif et faire joujou | |
SZ=15 | |
DIMX=60 | |
DIMY=80 | |
can = sg.Canvas(size=(DIMX*SZ*1.01,DIMY*SZ*.87)) | |
status=sg.Text('status') | |
win = sg.Window("titre",[[can,],[status,],], finalize=True) | |
c = can.TKCanvas | |
from time import sleep, time | |
from random import randint, shuffle | |
from os import system | |
system("rm *ps output2.mp4 ev*.jpg") | |
seed=int(time()) | |
print("seed %d" % seed) | |
ORD=1 | |
def save(canvas, name="save"): | |
global ORD | |
with open("%s-%04d.ps"% (name, ORD), "w") as f: | |
ORD+=1 | |
status.update("%4d" % ORD) | |
f.write(canvas.postscript()) | |
def will_fall(x,y,c=c): | |
global new_filled, old_filled, obstacle, to_remove | |
state=False | |
if (x,y) in obstacle or y==DIMY-1 or y==0: | |
new_filled -= {(x,y)} | |
old_filled -= {(x,y)} | |
return | |
# from pdb import set_trace;set_trace() | |
ostate = (x,y) in old_filled and 'filled' or "empty" | |
done = False | |
if ostate == "empty": | |
for nx, ny in get_top_neighbour(x,y): | |
to_fill = False | |
if not done: | |
fally = (nx,ny) in old_filled and (nx,ny) not in obstacle | |
to_fill = ostate == "empty" and fally==True | |
if(to_fill) and ostate=="empty": | |
done = True | |
new_filled|= {(x,y)} | |
#to_remove |= {(nx,ny)} | |
old_filled -= {(nx,ny)} | |
return | |
if done: return | |
if ostate == "filled": | |
new_filled|= {(x,y)} | |
return | |
# case obstacle | |
return False | |
def cell(x,y, **kw): | |
hs=0.866 # heigth of an equilateral triangle | |
px,py=x/2-1/2*(y%2),y-2/3 | |
#px,py=x-(y%2),y-2/3 | |
xr,yr=px*SZ+SZ,py*hs*SZ+SZ | |
# triangle compliqué à dessiner | |
r=SZ/2 | |
i=c.create_oval(xr-r,yr-r,xr+r,yr+r, | |
fill=["","black"][kw.get("state","empty")=="filled"], outline="", | |
tags=( | |
"os:%s" % (kw.get("state","empty" )), | |
"p:%d,%d" % (x/2,y), | |
"rp:%d,%d,%d" % (xr,yr,r), | |
"bb:%d,%d,%d,%d" % (xr-r, yr-r, xr+r, yr+r), | |
)) | |
c.update() | |
return i | |
def get_info(*tag,c=c): | |
tag = len(tag)==1 and tag or "p:%d,%d" % tag | |
e = c.gettags(tag) | |
sw=dict( | |
p = lambda s:("pos", tuple(map(int,s.split(",")))), | |
rp = lambda s:("real_pos", tuple(map(int,s.split(",")))), | |
os = lambda s:("old_state", s), | |
bb = lambda s:("box", tuple(map(int,s.split(",")))), | |
) | |
return dict( | |
sw.get(k,lambda v:v)(v) for k,v in map(lambda s:s.split(":"), e)) | |
def get_neighbour(x,y): | |
ye = [(-1, -1), (1, 0), (0, -1), (-1, 1), (-1, 0), (0, 1)] | |
yo= [(1, 1), (-1, 0), (0, -1), (1, 0), (0, 1), (1, -1)] | |
#yo = ye = [ (-1,1), (1, 1), (1,0), (1,-1),(-1,-1), (-1,0) ] | |
return set( ((dxdy[0]+x)%DIMX,(y+dxdy[1])%DIMY) for dxdy in [yo,ye][y%2]) | |
def get_falling_neighbour(x,y): | |
return set( ((dxdy[0]+x)%DIMX,(y+dxdy[1])%DIMY) for dxdy in [(0,-1)]) | |
def get_top_neighbour(x,y): | |
ye = [ (0,-1),(-1,-1) ] | |
yo = [ (0,-1),(1,-1) ] | |
to_ret= list( ((dxdy[0]+x)%DIMX,(y+dxdy[1])%DIMY) for dxdy in [yo,ye][y%2] ) | |
shuffle(to_ret) | |
return to_ret | |
def get_real_neighbour(x,y): | |
return set( | |
map(itemgetter("pos"), | |
map(get_info, c.find_overlapping(*get_info(x,y)["box"]) | |
)))-{(x,y)} | |
c.configure(bg="white") | |
seen=set() | |
old_filled=set() | |
new_filled=set() | |
obstacle=set() | |
to_remove=set() | |
for y in range(DIMY): | |
for x in range(0,DIMX*2,2): | |
#state="filled" if (x in {6,12} or y in {2,7}) else "empty" | |
#state="filled" if (y==0) else "empty" | |
state="empty" | |
if randint(-9,4) >0 and 0<y<DIMY-12: | |
obstacle |= {(x/2,y)} | |
# if x+y > DIMX and x-y<DIMX: | |
# if y%7==0 and y+35<DIMY: | |
# if (x %6 ==0) : | |
# obstacle |= {(x/2,y)} | |
# if y%7==3 and y+35<DIMY: | |
# if (x %6==4): | |
# obstacle |= {(x/2,y)} | |
if y+10>DIMY: | |
if x%7==4: | |
obstacle |= {(x/2,y)} | |
if y==DIMY-1: | |
obstacle |= {(x/2,y)} | |
# if (x,y) not in obstacle: | |
# state = randint(-5, 0) >= 0 and "filled" or "empty" | |
# if state=="filled": | |
# old_filled |= {(x/2,y)} | |
assert {(x,y)} not in seen | |
elt=cell( | |
x, | |
y, | |
state=state, | |
outline="black") | |
c.itemconfigure("p:%d,%d" % (x/2,y),fill="red" if (x/2,y) in obstacle else '') | |
seen.update(((x,y),),) | |
#assert get_real_neighbour(3,3)==get_neighbour(3,3) | |
def randrange(x): | |
c = list(range(x)) | |
shuffle(c) | |
return c | |
def live_neighbour(x,y,c=c): | |
if (x,y) in obstacle or (x,y) in old_filled: return 1 | |
return sum(map(lambda x : x in old_filled,get_neighbour(x,y))) | |
def falling_neighbour(x,y,c=c): | |
return sum(map(lambda x : x in old_filled,get_falling_neighbour(x,y))) | |
ORD=0 | |
save(c, "ev") | |
for i in range(400): | |
#sleep(1) | |
if i < 200: | |
for x in range((DIMX//2)-15, (DIMX//2+15)): | |
new_filled |= {(x,0)} | |
for y in range(DIMY,0,-1): | |
for x in randrange(DIMX): | |
will_fall(x,y) | |
if (x,y) not in to_remove: | |
c.itemconfigure("p:%d,%d" % (x,y),fill="black" if (x,y) in new_filled else '') | |
else: | |
new_filled -= {(x,y)} | |
if (x,y) in obstacle: | |
c.itemconfigure("p:%d,%d" % (x,y),fill="red") | |
c.update() | |
old_filled=new_filled | |
to_remove=set() | |
save(c, "ev") | |
new_filled=set() | |
[
output2.mp4
](url)
rev 5 Stop being a left leaning coder (bolchevik) and be a centrist. Now all Galton are belong to us
output2.mp4
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
output2.mp4