ConwayLife.com - A community for Conway's Game of Life and related cellular automata
Home  •  LifeWiki  •  Forums  •  Download Golly

Golly scripts

For scripts to aid with computation or simulation in cellular automata.

Re: Golly scripts

Postby 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?
bobo2b3o2b2o2bo3bobo$obobobo3bo2bobo3bobo$obobob2o2bo2bobo3bobo$o3bobo3bo2bobobobo$o3bob3o2b2o3bobo2bo!
User avatar
SuperSupermario24
 
Posts: 119
Joined: July 22nd, 2014, 12:59 pm
Location: Within the infinite expanses of the Life universe

Re: Golly scripts

Postby 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.
User avatar
Andrew
Moderator
 
Posts: 678
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia

Re: Golly scripts

Postby 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

## 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

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
shouldsee
 
Posts: 406
Joined: April 8th, 2016, 8:29 am

Re: Golly scripts

Postby 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.

# 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.
wildmyron
 
Posts: 845
Joined: August 9th, 2013, 12:45 am

Re: Golly scripts

Postby 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!).
# 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")
wildmyron
 
Posts: 845
Joined: August 9th, 2013, 12:45 am

Re: Golly scripts

Postby 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
#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
gameoflifemaniac
 
Posts: 662
Joined: January 22nd, 2017, 11:17 am
Location: There too

Re: Golly scripts

Postby 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

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.)
User avatar
dvgrn
Moderator
 
Posts: 4845
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI

Re: Golly scripts

Postby 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
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.
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!
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!
M. I. Wright
 
Posts: 356
Joined: June 13th, 2015, 12:04 pm

Previous

Return to Scripts

Who is online

Users browsing this forum: No registered users and 2 guests