Golly scripts

For scripts to aid with computation or simulation in cellular automata.
User avatar
SuperSupermario24
Posts: 120
Joined: July 22nd, 2014, 12:59 pm
Location: Within the infinite expanses of the Life universe

Re: Golly scripts

Post by SuperSupermario24 » March 21st, 2018, 12:35 pm

Andrew wrote:Actually, it should be. Like g.new and g.open, g.save also tells Golly to stop remembering changes. This is the case for both Lua and Python. I'd completely forgotten doing that, so thanks for prodding me to check. I'll update the docs for the next release.
That does make me wonder, though; why is it necessary that Golly remember every change that the script makes as it happens? Would it not be feasible to just save the current state of the universe/layers/etc. right before the script runs, so that you could just revert back to that after the script exits if so desired?

Code: Select all

bobo2b3o2b2o2bo3bobo$obobobo3bo2bobo3bobo$obobob2o2bo2bobo3bobo$o3bobo3bo2bobobobo$o3bob3o2b2o3bobo2bo!

User avatar
Andrew
Moderator
Posts: 764
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia
Contact:

Re: Golly scripts

Post by Andrew » March 21st, 2018, 6:31 pm

SuperSupermario24 wrote:Would it not be feasible to just save the current state of the universe/layers/etc. right before the script runs, so that you could just revert back to that after the script exits if so desired?
If only Life was that simple. Unfortunately (for me) we're dealing with an app that can create/edit patterns with billions/trillions of cells. If the current state has a monstrously large pattern and you run a script that simply changes a few cells then there would be a very long delay while the state is saved before your script even runs.

shouldsee
Posts: 406
Joined: April 8th, 2016, 8:29 am

Re: Golly scripts

Post by shouldsee » March 25th, 2018, 11:05 pm

The fixed "mutate.py" takes the current rulestring and flips a random hensel configuration. It depends on the messy collection "KBs.py" and please place them in the same folder.

mutate.py

Code: Select all

## Written by Feng (shouldsee.gem@gmail.com) March 2018.
import golly
import KBs

import random


# rulestr=golly.getstring('NTCA number',golly.getclipstr()).split('_')[-1];
kb = KBs.kb_2dntca()
# alias = golly.getrule()
curr=golly.getrule().split(':')
if len(curr)==1:
	curr+=['']
alias,suffix = curr

rulestr = kb.alias2rulestr(alias)
bitstr = KBs.hex2bin(rulestr,102)
# assert KBs.bin2hex(bitstr)==rulestr
bitlst = list(bitstr)

idx =  random.randint(0,102)
flip = {'0':'1','1':'0'}
bitlst[idx] = flip[bitlst[idx]]
rulestr = KBs.bin2hex(''.join(bitlst))
alias = kb.rulestr2alias(rulestr)


golly.note(alias)
golly.setclipstr(alias)

golly.setrule('%s:%s'%(alias,suffix))
KBs.py

Code: Select all

import numpy as np
import pickle
import itertools, collections, re
import copy
import random
import collections 
count = collections.Counter
identity = lambda x:x
# import collections
base2bin=lambda data,scale,num_of_bits: bin(int(data, scale))[2:].zfill(num_of_bits);
hex2bin=lambda hexdata,num_of_bits: base2bin(hexdata,16,num_of_bits);
bin2hex = lambda bitstr:hex(int(bitstr,2)).lstrip('0x').rstrip('L')

# from astropy.convolution import convolve
# convolve_int=lambda a,fir,method:np.around(convolve(a,fir,method)).astype(np.int);
import scipy.ndimage
convolve_int=lambda a,fir,method,**kwargs:scipy.ndimage.filters.convolve(a,fir,mode = method,**kwargs)

hensellist=['b0_','b1c','b1e','b2a','b2c','b3i','b2e','b3a','b2k','b3n','b3j','b4a','s0_','s1c','s1e','s2a','s2c','s3i','s2e','s3a','s2k','s3n','s3j','s4a','b2i','b3r','b3e','b4r','b4i','b5i','s2i','s3r','s3e','s4r','s4i','s5i','b2n','b3c','b3q','b4n','b4w','b5a','s2n','s3c','s3q','s4n','s4w','s5a','b3y','b3k','b4k','b4y','b4q','b5j','b4t','b4j','b5n','b4z','b5r','b5q','b6a','s3y','s3k','s4k','s4y','s4q','s5j','s4t','s4j','s5n','s4z','s5r','s5q','s6a','b4e','b5c','b5y','b6c','s4e','s5c','s5y','s6c','b5k','b6k','b6n','b7c','s5k','s6k','s6n','s7c','b4c','b5e','b6e','s4c','s5e','s6e','b6i','b7e','s6i','s7e','b8_','s8_',];
rca2ntca=[0, 1, 2, 3, 1, 4, 3, 5, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 13, 16, 15, 17, 14, 15, 18, 19, 20, 21, 22, 23, 2, 8, 6, 10, 3, 9, 7, 11, 24, 25, 26, 27, 25, 28, 27, 29, 14, 20, 18, 22, 15, 21, 19, 23, 30, 31, 32, 33, 31, 34, 33, 35, 1, 4, 8, 9, 36, 37, 38, 39, 3, 5, 10, 11, 38, 39, 40, 41, 13, 16, 20, 21, 42, 43, 44, 45, 15, 17, 22, 23, 44, 45, 46, 47, 8, 48, 49, 50, 38, 51, 52, 53, 25, 54, 55, 56, 57, 58, 59, 60, 20, 61, 62, 63, 44, 64, 65, 66, 31, 67, 68, 69, 70, 71, 72, 73, 2, 8, 24, 25, 8, 48, 25, 54, 6, 10, 26, 27, 49, 50, 55, 56, 14, 20, 30, 31, 20, 61, 31, 67, 18, 22, 32, 33, 62, 63, 68, 69, 6, 49, 26, 55, 10, 50, 27, 56, 26, 55, 74, 75, 55, 76, 75, 77, 18, 62, 32, 68, 22, 63, 33, 69, 32, 68, 78, 79, 68, 80, 79, 81, 3, 9, 25, 28, 38, 51, 57, 58, 7, 11, 27, 29, 52, 53, 59, 60, 15, 21, 31, 34, 44, 64, 70, 71, 19, 23, 33, 35, 65, 66, 72, 73, 10, 50, 55, 76, 40, 82, 59, 83, 27, 56, 75, 77, 59, 83, 84, 85, 22, 63, 68, 80, 46, 86, 72, 87, 33, 69, 79, 81, 72, 87, 88, 89, 1, 36, 8, 38, 4, 37, 9, 39, 8, 38, 49, 52, 48, 51, 50, 53, 13, 42, 20, 44, 16, 43, 21, 45, 20, 44, 62, 65, 61, 64, 63, 66, 3, 38, 10, 40, 5, 39, 11, 41, 25, 57, 55, 59, 54, 58, 56, 60, 15, 44, 22, 46, 17, 45, 23, 47, 31, 70, 68, 72, 67, 71, 69, 73, 4, 37, 48, 51, 37, 90, 51, 91, 9, 39, 50, 53, 51, 91, 82, 92, 16, 43, 61, 64, 43, 93, 64, 94, 21, 45, 63, 66, 64, 94, 86, 95, 9, 51, 50, 82, 39, 91, 53, 92, 28, 58, 76, 83, 58, 96, 83, 97, 21, 64, 63, 86, 45, 94, 66, 95, 34, 71, 80, 87, 71, 98, 87, 99, 3, 38, 25, 57, 9, 51, 28, 58, 10, 40, 55, 59, 50, 82, 76, 83, 15, 44, 31, 70, 21, 64, 34, 71, 22, 46, 68, 72, 63, 86, 80, 87, 7, 52, 27, 59, 11, 53, 29, 60, 27, 59, 75, 84, 56, 83, 77, 85, 19, 65, 33, 72, 23, 66, 35, 73, 33, 72, 79, 88, 69, 87, 81, 89, 5, 39, 54, 58, 39, 91, 58, 96, 11, 41, 56, 60, 53, 92, 83, 97, 17, 45, 67, 71, 45, 94, 71, 98, 23, 47, 69, 73, 66, 95, 87, 99, 11, 53, 56, 83, 41, 92, 60, 97, 29, 60, 77, 85, 60, 97, 85, 100, 23, 66, 69, 87, 47, 95, 73, 99, 35, 73, 81, 89, 73, 99, 89, 101];
henseldict={'0': ['_'],
 '1': ['c', 'e'],
 '2': ['a', 'c', 'e', 'k', 'i', 'n'],
 '3': ['i', 'a', 'n', 'j', 'r', 'e', 'c', 'q', 'y', 'k'],
 '4': ['a', 'r', 'i', 'n', 'w', 'k', 'y', 'q', 't', 'j', 'z', 'e', 'c'],
 '5': ['i', 'a', 'j', 'n', 'r', 'q', 'c', 'y', 'k', 'e'],
 '6': ['a', 'c', 'k', 'n', 'e', 'i'],
 '7': ['c', 'e'],
 '8': ['_']}


rca2ntca=np.array(rca2ntca,np.int);
henselidx={k: v for v, k in enumerate(hensellist)};
subconf='_cekainyqjrtwz';
p_NOTnumletter = re.compile(r'[^\da-zA-Z\-]')    

try:
    from data import *
    with open('tp','rb') as f:  # Python 3: open(..., 'rb')
       hdist, tst_data = pickle.load(f)
       hdist = np.array(hdist).reshape([512,512]);
except:
    print('[WARN]Not finding data.py')




def invert(s):
    num = s[0]
    conf = henseldict[num]
    return ''.join([num]+[x for x in conf if x not in s])

def ntuple(lst,n):
    """ntuple([0,3,4,10,2,3], 2) => [(0,3), (4,10), (2,3)]
    
    Group a list into consecutive n-tuples. Incomplete tuples are
    discarded e.g.
    
    >>> group(range(10), 3)
    [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
    """    
    return zip(*[lst[i::n] for i in range(n)])

def add_all(s,prime,sold,neg=0):
    for c in subconf:
        conf=prime+sold+c;
        try:
            s[henselidx[conf]]=str(1-neg);
        except KeyError:
            pass
class kb_2dntca():
    def __init__(self):
        self.familyname='2dntca'
        pass
    def rulestr2alias(self, rulestr):
        OUT = ''
        # rulestr =  '000000000060031c61c67f86a0'
        r=hex2bin(rulestr,102);
        r=r[::-1];
        rule=[i for i,x in enumerate(r) if x=='1'];
#         print r
        lst = [hensellist[i] for i in rule]
        lst.sort()
        
        #### group by B/S
        d = collections.OrderedDict((('b',{}),('s',{}))) ### set default
#         d = {'b':{},'s':{}}   ### set default
        d.update(
            {k:list(gp) for k,gp in itertools.groupby(lst, lambda x:x[0])}        
        )
        for k,lst in d.items():
            d[k] = {k:list(gp) for k,gp in itertools.groupby(lst, lambda x:x[1])}
            
        for bs, dd in d.items():
            OUT += bs
            for k,lst in dd.items():
                OUT += k + ''.join( conf[-1] for conf in lst)
        OUT = OUT.replace('_','')
        alias = OUT
        return alias


    def alias2rulestr(self,alias): 
    # alias.replace('-','')
        alias = re.sub('(\d-[a-zA-Z]+)',lambda o:invert(o.group()),alias)
        alias = p_NOTnumletter.sub( '', alias).lower()
        OUT = ['0']*102
        d = collections.OrderedDict((('b',{}),('s',{}))) ### set default
        # d.update()
        # alias.split('s')
        s = alias
        lst = [x for x  in re.split("([bs])", s) if x]
        if len(lst) % 2: #### Padding to even length
            lst += ['']
        d  = dict(ntuple(lst,2))
        idxs = []
        for k, v in d.items():
            s = v
            lst = [x for x in re.split("(\d)", s) if x]
            L  = len(lst)
            v_old = ''
            for i,v in enumerate(lst):
                if v.isdigit():
                    if v_old.isdigit():
                        idx = [henselidx.get( k + v_old + c,None) for c in subconf]
                        idxs.extend(idx)
                    if i + 1 == L:
                        idx = [henselidx.get( k + v + c,None) for c in subconf]
                        idxs.extend(idx)
                    num = v
                else:
                    idx = [henselidx[ k + num + v_i]  for v_i in v ]
                    idxs.extend(idx)
                v_old = v
        idxs = [ x for x in idxs if x is not None] 
        for i in idxs:
            if not i is None:
                OUT[i] = '1'
        bitstr=''.join(OUT[::-1]);
        hexstr=hex(int(bitstr,2)).lstrip('0x').rstrip('L').zfill(26)
        return hexstr
#     def alias2rulestr(self, alias):
#         alias = p_NOTnumletter.sub( '', alias).lower()
#         OUT = ['0']*102
#         # print alias
#         d = collections.OrderedDict((('b',{}),('s',{}))) ### set default
#         # d.update()
#         # alias.split('s')
#         s = alias
#         lst = [x for x  in re.split("([bs])", s) if x]
#         if len(lst) % 2: #### Padding to even length
#             lst += ['']
#         d  = dict(ntuple(lst,2))
#         idxs = []
#         for k, v in d.items():
#             s = v
#             lst = [x for x in re.split("(\d)", s) if x]
#             L  = len(lst)
#             v_old = ''
#             for i,v in enumerate(lst):
#                 if v.isdigit():
#                     if v_old.isdigit():
#                         idx = [henselidx.get( k + v_old + c,None) for c in subconf]
#                         idxs.extend(idx)
#                     if i + 1 == L:
#                         idx = [henselidx.get( k + v + c,None) for c in subconf]
#                         idxs.extend(idx)
#                     num = v
#                 else:
#                     idx = [henselidx[ k + num + v_i]  for v_i in v ]
#                     idxs.extend(idx)
#                 v_old = v
#         idxs = [ x for x in idxs if x is not None] 
#         for i in idxs:
#             if not i is None:
#                 OUT[i] = '1'
#         bitstr=''.join(OUT[::-1]);
#         hexstr=hex(int(bitstr,2)).lstrip('0x').rstrip('L').zfill(26)
#         return hexstr
    def rulestr2adv(self,rulestr):
        ruleprj=np.array( 
            list(hex2bin(rulestr,102)[::-1]),
            np.int);
        adv = self.bin2adv(ruleprj)
#         ruleprj=np.array(list(hex2bin(rulestr,102)[::-1]));
#         fir=(2**np.arange(0,9)).reshape([1,3,3]);
#         pj=rca2ntca;
#         def adv(a,horizon):
#             return ruleprj[pj[convolve_int(a,fir,'wrap').astype(np.int)]]
#         # adv=lambda a, horizon: ruleprj[pj[convolve_int(a,fir,'wrap').astype(np.int)]]
        return adv 
    def conv(self,a):
        fir=(2**np.arange(0,9)).reshape([1,3,3]);
        pj=rca2ntca;
        return pj[convolve_int(a,fir,'wrap').astype(np.int)]
    def bin2adv(self, ruleprj):
        if isinstance(ruleprj,str):
            ruleprj = list(ruleprj)
        ruleprj = np.array(ruleprj,np.int)
        def adv(a,horizon=0):
            return ruleprj[self.conv(a)]
        return adv
    def rstr(self,callback=(lambda x:bin2hex(x).zfill(26)) ):
        r = '{:0102b}'.format(random.randrange(2**102))
        if callback is not None:
            r = callback(r)
        return r
    def randadv(self):
        return self.bin2adv(self.rstr(None))
    def bulk_rstr(self,seed = 0,bsize=2**18,**kwargs):
        random.seed(seed)
        lst = [{'family':self.familyname,
                'rulestr':self.rstr(**kwargs)} for x in range(bsize)]
        return lst    
    
    
class kb_2dtca():
#     def rulestr2alias(rulestr):
#         r=base2bin(int(rulestr),18,2);
#         r=r[:1:-1];
#         r+='0'*(18-len(r));
#         rule=[i for x,i in zip(r,range(len(r))) if x=='1'];
#         alias='b';
#         ps=1;
#         for a in rule:
#             if a>8 and ps:
#                 alias+='s';
#                 ps=0;
#             alias+=str((a)%9)
#         if ps==1:
#             alias+='s';
#         return alias        
    def alias2rulestr(self, ali):
        rule=['0']*18;
        ali=ali.replace('/','').lower().lstrip('b');
        (b,s)=ali.split('s');
        lst=list(str(int(i)+9) for i in s);
        bs=list(b)+(lst)
        for i in bs:
            rule[int(i)]='1';
        rnum=(''.join(rule[::-1]),2);
        return(rnum);
    def rulestr2adv(self,rulestr):
        #take an numpy array and convolution across the axis=[1,2];
        # project the convolved array back to value space according to the rule
        # 
        hex2bin(rulestr)
                                       
class CA_sys():
    def __init__(self,familyname = None,rulestr=None,dimsiz=None,adv=None,rdf=None):
#         siz=[600,100,400];
        if dimsiz is None:
            dimsiz = [128,128,32**2]
        if rulestr is None:
            rulestr = '0c83820e0060061941946a68f0'
        if familyname is None:
            familyname = '2dntca'
        self.familyname=familyname;
        self.rulestr=rulestr;
        self.adv=adv;
        self.dimsiz=dimsiz;
        self.change_size();
#         self.family = globals().get('familyname')
        self.family=eval('kb_%s()'%self.familyname);
        self.rulestr2alias()
#         self.
        if rdf==None:
            self.rdf=lambda:(np.random.random(self.siz)<=0.5).astype(np.int);

    def change_size(self,dimsiz=None):
        if dimsiz==None:
            dimsiz=self.dimsiz;
        N,hmax,ksq=dimsiz
        self.N=N;
        self.hmax=hmax;
        dd=int(ksq**0.5);
        self.siz = (N,dd,dd);
        
    def rulestr2alias(self):
#         kb=eval('kb_%s()'%self.familyname);
        # kb=eval('kb_%s'%self.familyname);
        self.alias=self.family.rulestr2alias(self.rulestr);
        self.adv=self.family.rulestr2adv(self.rulestr);
    def alias2rulestr(self):
#         kb=eval('kb_%s()'%self.familyname);
        # kb=eval('kb_%s'%self.familyname);
        self.rulestr=self.family.alias2rulestr(self.alias)
        self.adv    =self.family.rulestr2adv(self.rulestr)            
    def as_config(self):
        conf = {'family':self.family.familyname,
               'rulestr':self.rulestr,
                'alias':self.alias,}
        return conf
#     def change_adv(familyname,rulestr):

kb=kb_2dntca();
# kb.rulestr2alias('000000000060031c61c67f86a0')
kb.alias2rulestr('b3/s23')

    
# @function

# @function
def measure_temperature(sys0=None,hdist=None,*args,**kwargs):
#     varargin = measure_temperature.varargin
#     nargin = measure_temperature.nargin
    sysX=copy.copy(sys0)
    jmax=sysX.N;
    avi=sysX.rdf().astype(np.int)
    siz=avi.shape
    siz=(sysX.hmax,)+siz;
    tmp=np.zeros(siz)
    smtmp=np.zeros(siz)

    avc=avi
    i=0
    fir=np.reshape(2 ** (np.arange(0,9)),[1,3,3])
    trans=6
    mtp=0
    stp=0
    while i+1 < sysX.hmax:

        i=i + 1
        avcnew=(sysX.adv(avc,i))
        cavc=convolve_int(avc,fir,'wrap').astype(np.int);
        cavcnew=convolve_int(avcnew,fir,'wrap').astype(np.int);
        idx=np.ravel_multi_index((cavc,cavcnew),[2**9,2**9]);
        tmp[i,:,:,:]=np.expand_dims(hdist.flat[idx],0)
        if i >= trans:
            smtmpnow=np.mean(tmp[i - trans:i,:,:,:],axis=0)
            smtmp[i - trans,:,:,:]=smtmpnow
            if i >= trans + 10:
                mtp=np.mean(smtmpnow.flat)
                stpmat=((smtmp[i - trans,:,:,:] - smtmp[i - trans - trans,:,:,:]))
                a=np.mean(np.abs(stpmat.flat))
                b=abs(np.mean(stpmat.flat))
                stp=a - b
                stp1=np.mean(avcnew.flat)
                stp1=min(stp1,1 - stp1)
        avc=avcnew;
        #     im1=[avc(1,:,:)];
        if mtp < 0.02 and i > 20:
            break
    
    fam_alias=sys0.familyname+'_'+sys0.alias;
# /home/shouldsee/Documents/repos/CA_tfmat/custom_function/measure_temperature.m:55
    # s=sprintf('%s\\t%s\\t%d\\t%f\\t%f\\t%f\\n',fam_alias,num2str(sys0.od),i,mtp,stp,stp1)
    s='{}\t{}\t{:d}\t{:f}\t{:f}\t{:f}\n'.format(fam_alias,sysX.rulestr,i,mtp,stp,stp1)
# /home/shouldsee/Documents/repos/CA_tfmat/custom_function/measure_temperature.m:56
    return s
    
# if __name__ == '__main__':
#     pass
    


### Profiling loop
### Profiling loop
def profile(input_list, log = []):
    # global log
    output_data=[];    
    repeat=2;
    # input_list=[input_rulestr];
    ipt_list=input_list*repeat;
    # for i in range(5):
    l_ipt=len(input_list)
    log += ['Log of the process:'];
    logs='Starting to profile {:d} rules at {:d} replicates,\n totaling {:d} instances'.format(l_ipt,repeat,l_ipt*repeat);
    log += [logs];
    # print('Starting to profile {:d} rules at {:d} replicates,\n totaling {:d} instances'.format(l_ipt,repeat,l_ipt*repeat))

    for num,rulestr in enumerate(ipt_list):
        ca1=CA_sys(familyname,rulestr,[400,100,400]);
        ca1.rulestr2alias();
        s=measure_temperature(ca1,hdist);
        output_data+=[s];
    #     print('{:d} of {:d}'.format(num,len(ipt_list)))
        logs =('{:d} of {:d} '.format(num,len(ipt_list)));
        log += [logs];
    temp_data=[];
    # sample_data=[]
    for line in output_data:
        temp_data+=[line.rstrip('\n').split('\t')];
    sample_data=np.array(temp_data)
    # print('data is succesfully generated at {:d} replicates'.format(repeat))
    logs=('data is succesfully generated at {:d} replicates'.format(repeat))

    log  += [logs];

    # print('\n Detail of the input:')
    logs='\n Detail of the input:';
    log+=[logs];
    for k,v in ca1.__dict__.items():
        if not callable(v):
    #         print(k+str(v).ljust(-10))
    #         print("{:5} {:<15} {:<10}".format('',k, str(v)))

            logs=("{:5} {:<15} {:<10}".format('',k, str(v)));
            log+=[logs];
    return( [sample_data,log]);
    
if __name__ == '__main__':
    INPUT = "B345678/S012678"
    exp = "3f9fbe3e001fff07e07e15fea0"
    act = kb.alias2rulestr(INPUT)
    assert act == exp,'expected:%s, actual:%s' %(exp,act)
#     pass

def guess(i=None,sysX=None,dct=None):
    dimsiz = (256,2**7,24**2)
    if i is not None:
        rstr = tst_data[i][1]
        familyname='2dntca'
    if dct is not None:
        familyname = dct['family']
        rstr = dct['rulestr']
    if sysX is None:
        sysX = CA_sys(familyname,rstr,dimsiz)
        # sysX = KBs.CA_sys('2dntca',tst_data[i][1],(200,2**7,400))
        sysX.rulestr2alias()
    else:
        sysX.dimsiz = dimsiz
        sysX.change_size()# spspa.distance
    return sysX


import IPython.display as ipd
def lview(self):
    fmt = 'http://newflaw.com/view.php?rule_alias={:}'
    uri = fmt.format(self.alias)
    print uri
    ele = '<iframe src="{}" width=600 height=500></iframe>'.format(uri)
    ipd.display(ipd.HTML(ele))
    return uri


def sample(self,ini=None,adv = None):
    '''
    Sample an iterator ('CA_sys' object)
    '''
    if adv is None:
        adv = self.adv
    if ini is None:
        ini=self.rdf().astype(int)
    avc = ini
    hist = np.zeros((self.hmax,)+avc.shape,dtype=np.int)
    for i in range(self.hmax):
        hist[i]=avc
        avc=(adv(avc)) 
    return hist
def fill_ber(arr,p=0.5):
    '''
    Create bernouli using shape of arr
    '''
    return np.random.random(arr.shape) < p
def mix_adv(fA,fB,pa=0):
    '''
    Mixing two iterator
    '''
    if pa==1:
        adv = lambda arr:fA(arr)
    elif pa==0:
        adv = lambda arr:fB(arr)
    else:
        def adv(arr):
            mask = np.random.random(arr.shape)<pa
            A = fA(arr)
            B = fB(arr)
#             arr[mask] = A
#             arr[~mask] = B
            out = A*mask + B*(1-mask)
            return out
#             return arr
    return adv
def cov2cor(COV):
    D = np.diag(COV)
    COR = COV /  np.sqrt(D[:,None]*D[None,:])
    return COR


# Fast run length encoding
def rle (img):
    '''
    Source:https://www.kaggle.com/hackerpoet/even-faster-run-length-encoder
    '''
    x = np.ravel(img)
#     flat_img = img.flatten()
#     flat_img = np.where(flat_img > 0.5, 1, 0).astype(np.uint8)
    ### startswith 0->1
    ### endswith 1->0
    starts = np.array((x[:-1] == 0) & (x[1:] == 1))
    ends = np.array((x[:-1] == 1) & (x[1:] == 0))
    starts_ix = np.where(starts)[0] + 1
    ends_ix = np.where(ends)[0] + 1
    if x[0] == 1:
        starts_ix = np.insert(starts_ix,0,0)
    if x[-1] == 1:
        ends_ix = np.append(ends_ix,len(x))
    lengths = ends_ix - starts_ix
    
    return starts_ix, lengths
def _gollyrle(x):
    sl = rle(x)
    sym='bo$'
    S = 0
    out = ''
    L = 0
#     if s
    if sl[0].size!=0:
        for s,l1 in zip(*sl):
            l0 = s-S-L
            if l0>0:        
                inc = '%d%s'%(l0,sym[0]) ### number of zeros
                out = '%s%s'%(out,inc)
            inc = '%d%s'%(l1,sym[1])
            out = '%s%s'%(out,inc)
            S = s
            L = l1
    l0 = len(x)-S-L
    if l0>0:        
        inc = '%d%s'%(l0,sym[0]) ### number of zeros
        out = '%s%s'%(out,inc)
#     out='%s%s'%(out,sym[-1])
    return out
_gollyrle(map(int,list('010001001010')))
def gollyrle(arr):
    return '$'.join(map(_gollyrle,arr))
def hill(x):
    return 1-abs(2*x-1)
def sflatten(arr):
    return np.reshape(arr,(len(arr),-1))
def sexpand(arr,d=2):
    S = arr.shape
    root = int(round(S[1]**(1./d)) )
    out = np.reshape(arr,(S[0],)+(root,)*d)
    return out


def showsptime(arr,ax=None,**kwargs):
    if ax is None:
        fig,ax = plt.subplots(1,1,figsize=[12,4])    
    return ax.pcolormesh(sflatten(arr),**kwargs)
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import IPython.display as ipd
def animate(arr,html = 1,nFrame=20):
    '''
    Animate a list of 2d-array
    '''
    fig, ax = plt.subplots()
    im = ax.imshow(arr[0])
    ax.grid()
    data_gen = arr

    def run(data):
        im.set_data(data)
        return im,

    ani = animation.FuncAnimation(fig, run, 
                                  data_gen, 
                                  blit=False,
                                  interval=1000./nFrame,
                                  repeat=1, init_func=None)
    plt.close()
    if html:
        ani = ipd.HTML(ani.to_html5_video())
    return ani

wildmyron
Posts: 1274
Joined: August 9th, 2013, 12:45 am

Re: Golly scripts

Post by wildmyron » June 12th, 2018, 11:49 pm

I've been using dvgrn's find-object.py script for some time now which works great until the population in the layer being searched through starts to go up. Here's a modified version which uses Python sets instead of lists to test for cell presence. It is significantly faster on layers with populations > 10,000. I also implemented object population matching which uses the selection around the object of interest as a bounding box around the potential matches. I haven't tested it with multistate patterns, but it should work.

Code: Select all

# find-object.py
# Search for another copy of the object in the current selection.
# If one is found, move the selection to the new location.
# If the selected orientation is not found, try other orientations.
# AUTHOR: Dave Green, Jan 2014
# CONTRIBUTORS: Arie Paap
# TODO:  update to find other phases (borrow code from glider-rewinder.py?)

import golly as g

r=g.getselrect()
if len(r)==0: g.exit("No selection.  Need a selected object to search with.")
searchobj=g.getcells(r)
rectcells=[r[0],r[1],r[0]+r[2]-1,r[1],r[0],r[1]+r[3]-1,r[0]+r[2]-1,r[1]+r[3]-1]
objlist=[searchobj,g.transform(searchobj,0,0,0,1,-1,0),
           g.transform(searchobj,0,0,-1,0,0,-1),g.transform(searchobj,0,0,0,-1,1,0),
           g.transform(searchobj,0,0,0,1,1,0),g.transform(searchobj,0,0,1,0,0,-1),
           g.transform(searchobj,0,0,0,-1,-1,0),g.transform(searchobj,0,0,-1,0,0,1)]
rectcelllist=[rectcells,g.transform(rectcells,0,0,0,1,-1,0),
           g.transform(rectcells,0,0,-1,0,0,-1),g.transform(rectcells,0,0,0,-1,1,0),
           g.transform(rectcells,0,0,0,1,1,0),g.transform(rectcells,0,0,1,0,0,-1),
           g.transform(rectcells,0,0,0,-1,-1,0),g.transform(rectcells,0,0,-1,0,0,1)]
normlist=[]
normrect=[]
for ii,obj in enumerate(objlist):
  sorted=g.evolve(obj,0)
  normlist+=[g.transform(sorted,-sorted[0],-sorted[1])]
  rect=g.evolve(rectcelllist[ii],0)
  normrect+=[[rect[0]-sorted[0],rect[1]-sorted[1],rect[6]-rect[0]+1,rect[7]-rect[1]+1]]

# offset=1000
# for obj in normlist:
#   g.putcells(obj, offset, 0)
#   offset+=100

g.putcells(searchobj,0,0,1,0,0,1,"xor")
all=g.getcells(g.getrect())
g.putcells(searchobj,0,0,1,0,0,1,"xor")

allcells=set()
step=len(all)%2+2
for i in range(0,len(all)-1,step):
  allcells.add(tuple(all[i:i+step])) # simplified version of two-state/multistate test

count=0
for ii, obj in enumerate(normlist):
  g.show("Object orientation "+str(count))
  g.update()
  count+=1
  # g.note(str(obj))
  for cell in allcells: # go through each cell in the pattern, check if there's a perfect match
    offsetobj=[]

    # separate search for two-state and multistate rules
    if len(obj)%2:
      # list has odd parity -- must be a multistate list, so break into groups of three
      for i in range(0,len(obj)-1,3):
        offsetobj+=[(obj[i]+cell[0],obj[i+1]+cell[1],obj[2])]
    else:
      # list has even parity -- must be a two-state list, so break into groups of two
      for i in range(0,len(obj),2):
        offsetobj+=[(obj[i]+cell[0],obj[i+1]+cell[1])]
    # g.note(str(offsetobj))
    match=1
    for c in offsetobj:
      if c not in allcells:
        match=0
        break

    if match==1:
      rect=normrect[ii]
      rect[0:2]=[rect[0]+cell[0],rect[1]+cell[1]]
      if not len(g.getcells(rect))==len(obj):
        # Don't match if number of cells in selection differs
        continue
      g.select(rect)
      g.fitsel()
      g.exit("Found something.")
g.show("No matches found")
Edit: Hmm, there's a bug with this version of the script where if it finds a false match first (i.e. additional On cells in the bbox) it fails to find any true matches. I've removed the use of continue but the bug is still present and I'm really struggling to work out why.
The latest version of the 5S Project contains over 221,000 spaceships. Tabulated pages up to period 160 are available on the LifeWiki.

wildmyron
Posts: 1274
Joined: August 9th, 2013, 12:45 am

Re: Golly scripts

Post by wildmyron » June 14th, 2018, 11:07 pm

Fixed a bug which meant false matches would cause true matches to be skipped.
I was caught out by Python's automatic references to lists again (this time it was to a nested list!).

Code: Select all

# find-object.py
# Search for another copy of the object in the current selection.
# If one is found, move the selection to the new location.
# If the selected orientation is not found, try other orientations.
# AUTHOR: Dave Green, Jan 2014
# CONTRIBUTORS: Arie Paap
# TODO:  update to find other phases (borrow code from glider-rewinder.py?)

import golly as g

r=g.getselrect()
if len(r)==0: g.exit("No selection.  Need a selected object to search with.")
searchobj=g.getcells(r)
rectcells=[r[0],r[1],r[0]+r[2]-1,r[1],r[0],r[1]+r[3]-1,r[0]+r[2]-1,r[1]+r[3]-1]
objlist=[searchobj,g.transform(searchobj,0,0,0,1,-1,0),
           g.transform(searchobj,0,0,-1,0,0,-1),g.transform(searchobj,0,0,0,-1,1,0),
           g.transform(searchobj,0,0,0,1,1,0),g.transform(searchobj,0,0,1,0,0,-1),
           g.transform(searchobj,0,0,0,-1,-1,0),g.transform(searchobj,0,0,-1,0,0,1)]
rectcelllist=[rectcells,g.transform(rectcells,0,0,0,1,-1,0),
           g.transform(rectcells,0,0,-1,0,0,-1),g.transform(rectcells,0,0,0,-1,1,0),
           g.transform(rectcells,0,0,0,1,1,0),g.transform(rectcells,0,0,1,0,0,-1),
           g.transform(rectcells,0,0,0,-1,-1,0),g.transform(rectcells,0,0,-1,0,0,1)]
# Mirror y-axis only
objlist=[searchobj,g.transform(searchobj,0,0,1,0,0,-1)]
rectcelllist=[rectcells,g.transform(rectcells,0,0,1,0,0,-1)]
normlist=[]
normrect=[]
for ii,obj in enumerate(objlist):
  sorted=g.evolve(obj,0)
  normlist+=[g.transform(sorted,-sorted[0],-sorted[1])]
  rect=g.evolve(rectcelllist[ii],0)
  normrect+=[[rect[0]-sorted[0],rect[1]-sorted[1],rect[6]-rect[0]+1,rect[7]-rect[1]+1]]

# offset=1000
# for obj in normlist:
#   g.putcells(obj, offset, 0)
#   offset+=100

g.putcells(searchobj,0,0,1,0,0,1,"xor")
all=g.getcells(g.getrect())
g.putcells(searchobj,0,0,1,0,0,1,"xor")

allcells=set()
step=len(all)%2+2
for i in range(0,len(all)-1,step):
  allcells.add(tuple(all[i:i+step])) # simplified version of two-state/multistate test

count=0
for ii, obj in enumerate(normlist):
  g.show("Object orientation "+str(count))
  g.update()
  count+=1
  # g.note(str(obj))
  for cell in allcells: # go through each cell in the pattern, check if there's a perfect match
    offsetobj=[]

    # separate search for two-state and multistate rules
    if len(obj)%2:
      # list has odd parity -- must be a multistate list, so break into groups of three
      for i in range(0,len(obj)-1,3):
        offsetobj+=[(obj[i]+cell[0],obj[i+1]+cell[1],obj[2])]
    else:
      # list has even parity -- must be a two-state list, so break into groups of two
      for i in range(0,len(obj),2):
        offsetobj+=[(obj[i]+cell[0],obj[i+1]+cell[1])]
    # g.note(str(offsetobj))
    match=1
    for c in offsetobj:
      if c not in allcells:
        match=0
        break

    if match==1:
      rect=normrect[ii][:]
      rect[0:2]=[rect[0]+cell[0],rect[1]+cell[1]]
      # Don't match if number of cells in selection differs
      if len(g.getcells(rect))==len(obj):
        g.select(rect)
        g.fitsel()
        g.exit("Found something.")
g.show("No matches found")
The latest version of the 5S Project contains over 221,000 spaceships. Tabulated pages up to period 160 are available on the LifeWiki.

User avatar
gameoflifemaniac
Posts: 775
Joined: January 22nd, 2017, 11:17 am
Location: There too

Re: Golly scripts

Post by gameoflifemaniac » September 15th, 2018, 2:43 pm

Nathaniel wrote:I got sick of calculating it by hand, so I made a Python script that calculates the heat of the current pattern (oscillator or spaceship) over a user-specified number of generations. It also outputs the maximum and minimum changes that were noticed when going from one generation to the next.

heat.py

Code: Select all

#Golly Python script
#Written by Nathaniel Johnston
#March 28, 2009

''' Calculate the heat of the current oscillator/spaceship '''       

from glife import getstring, validint 
import golly as g

def chunks(l,w):
    for i in xrange(0, len(l), 2):
        yield l[i]+(w*l[i+1])

if g.empty(): g.exit("The pattern is empty.")
s = g.getstring("Enter the period:","", "Heat calculator")

if not validint(s): 
  g.exit('Bad number: %s' % s)
numsteps = int(s)
if numsteps < 2:
  g.exit('Period must be at least 2.')

g.show('Processing...')

heat = 0;
maxheat = 0;
minheat = 9*g.getpop();

for i in range(0, numsteps):
  bb = g.getrect()
  clist = list(chunks(g.getcells(bb), bb[2]+2))
  g.run(1)
  dlist = list(chunks(g.getcells(g.getrect()), bb[2]+2))
  theat = (len(clist)+len(dlist)-2*len([x for x in set(clist).intersection( set(dlist) )]))
  heat += theat
  maxheat = max(theat, maxheat)
  minheat = min(theat, minheat)

g.show('Heat: %.4f, Max change: %d, Min change: %d' % ((float(heat)/numsteps), maxheat, minheat))
Can someone modify the script so that it finds the period of an object itself?
https://www.youtube.com/watch?v=q6EoRBvdVPQ
One big dirty Oro. Yeeeeeeeeee...

User avatar
dvgrn
Moderator
Posts: 5889
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Golly scripts

Post by dvgrn » September 15th, 2018, 2:51 pm

gameoflifemaniac wrote:Can someone modify the script so that it finds the period of an object itself?
This can be done by starting with oscar.py, copy-and pasting Nathaniel's script into a subroutine, and calling that subroutine after the line

Code: Select all

g.show("Oscillator detected (period = " + str(period) + ")")
in oscar.py. Try it! You'll probably find all kinds of new ways to get Python errors for a while, but if you look each of them up on Google, eventually you'll fix them all and the script will work.

(If you want someone else to do the coding, you'd be better off posting in the Script request thread. This one is kind of supposed to be for scripts that you complete yourself.)

M. I. Wright
Posts: 372
Joined: June 13th, 2015, 12:04 pm

Re: Golly scripts

Post by M. I. Wright » October 13th, 2018, 6:36 pm

Not sure if it's been done already, but here's a Lua thing to match a 3x3 selection to its Hensel-notation representation. Should come in handy when twiddling with a rule's individual r4r transitions.

match-napkin.lua

Code: Select all

local g = golly()

local hashes = {
    [31415962]      = "0 (birth)",           [530404524]     = "0 (survival)",
    [529404522]     = "1c (birth)",          [912400124]     = "1c (survival)",
    [529404523]     = "1e (birth)",          [189020161]     = "1e (survival)",

    [911400120]     = "2c (birth)",          [-71026502]     = "2c (survival)",
    [189020160]     = "2e (birth)",          [-571917758]    = "2e (survival)",
    [912400127]     = "2k (birth)",          [-208510179]    = "2k (survival)",
    [911400123]     = "2a (birth)",          [2057800913]    = "2a (survival)",
    [192020168]     = "2i (birth)",          [-1296297726]   = "2i (survival)",
    [909400118]     = "2n (birth)",          [-207510176]    = "2n (survival)",

    [-70026498]     = "3c (birth)",          [-726800124]    = "3c (survival)",
    [-571917759]    = "3e (birth)",          [-1264981609]   = "3e (survival)",
    [1923317244]    = "3k (birth)",          [1090604798]    = "3k (survival)",
    [2057800912]    = "3a (birth)",          [1964322194]    = "3a (survival)",
    [2058800913]    = "3i (birth)",          [-305988905]    = "3i (survival)",
    [-71026503]     = "3n (birth)",          [-723800113]    = "3n (survival)",
    [-70026497]     = "3y (birth)",          [-726800123]    = "3y (survival)",
    [2060800923]    = "3q (birth)",          [1239942225]    = "3q (survival)",
    [2057800914]    = "3j (birth)",          [1240942228]    = "3j (survival)",
    [2060800920]    = "3r (birth)",          [1239942226]    = "3r (survival)",

    [-864283798]    = "4c (birth)",          [-854086744]    = "4c (survival)",
    [-546601662]    = "4e (birth)",          [-1380048556]   = "4e (survival)",
    [-1444180072]   = "4k (birth)",          [530194514]     = "4k (survival)",
    [-305988908]    = "4a (birth)",          [-1273047602]   = "4a (survival)",
    [-1447180078]   = "4i (birth)",          [531194516]     = "4i (survival)",
    [-306988909]    = "4n (birth)",          [-1270047595]   = "4n (survival)",
    [-5420165]      = "4y (birth)",          [-545157811]    = "4y (survival)",
    [1965322196]    = "4q (birth)",          [522807454]     = "4q (survival)",
    [518562269]     = "4j (birth)",          [1606159803]    = "4j (survival)",
    [1964322193]    = "4r (birth)",          [525807463]     = "4r (survival)",
    [-306988910]    = "4t (birth)",          [-1270047596]   = "4t (survival)",
    [518562270]     = "4w (birth)",          [1606159800]    = "4w (survival)",
    [143423708]     = "4z (birth)",          [296883550]     = "4z (survival)",

    [1797604814]    = "5c (birth)",          [2035211896]    = "5c (survival)",
    [-483441751]    = "5e (birth)",          [-1928248821]   = "5e (survival)",
    [1222852148]    = "5k (birth)",          [-1529110102]   = "5k (survival)",
    [-1883842740]   = "5a (birth)",          [-1187321398]   = "5a (survival)",
    [1437399681]    = "5i (birth)",          [492115963]     = "5i (survival)",
    [-1318332495]   = "5n (birth)",          [-1169857965]   = "5n (survival)",
    [1080224867]    = "5y (birth)",          [1309831925]    = "5y (survival)",
    [-1773986061]   = "5q (birth)",          [235486005]     = "5q (survival)",
    [-1102388650]   = "5j (birth)",          [147168548]     = "5j (survival)",
    [-1773986064]   = "5r (birth)",          [235486006]     = "5r (survival)",

    [386020]        = "6c (birth)",          [-968761566]    = "6c (survival)",
    [-758004836]    = "6e (birth)",          [-1248825190]   = "6e (survival)",
    [568494839]     = "6k (birth)",          [-2003852311]   = "6k (survival)",
    [-648148160]    = "6i (birth)",          [173982214]     = "6i (survival)",
    [-1023075437]   = "6a (birth)",          [1159834581]    = "6a (survival)",
    [-1246782566]   = "6n (birth)",          [394705048]     = "6n (survival)",

    [256455398]     = "7c (birth)",          [-1368619556]   = "7c (survival)",
    [-1084579229]   = "7e (birth)",          [-762027739]    = "7e (survival)",

    [-1665406922]   = "8 (birth)",           [1241619884]    = "8 (survival)",
}

local selrect = g.getselrect()
if #selrect == 0 or selrect[3] ~= 3 or selrect[4] ~= 3 then
    g.exit("Select a 3x3 patch of cells first.")
end

for _=1, 4 do
    for _=1, 2 do
        local nbhd = hashes[g.hash(selrect)]
        if nbhd ~= nil then
            g.show(nbhd)
        end
        g.flip(1)
    end
    g.rotate(1)
end


Hashes were gathered by running another script over these two transcriptions of the table I'd had laying around.

Code: Select all

x = 27, y = 39, rule = B/S012345678
3bo2bob2ob2obobo2bo2b5o$bo2bo2bo2bo2bob12o$9bo2bobob11o$4bo2bo2bo2bobo
b2ob2obo$4bob2ob6obo2b5o$13bob9o$6bo2bo2bobob2ob2o$7b2ob4ob2ob2o$10bo
2bobob4o$6b2ob2ob3o2bo2bo$7bob2o2b2ob5o$15b6o$7bob4obo3bobo$7bo2bob9o$
7bo7b4obo$6bo2bob4obo2b2o$7bo2b2obob2ob3o$8bo3bo2b5o$9bob2obobo$10bo2b
5o$10bobo2bobo$9b2ob2o3bo$10bob2ob3o$11bo2b3o$9b2ob2o3bo$10b2ob4o$13bo
b3o$9b2ob2o3bo$10bob6o$10bo4bobo$12b3o$13bo$13bo$12b2o$13b2o$14bo$12b
2o$13bo$13b2o!

Code: Select all

x = 27, y = 39, rule = B/S012345678
3bo2bob2ob2obobo2bo2b5o$15bob2ob2ob2obo$9bo2bobob11o$4bo2bo2bo2bobob2o
b2obo$6bo2bob2obo5b2obo$13bob9o$6bo2bo2bobob2ob2o$8bo2b2o2bo2bo$10bo2b
obob4o$6b2ob2ob3o2bo2bo$9bo4bo2b2obo$15b6o$7bob4obo3bobo$12bob2ob2obo$
7bo7b4obo$6bo2bob4obo2b2o$11bo3bo2bobo$8bo3bo2b5o$9bob2obobo$14b2obo$
10bobo2bobo$9b2ob2o3bo$12bo2bobo$11bo2b3o$9b2ob2o3bo$11bo2b2o$13bob3o$
9b2ob2o3bo$12bob2obo$10bo4bobo$12b3o2$13bo$12b2o$14bo$14bo$12b2o2$13b
2o!
gamer54657 wrote:God save us all.
God save humanity.

hgkhjfgh
nutshelltlifeDiscord 'Conwaylife Lounge'

User avatar
Senso
Posts: 18
Joined: November 1st, 2018, 11:36 am
Location: I call that one "shoving match"

Re: Golly scripts

Post by Senso » November 22nd, 2018, 8:04 am

When trying a new rule or working on new CAs, I often want to generate many random soups in succession and see how they fare, to get a quick idea of pattern potentials, explosions, infinite growth, etc.

I would normally do Control-n, select a 20x20 grid, hit Control-5, lay on the space key and repeat. That got old quickly, especially having to draw the selection, so I made this script and bound it to Control-4.
It creates a random 16x16 selection and runs 200 generations, stopping if at any time the universe is empty.

Code: Select all

import time
import golly as g
import random
random.seed()

GRID_SIZE = (16, 16)
GENS = 200

def better_randfill():
    g.new('')
    g.select([-10, -10, GRID_SIZE[0], GRID_SIZE[1]])
    g.randfill(random.randrange(35, 55))
    g.select([])
    g.autoupdate(True)

    cnt = 0
    for x in range(0,GENS):

        cnt += 1
        if not g.empty():
            g.run(1)
            time.sleep(0.05)
        else:
            break
        if cnt % 10 == 0:
            g.fit()

better_randfill()
I haven't found a way to increase the delay between generations in order to get a better view of what happens (setstep() is ignored by run()).
Last edited by Senso on November 24th, 2018, 6:15 am, edited 2 times in total.

User avatar
dvgrn
Moderator
Posts: 5889
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Golly scripts

Post by dvgrn » November 22nd, 2018, 8:24 am

Senso wrote:I haven't found a way to increase the delay between generations in order to get a better view of what happens (setstep() is ignored by run()).
You could borrow various amounts of code from Golly's Scripts/Python/heisenburp.py if you want. In simplest form it's

Code: Select all

from time import sleep

delay = .1  # units are seconds
sleep(delay)
but the delay could be controlled from the keyboard while the script is running, as heisenburp.py does with the + and - keys. It doesn't exactly match Golly's behavior outside of the script. All the weird math in there with the transitions between the tickstep and delay variables is just trying to make a smooth transition between showing every generation (with variable delays) and skipping generations.

User avatar
Senso
Posts: 18
Joined: November 1st, 2018, 11:36 am
Location: I call that one "shoving match"

Re: Golly scripts

Post by Senso » November 22nd, 2018, 9:10 am

dvgrn wrote: You could borrow various amounts of code from Golly's Scripts/Python/heisenburp.py if you want. In simplest form it's

Code: Select all

from time import sleep

delay = .1  # units are seconds
sleep(delay)
but the delay could be controlled from the keyboard while the script is running, as heisenburp.py does with the + and - keys. It doesn't exactly match Golly's behavior outside of the script. All the weird math in there with the transitions between the tickstep and delay variables is just trying to make a smooth transition between showing every generation (with variable delays) and skipping generations.
Strangely, I did originally used time.sleep() between each generation run but it made Golly not terminate the script for 5-10 seconds after it was done. Nothing happening, generation 200 reached, but Golly's state is still in "script running", with the Stop button showing in the top-left. I thought time.sleep() might be messing with Golly internal loops and ditched that idea.

User avatar
dvgrn
Moderator
Posts: 5889
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Golly scripts

Post by dvgrn » November 22nd, 2018, 4:47 pm

Senso wrote:Strangely, I did originally used time.sleep() between each generation run but it made Golly not terminate the script for 5-10 seconds after it was done. Nothing happening, generation 200 reached, but Golly's state is still in "script running", with the Stop button showing in the top-left. I thought time.sleep() might be messing with Golly internal loops and ditched that idea.
I've definitely seen Golly behave significantly differently on different systems, particularly in regard to screen updates. For example, Golly on my current (newer) laptop doesn't update the screen reliably when I hold down the Space bar or Ctrl-Z, so the screen seems to freeze between updates. On other systems Space or Ctrl+Z allowed for smooth animation. Not that that's likely to be relevant here, just another example. I've never seen anything quite like what you're describing.

If you can get the same behavior back by re-adding time.sleep() calls, I'd be interested to try out that version of your script and see if it causes any trouble on my system. It really shouldn't be causing any particular trouble with Golly -- at least, no one has reported bad behavior related to time.sleep use in heisenburp.py (though maybe that's just because most people don't run it, or don't notice that you can change the speed, send new signals, etc. from inside the script.)

User avatar
Senso
Posts: 18
Joined: November 1st, 2018, 11:36 am
Location: I call that one "shoving match"

Re: Golly scripts

Post by Senso » November 23rd, 2018, 7:03 am

dvgrn wrote: If you can get the same behavior back by re-adding time.sleep() calls, I'd be interested to try out that version of your script and see if it causes any trouble on my system. It really shouldn't be causing any particular trouble with Golly -- at least, no one has reported bad behavior related to time.sleep use in heisenburp.py (though maybe that's just because most people don't run it, or don't notice that you can change the speed, send new signals, etc. from inside the script.)
Turns out it was my fault. I was doing:

Code: Select all

        if g.getpop() > 0:
            g.run(1)
            time.sleep(0.05)
        else:
            break
The Golly documentation seems to say that getpop() with no argument returns an int but it's actually not. So my script kept running until the 200th generation even when the universe was empty, explaining the ~8-10 seconds of "stalling". So casting getpop() to an int fixed my issue and I can now use time.sleep():

Code: Select all

        if int(g.getpop()) > 0:
            g.run(1)
            time.sleep(0.05)
        else:
            break

User avatar
dvgrn
Moderator
Posts: 5889
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Golly scripts

Post by dvgrn » November 23rd, 2018, 8:55 am

Senso wrote:The Golly documentation seems to say that getpop() with no argument returns an int but it's actually not. So my script kept running until the 200th generation even when the universe was empty, explaining the ~8-10 seconds of "stalling".
Makes sense -- thanks for the report. Yeah, it's not really safe for Golly to return an int for population, because a HashLife universe can so easily reach population levels that don't fit in an integer type.

User avatar
Andrew
Moderator
Posts: 764
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia
Contact:

Re: Golly scripts

Post by Andrew » November 24th, 2018, 2:43 am

Senso wrote:The Golly documentation seems to say that getpop() with no argument returns an int but it's actually not.
The first sentence in the getpop docs is "Return the current population as a string.". I'm a bit surprised that Python allows comparison of a string with an int -- seems a daft decision that is only likely to cause hard-to-find bugs. Anyway, if you only want to test if the population is non-zero then it's simpler and faster to use g.empty:

Code: Select all

if not g.empty():

User avatar
Senso
Posts: 18
Joined: November 1st, 2018, 11:36 am
Location: I call that one "shoving match"

Re: Golly scripts

Post by Senso » November 24th, 2018, 5:36 am

Andrew wrote: The first sentence in the getpop docs is "Return the current population as a string.". I'm a bit surprised that Python allows comparison of a string with an int -- seems a daft decision that is only likely to cause hard-to-find bugs. Anyway, if you only want to test if the population is non-zero then it's simpler and faster to use g.empty:

Code: Select all

if not g.empty():
Python 2 allows comparison of strings and int -- Python 3 doesn't. There's no reason not to move to Python 3. :)
Thanks for empty(), I didn't see that.

User avatar
simsim314
Posts: 1702
Joined: February 10th, 2014, 1:27 pm

Re: Golly scripts

Post by simsim314 » March 3rd, 2019, 5:29 am

Here is a script that tries to create converter from salvo of gliders to something there is already script for - well separated gliders. I think I've modified chris_c script and added something to it - not sure exactly what (probably the construction order is dynamic - and more effort is done to make it smaller).

Just wanted to drop it here, not sure I posted on the forum.

Code: Select all

import golly as g 

gld = g.parse("3o$2bo$bo!")

#ini_gld = g.parse("o$b2o$2o!", -90, 12)
#edge_shoot = g.parse("67b2o$67bobo$69bo$63b2o3bob2o$64bo3bo3bo$52b2o10bob2ob2obo$27b2o24bo11bobobobo$28bo13b2o6b3o$28bobo11b2o6bo$18bo10b2o$16b3o$15bo$15b2o$2o$bo$bob2o$2bo2bo$3b2o$18b2o$18b2o$56b2o$56b2o2$27bo3b2o$26bobo3bo5b2o3b2o$25bobo3bo7bo3bo$21b2obobo3bo5b3o5b3o$21b2obo2b4obo3bo9bo$25bobo3bobo$21b2ob2o2bo2bobo$22bobo2b2o3bo$10b2o10bobo$10b2o11bo!", -91, 8)

#ini_gld = g.parse("obo$b2o$bo!", -91, -15)
#edge_shoot = g.parse("$43b2o$43b2o5b2o$50b2o3$23bo5b2o17b2o$21b3o6bo17b2o$20bo9bobo21b2o$20b2o9b2o21b2o$5b2o$6bo$6bob2o$7bo2bo$8b2o$23b2o$23b2o4$32bo3b2o$31bobo3bo20bo$30bobo3bo19b3o$26b2obobo3bo19bo$26b2obo2b4obo17b2o$30bobo3bobo$26b2ob2o2bo2bobo$27bobo2b2o3bo$15b2o10bobo$15b2o11bo5$35b2o$35b2o9$50b2o$50b2o!", -90, -10)
#g.putcells(edge_shoot)
#g.putcells(ini_gld)

l_edge1 = g.parse("2$42b2o$42b2o5b2o$49b2o3$22bo5b2o17b2o$20b3o6bo17b2o$19bo9bobo21b2o$19b2o9b2o21b2o$4b2o$5bo$5bob2o$6bo2bo$7b2o$22b2o$22b2o4$31bo3b2o$30bobo3bo20bo$29bobo3bo19b3o$25b2obobo3bo19bo$25b2obo2b4obo17b2o$29bobo3bobo$25b2ob2o2bo2bobo$26bobo2b2o3bo$14b2o10bobo$14b2o11bo5$34b2o$34b2o9$49b2o$49b2o!", -117, 17)
l_gld1 = g.parse("obo$b2o$bo!", -119, 13)

l_edge2 = g.parse("41b2o$41b2o5b2o$48b2o3$21bo5b2o17b2o$19b3o6bo17b2o$18bo9bobo21b2o$18b2o9b2o21b2o$3b2o$4bo$4bob2o$5bo2bo$6b2o$21b2o$21b2o4$30bo3b2o$29bobo3bo$28bobo3bo$24b2obobo3bo$24b2obo2b4obo$28bobo3bobo$24b2ob2o2bo2bobo$25bobo2b2o3bo$13b2o10bobo$13b2o11bo2$59b2o$59bo$57bobo$57b2o$33b2o$33b2o6$35b2o$36bo$33b3o$33bo$34b2o$35bo$35bobo$36b2o5$34b2o$35bo$22b2o11bobo$23bo12b2o$23bobo$24b2o2b2o$28b2o4$23b2o4b2o$23b2o4b2o7$24b2o$23bobo$23bo$22b2o!", -86, 108 - 150)
l_gld2 = g.parse("obo$b2o$bo!", -89, 102 - 150)

l_edge3 = g.parse("2$43b2o$43b2o5b2o$50b2o3$23bo5b2o17b2o$21b3o6bo17b2o$20bo9bobo21b2o$20b2o9b2o21b2o$5b2o$6bo$6bob2o$7bo2bo$8b2o$23b2o$23b2o4$32bo3b2o$31bobo3bo$30bobo3bo$26b2obobo3bo$26b2obo2b4obo$30bobo3bobo$26b2ob2o2bo2bobo$27bobo2b2o3bo$15b2o10bobo$15b2o11bo2$61b2o$61bo$59bobo$59b2o$35b2o$35b2o6$37b2o$38bo$35b3o$35bo$36b2o$37bo$37bobo$38b2o5$36b2o$37bo$24b2o11bobo$25bo12b2o$25bobo$26b2o2b2o$30b2o4$25b2o4b2o$25b2o4b2o7$26b2o$25bobo$25bo$24b2o!", -88, 256 - 300)
l_gld3 = g.parse("$bobo$2b2o$2bo!", -90, 251-300)

l_edge4 = g.parse("12$69bo$67b3o$66bo$66b2o5$45bo$45b3o$48bo$47b2o$71b2o$28bo42b2o$28b3o$31bo50bo$18bo11b2o11b2o35b3o$16b3o24b2o34bo$15bo63b2o$15b2o$2o$bo$bob2o$2bo2bo$3b2o$18b2o$18b2o34b2o$53bobo$53bo$52b2o$27bo3b2o$26bobo3bo25b2o$25bobo3bo26b2o$21b2obobo3bo12b2o$21b2obo2b4obo10bo$25bobo3bobo10b3o$21b2ob2o2bo2bobo12bo$22bobo2b2o3bo$10b2o10bobo$10b2o11bo3b2o35b2o$27bobo35bo$28bo33b3o$62bo!", -108, 437-450)
l_gld4 = g.parse("$bo$2b2o$b2o!", -114, 453-450)

l_edge5 = g.parse("4$45bo$45b3o$41bo6bo$40bobo4b2o$40bobo$38b3ob2o20bo$37bo24b3o$31bo6b3ob2o17bo$31b3o6bob2o17b2o$34bo$21bo11b2o$19b3o36b2o$18bo38bo2bo$18b2o38b2o$3b2o$4bo$4bob2o$5bo2bo$6b2o$21b2o$21b2o23b2o$46bo$47b3o$49bo$30bo3b2o$29bobo3bo$28bobo3bo$24b2obobo3bo$24b2obo2b4obo$28bobo3bobo$24b2ob2o2bo2bobo$25bobo2b2o3bo$13b2o10bobo$13b2o11bo3b2o$30bobo$31bo!", -119, 635-600)
l_gld5 = g.parse("2$4bo$2bobo$3b2o!", -124, 636-600)

l_edge6 = g.parse("3$69b2o$69bobo$71bo$65b2o3bob2o$66bo3bo3bo$54b2o10bob2ob2obo$29b2o24bo11bobobobo$30bo13b2o6b3o$30bobo11b2o6bo$20bo10b2o$18b3o$17bo$17b2o$2b2o$3bo$3bob2o$4bo2bo$5b2o$20b2o$20b2o$58b2o$58b2o2$29bo3b2o$28bobo3bo5b2o3b2o$27bobo3bo7bo3bo$23b2obobo3bo5b3o5b3o$23b2obo2b4obo3bo9bo$27bobo3bobo$23b2ob2o2bo2bobo$24bobo2b2o3bo$12b2o10bobo$12b2o11bo!", -115, 777-750)
l_gld6 = g.parse("2$2bo$3b2o$2b2o!", -120, 776-750)

r_edge1 = g.parse("2$30b2o$29bo2bo$30bobo$29b2o2b2o$31b2o2bo$29b2o3b2o$30bo$29bo$30b3o$32bo6$14b2o$14b2o3$29b2o$28bobo$28bo$27b2o4$8b2o$9bo$9bobo$10b2o15b2o$27b2o3$10b2o$9bobo$9bo$8b2o3$6b2o$5bo2bo2b2o$6b2o2bobo$8b2o16b2o$8bo17bo$5b2obo2bo15b3o$5bob2obobo16bo$9bobo$6b2o2bo$4b3ob2o$3bo$4b3ob2o$6bob2o2$16b2o$16b2o7b2o$25bo$23bobo$23b2o4$3b2o$3b2o5$19bo$18bobo$18bobo$19bo$20b3o$22bo!", -64, 41)
r_gld1 = g.parse("b2o$2o$2bo!", -30, 118)

r_edge2 = g.parse("$30b2o$30bo$28bobo$28b2o$24bo$23bobo$23bobo$24bo8$13b2o$14bo18b2o$14bobo16bobo$15b2o18bo$35b2o2$28b2obo$28b2ob3o$34bo$28b2ob3o$29bobo$29bobo$30bo$6b2o$5bo2bo2b2o$6b2o2bobo13b2o$8b2o16bobo$2bo5bo19bo$bobob2obo2bo16b2o$2b2obob2obobo$9bobo$6b2o2bo$4b3ob2o$3bo$4b3ob2o$6bob2o2$16b2o$16b2o7b2o$25bo$23bobo$23b2o4$3b2o$3b2o5$19bo$18bobo$18bobo$19bo$20b3o$22bo!", 76-150, 56)
r_gld2 = g.parse("2o$obo$o!", 111-150, 121)

r_edge3 = g.parse("3$34b2o$34bo$32bobo$32b2o7$37b2o$37b2o2$49b2o$49bo$47bobo$47b2o$10b2o$9bobo$9bo$8b2o3$18b2o$18b2o3$23b2o$24bo$21b3o$21bo4$39b2o$39bobo$14b2o25bo$15bo25b2o$15bobo15b2o$16b2o15b2o10$14b2o$13bo2bo2b2o$14b2o2bobo13b2o$16b2o16bobo$10bo5bo19bo$9bobob2obo2bo16b2o$10b2obob2obobo$17bobo$14b2o2bo$12b3ob2o$11bo$12b3ob2o$14bob2o2$24b2o$24b2o7b2o$33bo$31bobo$31b2o4$11b2o$11b2o5$27bo$26bobo$26bobo$27bo$28b3o$30bo!", 252-300, 25)
r_gld3 = g.parse("2$2b2o$b2o$3bo!", 293-300, 111)

r_edge4 = g.parse("11$61bo$59b3o$58bo$58b2o4$83b2o$83b2o3$89b2o$89b2o$85b2o$85b2o4$90b2o$90b2o4$35b2o6b2o$35bo7bo5b2o15b2o$36b3o5b3obobo14bo2bo2b2o$38bo7bobo7b2o8b2o2bobo$47b2o7b2o10b2o$68bo$65b2obo2bo$27b2o36bob2obobo10b2o$27b2o3b2o35bobo11bo$32b2o32b2o2bo13b3o$64b3ob2o16bo$63bo$19b2o12b2o29b3ob2o$20bo6b2o4bo32bob2o$17b3o7b2o5b3o$17bo18bo39b2o$76b2o7b2o$85bo$83bobo$83b2o4$63b2o$63b2o5$79bo$78bobo$78bobo$79bo$80b3o$82bo!", 402-450, 17)
r_gld4 = g.parse("bo$2o$obo!", 497-450, 88)

r_edge5 = g.parse("10$45bo$43b3o$42bo$42b2o4$67b2o$67b2o3$73b2o$73b2o$69b2o$69b2o4$74b2o$74b2o4$19b2o6b2o$19bo7bo5b2o15b2o$20b3o5b3obobo14bo2bo2b2o$22bo7bobo7b2o8b2o2bobo$31b2o7b2o10b2o$52bo$49b2obo2bo$11b2o36bob2obobo10b2o$11b2o3b2o35bobo11bo$16b2o32b2o2bo13b3o$48b3ob2o16bo$47bo$3b2o12b2o29b3ob2o$4bo6b2o4bo32bob2o$b3o7b2o5b3o$bo18bo39b2o$60b2o7b2o$69bo$67bobo$67b2o4$47b2o$47b2o5$63bo$62bobo$62bobo$63bo$64b3o$66bo!", 568-600, 18)
r_gld5 = g.parse("bo$2o$obo!", 647-600, 88)

r_edge6 = g.parse("5$30b2o$30bo$28bobo$28b2o13b2o$43b2o3$7b2o40b2o$7b2o40b2o$45b2o$45b2o4$50b2o$50b2o5$26b2o$25bo2bo2b2o$17b2o7b2o2bobo$17b2o9b2o$28bo$25b2obo2bo$25bob2obobo10b2o$29bobo11bo$26b2o2bo13b3o$24b3ob2o16bo$23bo$24b3ob2o$26bob2o2$36b2o$36b2o7b2o$45bo$43bobo$43b2o4$23b2o$23b2o5$39bo$38bobo$38bobo$39bo$40b3o$42bo!", 681-750, 57)
r_gld6 = g.parse("bo$2o$obo!", 736-750, 118)

l_edge = [l_edge1,l_edge2,l_edge3,l_edge4,l_edge5,l_edge6,r_edge1,r_edge2,r_edge3,r_edge4,r_edge5,r_edge6]
l_gld = [l_gld1,l_gld2,l_gld3,l_gld4,l_gld5,l_gld6,r_gld1,r_gld2,r_gld3,r_gld4,r_gld5,r_gld6]

clock_insert = g.parse("52$306bob2o$306b2obo2$304b5o$304bo4bo2b2o$307bo2bo2bo$307b2obobo$304bo5bob2o$303bobo4bo$303bo2bo2b2o$304b2o9$296b2o$296b2o8$282b2o22b2o$281bo2bo21bo$282b2o23b3o$309bo6$299b2o$299bo$300b3o$302bo7$352bob2o$352b2obo2$350b5o$350bo4bo2b2o$353bo2bo2bo$353b2obobo$350bo5bob2o$228bo120bobo4bo$228b3o118bo2bo2b2o$224bo6bo118b2o$223bobo4b2o$223bobo$221b3ob2o20bo$220bo24b3o$214bo6b3ob2o17bo$214b3o6bob2o17b2o$217bo$204bo11b2o$202b3o36b2o99b2o$201bo38bo2bo98b2o$201b2o38b2o$186b2o$187bo$187bob2o$188bo2bo$189b2o$204b2o$204b2o23b2o97b2o22b2o$229bo97bo2bo21bo$230b3o95b2o23b3o45bob2o$232bo122bo45b2obo$213bo3b2o$212bobo3bo180b5o$211bobo3bo181bo4bo2b2o$207b2obobo3bo185bo2bo2bo$207b2obo2b4obo74bo108b2obobo$211bobo3bobo73b3o49b2o52bo5bob2o$207b2ob2o2bo2bobo69bo6bo48bo52bobo4bo$208bobo2b2o3bo69bobo4b2o49b3o49bo2bo2b2o$196b2o10bobo77bobo57bo50b2o$196b2o11bo76b3ob2o20bo$285bo24b3o$191bo87bo6b3ob2o17bo$189b3o87b3o6bob2o17b2o$175bo12bo93bo$164b2o7b3o12b2o79bo11b2o$165bo6bo94b3o36b2o$165bobo4b2o34bo57bo38bo2bo$166b2o38b3o57b2o38b2o83b2o$205bo45b2o138b2o$192b2o11b2o11bo33bo$192b2o24b3o31bob2o$221bo31bo2bo$220b2o32b2o$235b2o32b2o$235bo33b2o23b2o$232b2obo58bo56bo$231bo2bo60b3o53b3o23b2o22b2o$232b2o63bo49bo6bo21bo2bo21bo$217b2o59bo3b2o62bobo4b2o22b2o23b3o$181b2o34b2o58bobo3bo62bobo55bo$181bobo92bobo3bo61b3ob2o20bo$183bo88b2obobo3bo61bo24b3o$157b2o24b2o87b2obo2b4obo53bo6b3ob2o17bo$158bo45b2o3bo66bobo3bobo52b3o6bob2o17b2o$155b3o46bo3bobo61b2ob2o2bo2bobo55bo$155bo9b2o24b2o12bo3bobo61bobo2b2o3bo43bo11b2o53b2o$164bobo23bobo13bo3bobob2o45b2o10bobo49b3o36b2o28bo$164bo25bo13bob4o2bob2o45b2o11bo49bo38bo2bo28b3o$163b2o24b2o12bobo3bobo112b2o38b2o31bo$203bobo2bo2b2ob2o36b2o4bo50b2o$204bo3b2o2bobo28b2o7bobo2bobo50bo$212bobo10b2o17bo9b4o2bo49bob2o$213bo11b2o17bobo6bo4bobo50bo2bo$245b2o6b2o2b2ob2o50b2o$263bo63b2o$228b2o27b2ob4o2bo60b2o23b2o$228b2o27b2obo3b3o85bo$263bo89b3o$263b2o90bo$336bo3b2o$335bobo3bo$334bobo3bo$278bo51b2obobo3bo$278b3o49b2obo2b4obo$281bo52bobo3bobo$280b2o48b2ob2o2bo2bobo$295b2o34bobo2b2o3bo$295bo23b2o10bobo$292b2obo23b2o11bo$291bo2bo$218b2o72b2o$218b2o57b2o$277b2o2$251b2o$239b2o9bobo4b2o$239bobo8bo6bo6b2o3bo$241bo7b2o7b3o3bo3bobo$241b2o17bo4bo3bobo$266bo3bobob2o$264bob4o2bob2o$263bobo3bobo$263bobo2bo2b2ob2o$264bo3b2o2bobo$272bobo10b2o$273bo11b2o8$268b2o$268b2o$289bo$287b3o$286bo$286b2o165b2o$445b2o5bobo$306bo138bobo4bo$304b3o140bo2b2ob4o$303bo142b2obobobo2bo$290b2o11b2o11bo130bobobobo$290b2o24b3o128bobob2o$319bo128bo$318b2o$333b2o126b2o$258b2o73bo118b2o7bo$258b2o70b2obo118b2o5bobo$329bo2bo126b2o$330b2o$315b2o$279b2o34b2o$279bobo$281bo$281b2o$302b2o3bo141b2o18b2o$302bo3bobo141bo18bo$289b2o12bo3bobo137b3o21bo$288bobo13bo3bobob2o133bo2b3o14b5o$288bo13bob4o2bob2o135bo2bo13bo$287b2o12bobo3bobo138b2o2bobo12b3o$301bobo2bo2b2ob2o139b2o15bo$302bo3b2o2bobo154b4o$310bobo10b2o137b2o3bo3b2o$311bo11b2o137b2o4b3o2bo$470bob2o$447bo22bo$307bo18bo118b3o21b2o$307b3o6b2o6b3o117bo$310bo5b2o5bo120b2o$309b2o12b2o136b2o$461bo$462b3o$322b2o52bo87bo$317b2o3b2o50b3o$317b2o54bo$373b2o59b2o$433bobo5b2o$350bo42bo39bo7b2o$328bo7bo13b3o38b3o38b2o$326b3o5b3o16bo36bo$325bo7bo18b2o23b2o11b2o11bo42bo$325b2o6b2o42b2o24b3o36b2obobo$406bo34bobobobo$405b2o31bo2bobobob2o$420b2o16b4ob2o2bo$420bo21bo4bobo$417b2obo19bobo5b2o$416bo2bo20b2o$343b2o72b2o$343b2o57b2o$366b2o34b2o$366bobo$368bo$356b2o10b2o$356bo32b2o3bo$346b2o9b3o29bo3bobo$347bo11bo16b2o12bo3bobo$346bo28bobo13bo3bobob2o$346b2o27bo13bob4o2bob2o$374b2o12bobo3bobo$388bobo2bo2b2ob2o$389bo3b2o2bobo$397bobo10b2o$398bo11b2o220$431b2o11bo$431b2o10bobo$443bobo2b2o3bo$442b2ob2o2bo2bobo$446bobo3bobo12b2o$442b2obo2b4obo13bo27b2o$442b2obobo3bo13bobo28bo$446bobo3bo12b2o16bo11bo$447bobo3bo29b3o9b2o$448bo3b2o32bo$473b2o10b2o$474bo$474bobo$439b2o34b2o$439b2o57b2o$424b2o72b2o$423bo2bo$422bob2o$422bo$421b2o$436b2o$436bo$437b3o24b2o42b2o6b2o$394b2o43bo11b2o11b2o23b2o18bo7bo$386b2o5bobo56bo36bo16b3o5b3o$386bobo4bo55b3o38b3o13bo7bo$388bo2b2ob4o51bo42bo$387b2obobobo2bo$388bobobobo73b2o$388bobob2o75bo54b2o$389bo76b3o50b2o3b2o$466bo52b2o$402b2o$393b2o7bo$375bo17b2o5bobo115b2o12b2o$375b3o22b2o117bo5b2o5bo$378bo137b3o6b2o6b3o$377b2o137bo18bo2$503b2o11bo$369b2o132b2o10bobo$369bo145bobo2b2o3bo$366b2obo20b2o122b2ob2o2bo2bobo$366bo2b3o4b2o13bo126bobo3bobo12b2o$367b2o3bo3b2o10b3o123b2obo2b4obo13bo$369b4o15bo125b2obobo3bo13bobo$369bo15b2obo129bobo3bo12b2o$370b3o12bobob2o128bobo3bo$373bo13bo3bo128bo3b2o$368b5o14b3o2bo152b2o$368bo21b3o153bo$370bo18bo156bobo$369b2o18b2o120b2o34b2o$511b2o$496b2o$495bo2bo$494bob2o70b2o$494bo73b2o$493b2o$379b2o127b2o$378bobo5b2o120bo$378bo7b2o121b3o24b2o$377b2o132bo11b2o11b2o$524bo$391bo129b3o$387b2obobo128bo$386bobobobo$383bo2bobobob2o146b2o$383b4ob2o2bo148bo$387bo4bobo143b3o$385bobo5b2o143bo$385b2o171b2o$558b2o8$541b2o11bo$541b2o10bobo$553bobo2b2o3bo$552b2ob2o2bo2bobo$556bobo3bobo$552b2obo2b4obo$552b2obobo3bo$556bobo3bo4bo17b2o$557bobo3bo3b3o7b2o7bo$558bo3b2o6bo6bo8bobo$569b2o4bobo9b2o$575b2o2$549b2o$549b2o57b2o$534b2o72b2o$533bo2bo$495bo11b2o23bob2o$494bobo10b2o23bo$486bo3b2o2bobo34b2o$485bobo2bo2b2ob2o48b2o$485bobo3bobo52bo$486bob4o2bob2o49b3o$488bo3bobob2o51bo$487bo3bobo$486bo3bobo$486b2o3bo$472bo90b2o$472b3o89bo$475bo85b3o3bob2o27b2o$474b2o23b2o60bo2b4ob2o27b2o$499b2o63bo$514b2o50b2ob2o2b2o6b2o$513bo2bo50bobo4bo6bobo17b2o11bo$514b2obo49bo2b4o9bo17b2o10bobo$517bo50bobo2bobo7b2o28bobo2b2o3bo$517b2o50bo4b2o36b2ob2o2bo2bobo$430bo31b2o38b2o112bobo3bobo12b2o24b2o$430b3o28bo2bo38bo49bo11b2o45b2obo2b4obo13bo25bo$433bo28b2o36b3o49bobo10b2o45b2obobo3bo13bobo23bobo$432b2o53b2o11bo43bo3b2o2bobo61bobo3bo12b2o24b2o9bo$487bo55bobo2bo2b2ob2o61bobo3bo46b3o$459b2o17b2obo6b3o52bobo3bobo66bo3b2o45bo$460bo17b2ob3o6bo53bob4o2bob2o87b2o24b2o$457b3o24bo61bo3bobob2o88bo$457bo20b2ob3o61bo3bobo92bobo$423bo55bobo62bo3bobo58b2o34b2o$423b3o23b2o22b2o4bobo62b2o3bo59b2o$426bo21bo2bo21bo6bo49bo63b2o$425b2o22b2o23b3o53b3o60bo2bo$476bo56bo58bob2o$532b2o23b2o33bo$557b2o32b2o$572b2o32b2o$571bo2bo31bo$572b2obo31b3o24b2o$575bo33bo11b2o11b2o$435b2o138b2o45bo$435b2o83b2o38b2o57b3o38b2o$519bo2bo38bo57bo34b2o4bobo$520b2o36b3o94bo6bo$545b2o11bo79b2o12b3o7b2o$545bo93bo12bo$517b2o17b2obo6b3o87b3o$518bo17b2ob3o6bo87bo$515b3o24bo$515bo20b2ob3o76bo11b2o$427b2o50bo57bobo77bobo10b2o$422b2o2bo2bo49b3o49b2o4bobo69bo3b2o2bobo$422bo4bobo52bo48bo6bo69bobo2bo2b2ob2o$419b2obo5bo52b2o49b3o73bobo3bobo$420bobob2o108bo74bob4o2bob2o$419bo2bo2bo185bo3bobob2o$419b2o2bo4bo181bo3bobo$424b5o180bo3bobo$609b2o3bo$423bob2o45bo122bo$423b2obo45b3o23b2o95b3o$475bo21bo2bo97bo$474b2o22b2o97b2o23b2o$622b2o$637b2o$636bo2bo$637b2obo$640bo$640b2o$585b2o38b2o$484b2o98bo2bo38bo$484b2o99b2o36b3o$610b2o11bo$610bo$582b2o17b2obo6b3o$583bo17b2ob3o6bo$580b3o24bo$580bo20b2ob3o$602bobo$596b2o4bobo$476b2o118bo6bo$471b2o2bo2bo118b3o$471bo4bobo120bo$468b2obo5bo$469bobob2o$468bo2bo2bo$468b2o2bo4bo$473b5o2$472bob2o$472b2obo7$525bo$525b3o$528bo$527b2o6$518bo$518b3o23b2o$521bo21bo2bo$520b2o22b2o8$530b2o$530b2o9$522b2o$517b2o2bo2bo$517bo4bobo$514b2obo5bo$515bobob2o$514bo2bo2bo$514b2o2bo4bo$519b5o2$518bob2o$518b2obo!", -519, -289)
gld_r_clock = g.parse("b2o$2o$2bo!", 119, 390)
gld_l_clock = g.parse("o$b2o$2o!", -332, -177)

for i in range(6):
	l_gld[i] = g.transform(l_gld[i], -512, -512)
	l_gld[i + 6] = g.transform(l_gld[i+6], 512, 512)


gld_clock = []
gld_clock.extend(gld_l_clock)
gld_clock.extend(gld_r_clock)


l_edge.append(clock_insert)
l_gld.append(gld_clock)

init_location = 75 
def is_there(x, y, gl):
	l = len(gl)
	for i in range(1, l, 2):
		xg = gl[i - 1]
		yg = gl[i]
		
		if g.getcell(x + xg, y + yg) == 0:
			return False
	
	for i in range(1, l, 2):
		xg = gl[i - 1]
		yg = gl[i]
		g.setcell(x + xg, y + yg, 0)
	return True
	

def	is_there_glider(x, y):
	for i in range(4):
		gl = g.evolve(gld, i)
		
		if is_there(x, y, gl):
			return i
			
	return -1
	
def find_and_remove_all():
	cells = g.getcells(g.getrect())
	
	l = len(cells)
	res = []
	
	for i in range(1, l, 2):
		x = cells[i - 1]
		y = cells[i]
		
		for k in range(-1, 2):
			for l in range(-1, 2):
				idx = is_there_glider(x + k, y + l)
				if idx >= 0:
					res.append((x + k, y + l, idx))

	return res

def find_all_glider_idx(gliders_in, ini_rect, count):
	
	gliders_in.sort(key=lambda tup: (1.01 * tup[0] + tup[1]))
	valids = []
	for g_i in range(len(gliders_in)):
		for edge_i in range(len(l_edge)):
			edge_shoot = l_edge[edge_i]
			ini_gld = l_gld[edge_i]
		
			g.new("")
			step_d = 128
			x, y, idx = gliders_in[g_i]
			g.putcells(edge_shoot, x, y)
			g.putcells(g.evolve(ini_gld, idx), x, y)

			for i in range(len(gliders_in)):
				if i == g_i: 
					continue 
					
				x, y, idx = gliders_in[i]
				g.putcells(g.evolve(gld, idx), x - step_d - 512, y + step_d + 512)

			pop = int(g.getpop())
			g.setstep(3)
			g.step()
			g.step()
			g.step()
			g.step()
			g.step()
			g.step()
			g.step()
			
			if count == len(g.getcells([ini_rect[0] + 2 * step_d, ini_rect[1] - 2 * step_d, ini_rect[2], ini_rect[3]])) and (pop ==  int(g.getpop()) or pop - 5 ==  int(g.getpop())):
				valids.append((g_i,edge_i))
				break 

	return valids

def recursive_search(gliders_in, ini_rect, count, depth_trials):
	g.show("remains glider " + str(count/ 10))
	g.update()
	
	valids = find_all_glider_idx(gliders_in, ini_rect, count)	
	
	if len(valids) == 0:
		return False
		
	if len(gliders_in) % 2 == 0:
		valids.reverse()

	g_i,edge_i = valids[0]
	
	if edge_i == 12:
		del valids[0]
		valids.append((g_i, edge_i))
	
	seq = []
	
	for g_i,edge_i in valids:
		if edge_i == 12 and depth_trials < 3:
			continue 
		else:
			depth_trials = 0 
			
		new_gliders_in = []
		for i in range(len(gliders_in)):
			if i == g_i: 
				continue 
			
			new_gliders_in.append(gliders_in[i])
			
		if len(new_gliders_in) > 0:
			recurse_seq, depth_trials = recursive_search(new_gliders_in, ini_rect, count - 10, depth_trials)
			
			if recurse_seq == False:
				depth_trials += 1 
				continue
			
			seq.append((g_i,edge_i))
			
			for j, edge_j in recurse_seq:
				if j >= g_i:
					seq.append((j + 1, edge_j))
				else:
					seq.append((j, edge_j))
			
			return seq, depth_trials
		else:
			return [(g_i,edge_i)], depth_trials
			
	if len(seq) == 0:
		return False, depth_trials
	
	return seq, depth_trials
			
ini_rect = g.getrect()
count = len(g.getcells(ini_rect))
gliders_in = find_and_remove_all()

seq, _ = recursive_search(gliders_in, ini_rect, count, 0)	

g.show(str(seq))

if seq == False:
	g.new("")
	for x, y, idx in gliders_in:
		g.putcells(g.evolve(gld, idx), x, y)
	g.exit("Failed to solve")

step_d = 250

for i,edge_i in seq:
	if edge_i == 12:
		step_d = 350
		
total_d = len(gliders_in) * step_d
g.new("")
total_l = total_d
total_r = total_d

for i,edge_i in seq:
	x, y, idx = gliders_in[i]
	edge_shoot = l_edge[edge_i]
	ini_gld = l_gld[edge_i]

	g.putcells(edge_shoot, x + total_d, y - total_d,)
	
	if edge_i < 6:
		g.putcells(g.evolve(ini_gld, idx), x, y - 2 * total_d)
	elif edge_i < 12:	
		g.putcells(g.evolve(ini_gld, idx), x + 2 * total_d, y)
	else:
		pass
		g.putcells(g.evolve(gld_r_clock, idx), x + 2 * total_d, y)
		g.putcells(g.evolve(gld_l_clock, idx),  x, y - 2 * total_d)
		
	total_d -= step_d
	

User avatar
dvgrn
Moderator
Posts: 5889
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Golly scripts

Post by dvgrn » March 3rd, 2019, 11:32 am

simsim314 wrote:Here is a script that tries to create converter from salvo of gliders to something there is already script for - well separated gliders. I think I've modified chris_c script and added something to it - not sure exactly what (probably the construction order is dynamic - and more effort is done to make it smaller).

Just wanted to drop it here, not sure I posted on the forum.
You did post it once, in the Construction Practice thread, but here is probably a better place for it.

It would probably help a lot if all of these utility scripts had a name on a header line -- just

# well-separated-gliders-to-salvo.py

or something like that -- and if that exact name also got consistently mentioned in the text of post. That would make it a bit easier to find scripts with at least a Google search.

The forums' search function has two horrible problems as far as script searching goes. One is that if you try to search for a word with hyphens in it, it falsely claims that the word is never used on the forums -- but if you take out the hyphens and search for the separate words, it can find what you're looking for after all.

The other is that searches don't find anything that's inside

Code: Select all

 boxes. (!)

I've started to make a habit of checking in scripts like this to [url=https://github.com/dvgrn/b3s23life]a new subfolder on GitHub[/url] whenever I finish one, or whenever I find myself having to dig one up from an old forum posting.  Maybe you could make a Utility Scripts area somewhere and do the same?  Or if you like the looks of my b3s23life repository, I'll certainly accept contributions.

Post Reply