Script works like original glider.c, but it can read oscillators.db and databases for rules with hexagonal and (maybe) von Neumann neighborhood.
Code: Select all
from math import gcd
#Options
map_count_rules=True
file="new-gliders.db" #Name of database
wid=72 #Width of RLE (default: 72)
old_speed=False #if True, use format "2c/5 slope 2"; if False, use format "(2,1)c/5"
#
au="" #part of command (author)
map_char="0123456789abcdefghijklmnopqrstuvwxyz"
com_p="nuvpis/*"
disc_0=":UNKNOWN:"
files={"new-gliders.db": ("OT","glider",True), "oscillators.db": ("OT","oscillator",True), "hex-gliders.db": ("Hex","glider",True), "hex-oscillators.db": ("Hex","oscillator",True), "vn-gliders.db": ("von_Neumann","glider",True), "vn-oscillators.db": ("von_Neumann","oscillator",True)}
mode=files[file][0] #OT #Hex #von_Neumann
neighbors={"OT": 8, "Hex": 6, "von_Neumann": 4}
postfix={"OT": "", "Hex": "H", "von_Neumann": "V"}
type=files[file][1] #glider #oscillator
pat_type={"glider": ("Glider", "glider", "gliders"), "oscillator": ("Oscillator", "oscillator", "oscillators")}
auth_splitters=[[","],[";"],[" based"," by "],[" improved"," by "],[" reduced"," by "],[" and "],[" by D8_2 reduction of an existing ship"],[" by D8_2 reduction of "]]
auth_date=[[" before "],[" early ","s"],[" pre-"]]
hex_glider_impossible=[["2","012","2456","0123456","No gliders can exist: with B2/S012H, patterns can not shrink."],["23","01","23456","0123456","No gliders can exist: with B23/S01H, patterns can not shrink."],["234","0","23456","023456","No gliders can exist: with B234/S0H, patterns can not shrink."],["23","0234","2356","023456","No gliders can exist: with B23/S0234H, patterns can not permanently shrink."],["2","12345","23456","123456","No gliders can exist: with S12345H, connected patterns can not shrink"]]
hex_glider_impossible+=[["0123456","","0123456","",""],["012456","4","0123456","4",""],["012","5","0123456","45",""],["013456","4","013456","4",""],["01356","34","013456","34",""],["013","5","013456","345",""],["01","5","01456","2345",""],["016","1234","0156","1234",""],["01","15","0156","12345",""],["0","5","023456","012345",""]]
hex_glider_impossible+=[["012","","0123456","0123","No gliders can exist: with B012H and without S456H, patterns can not escape bounding orthogonal hexagon"]]
hex_glider_impossible+=[["0","","01","01","No gliders can exist: with B0H and without B23456/S23456H, patterns can not escape bounding orthogonal hexagon"]]
hex_glider_impossible+=[["01234","01234","0123456","012345","No gliders can exist: with B01234/S01234H and without S6H, patterns can not escape bounding orthogonal hexagon"]]
if mode == "Hex": glider_impossible=hex_glider_impossible
else: glider_impossible = []
g=gt=""
def load_db():
global g, gt
if files[file][2]==True:
f=open(file)
g=f.read()
f.close()
gt=g.split("\n")
g=[i.split(":") for i in (gt[:-1] if gt[-1]=="" else gt)]
load_db()
def N1(): return neighbors[mode]+1
def pt(x): return pat_type[type][x]
int_to_trans=lambda x: "".join(str(i) for i in range(N1()) if x//(2**i)%2==1)
trans_to_int=lambda x: sum(2**i for i in range(N1()) if str(i) in x)
inv_trans=lambda x: "".join(str(i) for i in range(N1()) if not str(N1()-1-i) in x)
def authors(info):
i=info
for j in auth_splitters+auth_date:
if len(j)==1:
i=i.replace(j[0],":")
elif len(j)==2:
while j[0] in i:
sliceA=i.find(j[0])
sliceB=i.find(j[1],sliceA+len(j[0]))
if sliceB<0: break
i=i[:sliceA]+":"+i[sliceB+len(j[1]):]
i=i.split(":")
j=0
while j < len(i):
if i[j] in [""," "]:
i.pop(j)
continue
if i[j][0]==" ": i[j]=i[j][1:]
if i[j][-1]==" ": i[j]=i[j][:-1]
if i[j].isnumeric() or (i[j][:-1].isnumeric() and i[j][-1]=="?"):
i.pop(j)
continue
j+=1
if i==[]: i=[disc_0]
return i
def exclude(command,dx1,dy1,p1,p,info=""):
if not au=="":
if not au in authors(info): return True
cond=False
if "c" in command:
vel=command
for k in com_p:
vel=vel.replace(k,"")
vel=vel.split("c")
if type=="oscillator":
if vel[1]=="":
if p==1: cond=True
elif p==int(vel[1]): cond=True
else:
tdx=tdy=tp=0
if vel[0]=="": tdx=1
elif "," in vel[0]: tdx,tdy=[int(k) for k in vel[0].split(",")]
else: tdx=int(vel[0])
if "d" in vel[1]: tdy=tdx
elif "k" in vel[1]: tdy=-1
elif "o" in vel[1]: tdy=0
elif not "," in vel[0]: tdy=-2
tp=vel[1]
for k in "odk": tp=tp.replace(k,"")
if tp=="": tp=1
else: tp=int(tp)
if tdy==-1:
if dy1>0 and dy1<dx1 and dx1*tp==tdx*p1: cond=True
elif tdy==-2:
if dx1*tp==tdx*p1: cond=True
else:
if dx1*tp==tdx*p1 and dy1*tp==tdy*p1: cond=True
else:
if "o" in command and dy1==0: cond=True
elif "d" in command and dy1==dx1: cond=True
elif "k" in command and dx1>dy1>0: cond=True
elif not ("o" in command or "d" in command or "k" in command): cond=True
return not cond
def tr4(width,index):
if (index//width)%4 in [0,3]: return 0
return 1
def detect_shift(d1,d2):
if mode=="Hex":
dx=int(d1)
dy=int(d2)
if dx<0 and dy>0:
if dx+dy>0: dy+=dx
else: dx+=dy
elif dx>0 and dy<0:
if dx+dy>0: dx+=dy
else: dy+=dx
else:
dx=int(d1)
dy=int(d2)
return abs(dx), abs(dy)
def detect_speed(dx,dy,p):
if "/" in p:
mod=int(p.split("/")[1])
p0=int(p[:-2])
else:
mod=1
p0=int(p)
dx0,dy0=max(dx,dy),min(dx,dy)
gcd3=gcd(p0,gcd(dx0,dy0))
p1=p0//gcd3
dx1=dx0//gcd3
dy1=dy0//gcd3
return dx1,dy1,p1,p0,mod,gcd3
def get_rule_sets(min1,min2,max1,max2):
minset1=set(min1[1:])
maxset1=set(max1[1:])
if mode in ["Hex","von_Neumann"]:
minset2=set(min2[1:-1])
maxset2=set(max2[1:-1])
else:
minset2=set(min2[1:])
maxset2=set(max2[1:])
return minset1,minset2,maxset1,maxset2
def print_rule_simple(amin1,amin2,amax1,amax2,agens):
anumstr="".join(str(i) for i in range(N1()))
print("#C B"+anumstr+" S"+anumstr+(" G"+str(agens) if agens>2 else "")+(" "+postfix[mode] if len(postfix[mode])>0 else ""))
bs=""
ss=""
minset1,minset2,maxset1,maxset2=get_rule_sets(amin1,amin2,amax1,amax2)
for i1 in range(N1()):
if str(i1) not in maxset1: bs+="-"
elif str(i1) in minset1: bs+="X"
else: bs+=" "
if str(i1) not in maxset2: ss+="-"
elif str(i1) in minset2: ss+="X"
else: ss+=" "
print("#C "+bs+" "+ss+"\n#C")
def print_RLE(ax,ay,arul,RLE):
print("x = "+ax+", y = "+ay+", rule = "+arul)
t1=""
t0=""
k=0
for j in RLE:
if j in "bo$":
if len(t0)+len(t1)>=wid:
print(t1)
t1=t0+j
k=0
else:
t1+=t0+j
t0=""
else: t0+=j
k+=1
if j=="!":
if len(t1)==wid: print(t1+"\n!")
else: print(t1+"!")
def glider(n,rul="",com=":"):
gi=g[n-1]
if rul=="": rul=gi[2]
x1,x2=rul.split("/")
min1,min2=gi[2].split("/")
max1,max2=gi[3].split("/")
if (set(min1) <= set(x1) <= set(max1)) and (set(min2) <= set(x2) <= set(max2)):
if True:
dx, dy=detect_shift(gi[5],gi[6])
dx1,dy1,p1,p,mod,gcd3=detect_speed(dx,dy,gi[4])
if com!=":" and exclude(com,dx1,dy1,p1,p,g[i][1]): return False
if dx1==1: dx2=""
else: dx2=dx1
if dx==dy and dx1==0:
if p==1: speed=", still life"
else: speed=f", period {p}"+(f" (mod {p//mod})" if mod>1 else "")
elif dy1==0: speed=f", {dx2}c/{p1} orthogonal"
elif dy==dx: speed=f", {dx2}c/{p1} diagonal"
else:
if old_speed:
sg=gcd(dx1,p1)
if dx1==sg: dx3=""
else: dx3=dx1//sg
if p1==sg: p3=""
else: p3=f"/{p1//sg}"
sg1=gcd(dx1,dy1)
if dy1==sg1: dy4=""
else: dy4="/{dy1//sg1}"
dx4=dx1//sg1
speed=f", {dx3}c{p3} slope {dx4}{dy4}"
if sg>1: speed+=" (period "+str(p)+")"
else: speed=f", ({dx1},{dy1})c/{p1}"
if not (dx==dy and dx1==0):
if mod==2: speed+=" (period "+str(p)+"/2)"
elif gcd3>1: speed+=" (period "+str(p)+")"
if gi[0]=="": print("#C "+pat_type[type][0]+" "+str(n)+speed)
else: print("#C The "+gi[0]+" ("+pat_type[type][1]+" "+str(n)+")"+speed)
if gi[1]!="": print("#C Discovered by "+gi[1]+"\n#C")
else: print("#C")
print_rule_simple(min1,min2,max1,max2,2)
print_RLE(gi[7],gi[8],rul,gi[9])
return True
return False
def create_map(m1,d1,m2,d2,command=""):
mapset=False
if "v" in command or "u" in command: mapset=True
if mapset: map=[set() for i in range(4**(neighbors[mode]+1))]
else: map=[0]*(4**(neighbors[mode]+1))
for i in g:
dx,dy=detect_shift(i[5],i[6])
dx1,dy1,p1,p,mod,gcd3=detect_speed(dx,dy,i[4])
if exclude(command,dx1,dy1,p1,p,i[1]): continue
min1,min2=i[2].split("/")
max1,max2=i[3].split("/")
gminset1,gminset2,gmaxset1,gmaxset2=get_rule_sets(min1,min2,max1,max2)
add0=0
addb=[]
for j in range(N1()):
if str(j) in gminset1:
add0+=2**j
if str(j) in gminset2:
add0+=2**(j+N1())
for j in range(2**len(gmaxset1-gminset1)):
add1=add0
for k in range(len(gmaxset1-gminset1)):
if j//(2**(len(gmaxset1-gminset1)-k-1))%2==1: add1+=2**(int(list(gmaxset1-gminset1)[k]))
addb+=[add1]
for a in addb:
for j in range(2**len(gmaxset2-gminset2)):
ak=a
for k in range(len(gmaxset2-gminset2)):
if j//(2**(len(gmaxset2-gminset2)-k-1))%2==1: ak+=2**(int(list(gmaxset2-gminset2)[k])+N1())
if ak%2==1:
b0_a=trans_to_int(inv_trans(int_to_trans(ak%(2**N1()))))*(2**N1())+trans_to_int(inv_trans(int_to_trans(ak//(2**N1()))))
for add1 in [ak]+([b0_a] if ak%2==1 else []):
if mapset:
if "u" in command: map[add1].add((max(dx,dy),min(dx,dy),p))
else: map[add1].add((dx1,dy1,p1))
elif "p" in command:
if map[add1]==0: map[add1]=p
else: map[add1]=max(map[add1],p)
elif "i" in command:
if map[add1]==0: map[add1]=p1
else: map[add1]=max(map[add1],p1)
elif "s" in command:
if map[add1]==0: map[add1]=max(int(i[7]),int(i[8]))
else: map[add1]=min(map[add1],max(int(i[7]),int(i[8])))
else: map[add1]+=1
colsM=sum(2**int(i) for i in m1)
rowsM=sum(2**int(i) for i in m2)
cols=[colsM+sum(2**int(d1[j])*tr4(2**(len(d1)-j-1),i) for j in range(len(d1))) for i in range(2**len(d1))]
rows=[rowsM+sum(2**int(d2[j])*tr4(2**(len(d2)-j-1),i) for j in range(len(d2))) for i in range(2**len(d2))]
text=" "*(len(m2)+len(d2)+3)+" B"*2**len(d1)+"\n"
for i in m1:
text+=" "*(len(m2)+len(d2)+3)+(" "+i)*2**len(d1)+"\n"
for i in d1:
text+=" "*(len(m2)+len(d2)+4)
for j in cols:
if j//(2**int(i))%2==1: text+=i+" "
else: text+=" "
text+="\n"
text+=" "*(len(m2)+len(d2)+3)+"-"*2**len(d1)*2+"\n"
q=[]
co1=0
co2=0
for i in rows:
text+="S"+m2
for j in d2:
if i//(2**int(j))%2==1: text+=j
else: text+=" "
text+=" :"
for j in cols:
non_im=True
for k in glider_impossible:
if (set(k[0]) <= set(int_to_trans(j)) <= set(k[2])) and (set(k[1]) <= set(int_to_trans(i)) <= set(k[3])):
text+=" "
non_im=False
if non_im and j%2==1:
for k0 in glider_impossible:
if (set(k0[0]) <= set(inv_trans(int_to_trans(i))) <= set(k0[2])) and (set(k0[1]) <= set(inv_trans(int_to_trans(j))) <= set(k0[3])):
text+=" "
non_im=False
if non_im:
co1+=1
t=map[i*2**N1()+j]
if mapset: t=len(t)
ti=t
if t>35: t="X"
elif t==0: t="."
else: t=map_char[t]
if not t==".": co2+=1
text+=" "+t
else:
t=map[i*2**N1()+j]
if mapset:
if len(t)>0:
print("Error!")
print(t)
else:
if t>0:
print("Error!")
print(t)
text+="\n"
print(text)
if map_count_rules: print(f"\n{co2}/{co1}")
def create_list(command=""):
print(f"Totalistic rules for which {command}{'' if command=='' else ' '}{pt(2)}{' discovered by '+au if not au=='' else ''} are known:\n\n B{''.join(str(i) for i in range(N1()))} S{''.join(str(i) for i in range(N1()))} {postfix[mode]}")
map=[False]*(4**(neighbors[mode]+1))
for i in g:
dx,dy=detect_shift(i[5],i[6])
dx1,dy1,p1,p,mod,gcd3=detect_speed(dx,dy,i[4])
if exclude(command,dx1,dy1,p1,p,i[1]): continue
min1,min2=i[2].split("/")
max1,max2=i[3].split("/")
gminset1,gminset2,gmaxset1,gmaxset2=get_rule_sets(min1,min2,max1,max2)
add0=0
addb=[]
for j in range(N1()):
if str(j) in gminset1:
add0+=2**j
if str(j) in gminset2:
add0+=2**(j+N1())
for j in range(2**len(gmaxset1-gminset1)):
add1=add0
for k in range(len(gmaxset1-gminset1)):
if j//(2**(len(gmaxset1-gminset1)-k-1))%2==1: add1+=2**(int(list(gmaxset1-gminset1)[k]))
addb+=[add1]
for a in addb:
for j in range(2**len(gmaxset2-gminset2)):
add1=a
for k in range(len(gmaxset2-gminset2)):
if j//(2**(len(gmaxset2-gminset2)-k-1))%2==1: add1+=2**(int(list(gmaxset2-gminset2)[k])+N1())
map[add1]=True
num=0
for i in range(4**N1()):
j=bin(4**N1()-i-1)[2:]
j=j[::-1]+"0"*(2*N1()-len(j))
j=int(j,2)
if map[j]:
num+=1
text=" "
for k in range(N1()):
if j%2==1: text+="X"
else: text+="-"
j=j//2
text+=" "
for k in range(N1()):
if j%2==1: text+="X"
else: text+="-"
j=j//2
print(text)
print(f"\nTotal: {num} rules\n")
def all_discoverers(command=""):
a={}
b={}
d={}
for i in g:
dx,dy=detect_shift(i[5],i[6])
dx1,dy1,p1,p,mod,gcd3=detect_speed(dx,dy,i[4])
if exclude(command,dx1,dy1,p1,p,i[1]): continue
j=authors(i[1])
for k in j:
if k in a:
a[k]+=1
b[k]+=1/len(j)
d[k]+=1 if len(j)>1 else 0
else:
a[k]=1
b[k]=1/len(j)
d[k]=1 if len(j)>1 else 0
l=12
for i in a.keys():
l=max(l,len(i))
c=sorted(a.items(), key=lambda x: x[1])[::-1]
print(f"\nAll discoverers of {command}{'' if command=='' else ' '}{pt(2)}:\n")
print(" Discoverer"+" "*(l-6)+"Total Partial Shared\n")
for i in c:
pA = i[0]
pB = str(i[1])
pC = str(round(b[i[0]], 4))
pD = str(d[i[0]])
print(" "+pA+" "*(l-len(pA)+4)+pB+" "*(8-len(pB))+pC+" "*(13-len(pC))+pD)
print(f"\nTotal: {len(c)} discoverers\n")
def all_gliders(command=""):
print()
for i in range(len(g)):
dx,dy=detect_shift(g[i][5],g[i][6])
dx1,dy1,p1,p,mod,gcd3=detect_speed(dx,dy,g[i][4])
if exclude(command,dx1,dy1,p1,p,g[i][1]): continue
glider(i+1)
print()
usage_glider=f"""usage:
glider n to output the nth known glider
glider _n to output the nth row from the database
glider B3/S23 to list known gliders for this rule (add "=" before additional options)
glider -string to list rules having gliders with matching speed and direction
glider )string to chart matching gliders for each rule with B0 and without S8
glider @string to chart matching gliders for each rule with B2
glider #string to chart matching gliders for each rule with B3
glider @#string to chart matching gliders for each rule with B23
glider * to list all gliders
glider a to list all discoverers
options for -, @, and # (multiple options must be in the given order):
n to chart counts of the number of known gliders (the default)
u to chart counts of the number of known unsimplified speeds
v to chart counts of the number of known simplified speeds
p to chart the maximum known glider unsimplified period
i to chart the maximum known glider simplified period
s to chart the minimum known glider size
MMc/NN to chart only gliders with the given speed
XX,YYc/PP to chart only gliders with the given speed (XX is greater than YY)
d to chart only gliders that move diagonally
k to chart only gliders that move at a knights move or other off-slope
o to chart only gliders that move orthogonally
:PERSON to chart only gliders discovered by specified person
"""
usage_oscillator=f"""usage:
glider n to output the nth known oscillator
glider _n to output the nth row from the database
glider B3/S23 to list known oscillators for this rule (add "=" before additional options)
glider -string to list rules having oscillators with matching period
glider )string to chart matching oscillators for each rule with B0 and without S8
glider !string to chart matching oscillators for each rule with B1
glider @string to chart matching oscillators for each rule with B2
glider #string to chart matching oscillators for each rule with B3
glider @#string to chart matching oscillators for each rule with B23
glider ;string to chart matching oscillators for each rule without B0, B1, B2 or B3
glider * to list all oscillators
glider a to list all discoverers
options for -, @, and # (multiple options must be in the given order):
n to chart counts of the number of known oscillators (the default)
p to chart the maximum known oscillator period
s to chart the minimum known oscillator size
cPP to chart only oscillator with the given period
:PERSON to chart only oscillators discovered by specified person
"""
while True:
com=input(">>>")
if ":" in com:
com,au=com.split(":")
if au=="": au=disc_0
else: au=""
if com.isnumeric():
if int(com)>len(g): print(f"only {len(g)} {pat_type[type][2]} are known")
elif com=="0": print("numbering starts from 1")
else: glider(int(com))
print()
elif com.startswith(")"):
if mode=="OT": create_map("0","12345678","","01234567",com[1:])
elif mode=="Hex": create_map("0","123456","","012345",com[1:])
elif mode=="von_Neumann": create_map("0","1234","","0123",com[1:])
elif com.startswith("@#"):
if mode=="OT": create_map("23","45678","","012345678",com[1:])
elif mode=="Hex": create_map("23","456","","0123456",com[1:])
elif mode=="von_Neumann": create_map("23","4","","01234",com[1:])
elif com.startswith("!"):
if mode=="OT": create_map("1","2345678","","012345678",com[1:])
elif mode=="Hex": create_map("1","23456","","0123456",com[1:])
elif mode=="von_Neumann": create_map("1","234","","01234",com[1:])
elif com.startswith("@"):
if mode=="OT": create_map("2","45678","","012345678",com[1:])
elif mode=="Hex": create_map("2","3456","","0123456",com[1:])
elif mode=="von_Neumann": create_map("2","34","","01234",com[1:])
elif com.startswith("#"):
if mode=="OT": create_map("3","45678","","012345678",com[1:])
elif mode=="Hex": create_map("3","456","","0123456",com[1:])
elif mode=="von_Neumann": create_map("3","4","","01234",com[1:])
elif com.startswith(";"):
if mode=="OT": create_map("","45678","","012345678",com[1:])
elif mode=="Hex": create_map("","456","","0123456",com[1:])
elif mode=="von_Neumann": create_map("","4","","01234",com[1:])
elif com.startswith("-"):
create_list(com[1:])
elif "/" in com and ((not "c/" in com) or "B" in com):
if "=" in com: com = com.split("=")
else: com = [com,""]
comB, comS=com[0].replace(postfix[mode],"").replace("B","").replace("S","").split("/")
for k in glider_impossible:
if (set(k[0]) <= set(comB) <= set(k[2])) and (set(k[1]) <= set(comS) <= set(k[3])):
print(k[4])
break
else:
no=True
for i in range(len(g)):
if glider(i+1,com[0],com[1]):
print()
no=False
if "0" in comB:
if glider(i+1,"B"+inv_trans(comS)+"/S"+inv_trans(comB)+postfix[mode]):
print()
no=False
if no: print("No gliders are known.")
elif com.startswith("_"):
try: print(gt[int(com[1:])-1])
except: print("Invalid command!")
elif com.startswith("*"): all_gliders(com[1:])
elif com.startswith("a"): all_discoverers(com[1:])
elif com in ["","c","help","usage","?"]:
if type=="glider": print(usage_glider)
else: print(usage_oscillator)
elif com in ["update","refresh","reload"]:
try:
load_db()
print("Database reloaded")
except:
print("Failed to reload database")
elif com in ["quit","exit"]: break
Test commands:
Code: Select all
#####For new-gliders.db
B38/S23=2,1c/6 #the Sprayer
a2,1c/5 #all discoverers of (2,1)c/5 spaceships
a: #number of gliders discovered by unknown people
#v #counts of the numbers of known glider simplified speeds in B3 rules
###For example: B3/S35 has 3 simplified speeds (c/2o, c/3o, c/4o)
#v #counts of the numbers of known glider unsimplified speeds in B3 rules
###For example: B3/S35 has 5 unsimplified speeds (c/2o, 2c/4o, 4c/8o, c/3o, c/4o)
_22222 #DB string of an oblique spaceship in Pedestrian Life
#####For oscillators.db
B3/S23=c43 #p43 Snark loop
#c41 #map of B3 rules with known p41 oscillators
B3/S23:carybe #p36 shuttle
- Solve potential issues with B0 maps
- Rework "-" option and implement "+" option (to list redundant gliders/oscillators)
- Figure out which rules cannot contain oscillators/spaceships and rework "glider_impossible" for databases other than hex-gliders.db
- Add option to search for pattern by name
- Make code more readable