Majestas32 wrote:Isotropic. And I think Calcyman talked about every rule actually having a strobing checkerboard dual?
Yes.
Majestas32 wrote:Isotropic. And I think Calcyman talked about every rule actually having a strobing checkerboard dual?
x = 7, y = 7, rule = B3/S23
$b3o$bo2bo$5bo$2bobo!
x = 17, y = 11, rule = B3/S23
13b3o2$o$o$o2$2b3o2$o$o14b2o$o14b2o!
is there some way to search for a pattern manually, and how do the coordinate systems for the entries work?
x=3, y=3
3o$o$bo!
does this come up in the files somewhere?
x = 17, y = 10, rule = B3/S23
b2ob2obo5b2o$11b4obo$2bob3o2bo2b3o$bo3b2o4b2o$o2bo2bob2o3b4o$bob2obo5b
o2b2o$2b2o4bobo2b3o$bo3b5ob2obobo$2bo5bob2o$4bob2o2bobobo!
Also, does this come up in the files somewhere? It would indicate the presence of a spark I have been looking for a synthesis of.
x=14, y=8
bo$2bo$3o2$12bo$b2o8b2o$2b2o7bobo$bo!
x=10, y=8
8bo$9bo$7b3o$bo$2bo$3o3bo$6b2o$5bobo!
Majestas32 wrote:danny wrote:77topaz wrote:Is it possible to make a script that, given an apgcode, looks through Catagolue for rules where that apgcode has appeared and then returns the rule in which that pattern is the most common relative to other patterns (i.e. in which it appears the highest in the list of most common objects)?
This gets a +1 from me because I need to find that one rule where the bun evolves into a monogram
I also need to find the rule with the xp104_hexeh and xp14_eh so +1
Also the xp170 OMOS from my avatar
77topaz wrote:77topaz wrote:Is it possible to make a script that, given an apgcode, looks through Catagolue for rules where that apgcode has appeared and then returns the rule in which that pattern is the most common relative to other patterns (i.e. in which it appears the highest in the list of most common objects)?
Has anyone looked into this yet?
danny wrote:How do I find a reaction that shoots one of two ships at a few common still lifes and looks for a reaction that pulls it (-1,0) and outputs the same ship heading the other way, all in a specific rule?
I was thinking it could run soups and pick random still lifes out of there, and position them close enough together
# build-dot-field-v10.py
import golly as g
from time import sleep
from glife.text import make_text
MAXOBJ=5
xsize,ysize=5,12
# Obtains an alphanumeric representation of a pattern that
# fits within a 40-by-40 bounding box. This representation is
# alphanumeric and lowercase, and so much more compact than RLE. Compare:
#
# It is a generalisation of a notation created by Allan Weschler in 1992.
# By Adam P. Goucher
def canonise_orientation(rect):
representation = ""
ox, oy, length, breadth = rect
a, b, c, d = 1, 0, 0, 1
chars = "0123456789abcdefghijklmnopqrstuvwxyz"
for v in xrange(int((breadth-1)/5)+1):
zeroes = 0
if (v != 0):
representation += "z"
for u in xrange(length):
baudot = 0
for w in xrange(5):
x = ox + a*u + b*(5*v + w)
y = oy + c*u + d*(5*v + w)
baudot = (baudot >> 1) + 16*g.getcell(x, y)
if (baudot == 0):
zeroes += 1
else:
if (zeroes > 0):
if (zeroes == 1):
representation += "0"
elif (zeroes == 2):
representation += "w"
elif (zeroes == 3):
representation += "x"
else:
representation += "y"
representation += chars[zeroes - 4]
zeroes = 0
if baudot >= len(chars): g.warn("baudot = " + str(baudot))
representation += chars[baudot]
return representation
# check that a layer is available for canonisation
testlayer = g.getlayer()
canonlayer = -1
for i in xrange(g.numlayers()):
if g.getname(i) == "canonical":
canonlayer = i
break
if canonlayer == -1 and g.numlayers() == g.maxlayers():
g.exit("You need to delete a layer.")
if canonlayer == -1:
canonlayer = g.addlayer()
g.setrule('B2/S0')
g.new("canonical")
g.setlayer(testlayer)
patdict={}
# for i in range(2,xsize+1):
# for j in range(2,ysize+1):
# patdict[str([i,j])]=[]
objs=["o!", ] # dot
objlist=[]
for i in range(len(objs)):
# normalize so that the first ON cell in the list is always (0,0)
templist=g.parse(objs[i])
objlist+=[g.transform(templist,-templist[0],-templist[1])]
numobjs=len(objlist)
zonelist=[]
for item in objlist:
g.setrule("B12345678/S012345678")
zone=g.evolve(item,2)
zonelist+=[zone] # includes cells for object also
g.setrule("LifeHistory")
nearlist=[[i,j] for i in range(-1,xsize+1) for j in range(-1,ysize+1) if i<0 or i>=xsize or j<0 or j>=ysize]
count,x,y,ptr,filledlist,searchlist=0,0,0,0,[],[]
canonlist=[]
while y==0 or len(searchlist)>0:
overlap=([x,y] in nearlist)
# place current object
#############################################
# TODO: if there's an overlap, set ptr to max value, then handle all cases with same code at the bottom
if overlap==0:
# g.show(str(ptr))
obj=g.transform(objlist[ptr],x,y)
objpairs=[[obj[i],obj[i+1]] for i in range(0,len(obj),2)]
for item in objpairs:
if item in nearlist:
overlap=1
break
if overlap==0:
zone=g.transform(zonelist[ptr],x,y)
zonepairs=[[zone[i],zone[i+1]] for i in range(0,len(zone),2)]
for item in zonepairs:
if item in filledlist:
overlap=2
break
if overlap==0:
g.new("TestPage")
newptr,newx,newy=ptr+1,x,y
if newptr>=len(objlist): newptr,newx=0,newx+1
if newx>=xsize-1: newptr,newx,newy=0,0,y+1
searchlist.append([newx,newy,newptr,nearlist[:],filledlist[:]]) # add next state to search to the stack
nearlist+=zonepairs
filledlist+=objpairs
for i,j in nearlist: g.setcell(i,j,2)
minx, maxx, miny, maxy = 999,-999,999,-999
for i,j in filledlist:
g.setcell(i,j,1)
if i<minx:minx=i
elif i>maxx:maxx=i
if j<miny: miny=j
elif j>maxy: maxy=j
# Take a snapshot of the current successful placement
# [e.g., successful if two objects placed, etc.]
if minx==0 and miny==0:
keystr=str(maxx+1)+"x"+str(maxy+1)
if keystr not in patdict: patdict[keystr]=[]
if filledlist not in patdict[keystr]:
# Add to pattern list
patdict[keystr]+=[filledlist[:]]
# Add canonical representation to list
g.setlayer(canonlayer)
g.new("")
g.putcells([v for cell in filledlist for v in cell])
canon = canonise_orientation(g.getrect())
canonlist.append("xs" + str(len(filledlist)) + "_" + canon)
g.setlayer(testlayer)
# Now continue searching from where we are
count+=1
if count%100==0:
g.show(str(count))
g.update()
ptr,x=0,x+4
else: # the neighbor zone for this object overlaps some already placed object
ptr+=1
if ptr>=len(objlist): ptr,x=0,x+1
else: # the object itself overlaps some already placed object's neighborhood
ptr+=1
if ptr>=len(objlist): ptr,x=0,x+1
else:
ptr,x=0,x+1
if x>=xsize-1: ptr,x,y=0,0,y+1
if y>=ysize-1 or len(searchlist)>=MAXOBJ:
# we've reached the end of the frame.
# Time to clear the last placed object, go back to that x,y
if searchlist==[]:
g.new("TestPage")
g.exit("Search is complete.")
x,y,ptr,nearlist,filledlist=searchlist.pop()
# no point in looking at configurations with no object on the first line
g.new("TestPage")
outstr="Total count = " + str(count)
t=make_text(outstr,"mono")
g.putcells(t,0,-20)
for item in sorted(patdict):
y=0
t = make_text(item + " ("+str(len(patdict[item]))+")","mono")
outstr+="\n" + item + " ("+str(len(patdict[item]))+")"
g.putcells(t,x-6,y)
y+=30
for pat in patdict[item]:
for cell in pat:
g.setcell(cell[0]+x,cell[1]+y,1)
y+=20
x+=100
g.setclipstr(outstr)
results = "@Dot constellations in fields of size " + str(xsize) + " x " + str(ysize) + "\n"
for patt in canonlist:
results += patt + "\n"
filename = "dot-field.txt"
try:
f = open(filename, 'w')
f.write(results)
f.close()
except:
g.warn("Unable to create results file:\n" + filename)
g.exit("Search is complete.")
wildmyron wrote:I haven't written anything related to CataGoLue, but I suspect that anyone who does is (and definitely should be) wary of running scripts that make lots of requests (over many different rules in this case) because that kind of thing can result in disk operations going over quota and therefore CataGoLue being down for the rest of the day.
77topaz wrote:wildmyron wrote:I haven't written anything related to CataGoLue, but I suspect that anyone who does is (and definitely should be) wary of running scripts that make lots of requests (over many different rules in this case) because that kind of thing can result in disk operations going over quota and therefore CataGoLue being down for the rest of the day.
Are you saying that could happen just from viewing/searching pages, as opposed to uploading things to the server via apgsearch?
77topaz wrote:Are you saying that could happen just from viewing/searching pages, as opposed to uploading things to the server via apgsearch?
On Discord, danny wrote:i want a script that adds or removes a random transition from a rule
that isn't b012a...
-- change-random-isotropic-bit-v1.0.lua
local g = golly()
local gp = require "gplus"
local split = gp.split
function fixrule(s)
return (gp.split(s,":")) -- remove bounded grid spec, if any
end
g.addlayer("temp") -- keep and test the current rule
fixedrule=fixrule(g.getrule())
g.setrule(fixedrule)
bitlist={{-1,-1,256},{0,-1,128},{1,-1,64},{-1,0,32},{0,0,16},{1,0,8},{-1,1,4},{0,1,2},{1,1,1}}
for j = 1, 9 do
x, y, bit = table.unpack(bitlist[j])
for i=0, 511 do
if i&bit>0 then g.setcell(i*5+x,y,1) end
end
end
g.run(1)
bits={}
ind=1
local rulestr, invstr, revinvstr="","",""
for k=0,2555,5 do
if g.getcell(k,0)==1 then
rulestr=rulestr.."1"
invstr=invstr.."0"
revinvstr="0"..revinvstr
else
rulestr=rulestr.."0"
invstr=invstr.."1"
revinvstr="1"..revinvstr
end
end
-- build base64 lookup table
local lookupstr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
bdict={}
for i=1,64 do
local w=""
for j=0,5 do
if (i-1)&2^j>0 then w="1"..w else w="0"..w end
end
bdict[w]=lookupstr:sub(i,i)
end
-- decide whether to use standard, inverted, or inverted-reversed bitstring, based on fixedrule
-- (assuming Golly is correctly simulating rules with B0, with or without S8, to avoid strobing)
-- the following won't work for anisotropic non-totalistic rules defined by MAP strings, so
-- we'd have to decode the MAP rulestring into a 512-bit string in that case, check first and last digits.
if string.match(fixedrule,"B0") then rulestr = string.match(fixedrule,"S.*8") and revinvstr or invstr end
g.dellayer()
-- lookup table for isotropic bits
-- Each of the 102 isotropic bits corresponds to one or more bits in the 512-bit binary MAP string.
-- In this script we're assuming the rule in question is isotropic,
-- so technically we just need to check one of the neighborhoods to know the bit setting for all of them.
-- However, we'll check all the anisotropic bits, just to prove that everything is working...
isotropicbits ={{"0",0},
{"1c",1,4,64,256},
{"1e",2,8,32,128},
{"2a",3,6,9,36,72,192,288,384},
{"2c",5,65,260,320},
{"2e",10,34,136,160},
{"2i",40,130},
{"2k",12,33,66,96,129,132,258,264},
{"2n",68,257},
{"3c",69,261,321,324},
{"3e",42,138,162,168},
{"3a",11,38,200,416},
{"3i",7,73,292,448},
{"3k",98,140,161,266},
{"3n",13,37,67,193,262,328,352,388},
{"3j",14,35,74,137,164,224,290,392},
{"3q",70,76,100,196,259,265,289,385},
{"3r",41,44,104,131,134,194,296,386},
{"3y",97,133,268,322},
{"4c",325},
{"4e",170},
{"4a",15,39,75,201,294,420,456,480},
{"4i",45,195,360,390},
{"4k",99,141,165,225,270,330,354,396},
{"4n",71,77,263,293,329,356,449,452},
{"4j",106,142,163,169,172,226,298,394},
{"4q",102,204,267,417},
{"4r",43,46,139,166,202,232,418,424},
{"4y",101,197,269,323,326,332,353,389},
{"4t",105,135,300,450},
{"4w",78,228,291,393},
{"4z",108,198,297,387},
{"5c",171,174,234,426},
{"5e",327,333,357,453},
{"5a",79,295,457,484},
{"5i",47,203,422,488},
{"5k",229,334,355,397},
{"5n",107,143,167,233,302,428,458,482},
{"5j",103,205,271,331,358,421,460,481},
{"5q",110,206,230,236,299,395,419,425},
{"5r",109,199,301,361,364,391,451,454},
{"5y",173,227,362,398},
{"6c",175,235,430,490},
{"6e",335,359,461,485},
{"6a",111,207,303,423,459,486,489,492},
{"6i",365,455},
{"6k",231,237,363,366,399,429,462,483},
{"6n",238,427},
{"7c",239,431,491,494},
{"7e",367,463,487,493},
{"8",495}
}
-- pick a random bit to flip that isn't B012a
bschoice = math.random(2) -- 1=B, 2=S
-- flipping any bit would look like this:
-- flippedbit = isotropicbits[math.random(#isotropicbits)]
if bschoice==1 then -- with B bits, don't use the first four
flippedbit = isotropicbits[math.random(#isotropicbits-4)+4]
else
flippedbit = isotropicbits[math.random(#isotropicbits)]
end
-- g.note("Chosen bit is "..flippedbit[1])
finalrule = "B"
for i,bitlist in ipairs(isotropicbits) do
sum=0
bitname = bitlist[1]
for j=2,#bitlist do
value = bitlist[j]
sum=sum+tonumber(string.sub(rulestr,value+1,value+1))
end
if sum==#bitlist-1 then s=bitname else s="" end -- assuming isotropic rule here
if bitname==flippedbit[1] and bschoice==1 then
-- g.note("B bitname ="..bitname.." and s="..s)
s = (s=="") and bitname or ""
end
finalrule = finalrule..s
end
finalrule = finalrule.."/S"
for i,bitlist in ipairs(isotropicbits) do
sum=0
bitname = bitlist[1]
for j=2,#bitlist do
value = bitlist[j]+16
sum=sum+tonumber(string.sub(rulestr,value+1,value+1))
end
if sum==#bitlist-1 then s=bitname else s="" end -- assuming isotropic rule here
if bitname==flippedbit[1] and bschoice==2 then
-- g.note("S bitname ="..bitname.." and s="..s)
s = (s=="") and bitname or ""
end
finalrule = finalrule..s
end
-- g.note(finalrule)
g.setrule(finalrule)
canonicalrule = g.getrule()
-- g.note(canonicalrule)
Apple Bottom wrote:If anyone's interested, I can share these on archive.org so that we won't all have to start hitting Catagolue with thousands of requests.
77topaz wrote:That seems like a good idea, though we'd still need the script to be able to search that database.
Saka wrote:What about a script that picks a random rule from a given rule range?
x = 17, y = 10, rule = B3/S23
b2ob2obo5b2o$11b4obo$2bob3o2bo2b3o$bo3b2o4b2o$o2bo2bob2o3b4o$bob2obo5b
o2b2o$2b2o4bobo2b3o$bo3b5ob2obobo$2bo5bob2o$4bob2o2bobobo!
Saka wrote:Saka wrote:What about a script that picks a random rule from a given rule range?
danny wrote:Saka wrote:Saka wrote:What about a script that picks a random rule from a given rule range?
+1 support, having a random variant picker that still keeps some dynamics would be pretty cool
Majestas32 wrote:The format will probably be in macbi's partial notation and probs just remove or add 1 bit in the list at random
Macbi wrote:... put a "p" at the front of the rule to indicate that the rule is only being "partially" specified. After that:
If a number isn't present then none of those transitions are allowed.
If a number is present without any letters after it then all of its transitions are allowed.
If a number is present with some letters after it, followed by a minus sign, followed by some more letters, then the letters in the first group must be in the rule, the letters in the second group can't be in the rule, and any letters not mentioned can be in the rule or not.
Also if you just write "p" then it allows all of the rules.
So for example the command "-r pB34a/S2-c3a-c" would search for rules in which any B3 transitions were allowed to be present or not, any B4 were allowed to be present or not except B4a has to be, any S2 transitions would be allowed or not except S2c can't be used, and all S3 transitions can be used or not except S3a must be present and S3c can't be. No other transitions would be allowed.
import golly as g
import re
################## from LLS_literal_manipulation
#
possible_transitions = {"0":sorted("c"),
"1":sorted("ce"),
"2":sorted("cekain"),
"3":sorted("cekainyqjr"),
"4":sorted("cekainyqjrtwz"),
"5":sorted("cekainyqjr"),
"6":sorted("cekain"),
"7":sorted("ce"),
"8":sorted("c")}
def rule_from_rulestring(rulestring, indent = 0, verbosity = 0):
if rulestring == None:
return None
else:
rule = {}
rulestring = rulestring.strip()
original_rulestring = rulestring
partial_flag = False
if rulestring[0] in ["p", "P"]:
partial_flag = True
if len(rulestring) == 1:
rulestring = "B012345678/S012345678"
else:
rulestring = rulestring[1:]
rulestring = re.sub(' ', '', rulestring.upper())
rulestrings = re.split("/", rulestring)
if len(rulestrings) == 1:
assert "B" in rulestring or "S" in rulestring, 'Rule string not recognised (no "B" or "S")'
B_position = rulestring.find("B")
S_position = rulestring.find("S")
rulestring = rulestring.strip("BS")
rulestrings = re.split("[BS]*", rulestring)
assert len(rulestrings) < 3, "Rule string not recognised"
if B_position > S_position:
birth_string = rulestrings[1] if len(rulestrings) == 2 else ""
survival_string = rulestrings[0]
else:
birth_string = rulestrings[0]
survival_string = rulestrings[1] if len(rulestrings) == 2 else ""
else:
assert len(rulestrings) == 2, 'Rule string not recognised (too many "/"s)'
if "S" in rulestrings[0] or "B" in rulestrings[1]:
birth_string = rulestrings[1]
survival_string = rulestrings[0]
else:
birth_string = rulestrings[0]
survival_string = rulestrings[1]
assert "S" not in birth_string and "B" not in survival_string, "Rule string not recognised"
birth_string = re.sub('B', '', birth_string).lower()
survival_string = re.sub('S', '', survival_string).lower()
assert (birth_string == "" or birth_string[0] in "012345678") and (survival_string == "" or survival_string[0] in "012345678"), "Rule string not recognised"
if partial_flag:
variable_number = 0
for BS_letter, rulestring in zip(["B", "S"], [birth_string, survival_string]):
transitions = []
previous_number = 0
if rulestring != "":
for position in range(1,len(rulestring)):
if rulestring[position] in "012345678":
transitions.append(rulestring[previous_number:position])
previous_number = position
transitions.append(rulestring[previous_number:])
for transition in transitions:
number_of_neighbours = transition[0]
if not partial_flag:
if len(transition) == 1:
for character in possible_transitions[number_of_neighbours]:
rule[BS_letter + number_of_neighbours + character] = "1"
elif transition[1] == "-":
banned_characters = transition[2:]
assert all(character in possible_transitions[number_of_neighbours] for character in banned_characters), "Unrecognized character"
for character in possible_transitions[number_of_neighbours]:
if character in banned_characters:
rule[BS_letter + number_of_neighbours + character] = "0"
else:
rule[BS_letter + number_of_neighbours + character] = "1"
else:
characters = transition[1:]
assert all(character in possible_transitions[number_of_neighbours] for character in characters), "Unrecognized character"
for character in possible_transitions[number_of_neighbours]:
if character in characters:
rule[BS_letter + number_of_neighbours + character] = "1"
else:
rule[BS_letter + number_of_neighbours + character] = "0"
else:
if len(transition) == 1:
for character in possible_transitions[number_of_neighbours]:
rule[BS_letter + number_of_neighbours + character] = "rule_variable_" + str(variable_number)
variable_number += 1
else:
characters = transition[1:]
if "-" in characters:
characters, banned_characters = re.split("-", characters)
else:
banned_characters = ""
for character in possible_transitions[number_of_neighbours]:
if character in characters:
rule[BS_letter + number_of_neighbours + character] = "1"
elif character in banned_characters:
rule[BS_letter + number_of_neighbours + character] = "0"
else:
rule[BS_letter + number_of_neighbours + character] = "rule_variable_" + str(variable_number)
variable_number += 1
for number_of_neighbours in "012345678":
if not rule.has_key(BS_letter + number_of_neighbours + "c"):
for character in possible_transitions[number_of_neighbours]:
rule[BS_letter + number_of_neighbours + character] = "0"
return rule
g.note(str(rule_from_rulestring("pB34a/S2-c3a-c")))
{'S4e': '0', 'S4a': '0', 'S4c': '0', 'S4n': '0', 'S4i': '0', 'S4j': '0', 'S4k': '0', 'S4t': '0', 'S4w': '0', 'S4q': '0', 'S4r': '0', 'S4y': '0', 'S4z': '0', 'B2n': '0', 'B2k': '0', 'B2i': '0', 'B2e': '0', 'B2c': '0', 'B2a': '0', 'S5e': '0', 'S5c': '0', 'S5a': '0', 'S5n': '0', 'S5k': '0', 'S5j': '0', 'S5i': '0', 'S5r': '0', 'S5q': '0', 'S5y': '0', 'B5y': '0', 'B5r': '0', 'B5q': '0', 'B5j': '0', 'B5k': '0', 'B5i': '0', 'B5n': '0', 'B5c': '0', 'B5a': '0', 'B5e': '0', 'S6n': '0', 'B0c': '0', 'S6k': '0', 'S6i': '0', 'S6e': '0', 'S6c': '0', 'S6a': '0', 'B8c': '0', 'S7c': '0', 'S7e': '0', 'B3y': 'rule_variable_9', 'B3q': 'rule_variable_7', 'B3r': 'rule_variable_8', 'B3n': 'rule_variable_6', 'B3i': 'rule_variable_3', 'B3j': 'rule_variable_4', 'B3k': 'rule_variable_5', 'B3e': 'rule_variable_2', 'B3a': 'rule_variable_0', 'B3c': 'rule_variable_1', 'S8c': '0', 'B6c': '0', 'B6a': '0', 'B6e': '0', 'B6k': '0', 'B6i': '0', 'S0c': '0', 'B6n': '0', 'B1e': '0', 'B1c': '0', 'S1c': '0', 'S1e': '0', 'S2c': '0', 'S2a': 'rule_variable_22', 'S2e': 'rule_variable_23', 'S2k': 'rule_variable_25', 'S2i': 'rule_variable_24', 'S2n': 'rule_variable_26', 'B4t': 'rule_variable_18', 'B4w': 'rule_variable_19', 'B4q': 'rule_variable_16', 'B4r': 'rule_variable_17', 'B4y': 'rule_variable_20', 'B4z': 'rule_variable_21', 'B4e': 'rule_variable_11', 'S3n': 'rule_variable_31', 'B4a': '1', 'B4c': 'rule_variable_10', 'B4n': 'rule_variable_15', 'B4i': 'rule_variable_12', 'B4k': 'rule_variable_14', 'B4j': 'rule_variable_13', 'S3y': 'rule_variable_34', 'S3q': 'rule_variable_32', 'S3r': 'rule_variable_33', 'B7c': '0', 'S3i': 'rule_variable_28', 'B7e': '0', 'S3k': 'rule_variable_30', 'S3j': 'rule_variable_29', 'S3e': 'rule_variable_27', 'S3a': '1', 'S3c': '0'}
Users browsing this forum: No registered users and 3 guests