Skip to content

Instantly share code, notes, and snippets.

@jul
Last active May 6, 2024 11:58
Show Gist options
  • Save jul/37f418aefd27b69bb213fe1e2a563bfd to your computer and use it in GitHub Desktop.
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)
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()
@jul
Copy link
Author

jul commented May 5, 2024

output2.mp4

@jul
Copy link
Author

jul commented May 5, 2024

[

output2.mp4

](url)

@jul
Copy link
Author

jul commented May 6, 2024

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