Code: Select all
#new-glider.py v0.3 beta
#Written by May13, 2024
from math import gcd
from fnmatch import fnmatch, filter
from re import sub
#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"
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 cannot shrink.","True"],
["23","01","23456","0123456","No gliders can exist: with B23/S01H, patterns cannot shrink.","True"],
["234","0","23456","023456","No gliders can exist: with B234/S0H, patterns cannot shrink.","True"],
["23","0234","2356","023456","No gliders can exist: with B23/S0234H, patterns cannot permanently shrink.","True"],
["2","12345","23456","123456","No gliders can exist: with S12345H, connected patterns cannot shrink","True"],
["","","3456","0123456","No gliders can exist: without one of B02H, patterns cannot escape bounding box.","True"],
]
hex_glider_impossible+=[
["0123456","","0123456","","No gliders can exist: patterns cannot grow.","True"],
["012456","4","0123456","4","No gliders can exist: patterns cannot shrink.","True"],
["012","5","0123456","45","No gliders can exist: patterns cannot shrink.","True"],
["013456","4","013456","4","No gliders can exist: patterns cannot shrink.","True"],
["01356","34","013456","34","No gliders can exist: patterns cannot shrink.","True"],
["013","5","013456","345","No gliders can exist: patterns cannot shrink.","True"],
["01","5","01456","2345","No gliders can exist: patterns cannot shrink.","True"],
["016","1234","0156","1234","No gliders can exist: patterns cannot shrink.","True"],
["01","15","0156","12345","No gliders can exist: patterns cannot shrink.","True"],
["0","5","023456","012345","No gliders can exist: patterns cannot shrink.","True"]
]
hex_glider_impossible+=[
["012","","0123456","0123","No gliders can exist: with B012H and without one of S456H, patterns cannot escape bounding orthogonal hexagon.","True"],
["0","","01","01","No gliders can exist: with B0H and without one of B23456/S23456H, patterns cannot escape bounding orthogonal hexagon.","True"],
["01234","01234","0123456","012345","No gliders can exist: with B01234/S01234H and without S6H, patterns cannot escape bounding orthogonal hexagon.","True"]
]
moore_glider_impossible=[
["3","","345678","012345678","Gliders with speed (m,n)c/p, 2m+n>p, cannot exist with B3 and without B012.","speed_is_known and min_dx*2+min_dy>min_dp"],
["1","","12345678","012345678","No gliders can exist: with B1, any pattern expands in all directions.","True"],
["","","45678","012345678","No gliders can exist: without B2 or B3, patterns cannot escape bounding box.","True"],
["","0123","2345678","012345678","No gliders can exist: with S0123, trailing edge of pattern cannot die.","True"],
["23","0","2345678","012345678","No gliders can exist: with B23/S0, trailing edge of pattern cannot die.","True"],
["","123456","2345678","012345678","No gliders can exist: with S123456, connected patterns cannot shrink.","True"],
["34","12345","2345678","012345678","No gliders can exist: with B34/S12345, connected patterns cannot shrink.","True"],
["345","1234","2345678","012345678","No gliders can exist: with B345/S1234, connected patterns cannot shrink.","True"],
["","234567","345678","012345678","No gliders can exist: with S234567 and without B2, patterns cannot escape bounding diamond without immortal triangle.","True"],
["","","3678","678","No gliders can exist: without one of B245/S012345, patterns cannot escape bounding diamond.","True"],
["3","","345678","0123678","Diagonal speed limit is c/4 diagonal without one of B2/S45.","speed_is_known and min_dx*2+min_dy*2>min_dp"],
["3","345678","345678","012345678","With S345678 and without B2, fast diagonal growth impossible without creating immortal cross.","speed_is_known and min_dx*2+min_dy*2>min_dp"],
["3","","3","05678","speed >= c/2 (or c/3 diagonal) impossible without one of B2/S1234.","speed_is_known and min_dx*2+min_dy>=min_dp"]
]
vn_glider_impossible=[
["","","1234","01234","","True"]
]
if mode == "Hex" and type=="glider": glider_impossible=hex_glider_impossible
elif mode == "OT" and type=="glider": glider_impossible=moore_glider_impossible
elif mode == "von_Neumann" and type=="glider": glider_impossible=vn_glider_impossible
else: glider_impossible = []
g = gt = ""
triangular = lambda x: x*(x+1)//2
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)
b0_alt = lambda x: trans_to_int(inv_trans(int_to_trans(x%(2**N1()))))*(2**N1())+trans_to_int(inv_trans(int_to_trans(x//(2**N1()))))
def b0_alt2(x, y):
return trans_to_int(inv_trans(int_to_trans(y))), trans_to_int(inv_trans(int_to_trans(x)))
def remove(inp, rem):
return sub(f"[{rem}]", "", inp)
def hexagonal_area(x, y, code):
x = int(x)
y = int(y)
t = ""
new = True
u = 0
v = 0
zmax = 0
zmin = 0
for i in code:
if i.isdecimal(): t += i
elif i == "b":
if t == "": t = "1"
u += int(t)
t = ""
elif i == "$":
if t == "": t = "1"
zmax = max(zmax, u-v-1)
u = 0
v += int(t)
t = ""
new = True
elif i == "o":
if t == "": t = "1"
if new:
zmin = min(zmin, u-v)
new = False
u += int(t)
t = ""
return (x * y - triangular(x - 1 - zmax) - triangular(y - 1 + zmin))
def command_filter(string):
string = remove(string, "inpsuv*")
while '{' in string:
string=string[:string.index("{")]+string[string.index("}")+1:]
return string
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,name,info=""):
if not au=="":
if filter(authors(info),au)==[]: return True
cond=False
if "c" in command:
vel=command_filter(command)
vel=remove(vel, "/")
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]
tp=remove(tp, "odk")
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:
if dy1==0: cond=True
elif "d" in command:
if dy1==dx1: cond=True
elif "k" in command:
slope=command_filter(command[1:])
if slope=="":
if dx1>dy1>0: cond=True
else:
if "/" not in slope:
if dx1==dy1*int(slope) or (slope=="0" and dy1==0): cond = True
else:
slope = slope.split("/")
if dx1*int(slope[1])==dy1*int(slope[0]) or dx1*int(slope[0])==dy1*int(slope[1]): cond = True
else: cond=True
if "{" in command:
temp = command[command.index("{")+1:]
temp = temp[:temp.index("}")]
if not fnmatch(name, temp): return 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:])
p = postfix[mode]
if p!="":
minset2=set(min2[1:-len(p)])
maxset2=set(max2[1:-len(p)])
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][0],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[0],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())
for add1 in [ak]+([b0_alt(ak)] 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))]
if '0' in m1: rows=[rowsM+sum(2**int(d2[j])*(1-(tr4(2**(j),i))) for j in range(len(d2))) for i in range(2**len(d2))]
else: 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
speed_is_known = True
min_dx = 0
min_dy = 0
min_dp = 1
if type=="glider":
if "c" in command:
vel=command_filter(command)
vel=remove(vel, "/")
vel=vel.split("c")
if vel[0]=="": min_dx=1
elif "," in vel[0]:
min_dx = int(vel[0].split(",")[0])
min_dy = int(vel[0].split(",")[1])
else:
min_dx = int(vel[0])
vel[1] = remove(vel[1],"odk")
if vel[1]=="": min_dp = 1
else: min_dp = int(vel[1])
if "d" in command: min_dy = min_dx
if "k" in command or "o" in command: min_dy = 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])) and eval(k[5]):
text+=" "
non_im=False
break
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])) and eval(k[5]):
text+=" "
non_im=False
break
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"{co2}/{co1}\n")
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")
print(f" 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[0],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
if add1%2==1: map[b0_alt(add1)]=True
num=0
rules = []
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
rules.append(text)
for i in range(2*N1()+2):
rules_temp = []
for j in rules:
if j[-1-i]=='X':
l = list(j)
l[-1-i]='-'
text = ''.join(l)
if text in rules:
l[-1-i]=' '
rules_temp.append(''.join(l))
rules.remove(text)
else: rules_temp.append(j)
else: rules_temp.append(j)
rules = rules_temp.copy()
rules.sort()
print(*rules, sep='\n')
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[0],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]
if au!="": c=[i for i in c if fnmatch(i[0],au)]
print(f"All 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))
if pC.endswith('.0'): pC = pC[:-2]
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=""):
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][0],g[i][1]): continue
glider(i+1)
print()
def show_redundant_gliders(command=""):
dictionary = dict()
ret = dict()
k = 0
info = [""]*len(g)
for i in g:
k += 1
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[0],i[1]): continue
rule = i[2]
rule=remove(rule, "BSHV")
min1,min2=rule.split("/")
rule = i[3]
rule=remove(rule, "BSHV")
max1,max2=rule.split("/")
if mode=="Hex": area = hexagonal_area(i[7],i[8],i[9])
else: area = int(i[7])*int(i[8])
if area<=100: continue
l = [k,area,trans_to_int(min1),trans_to_int(min2),trans_to_int(max1),trans_to_int(max2)]
l.extend(b0_alt2(l[4],l[5]))
l.extend(b0_alt2(l[2],l[3]))
speed = (max(dx,dy),min(dx,dy),p)
if speed not in dictionary.keys(): dictionary[speed] = [l]
else: dictionary[speed].append(l)
info[k-1] = "{}x{} ({},{})c/{} {} - {}".format(i[7],i[8],dx,dy,p,i[2],i[3])
q = 0
k = 0
t = 0
for i in dictionary.values():
t += len(i)*(len(i)-1)
for speed in dictionary.keys():
data = dictionary[speed]
if len(data)==1: continue
if q/t>0.001: print(f"Loading: {round(100*k/t,2)}%")
for i in range(len(data)):
for j in range(len(data)):
if i==j: continue
if (data[i][2]&1)!=(data[j][2]&1): continue
if ((data[i][2]&data[j][2])^data[j][2]==0 and (data[i][3]&data[j][3])^data[j][3]==0 and (data[i][4]&data[j][4])^data[i][4]==0 and (data[i][5]&data[j][5])^data[i][5]==0):
if data[i][1]>data[j][1] or data[i][1]==data[j][1] and data[i][0]<data[j][0]:
ret[data[i][0]] = (f"{pat_type[type][0]} {data[i][0]} is made redundant by {pat_type[type][1]} {data[j][0]}.",data[j][0])
break
if data[i][2]&1 and ((data[i][6]&data[j][2])^data[j][2]==0 and (data[i][7]&data[j][3])^data[j][3]==0 and (data[i][8]&data[j][4])^data[i][8]==0 and (data[i][9]&data[j][5])^data[i][9]==0):
if data[i][1]>data[j][1] or data[i][1]==data[j][1] and data[i][0]<data[j][0]:
ret[data[i][0]] = (f"{pat_type[type][0]} {data[i][0]} is made redundant by {pat_type[type][1]} {data[j][0]}.",data[j][0])
break
q = len(data)*(len(data)-1)
k += q
print("Loading complete.\n")
for i in sorted(ret.keys()):
print(ret[i][0])
print(info[i-1])
print(info[ret[i][1]-1])
print()
print(f"Total redundant {pat_type[type][2]}: {len(ret)}\n")
usage_glider="""usage:
n to output the nth known glider
_n to output the nth row from the database
B3/S23 to list known gliders for this rule (add "=" before additional options)
-string to list rules having gliders with matching speed and direction
)string to chart matching gliders for each rule with B0 and without S8
@string to chart matching gliders for each rule with B2
#string to chart matching gliders for each rule with B3
@#string to chart matching gliders for each rule with B23
* to list all gliders
+ to list all redundant gliders
a to list all discoverers
options for -, ), @, #, and @#:
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
additional options:
MMc/NN to show only gliders with the given speed
XX,YYc/PP to show only gliders with the given speed (XX is greater than YY)
o to show only gliders that move orthogonally
d to show only gliders that move diagonally
k to show only gliders that move at a knights move or other off-slope
kMM/NN to show only gliders with slope MM/NN
{NAME} to show only gliders with a name matching the given wildcard
:PERSON to show only gliders discovered by specified person
"""
usage_oscillator="""usage:
n to output the nth known oscillator
_n to output the nth row from the database
B3/S23 to list known oscillators for this rule (add "=" before additional options)
-string to list rules having oscillators with matching period
)string to chart matching oscillators for each rule with B0 and without S8
!string to chart matching oscillators for each rule with B1
@string to chart matching oscillators for each rule with B2
#string to chart matching oscillators for each rule with B3
@#string to chart matching oscillators for each rule with B23
;string to chart matching oscillators for each rule without B0, B1, B2 or B3
* to list all oscillators
+ to list all redundant oscillators
a to list all discoverers
options for -, ), !, @, #, @#, and ;:
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
additional options:
cPP to show only oscillators with the given period
{NAME} to show only oscillators with a name matching the given wildcard
:PERSON to show 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.isdecimal():
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 'k' in com) or "B" in com):
if "=" in com: com = com.split("=")
else: com = [com,""]
command = com[1]
comB, comS=com[0].replace(postfix[mode],"").replace("B","").replace("S","").split("/")
speed_is_known = True
min_dx = 0
min_dy = 0
min_dp = 1
if type=="glider":
if "c" in command:
vel=command_filter(command)
vel=remove(vel,"/")
vel=vel.split("c")
if vel[0]=="": min_dx=1
elif "," in vel[0]:
min_dx = int(vel[0].split(",")[0])
min_dy = int(vel[0].split(",")[1])
else:
min_dx = int(vel[0])
vel[1] = remove(vel[1],"odk")
if vel[1]=="": min_dp = 1
else: min_dp = int(vel[1])
if "d" in command: min_dy = min_dx
if "k" in command or "o" in com: min_dy = 0
for k in glider_impossible:
if (set(k[0]) <= set(comB) <= set(k[2])) and (set(k[1]) <= set(comS) <= set(k[3])) and eval(k[5]):
print(k[4])
print()
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.\n")
elif com.startswith("_"):
num = int(com[1:])
if num>len(g): print(f"only {len(g)} {pat_type[type][2]} are known")
elif num<1: print("numbering starts from 1")
else: print(gt[num-1])
print()
elif com.startswith("*"): all_gliders(com[1:])
elif com.startswith("+"): show_redundant_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")
finally: print()
elif com in ["quit","exit"]: break
The command + should list pairs with bounding box >100 cells.
In fact, I missed the case when a newer spaceship fits into 100-cell bounding box, but the redundant one does not.