def lookforkeys(event, deltax, deltay): global oldcells, selrect, selpatt # look for keys used to flip/rotate selection if event == "key x none" or event == "key y none": # flip floating selection left-right or top-bottom if len(oldcells) > 0: g.clear(0) g.putcells(selpatt, deltax, deltay) if " x " in event: g.flip(0) else: g.flip(1) selpatt = g.transform(g.getcells(selrect), -deltax, -deltay) if len(oldcells) > 0: g.clear(0) g.putcells(oldcells) g.putcells(selpatt, deltax, deltay) g.update() return if event == "key > none" or event == "key < none": # rotate floating selection clockwise or anticlockwise; # because we use g.rotate below we have to use the exact same # calculation (see Selection::Rotate in wxselect.cpp) for rotrect: midx = selrect[0] + int((selrect[2]-1)/2) midy = selrect[1] + int((selrect[3]-1)/2) newleft = midx + selrect[1] - midy newtop = midy + selrect[0] - midx rotrect = [ newleft, newtop, selrect[3], selrect[2] ] if not rectingrid(rotrect): g.warn("Rotation is not allowed if selection would be outside grid.") return g.clear(0) if len(oldcells) > 0: g.putcells(oldcells) oldcells = g.join(oldcells, g.getcells(rotrect)) g.clear(0) g.select(rotrect) g.clear(0) g.select(selrect) g.putcells(selpatt, deltax, deltay) if " > " in event: g.rotate(0) else: g.rotate(1) selrect = g.getselrect() if selrect != rotrect: g.warn("Bug: selrect != rotrect") selpatt = g.transform(g.getcells(selrect), -deltax, -deltay) if len(oldcells) > 0: g.clear(0) g.putcells(oldcells) g.putcells(selpatt, deltax, deltay) g.update() return if event == "key h none": showhelp2() return g.doevent(event)
def saverule(self, name, comments, table, colours): ruledir = g.getdir("rules") filename = ruledir + name + ".rule" results = "@RULE " + name + "\n\n" results += "*** File autogenerated by saverule. ***\n\n" results += comments results += "\n\n@TABLE\n\n" results += table results += "\n\n@COLORS\n\n" results += colours results += "\n\n@ICONS\n\n" results += "circles\n" # Only create a rule file if it doesn't already exist; this avoids # concurrency issues when booting an instance of apgsearch whilst # one is already running. if not os.path.exists(filename): try: f = open(filename, 'w') f.write(results) f.close() except: g.warn("Unable to create rule table:\n" + filename)
def savegen(filename, gen): try: f = open(filename, 'w') f.write(gen) f.close() except: g.warn("Unable to save given gen in file:\n" + filename)
def lookforkeys(event): global oldcells, object # look for keys used to flip/rotate object if event == "key x none" or event == "key y none": # flip floating object left-right or top-bottom g.putcells(object, 0, 0, 1, 0, 0, 1, "xor") # erase object if len(oldcells) > 0: g.putcells(oldcells) obox = getminbox(object) if event == "key x none": # translate object so that bounding box doesn't change xshift = 2 * (obox.left + int(obox.wd/2)) if obox.wd % 2 == 0: xshift -= 1 object = g.transform(object, xshift, 0, -1, 0, 0, 1) else: # translate object so that bounding box doesn't change yshift = 2 * (obox.top + int(obox.ht/2)) if obox.ht % 2 == 0: yshift -= 1 object = g.transform(object, 0, yshift, 1, 0, 0, -1) oldcells = underneath(object) g.putcells(object) g.update() return if event == "key > none" or event == "key < none": # rotate floating object clockwise or anticlockwise # about the center of the object's bounding box obox = getminbox(object) midx = obox.left + int(obox.wd/2) midy = obox.top + int(obox.ht/2) newleft = midx + obox.top - midy newtop = midy + obox.left - midx rotrect = [ newleft, newtop, obox.ht, obox.wd ] if not rectingrid(rotrect): g.warn("Rotation is not allowed if object would be outside grid.") return g.putcells(object, 0, 0, 1, 0, 0, 1, "xor") # erase object if len(oldcells) > 0: g.putcells(oldcells) if event == "key > none": # rotate clockwise object = g.transform(object, 0, 0, 0, -1, 1, 0) else: # rotate anticlockwise object = g.transform(object, 0, 0, 0, 1, -1, 0) # shift rotated object to same position as rotrect obox = getminbox(object) object = g.transform(object, newleft - obox.left, newtop - obox.top) oldcells = underneath(object) g.putcells(object) g.update() return if event == "key h none": showhelp2() return g.doevent(event)
def lookforkeys(event): global oldcells, object # look for keys used to flip/rotate object if event == "key x none" or event == "key y none": # flip floating object left-right or top-bottom g.putcells(object, 0, 0, 1, 0, 0, 1, "xor") # erase object if len(oldcells) > 0: g.putcells(oldcells) obox = getminbox(object) if event == "key x none": # translate object so that bounding box doesn't change xshift = 2 * (obox.left + int(obox.wd / 2)) if obox.wd % 2 == 0: xshift -= 1 object = g.transform(object, xshift, 0, -1, 0, 0, 1) else: # translate object so that bounding box doesn't change yshift = 2 * (obox.top + int(obox.ht / 2)) if obox.ht % 2 == 0: yshift -= 1 object = g.transform(object, 0, yshift, 1, 0, 0, -1) oldcells = underneath(object) g.putcells(object) g.update() return if event == "key > none" or event == "key < none": # rotate floating object clockwise or anticlockwise # about the center of the object's bounding box obox = getminbox(object) midx = obox.left + int(obox.wd / 2) midy = obox.top + int(obox.ht / 2) newleft = midx + obox.top - midy newtop = midy + obox.left - midx rotrect = [newleft, newtop, obox.ht, obox.wd] if not rectingrid(rotrect): g.warn("Rotation is not allowed if object would be outside grid.") return g.putcells(object, 0, 0, 1, 0, 0, 1, "xor") # erase object if len(oldcells) > 0: g.putcells(oldcells) if event == "key > none": # rotate clockwise object = g.transform(object, 0, 0, 0, -1, 1, 0) else: # rotate anticlockwise object = g.transform(object, 0, 0, 0, 1, -1, 0) # shift rotated object to same position as rotrect obox = getminbox(object) object = g.transform(object, newleft - obox.left, newtop - obox.top) oldcells = underneath(object) g.putcells(object) g.update() return if event == "key h none": showhelp2() return g.doevent(event)
def main(): apgcode = canonise() if not apgcode: g.warn( 'Failed to detect periodic behaviour after {} generations.'.format( MAXPERIOD)) # 2G collisions if apgcode in twoGcols: cols = twoGcols[apgcode] else: cols = [] # 3G and 4G collisions for cFile in compFiles: with open(cFile) as cF: found_code = False for l in cF: line = l.strip() if not found_code: if line == apgcode: found_code = True continue elif '>' in line: in_code, gstr, _ = line.split(">") if in_code: g.warn( 'Non-empty starting target in glider collision - Not implemented' ) else: cols.append(gstr) else: break Ncols = len(cols) if Ncols: try: cols = [reconstruct(col) for col in cols] except Exception: g.note(str(cols)) g.new("solutions") g.show("{} collisions found".format(Ncols)) g.setname(apgcode) if Ncols <= 20: N = 5 else: N = math.ceil(math.sqrt(Ncols)) + 1 offset = 100 for i, col in enumerate(cols): g.putcells(col, int((i % N) * offset), int((i // N) * offset)) g.fit() else: g.note( "No glider collisions found for constellation {}. Better luck next time" .format(apgcode))
def firstdiffs(values): if len(values) < 1: g.warn("No polynomial entered, apparently.") g.exit() return # Just for good measure. if len(values) == 1: return [values[0]] if len(values) > 1: diffs = [] for n in xrange(len(values) - 1): diffs.append(values[n + 1] - values[n]) return firstdiffs(diffs) + [values[0]]
def firstvalues(terms): vals = [] for x in xrange(len(terms)): sum = 0 for exp, coef in enumerate(terms): sum += coef * x**exp if sum != int(sum): g.warn( "Polynomial must produce integer values when x is an integer.") g.exit() return vals.append(int(sum)) return vals
def TriangularTransitionsToRuleTree_CheckerboardMethod(neighborhood, n_states, transitions, rule_name): # Background state 0 has no checkerboard, we infer it from its neighboring cells. def encode_lower(s): return s def encode_upper(s): ### AKT: this code causes syntax error in Python 2.3: ### return [0 if se==0 else n_states+se-1 for se in s] temp = [] for se in s: if se == 0: temp.append(0) else: temp.append(n_states + se - 1) return temp total_states = n_states * 2 - 1 if total_states > 256: golly.warn("Number of states exceeds Golly's limit of 256!") golly.exit() tree = RuleTree(total_states, 4) for t in transitions: # as lower tree.add_rule( [ encode_lower(t[0]), # C encode_upper(t[2]), # S encode_upper(t[1]), # E encode_upper(t[3]), # W range(total_states) ], # N encode_lower(t[4])[0]) # C' # as upper tree.add_rule( [ encode_upper(t[0]), # C range(total_states), # S encode_lower(t[3]), # E encode_lower(t[1]), # W encode_lower(t[2]) ], # N encode_upper(t[4])[0]) # C' # output the rule tree golly.show("Compressing rule tree and saving to file...") tree.write(golly.getdir('rules') + rule_name + '.tree')
def parse_hex(hexstr): # parse a string like "FF00EE" or "FFFF0000EEEE" R, G, B = (0, 0, 0) if len(hexstr) == 6: R = int(hexstr[0:2], 16) G = int(hexstr[2:4], 16) B = int(hexstr[4:6], 16) elif len(hexstr) == 12: # only use upper 2 hex digits R = int(hexstr[0:2], 16) G = int(hexstr[4:6], 16) B = int(hexstr[8:10], 16) else: g.warn("Unexpected hex string: " + hexstr) return (R, G, B)
def parse_hex(hexstr): # parse a string like "FF00EE" or "FFFF0000EEEE" R, G, B = (0, 0, 0) if len(hexstr) == 6: R = int(hexstr[0:2],16) G = int(hexstr[2:4],16) B = int(hexstr[4:6],16) elif len(hexstr) == 12: # only use upper 2 hex digits R = int(hexstr[0:2],16) G = int(hexstr[4:6],16) B = int(hexstr[8:10],16) else: g.warn("Unexpected hex string: " + hexstr) return (R,G,B)
def TriangularTransitionsToRuleTree_CheckerboardMethod(neighborhood,n_states,transitions,rule_name): # Background state 0 has no checkerboard, we infer it from its neighboring cells. def encode_lower(s): return s def encode_upper(s): ### AKT: this code causes syntax error in Python 2.3: ### return [0 if se==0 else n_states+se-1 for se in s] temp = [] for se in s: if se==0: temp.append(0) else: temp.append(n_states+se-1) return temp total_states = n_states*2 - 1 if total_states>256: golly.warn("Number of states exceeds Golly's limit of 256!") golly.exit() tree = RuleTree(total_states,4) for t in transitions: # as lower tree.add_rule([encode_lower(t[0]), # C encode_upper(t[2]), # S encode_upper(t[1]), # E encode_upper(t[3]), # W range(total_states)], # N encode_lower(t[4])[0]) # C' # as upper tree.add_rule([encode_upper(t[0]), # C range(total_states), # S encode_lower(t[3]), # E encode_lower(t[1]), # W encode_lower(t[2])], # N encode_upper(t[4])[0]) # C' # output the rule tree golly.show("Compressing rule tree and saving to file...") tree.write(golly.getdir('rules') + rule_name + '.tree')
def saverule(self, name, comments, table, colours, icons): ruledir = g.getdir("rules") filename = ruledir + name + ".rule" results = "@RULE " + name + "\n\n" results += "*** File autogenerated by saverule. ***\n\n" results += comments results += "\n\n@TABLE\n\n" results += table results += "\n\n@COLORS\n\n" results += colours results += "\n\n@ICONS\n\n" results += icons # Change in behavior: always create rule table, # silently overwriting any file with the same name try: f = open(filename, 'w') f.write(results) f.close() except: g.warn("Unable to create rule table:\n" + filename)
def run_pattern_in_golly(pattern, comments, extended): if extended: try: extended = int(pattern[pattern.index('%')+1:]) except ValueError: #sometimes there's a % sign in the comments extended = False pattern_rle = pattern pattern = g.parse(pattern) if len(pattern) % 2 == 1: #multistate rule for some reason g.warn(pattern_rle) g.warn(str(pattern)) initial_pattern = pattern.copy() xs = pattern[::2] ys = pattern[1::2] min_x = 0 max_x = max(xs) min_y = 0 max_y = max(ys) min_min_x = min_x #these four are the permanent minima and maxima, used for determining maximum pattern size max_max_x = max_x min_min_y = min_y max_max_y = max_y for period in range(1, 1000): #maximum oscillator period if period == 999 and extended: pattern = g.evolve(pattern, extended - 999) pattern = g.evolve(pattern,1) if not pattern: g.warn('Not an oscillator, dies out completely: %s' % initial_pattern) return xs = pattern[::2] ys = pattern[1::2] min_min_x = min(min_min_x, min(xs)) #sets new absolute minima and maxima max_max_x = max(max_max_x, max(xs)) min_min_y = min(min_min_y, min(ys)) max_max_y = max(max_max_y, max(ys)) if pattern == initial_pattern: if extended: return (comments + convert_grid_to_rle(pattern), extended, max_max_x-min_min_x+1, max_max_y-min_min_y+1, -min_min_x, -min_min_y) else: return (comments + convert_grid_to_rle(pattern), period, max_max_x-min_min_x+1, max_max_y-min_min_y+1, -min_min_x, -min_min_y) #0: RLE. 1: period. 2, 3: maximum bounding box for x and y. 4, 5: Greatest negative for calculating offset. #if extended == 'file': #one at a time # return pattern g.warn('Not an oscillator, maximum generations reached: %s' % initial_pattern) return
l_binstring = [] def rev(s): return "".join(reversed(s)) d_inst2lineno = {} d_lineno2inst = {} for line in parsed: lineno, opcode, (d_1, n_1), (d_2, n_2), (d_3, n_3) = line if int(n_1) >= (1 << 8): g.warn( "Error: The first operand {} at line {} from instruction {} has a bitlength larger than 8. The first operand must have a bitlength less than 8 bits." .format(n_1, lineno, line)) g.exit("Bitlength error.") d_1, d_2, d_3 = map( lambda n: int2binstr((n + (1 << 16)) % (1 << 16), length=2), [d_1, d_2, d_3]) # n_1, n_2 = map(lambda n: int2binstr((n + (1 << 16)) % (1 << 16), length=16), [n_1, n_2]) n_1 = int2binstr((n_1 + (1 << 16)) % (1 << 16), length=8) n_2 = int2binstr((n_2 + (1 << 16)) % (1 << 16), length=16) n_3 = int2binstr((n_3 + (1 << 16)) % (1 << 16), length=8) bins = [opcode, n_1, d_1, n_2, d_2, n_3, d_3] linestr = "".join([rev(s) for s in bins]) rom_width = 3 + 8 + 2 + 16 + 2 + 8 + 2 # rom_width = 3+18+18+8+2
n_states = len(action_table) n_colors = len(action_table[0]) # (N.B. The terminology 'state' here refers to the internal state of the finite # state machine that each Turmite is using, not the contents of each Golly # cell. We use the term 'color' to denote the symbol on the 2D 'tape'. The # actual 'Golly state' in this emulation of Turmites is given by the # "encoding" section below.) n_dirs = 3 # TODO: check table is full and valid total_states = n_colors+n_colors*n_states*3 # problem if we try to export more than 255 states if total_states > 128: # max allowed using checkerboard emulation (see EmulateTriangular) golly.warn("Number of states required exceeds Golly's limit of 255.") golly.exit() # encoding: # (0-n_colors: empty square) def encode(c,s,d): # turmite on color c in state s facing away from direction d return n_colors + 3*(n_states*c+s) + d # http://rightfootin.blogspot.com/2006/09/more-on-python-flatten.html def flatten(l, ltypes=(list, tuple)): ltype = type(l) l = list(l) i = 0 while i < len(l): while isinstance(l[i], ltypes):
def ConvertTreeToRule(rule_name, total_states, icon_pixels): ''' Convert rule_name.tree to rule_name.rule and delete the .tree file. If rule_name.colors exists then use it to create an @COLORS section and delete the .colors file. If icon_pixels is supplied then add an @ICONS section. Format of icon_pixels (in this example there are 4 icons at each size): --------------------------------------------------------- | | | | | | | | | | | 31x31 | 31x31 | 31x31 | 31x31 | | | | | | | | | | | --------------------------------------------------------- | |.....| |.....| |.....| |.....| | 15x15 |.....| 15x15 |.....| 15x15 |.....| 15x15 |.....| | |.....| |.....| |.....| |.....| --------------------------------------------------------- |7x7|.........|7x7|.........|7x7|.........|7x7|.........| --------------------------------------------------------- The top layer of 31x31 icons is optional -- if not supplied (ie. the height is 22) then there are no gaps between the 15x15 icons. ''' rulepath = golly.getdir('rules')+rule_name+'.rule' treepath = golly.getdir('rules')+rule_name+'.tree' colorspath = golly.getdir('rules')+rule_name+'.colors' # get contents of .tree file try: treefile = open(treepath,'r') treedata = treefile.read() treefile.close() except: golly.exit('Failed to open .tree file: '+treepath) # if the .rule file already exists then only replace the @TREE section # so we don't clobber any other info added by the user if os.path.isfile(rulepath): ReplaceTreeSection(rulepath, treedata) os.remove(treepath) if os.path.isfile(colorspath): os.remove(colorspath) return # create a new .rule file rulefile = open(rulepath,'w') rulefile.write('@RULE '+rule_name+'\n\n') rulefile.write('@TREE\n\n') # append contents of .tree file, then delete that file rulefile.write(treedata) os.remove(treepath) # if .colors file exists then append @COLORS section and delete file if os.path.isfile(colorspath): colorsfile = open(colorspath,'r') rulefile.write('\n@COLORS\n\n') for line in colorsfile: if line.startswith('color') or line.startswith('gradient'): # strip off everything before 1st digit line = line.lstrip('colorgadient= \t') rulefile.write(line) colorsfile.close() os.remove(colorspath) # if icon pixels are supplied then append @ICONS section if len(icon_pixels) > 0: wd = len(icon_pixels[0]) ht = len(icon_pixels) iconsize = 15 # size of icons in top row if ht > 22: iconsize = 31 # 31x31 icons are present numicons = wd / iconsize # get colors used in all icons (we assume each icon size uses the same set of colors) colors, multi_colored = GetColors(icon_pixels, wd, ht) if len(colors) > 256: golly.warn('Icons use more than 256 colors!') rulefile.flush() rulefile.close() return if multi_colored: # create @COLORS section using color info in icon_pixels (not grayscale) rulefile.write('\n@COLORS\n\n') if numicons >= total_states: # extra icon is present so use top right pixel to set the color of state 0 R,G,B = icon_pixels[0][wd-1] rulefile.write('0 ' + str(R) + ' ' + str(G) + ' ' + str(B) + '\n') numicons -= 1 # set colors for each live state to the average of the non-black pixels # in each icon on top row (note we've skipped the extra icon detected above) for i in xrange(numicons): nbcount = 0 totalR = 0 totalG = 0 totalB = 0 for row in xrange(iconsize): for col in xrange(iconsize): R,G,B = icon_pixels[row][col + i*iconsize] if R > 0 or G > 0 or B > 0: nbcount += 1 totalR += R totalG += G totalB += B if nbcount > 0: rulefile.write(str(i+1) + ' ' + str(totalR / nbcount) + ' ' \ + str(totalG / nbcount) + ' ' \ + str(totalB / nbcount) + '\n') else: # avoid div by zero rulefile.write(str(i+1) + ' 0 0 0\n') # create @ICONS section using (r,g,b) triples in icon_pixels[row][col] rulefile.write('\n@ICONS\n') if ht > 22: # top row of icons is 31x31 CreateXPMIcons(colors, icon_pixels, 31, 0, 31, numicons, rulefile) CreateXPMIcons(colors, icon_pixels, 15, 31, 31, numicons, rulefile) CreateXPMIcons(colors, icon_pixels, 7, 46, 31, numicons, rulefile) else: # top row of icons is 15x15 CreateXPMIcons(colors, icon_pixels, 15, 0, 15, numicons, rulefile) CreateXPMIcons(colors, icon_pixels, 7, 15, 15, numicons, rulefile) rulefile.flush() rulefile.close()
movablepat+=basepat[i:i+2]+[1] elif cellcolor==4: output+=basepat[i:i+2]+[0] elif cellcolor==5: signal+=basepat[i:i+2]+[1] elif cellcolor==6: movablebb+=basepat[i:i+2]+[1] else: g.exit("Don't know that color: "+str(basepat[i+2])) errorstr = "" if len(fixedpat) == 0: errorstr += "fixed pattern (state 1, green) " if len(movablepat) == 0: errorstr += "movable pattern (state 3, white) " if len(output) == 0: errorstr += "output pattern (state 4, red) " if len(signal) == 0: errorstr += "signal pattern (state 5, yellow) " if errorstr != "": g.warn("Required cell states -- " +errorstr \ + "-- not found in " + basegun + " template pattern:\n" + item) if len(fixedpat)%2 == 0: fixedpat+=[0] if len(movablepat)%2 == 0: movablepat+=[0] if len(fixedbb)%2 == 0 : fixedbb+=[0] if len(movablebb)%2 == 0 : movablebb+=[0] if len(output)%2 == 0: output+=[0] if len(signal)%2 == 0: signal+=[0] g.new(basegun) g.putcells(fixedpat) g.putcells(fixedbb) g.putcells(movablepat, iadjustment*dx, iadjustment*dy) g.putcells(movablebb, iadjustment*dx, iadjustment*dy) done = 0 g.setrule("Life") r=g.getrect() while done == 0:
off_cells.append((x - ox - offset, y - oy)) results_layer = g.addlayer() g.setname("Results") g.setrule("Life") g.setalgo("QuickLife") work_layer = g.addlayer() g.setname("Work area") g.putcells(start_cells) g.putcells(example_cells) g.run(delay) if not all(g.getcell(x, y) for x, y in on_cells) or any(g.getcell(x, y) for x, y in off_cells): g.warn("Warning: the example pattern is not a solution") results = 0 def apply_sym(pat, symm): yield pat if "x" in symm: yield g.transform(pat, 0, 0, -1, 0, 0, 1) if "d" in symm: yield g.transform(pat, 0, 0, 0, 1, 1, 0) if "x" in symm and "d" in symm: yield g.transform(pat, 0, 0, 1, -1, 0) def testkey(): if results and g.getevent().startswith("key x"): g.setlayer(results_layer)
turmite_spec = "{{"+','.join(['{'+str((i+1)%n_colors)+','+d[spec[i]]+',0}' for i in range(n_colors)])+"}}" rule_name = prefix+'_'+spec action_table = eval(turmite_spec.replace('}',']').replace('{','[')) n_states = len(action_table) n_dirs=4 # (N.B. The terminology 'state' here refers to the internal state of the finite # state machine that each Turmite is using, not the contents of each Golly # cell. We use the term 'color' to denote the symbol on the 2D 'tape'. The # actual 'Golly state' in this emulation of Turmites is given by the # "encoding" section below.) total_states = n_colors+n_colors*n_states*n_dirs # problem if we try to export more than 255 states if total_states>255: golly.warn("Number of states required exceeds Golly's limit of 255\n\nMaximum 51 turns allowed.") golly.exit() # what direction would a turmite have been facing to end up here from direction # d if it turned t: would_have_been_facing[t][d] would_have_been_facing={ 1:[2,3,0,1], # no turn 2:[1,2,3,0], # right 4:[0,1,2,3], # u-turn 8:[3,0,1,2], # left } remap = [2,1,3,0] # N,E,S,W -> S,E,W,N not_arriving_from_here = [range(n_colors) for i in range(n_dirs)] # (we're going to modify them) for color in range(n_colors):
import golly as g base64dict = {} for index, char in enumerate( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"): base64dict[char] = "".join( ["1" if index & 2**(5 - j) else "0" for j in range(6)]) mapstr = g.getstring( "Enter MAP string to convert to a rule: ", "ARYXfhZofugWaH7oaIDogBZofuhogOiAaIDogIAAgAAWaH7oaIDogGiA6ICAAIAAaIDogIAAgACAAIAAAAAAAA" ) if mapstr[:3] == "MAP": mapstr = mapstr[3:] if len(mapstr) != 86: g.warn("Map string is the wrong length -- should be 86 characters.") g.exit() s = "".join([base64dict[char] for char in mapstr]) ruletablestr="@RULE MAPblahblahblah\nRule table for nontotalistic rule MAPblahblahblah" \ +"\n\n@TABLE\nn_states:2\nneighborhood:Moore\nsymmetries:none\n\n" for i in range(512): binary = "{0:09b}".format(i) for j in binary[4] + binary[:4] + binary[5:]: ruletablestr += j + "," ruletablestr += s[i] + "\n" g.setclipstr(ruletablestr.replace("blahblahblah", mapstr))
# print('No "x =": ' + i) patterns.append(i) show_message("Pass 1 of 3: processing pattern #" + str(len(patterns)),0.001) show_message('Total number of patterns: %s' % len(patterns),0.5) patterns = [] open_file2('oscillators.txt') data = [(0,1234567,0,0,0,0)] #this period 1234567 marks the end of the file count = 0 for i in patterns: count += 1 show_message('Pass 2 of 3: %s of %s done, %s seconds ' % (count-1, len(patterns), int(time.time() - start_time)),0) try: data.append(run_pattern_in_golly(i[i.index('= B3/S23')+9:], i[:i.index('x =')], '%' in i)) #max period 1000 without %, 100000 with % except ValueError: g.warn('"= B3/S23" not found: ' + i) show_message('All done, ' + str(int(time.time() - start_time)) + ' seconds',0.5) while None in data: #non-oscillators: data.remove(None) #for i in num_dict.values(): #digits from other files # if eval(i) in data: # data.remove(eval(i)) num_patterns = len(data) data.sort(key=lambda a:(a[1],a[3])) #first by period, then height num_periods = 0 comments = '' lvcomments = '#C [[ COLOR LABEL Yellow LABELSIZE 30 LABELALPHA .75 LABELVIEWDIST 20 LABELZOOMRANGE 1 64 LABELANGLE 330 ]]\n' grid = {}
"Moore":ConvertRuleTableTransitionsToRuleTree, "triangularVonNeumann":EmulateTriangular, "triangularMoore":EmulateTriangular, "Margolus":EmulateMargolus, "square4_figure8v":EmulateMargolus, "square4_figure8h":EmulateMargolus, "square4_cyclic":EmulateMargolus, "oneDimensional":EmulateOneDimensional, "hexagonal":EmulateHexagonal, } golly.show("Reading from rule table file...") n_states, neighborhood, transitions = ReadRuleTable(filename) if not neighborhood in Converters: golly.warn("Unsupported neighborhood: "+neighborhood) golly.show('') golly.exit() # all converters now create a .rule file golly.show("Building rule...") rule_name = Converters[neighborhood]( neighborhood, n_states, transitions, filename ) golly.new(rule_name+'-demo.rle') golly.setalgo('RuleLoader') golly.setrule(rule_name) golly.show('Created '+rule_name+'.rule and selected that rule.')
mode = lower(xym[2]) if mode=="c": mode="copy" if mode=="o": mode="or" if mode=="x": mode="xor" if not (mode == "copy" or mode == "or" or mode == "xor"): g.exit("Unknown mode: " + xym[2] + " (must be copy/or/xor)") else: mode = "or" # given parameters are valid so save them for next run try: f = open(INIFileName, 'w') f.write(answer) f.close() except: g.warn("Unable to save given parameters in file:\n" + INIFileName) # abort shift if the new selection would be outside a bounded grid if g.getwidth() > 0: gridl = -int(g.getwidth()/2) gridr = gridl + g.getwidth() - 1 newl = selrect[0] + x newr = newl + selrect[2] - 1 if newl < gridl or newr > gridr: g.exit("New selection would be outside grid.") if g.getheight() > 0: gridt = -int(g.getheight()/2) gridb = gridt + g.getheight() - 1 newt = selrect[1] + y newb = newt + selrect[3] - 1 if newt < gridt or newb > gridb:
def ReadRuleTable(filename): ''' Return n_states, neighborhood, transitions e.g. 2, "vonNeumann", [[0],[0,1],[0],[0],[1],[1]] Transitions are expanded for symmetries and bound variables. ''' f=open(filename,'r') vars={} symmetry_string = '' symmetry = [] n_states = 0 neighborhood = '' transitions = [] numParams = 0 for line in f: if line[0]=='#' or line.strip()=='': pass elif line[0:9]=='n_states:': n_states = int(line[9:]) if n_states<0 or n_states>256: golly.warn('n_states out of range: '+n_states) golly.exit() elif line[0:13]=='neighborhood:': neighborhood = line[13:].strip() if not neighborhood in SupportedSymmetries: golly.warn('Unknown neighborhood: '+neighborhood) golly.exit() numParams = len(SupportedSymmetries[neighborhood].items()[0][1][0]) elif line[0:11]=='symmetries:': symmetry_string = line[11:].strip() if not symmetry_string in SupportedSymmetries[neighborhood]: golly.warn('Unknown symmetry: '+symmetry_string) golly.exit() symmetry = SupportedSymmetries[neighborhood][symmetry_string] elif line[0:4]=='var ': line = line[4:] # strip var keyword if '#' in line: line = line[:line.find('#')] # strip any trailing comment # read each variable into a dictionary mapping string to list of ints entries = line.replace('=',' ').replace('{',' ').replace(',',' ').\ replace(':',' ').replace('}',' ').replace('\n','').split() vars[entries[0]] = [] for e in entries[1:]: if e in vars: vars[entries[0]] += vars[e] # vars allowed in later vars else: vars[entries[0]].append(int(e)) else: # assume line is a transition if '#' in line: line = line[:line.find('#')] # strip any trailing comment if ',' in line: entries = line.replace('\n','').replace(',',' ').replace(':',' ').split() else: entries = list(line.strip()) # special no-comma format if not len(entries)==numParams: golly.warn('Wrong number of entries on line: '+line+' (expected '+str(numParams)+')') golly.exit() # retrieve the variables that repeat within the transition, these are 'bound' bound_vars = [ e for e in set(entries) if entries.count(e)>1 and e in vars ] # iterate through all the possible values of each bound variable var_val_indices = dict(zip(bound_vars,[0]*len(bound_vars))) while True: ### AKT: this code causes syntax error in Python 2.3: ### transition = [ [vars[e][var_val_indices[e]]] if e in bound_vars \ ### else vars[e] if e in vars \ ### else [int(e)] \ ### for e in entries ] transition = [] for e in entries: if e in bound_vars: transition.append([vars[e][var_val_indices[e]]]) elif e in vars: transition.append(vars[e]) else: transition.append([int(e)]) if symmetry_string=='permute' and neighborhood in PermuteLater: # permute all but C,C' (first and last entries) for permuted_section in permu2(transition[1:-1]): permuted_transition = [transition[0]]+permuted_section+[transition[-1]] if not permuted_transition in transitions: transitions.append(permuted_transition) else: # expand for symmetry using the explicit list for s in symmetry: tran = [transition[i] for i in s] if not tran in transitions: transitions.append(tran) # increment the variable values (or break out if done) var_val_to_change = 0 while var_val_to_change<len(bound_vars): var_label = bound_vars[var_val_to_change] if var_val_indices[var_label]<len(vars[var_label])-1: var_val_indices[var_label] += 1 break else: var_val_indices[var_label] = 0 var_val_to_change += 1 if var_val_to_change >= len(bound_vars): break f.close() return n_states, neighborhood, transitions
mode = lower(xym[2]) if mode == "c": mode = "copy" if mode == "o": mode = "or" if mode == "x": mode = "xor" if not (mode == "copy" or mode == "or" or mode == "xor"): g.exit("Unknown mode: " + xym[2] + " (must be copy/or/xor)") else: mode = "or" # given parameters are valid so save them for next run try: f = open(INIFileName, 'w') f.write(answer) f.close() except: g.warn("Unable to save given parameters in file:\n" + INIFileName) # abort shift if the new selection would be outside a bounded grid if g.getwidth() > 0: gridl = -int(g.getwidth() / 2) gridr = gridl + g.getwidth() - 1 newl = selrect[0] + x newr = newl + selrect[2] - 1 if newl < gridl or newr > gridr: g.exit("New selection would be outside grid.") if g.getheight() > 0: gridt = -int(g.getheight() / 2) gridb = gridt + g.getheight() - 1 newt = selrect[1] + y newb = newt + selrect[3] - 1 if newt < gridt or newb > gridb:
def EmulateTriangular(neighborhood,n_states,transitions_list,input_filename): '''Emulate a triangularVonNeumann or triangularMoore neighborhood rule table with a rule tree.''' input_rulename = os.path.splitext(os.path.split(input_filename)[1])[0] # read rule_name+'.colors' file if it exists force_background = False background_color = [0,0,0] cfn = os.path.split(input_filename)[0] + "/" + input_rulename + ".colors" try: cf = open(cfn,'r') except IOError: # use Golly's default random colours random_colors=[[0,0,0],[0,255,127],[127,0,255],[148,148,148],[128,255,0],[255,0,128],[0,128,255],[1,159,0], [159,0,1],[255,254,96],[0,1,159],[96,255,254],[254,96,255],[126,125,21],[21,126,125],[125,21,126], [255,116,116],[116,255,116],[116,116,255],[228,227,0],[28,255,27],[255,27,28],[0,228,227], [227,0,228],[27,28,255],[59,59,59],[234,195,176],[175,196,255],[171,194,68],[194,68,171], [68,171,194],[72,184,71],[184,71,72],[71,72,184],[169,255,188],[252,179,63],[63,252,179], [179,63,252],[80,9,0],[0,80,9],[9,0,80],[255,175,250],[199,134,213],[115,100,95],[188,163,0], [0,188,163],[163,0,188],[203,73,0],[0,203,73],[73,0,203],[94,189,0],[189,0,94]] colors = dict(zip(range(len(random_colors)),random_colors)) else: # read from the .colors file colors = {0:[0,0,0]} # background is black for line in cf: if line[0:6]=='color ': entries = map(int,line[6:].replace('=',' ').replace('\n',' ').split()) if len(entries)<4: continue # too few entries, ignore if entries[0]==0: force_background = True background_color = [entries[1],entries[2],entries[3]] else: colors.update({entries[0]:[entries[1],entries[2],entries[3]]}) # (we don't support gradients in .colors) rule_name = input_rulename + '_emulated' # (we use a special suffix to avoid picking up any existing .colors or .icons) # make a rule tree and some icons if n_states <= 16: TriangularTransitionsToRuleTree_SplittingMethod(neighborhood,n_states,transitions_list,rule_name) MakeTriangularIcons_SplittingMethod(n_states,colors,force_background,rule_name) total_states = n_states * n_states elif neighborhood=='triangularVonNeumann' and n_states <= 128: TriangularTransitionsToRuleTree_CheckerboardMethod(neighborhood,n_states,transitions_list,rule_name) MakeTriangularIcons_CheckerboardMethod(n_states,colors,force_background,rule_name) total_states = n_states * 2 - 1 else: golly.warn('Only support triangularMoore with 16 states or fewer, and triangularVonNeumann\n'+\ 'with 128 states or fewer.') golly.exit() if n_states==2: # the icons we wrote are monochrome, so we need a .colors file to avoid # them all having different colors or similar from Golly's preferences c = open(golly.getdir('rules')+rule_name+".colors",'w') if force_background: c.write('color = 0 '+' '.join(map(str,background_color))+'\n') for i in range(1,total_states): c.write('color = '+str(i)+' '+' '.join(map(str,colors[1]))+'\n') c.flush() c.close() return rule_name
+ "where the percentage is an integer from 0 to 100\n" + "and the state values are integers from 1 to " + str(maxlive) + "\n" + "(maxstate is optional, default is minstate):", previousvals, "Randomly fill selection", ) # save given values for next time this script is called try: f = open(inifilename, "w") f.write(result) f.close() except: g.warn("Unable to save given values in file:\n" + inifilename) # extract and validate values pmm = result.split() if len(pmm) == 0: g.exit() if len(pmm) == 1: g.exit("You must supply min and max states.") if not validint(pmm[0]): g.exit("Bad percentage value: " + pmm[0]) if not validint(pmm[1]): g.exit("Bad minstate value: " + pmm[1]) perc = int(pmm[0]) minlive = int(pmm[1]) if perc < 0 or perc > 100: g.exit("Percentage must be from 0 to 100.")
def floodfill(): newstate = g.getoption("drawingstate") oldstate = newstate # wait for user to click a cell g.show("Click the region you wish to fill... (hit escape to abort)") while oldstate == newstate: event = g.getevent() if event.startswith("click"): # event is a string like "click 10 20 left none" evt, xstr, ystr, butt, mods = event.split() x = int(xstr) y = int(ystr) if x < minx or x > maxx or y < miny or y > maxy: # click is outside pattern's bounding box in unbounded universe g.warn("Click within the pattern's bounding box\n" + "otherwise the fill will be unbounded.") else: # note that user might have changed drawing state newstate = g.getoption("drawingstate") oldstate = g.getcell(x, y) if oldstate == newstate: g.warn("The clicked cell must have a different state\n" + "to the current drawing state.") else: g.doevent(event) # tell Golly to handle all further keyboard/mouse events g.getevent(False) # do flood fill starting with clicked cell g.show("Filling clicked region... (hit escape to stop)") clist = [(x, y)] g.setcell(x, y, newstate) oldsecs = time() while len(clist) > 0: # remove cell from start of clist x, y = clist.pop(0) newsecs = time() if newsecs - oldsecs >= 0.5: # show changed pattern every half second oldsecs = newsecs g.update() # check if any orthogonal neighboring cells are in oldstate if checkneighbor(x, y - 1, oldstate): g.setcell(x, y - 1, newstate) clist.append((x, y - 1)) if checkneighbor(x, y + 1, oldstate): g.setcell(x, y + 1, newstate) clist.append((x, y + 1)) if checkneighbor(x + 1, y, oldstate): g.setcell(x + 1, y, newstate) clist.append((x + 1, y)) if checkneighbor(x - 1, y, oldstate): g.setcell(x - 1, y, newstate) clist.append((x - 1, y)) # diagonal neighbors are more complicated because we don't # want to cross a diagonal line of live cells if checkneighbor(x + 1, y + 1, oldstate) and (g.getcell( x, y + 1) == 0 or g.getcell(x + 1, y) == 0): g.setcell(x + 1, y + 1, newstate) clist.append((x + 1, y + 1)) if checkneighbor(x + 1, y - 1, oldstate) and (g.getcell( x, y - 1) == 0 or g.getcell(x + 1, y) == 0): g.setcell(x + 1, y - 1, newstate) clist.append((x + 1, y - 1)) if checkneighbor(x - 1, y + 1, oldstate) and (g.getcell( x, y + 1) == 0 or g.getcell(x - 1, y) == 0): g.setcell(x - 1, y + 1, newstate) clist.append((x - 1, y + 1)) if checkneighbor(x - 1, y - 1, oldstate) and (g.getcell( x, y - 1) == 0 or g.getcell(x - 1, y) == 0): g.setcell(x - 1, y - 1, newstate) clist.append((x - 1, y - 1))
def moveobject(): global oldcells, object, object1 # wait for 1st click in live cell while True: event = g.getevent() if event.startswith("click"): # event is a string like "click 10 20 left none" evt, xstr, ystr, butt, mods = event.split() result = findlivecell(int(xstr), int(ystr)) if len(result) > 0: prevx = int(xstr) prevy = int(ystr) oldmouse = xstr + ' ' + ystr g.show("Extracting object...") x, y = result object = getobject(x, y) object1 = list(object) # save in case user aborts script if mods == "alt": # don't delete object oldcells = list(object) break else: g.warn("Click on or near a live cell belonging to the desired object.") elif event == "key h none": showhelp1() else: g.doevent(event) # wait for 2nd click while moving object g.show("Move mouse and click again..." + helpmsg) gotclick = False while not gotclick: event = g.getevent() if event.startswith("click"): evt, x, y, butt, mods = event.split() mousepos = x+' '+y gotclick = True else: if len(event) > 0: lookforkeys(event) mousepos = g.getxy() if len(mousepos) > 0 and mousepos != oldmouse: # mouse has moved, so move object g.putcells(object, 0, 0, 1, 0, 0, 1, "xor") # erase object if len(oldcells) > 0: g.putcells(oldcells) xstr, ystr = mousepos.split() x = int(xstr) y = int(ystr) if g.getwidth() > 0: # ensure object doesn't move beyond left/right edge of grid obox = getminbox( g.transform(object, x - prevx, y - prevy) ) if obox.left < gridl: x += gridl - obox.left elif obox.right > gridr: x -= obox.right - gridr if g.getheight() > 0: # ensure object doesn't move beyond top/bottom edge of grid obox = getminbox( g.transform(object, x - prevx, y - prevy) ) if obox.top < gridt: y += gridt - obox.top elif obox.bottom > gridb: y -= obox.bottom - gridb object = g.transform(object, x - prevx, y - prevy) oldcells = underneath(object) g.putcells(object) prevx = x prevy = y oldmouse = mousepos g.update()
# use name.tree to create name.rule (with no icons); # note that if name.rule already exists then we only replace the info in # the @TREE section to avoid clobbering any other info added by the user ConvertTreeToRule(name, n_states, []) golly.setalgo("RuleLoader") golly.setrule(name) golly.show("Created "+golly.getdir("rules")+name+".rule and switched to that rule.") except: import sys import traceback exception, msg, tb = sys.exc_info() golly.warn(\ '''To use this script, copy a Python format rule definition into the clipboard, e.g.: name = "ParityNWE" n_states = 5 n_neighbors = 4 # order for 4 neighbors is N, W, E, S, C def transition_function ( s ) : return ( s[0] + s[1] + s[2] ) % 5 For more examples, see the script file (right-click on it). ____________________________________________________ A problem was encountered with the supplied rule: '''+ '\n'.join(traceback.format_exception(exception, msg, tb))) golly.exit()
# (convert Mathematica code to Python and run eval) action_table = eval(spec.replace('}',']').replace('{','[')) n_states = len(action_table) n_colors = len(action_table[0]) # (N.B. The terminology 'state' here refers to the internal state of the finite # state machine that each Turmite is using, not the contents of each Golly # cell. We use the term 'color' to denote the symbol on the 2D 'tape'. The # actual 'Golly state' in this emulation of Turmites is given by the # "encoding" section below.) n_dirs = 6 # TODO: check table is full and valid total_states = n_colors + n_colors*n_states if total_states > 256: golly.warn("Number of states required exceeds Golly's limit of 256.") golly.exit() # encoding: # (0-n_colors: empty square) def encode(c,s): # turmite on color c in state s return n_colors + n_states*c + s # http://rightfootin.blogspot.com/2006/09/more-on-python-flatten.html def flatten(l, ltypes=(list, tuple)): ltype = type(l) l = list(l) i = 0 while i < len(l): while isinstance(l[i], ltypes):
n_states = len(action_table) n_colors = len(action_table[0]) # (N.B. The terminology 'state' here refers to the internal state of the finite # state machine that each Turmite is using, not the contents of each Golly # cell. We use the term 'color' to denote the symbol on the 2D 'tape'. The # actual 'Golly state' in this emulation of Turmites is given by the # "encoding" section below.) n_dirs = 4 # TODO: check table is full and valid total_states = n_colors + n_colors * n_states * n_dirs # problem if we try to export more than 255 states if total_states > 255: golly.warn("Number of states required exceeds Golly's limit of 255.") golly.exit() # encoding: # (0-n_colors: empty square) def encode(c, s, d): # turmite on color c in state s facing direction d return n_colors + n_dirs * (n_states * c + s) + d # http://rightfootin.blogspot.com/2006/09/more-on-python-flatten.html def flatten(l, ltypes=(list, tuple)): ltype = type(l) l = list(l) i = 0
def moveobject(): global oldcells, object, object1 # wait for click in or near a live cell while True: event = g.getevent() if event.startswith("click"): # event is a string like "click 10 20 left none" evt, xstr, ystr, butt, mods = event.split() result = findlivecell(int(xstr), int(ystr)) if len(result) > 0: prevx = int(xstr) prevy = int(ystr) oldmouse = xstr + ' ' + ystr g.show("Extracting object...") x, y = result object = getobject(x, y) object1 = list(object) # save in case user aborts script if mods == "alt": # don't delete object oldcells = list(object) break else: g.warn("Click on or near a live cell belonging to the desired object.") elif event == "key h none": showhelp() else: g.doevent(event) # wait for mouse-up while moving object g.show("Move mouse and release button...") mousedown = True while mousedown: event = g.getevent() if event.startswith("mup"): mousedown = False elif len(event) > 0: lookforkeys(event) mousepos = g.getxy() if len(mousepos) > 0 and mousepos != oldmouse: # mouse has moved, so move object g.putcells(object, 0, 0, 1, 0, 0, 1, "xor") # erase object if len(oldcells) > 0: g.putcells(oldcells) xstr, ystr = mousepos.split() x = int(xstr) y = int(ystr) if g.getwidth() > 0: # ensure object doesn't move beyond left/right edge of grid obox = getminbox( g.transform(object, x - prevx, y - prevy) ) if obox.left < gridl: x += gridl - obox.left elif obox.right > gridr: x -= obox.right - gridr if g.getheight() > 0: # ensure object doesn't move beyond top/bottom edge of grid obox = getminbox( g.transform(object, x - prevx, y - prevy) ) if obox.top < gridt: y += gridt - obox.top elif obox.bottom > gridb: y -= obox.bottom - gridb object = g.transform(object, x - prevx, y - prevy) oldcells = underneath(object) g.putcells(object) prevx = x prevy = y oldmouse = mousepos g.update()
def getstring(prompt): # this routine is deprecated golly.warn("Change the script to use the getstring() command\n"+ "from golly rather than from glife.") golly.exit("")
def EmulateTriangular(neighborhood,n_states,transitions_list,input_filename): '''Emulate a triangularVonNeumann or triangularMoore neighborhood rule table with a rule tree.''' input_rulename = os.path.splitext(os.path.split(input_filename)[1])[0] # read rule_name+'.colors' file if it exists force_background = False background_color = [0,0,0] cfn = os.path.split(input_filename)[0] + "/" + input_rulename + ".colors" try: cf = open(cfn,'r') except IOError: # use Golly's default random colours random_colors=[[0,0,0],[0,255,127],[127,0,255],[148,148,148],[128,255,0],[255,0,128],[0,128,255],[1,159,0], [159,0,1],[255,254,96],[0,1,159],[96,255,254],[254,96,255],[126,125,21],[21,126,125],[125,21,126], [255,116,116],[116,255,116],[116,116,255],[228,227,0],[28,255,27],[255,27,28],[0,228,227], [227,0,228],[27,28,255],[59,59,59],[234,195,176],[175,196,255],[171,194,68],[194,68,171], [68,171,194],[72,184,71],[184,71,72],[71,72,184],[169,255,188],[252,179,63],[63,252,179], [179,63,252],[80,9,0],[0,80,9],[9,0,80],[255,175,250],[199,134,213],[115,100,95],[188,163,0], [0,188,163],[163,0,188],[203,73,0],[0,203,73],[73,0,203],[94,189,0],[189,0,94]] colors = dict(list(zip(list(range(len(random_colors))),random_colors))) else: # read from the .colors file colors = {0:[0,0,0]} # background is black for line in cf: if line[0:6]=='color ': entries = list(map(int,line[6:].replace('=',' ').replace('\n',' ').split())) if len(entries)<4: continue # too few entries, ignore if entries[0]==0: force_background = True background_color = [entries[1],entries[2],entries[3]] else: colors.update({entries[0]:[entries[1],entries[2],entries[3]]}) # (we don't support gradients in .colors) rule_name = input_rulename + '_emulated' # (we use a special suffix to avoid picking up any existing .colors or .icons) # make a rule tree and some icons if n_states <= 16: TriangularTransitionsToRuleTree_SplittingMethod(neighborhood,n_states,transitions_list,rule_name) pixels = MakeTriangularIcons_SplittingMethod(n_states,colors,force_background,rule_name) total_states = n_states * n_states elif neighborhood=='triangularVonNeumann' and n_states <= 128: TriangularTransitionsToRuleTree_CheckerboardMethod(neighborhood,n_states,transitions_list,rule_name) pixels = MakeTriangularIcons_CheckerboardMethod(n_states,colors,force_background,rule_name) total_states = n_states * 2 - 1 else: golly.warn('Only support triangularMoore with 16 states or fewer, and triangularVonNeumann\n'+\ 'with 128 states or fewer.') golly.exit() if n_states==2: # the icons we wrote are monochrome, so we need a .colors file to avoid # them all having different colors or similar from Golly's preferences c = open(golly.getdir('rules')+rule_name+".colors",'w') if force_background: c.write('color = 0 '+' '.join(map(str,background_color))+'\n') for i in range(1,total_states): c.write('color = '+str(i)+' '+' '.join(map(str,colors[1]))+'\n') c.flush() c.close() # use rule_name.tree and rule_name.colors and icon info to create rule_name.rule ConvertTreeToRule(rule_name, total_states, pixels) return rule_name
cursorX += chars.find(data[i + 1]) + 4 i += 1 elif data[i] == 'z': cursorX = 0 cursorY += 5 else: col = chars.find(data[i]) if col & 1 != 0: cellList += [cursorX, cursorY] if col & 2 != 0: cellList += [cursorX, cursorY + 1] if col & 4 != 0: cellList += [cursorX, cursorY + 2] if col & 8 != 0: cellList += [cursorX, cursorY + 3] if col & 16 != 0: cellList += [cursorX, cursorY + 4] cursorX += 1 i += 1 return cellList filename = golly.opendialog('Choose an apgsearch log file') dirname = golly.opendialog('Choose a folder for the output', 'dir') with open(filename) as f: line = f.readline() while line: if not line.isspace() and line[0] != '@': code = line.split()[0] try: cellList = readApg(code) except Exception, args: golly.warn(args[0]) else: golly.store(cellList, dirname + code + '.rle') line = f.readline()
def create_average_colors(iconsection): global deadrgb colordata = "@COLORS\n\n" R, G, B = deadrgb colordata += "0 " + str(R) + " " + str(G) + " " + str(B) + "\n" width = 0 height = 0 num_colors = 0 chars_per_pixel = 0 xpmcount = 0 colordict = {} row = 0 state = 0 nbcount = 0 totalR = 0 totalG = 0 totalB = 0 for line in iconsection.splitlines(): if len(line) > 0 and line[0] == "\"": # extract the stuff inside double quotes line = line.lstrip("\"").rstrip("\"") xpmcount += 1 if xpmcount == 1: # parse "width height num_colors chars_per_pixel" header = line.split() width = int(header[0]) height = int(header[1]) num_colors = int(header[2]) chars_per_pixel = int(header[3]) elif xpmcount > 1 and xpmcount <= 1 + num_colors: # parse color index line like "A c #FFFFFF" or "AB c #FF009900BB00" key, c, hexrgb = line.split() rgb = parse_hex(hexrgb.lstrip("#")) colordict[key] = rgb elif xpmcount <= 1 + num_colors + height: # parse pixel data in line like "......AAA......" for col in xrange(width): offset = col*chars_per_pixel key = line[offset : offset + chars_per_pixel] if not key in colordict: g.warn("Unexpected key in icon data: " + key) return colordata if key[0] != ".": R, G, B = colordict[key] nbcount += 1 totalR += R totalG += G totalB += B row += 1 if row == width: # we've done this icon state += 1 if nbcount > 0: colordata += str(state) + " " + str(totalR / nbcount) + " " \ + str(totalG / nbcount) + " " \ + str(totalB / nbcount) + "\n" else: # avoid div by zero colordata += str(state) + " 0 0 0\n" # reset counts for next icon row = 0 nbcount = 0 totalR = 0 totalG = 0 totalB = 0 if xpmcount == 1 + num_colors + height: break colordata += "\n" return colordata
j = offset while ((backOff[(i + j) % period] >= 0) and (j < period)): j += 1 if j == period: backOff[i] = period - i break backOff[i] = j i = (i + j) % period rows = ["" for i in range(2 * period)] k = 0 mp = 0 x = firstrow.x y = firstrow.y for i in range(period): rows[k] = getrow(x, y - mp, width) rows[k + period] = getrow(x, y + 1 - mp, width) k += backOff[k] if k >= period: k -= period mp += 1 g.run(1) try: f = open('initrows.txt', 'w') for i in range(2 * period): f.write(rows[i] + "\n") f.close() except: g.warn("Unable to save file.")
f = open(savename, 'r') initdir = f.readline() f.close() except: # this should only happen the very 1st time initdir = g.getdir("data") # remove any existing extension from layer name and append .png initfile = g.getname().split('.')[0] + ".png" # prompt user for output file (image type depends on extension) outfile = g.savedialog("Save image file", "PNG (*.png)|*.png|BMP (*.bmp)|*.bmp|GIF (*.gif)|*.gif" + "|TIFF (*.tif)|*.tif|JPEG (*.jpg)|*.jpg", initdir, initfile) if len(outfile) > 0: im.save(outfile) g.show("Image saved as " + outfile) # remember file's directory for next time try: f = open(savename, 'w') f.write(os.path.dirname(outfile)) f.close() except: g.warn("Unable to save directory to file:\n" + savename) # on Mac OS X this opens the image file in Preview # but it causes an error on Windows # webbrowser.open("file://" + urllib.pathname2url(outfile))
def floodfill(): newstate = g.getoption("drawingstate") oldstate = newstate # wait for user to click a cell g.show("Click the region you wish to fill... (hit escape to abort)") while oldstate == newstate: event = g.getevent() if event.startswith("click"): # event is a string like "click 10 20 left none" evt, xstr, ystr, butt, mods = event.split() x = int(xstr) y = int(ystr) if x < minx or x > maxx or y < miny or y > maxy: # click is outside pattern's bounding box in unbounded universe g.warn("Click within the pattern's bounding box\n"+ "otherwise the fill will be unbounded.") else: # note that user might have changed drawing state newstate = g.getoption("drawingstate") oldstate = g.getcell(x, y) if oldstate == newstate: g.warn("The clicked cell must have a different state\n"+ "to the current drawing state.") else: g.doevent(event) # tell Golly to handle all further keyboard/mouse events g.getevent(False) # do flood fill starting with clicked cell g.show("Filling clicked region... (hit escape to stop)") clist = [ (x,y) ] g.setcell(x, y, newstate) oldsecs = time() while len(clist) > 0: # remove cell from start of clist x, y = clist.pop(0) newsecs = time() if newsecs - oldsecs >= 0.5: # show changed pattern every half second oldsecs = newsecs g.update() # check if any orthogonal neighboring cells are in oldstate if checkneighbor( x, y-1, oldstate): g.setcell( x, y-1, newstate) clist.append( (x, y-1) ) if checkneighbor( x, y+1, oldstate): g.setcell( x, y+1, newstate) clist.append( (x, y+1) ) if checkneighbor( x+1, y, oldstate): g.setcell( x+1, y, newstate) clist.append( (x+1, y) ) if checkneighbor( x-1, y, oldstate): g.setcell( x-1, y, newstate) clist.append( (x-1, y) ) # diagonal neighbors are more complicated because we don't # want to cross a diagonal line of live cells if checkneighbor( x+1, y+1, oldstate) and (g.getcell(x, y+1) == 0 or g.getcell(x+1, y) == 0): g.setcell( x+1, y+1, newstate) clist.append( (x+1, y+1) ) if checkneighbor( x+1, y-1, oldstate) and (g.getcell(x, y-1) == 0 or g.getcell(x+1, y) == 0): g.setcell( x+1, y-1, newstate) clist.append( (x+1, y-1) ) if checkneighbor( x-1, y+1, oldstate) and (g.getcell(x, y+1) == 0 or g.getcell(x-1, y) == 0): g.setcell( x-1, y+1, newstate) clist.append( (x-1, y+1) ) if checkneighbor( x-1, y-1, oldstate) and (g.getcell(x, y-1) == 0 or g.getcell(x-1, y) == 0): g.setcell( x-1, y-1, newstate) clist.append( (x-1, y-1) )
def ConvertTreeToRule(rule_name, total_states, icon_pixels): ''' Convert rule_name.tree to rule_name.rule and delete the .tree file. If rule_name.colors exists then use it to create an @COLORS section and delete the .colors file. If icon_pixels is supplied then add an @ICONS section. Format of icon_pixels (in this example there are 4 icons at each size): --------------------------------------------------------- | | | | | | | | | | | 31x31 | 31x31 | 31x31 | 31x31 | | | | | | | | | | | --------------------------------------------------------- | |.....| |.....| |.....| |.....| | 15x15 |.....| 15x15 |.....| 15x15 |.....| 15x15 |.....| | |.....| |.....| |.....| |.....| --------------------------------------------------------- |7x7|.........|7x7|.........|7x7|.........|7x7|.........| --------------------------------------------------------- The top layer of 31x31 icons is optional -- if not supplied (ie. the height is 22) then there are no gaps between the 15x15 icons. ''' rulepath = golly.getdir('rules') + rule_name + '.rule' treepath = golly.getdir('rules') + rule_name + '.tree' colorspath = golly.getdir('rules') + rule_name + '.colors' # get contents of .tree file try: treefile = open(treepath, 'r') treedata = treefile.read() treefile.close() except: golly.exit('Failed to open .tree file: ' + treepath) # if the .rule file already exists then only replace the @TREE section # so we don't clobber any other info added by the user if os.path.isfile(rulepath): ReplaceTreeSection(rulepath, treedata) os.remove(treepath) if os.path.isfile(colorspath): os.remove(colorspath) return # create a new .rule file rulefile = open(rulepath, 'w') rulefile.write('@RULE ' + rule_name + '\n\n') rulefile.write('@TREE\n\n') # append contents of .tree file, then delete that file rulefile.write(treedata) os.remove(treepath) # if .colors file exists then append @COLORS section and delete file if os.path.isfile(colorspath): colorsfile = open(colorspath, 'r') rulefile.write('\n@COLORS\n\n') for line in colorsfile: if line.startswith('color') or line.startswith('gradient'): # strip off everything before 1st digit line = line.lstrip('colorgadient= \t') rulefile.write(line) colorsfile.close() os.remove(colorspath) # if icon pixels are supplied then append @ICONS section if len(icon_pixels) > 0: wd = len(icon_pixels[0]) ht = len(icon_pixels) iconsize = 15 # size of icons in top row if ht > 22: iconsize = 31 # 31x31 icons are present numicons = wd / iconsize # get colors used in all icons (we assume each icon size uses the same set of colors) colors, multi_colored = GetColors(icon_pixels, wd, ht) if len(colors) > 256: golly.warn('Icons use more than 256 colors!') rulefile.flush() rulefile.close() return if multi_colored: # create @COLORS section using color info in icon_pixels (not grayscale) rulefile.write('\n@COLORS\n\n') if numicons >= total_states: # extra icon is present so use top right pixel to set the color of state 0 R, G, B = icon_pixels[0][wd - 1] rulefile.write('0 ' + str(R) + ' ' + str(G) + ' ' + str(B) + '\n') numicons -= 1 # set colors for each live state to the average of the non-black pixels # in each icon on top row (note we've skipped the extra icon detected above) for i in xrange(numicons): nbcount = 0 totalR = 0 totalG = 0 totalB = 0 for row in xrange(iconsize): for col in xrange(iconsize): R, G, B = icon_pixels[row][col + i * iconsize] if R > 0 or G > 0 or B > 0: nbcount += 1 totalR += R totalG += G totalB += B if nbcount > 0: rulefile.write(str(i+1) + ' ' + str(totalR / nbcount) + ' ' \ + str(totalG / nbcount) + ' ' \ + str(totalB / nbcount) + '\n') else: # avoid div by zero rulefile.write(str(i + 1) + ' 0 0 0\n') # create @ICONS section using (r,g,b) triples in icon_pixels[row][col] rulefile.write('\n@ICONS\n') if ht > 22: # top row of icons is 31x31 CreateXPMIcons(colors, icon_pixels, 31, 0, 31, numicons, rulefile) CreateXPMIcons(colors, icon_pixels, 15, 31, 31, numicons, rulefile) CreateXPMIcons(colors, icon_pixels, 7, 46, 31, numicons, rulefile) else: # top row of icons is 15x15 CreateXPMIcons(colors, icon_pixels, 15, 0, 15, numicons, rulefile) CreateXPMIcons(colors, icon_pixels, 7, 15, 15, numicons, rulefile) rulefile.flush() rulefile.close()
def create_average_colors(iconsection): global deadrgb colordata = "@COLORS\n\n" R, G, B = deadrgb colordata += "0 " + str(R) + " " + str(G) + " " + str(B) + "\n" width = 0 height = 0 num_colors = 0 chars_per_pixel = 0 xpmcount = 0 colordict = {} row = 0 state = 0 nbcount = 0 totalR = 0 totalG = 0 totalB = 0 for line in iconsection.splitlines(): if len(line) > 0 and line[0] == "\"": # extract the stuff inside double quotes line = line.lstrip("\"").rstrip("\"") xpmcount += 1 if xpmcount == 1: # parse "width height num_colors chars_per_pixel" header = line.split() width = int(header[0]) height = int(header[1]) num_colors = int(header[2]) chars_per_pixel = int(header[3]) elif xpmcount > 1 and xpmcount <= 1 + num_colors: # parse color index line like "A c #FFFFFF" or "AB c #FF009900BB00" key, c, hexrgb = line.split() rgb = parse_hex(hexrgb.lstrip("#")) colordict[key] = rgb elif xpmcount <= 1 + num_colors + height: # parse pixel data in line like "......AAA......" for col in range(width): offset = col * chars_per_pixel key = line[offset:offset + chars_per_pixel] if not key in colordict: g.warn("Unexpected key in icon data: " + key) return colordata if key[0] != ".": R, G, B = colordict[key] nbcount += 1 totalR += R totalG += G totalB += B row += 1 if row == width: # we've done this icon state += 1 if nbcount > 0: colordata += str(state) + " " + str(totalR // nbcount) + " " \ + str(totalG // nbcount) + " " \ + str(totalB // nbcount) + "\n" else: # avoid div by zero colordata += str(state) + " 0 0 0\n" # reset counts for next icon row = 0 nbcount = 0 totalR = 0 totalG = 0 totalB = 0 if xpmcount == 1 + num_colors + height: break colordata += "\n" return colordata
f = open(savename, 'r') initdir = f.readline() f.close() except: # this should only happen the very 1st time initdir = g.getdir("data") # remove any existing extension from layer name and append .png initfile = g.getname().split('.')[0] + ".png" # prompt user for output file (image type depends on extension) outfile = g.savedialog( "Save image file", "PNG (*.png)|*.png|BMP (*.bmp)|*.bmp|GIF (*.gif)|*.gif" + "|TIFF (*.tif)|*.tif|JPEG (*.jpg)|*.jpg", initdir, initfile) if len(outfile) > 0: im.save(outfile) g.show("Image saved as " + outfile) # remember file's directory for next time try: f = open(savename, 'w') f.write(os.path.dirname(outfile)) f.close() except: g.warn("Unable to save directory to file:\n" + savename) # on Mac OS X this opens the image file in Preview # but it causes an error on Windows # webbrowser.open("file://" + urllib.pathname2url(outfile))
# note that if name.rule already exists then we only replace the info in # the @TREE section to avoid clobbering any other info added by the user ConvertTreeToRule(name, n_states, []) golly.setalgo("RuleLoader") golly.setrule(name) golly.show("Created " + golly.getdir("rules") + name + ".rule and switched to that rule.") except: import sys import traceback exception, msg, tb = sys.exc_info() golly.warn(\ '''To use this script, copy a Python format rule definition into the clipboard, e.g.: name = "ParityNWE" n_states = 5 n_neighbors = 4 # order for 4 neighbors is N, W, E, S, C def transition_function ( s ) : return ( s[0] + s[1] + s[2] ) % 5 For more examples, see the script file (right-click on it). ____________________________________________________ A problem was encountered with the supplied rule: '''+ '\n'.join(traceback.format_exception(exception, msg, tb))) golly.exit()
def ReadRuleTable(filename): ''' Return n_states, neighborhood, transitions e.g. 2, "vonNeumann", [[0],[0,1],[0],[0],[1],[1]] Transitions are expanded for symmetries and bound variables. ''' f = open(filename, 'r') vars = {} symmetry_string = '' symmetry = [] n_states = 0 neighborhood = '' transitions = [] numParams = 0 for line in f: if line[0] == '#' or line.strip() == '': pass elif line[0:9] == 'n_states:': n_states = int(line[9:]) if n_states < 0 or n_states > 256: golly.warn('n_states out of range: ' + n_states) golly.exit() elif line[0:13] == 'neighborhood:': neighborhood = line[13:].strip() if not neighborhood in SupportedSymmetries: golly.warn('Unknown neighborhood: ' + neighborhood) golly.exit() numParams = len(SupportedSymmetries[neighborhood].items()[0][1][0]) elif line[0:11] == 'symmetries:': symmetry_string = line[11:].strip() if not symmetry_string in SupportedSymmetries[neighborhood]: golly.warn('Unknown symmetry: ' + symmetry_string) golly.exit() symmetry = SupportedSymmetries[neighborhood][symmetry_string] elif line[0:4] == 'var ': line = line[4:] # strip var keyword if '#' in line: line = line[:line.find('#')] # strip any trailing comment # read each variable into a dictionary mapping string to list of ints entries = line.replace('=',' ').replace('{',' ').replace(',',' ').\ replace(':',' ').replace('}',' ').replace('\n','').split() vars[entries[0]] = [] for e in entries[1:]: if e in vars: vars[entries[0]] += vars[e] # vars allowed in later vars else: vars[entries[0]].append(int(e)) else: # assume line is a transition if '#' in line: line = line[:line.find('#')] # strip any trailing comment if ',' in line: entries = line.replace('\n', '').replace(',', ' ').replace(':', ' ').split() else: entries = list(line.strip()) # special no-comma format if not len(entries) == numParams: golly.warn('Wrong number of entries on line: ' + line + ' (expected ' + str(numParams) + ')') golly.exit() # retrieve the variables that repeat within the transition, these are 'bound' bound_vars = [ e for e in set(entries) if entries.count(e) > 1 and e in vars ] # iterate through all the possible values of each bound variable var_val_indices = dict(zip(bound_vars, [0] * len(bound_vars))) while True: ### AKT: this code causes syntax error in Python 2.3: ### transition = [ [vars[e][var_val_indices[e]]] if e in bound_vars \ ### else vars[e] if e in vars \ ### else [int(e)] \ ### for e in entries ] transition = [] for e in entries: if e in bound_vars: transition.append([vars[e][var_val_indices[e]]]) elif e in vars: transition.append(vars[e]) else: transition.append([int(e)]) if symmetry_string == 'permute' and neighborhood in PermuteLater: # permute all but C,C' (first and last entries) for permuted_section in permu2(transition[1:-1]): permuted_transition = [ transition[0] ] + permuted_section + [transition[-1]] if not permuted_transition in transitions: transitions.append(permuted_transition) else: # expand for symmetry using the explicit list for s in symmetry: tran = [transition[i] for i in s] if not tran in transitions: transitions.append(tran) # increment the variable values (or break out if done) var_val_to_change = 0 while var_val_to_change < len(bound_vars): var_label = bound_vars[var_val_to_change] if var_val_indices[var_label] < len(vars[var_label]) - 1: var_val_indices[var_label] += 1 break else: var_val_indices[var_label] = 0 var_val_to_change += 1 if var_val_to_change >= len(bound_vars): break f.close() return n_states, neighborhood, transitions
rule_name = prefix + '_' + spec action_table = eval(turmite_spec.replace('}', ']').replace('{', '[')) n_states = len(action_table) n_dirs = 4 # (N.B. The terminology 'state' here refers to the internal state of the finite # state machine that each Turmite is using, not the contents of each Golly # cell. We use the term 'color' to denote the symbol on the 2D 'tape'. The # actual 'Golly state' in this emulation of Turmites is given by the # "encoding" section below.) total_states = n_colors + n_colors * n_states * n_dirs # problem if we try to export more than 255 states if total_states > 255: golly.warn( "Number of states required exceeds Golly's limit of 255\n\nMaximum 51 turns allowed." ) golly.exit() # what direction would a turmite have been facing to end up here from direction # d if it turned t: would_have_been_facing[t][d] would_have_been_facing = { 1: [2, 3, 0, 1], # no turn 2: [1, 2, 3, 0], # right 4: [0, 1, 2, 3], # u-turn 8: [3, 0, 1, 2], # left } remap = [2, 1, 3, 0] # N,E,S,W -> S,E,W,N not_arriving_from_here = [range(n_colors) for i in range(n_dirs)