Script request thread

For scripts to aid with computation or simulation in cellular automata.
User avatar
dvgrn
Moderator
Posts: 10670
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Script request thread

Post by dvgrn » April 21st, 2018, 5:45 pm

danny wrote:...I'm wondering if there's anything I could optimize besides the 'if str(x).find("0") == -1 and str(y).find("0") == -1:' line.
Sure, I can think of a few things, and I'm sure other people can come up with more.

First, you're doing the str(x) and str(y) conversions twice each, and it's easy to cut that in half by changing how you're doing the iteration. Probably something like this would be a little faster, but you'd have to do some speed trials to see how much:

Code: Select all

    for i in str(x):
      cx *= int(i)

    for i in str(y):
      cy *= int(i)
Then, an even less consequential improvement, you might possibly shave some tiny fractional seconds off with the right kind of lookup table. A dictionary might be the best:

Code: Select all

lookup=dict()
for i in "0123456789":
  lookup[i]=int(i)
Or you could try a simple list, and the ASCII value of each character:

Code: Select all

lookup=[0]*256
for i in "123456789":
  lookup[ord(i)]=int(i)
But then you'd need to use cx *= lookup[ord(i)] instead of just cx *= lookup, so again you'd have to run time trials to see which is better.

Or again, try doing everything numerically instead of converting to a string -- like

Code: Select all

k=x
while k>0:
  digit=k%10
  k-=digit
  k/=10
  # multiply cx by digit, etc.
String really are pretty slow sometimes compared to numeric calculations -- but I don't know if this is one of those times or not.

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Script request thread

Post by dani » April 24th, 2018, 5:50 pm

An update! I did something pretty clever, I removed the 'if str' statement and appended this at the end of the loop:

Code: Select all

  while "0" in str(x):
    x += 10 ** (len(str(x)) - str(x).find("0") - 1)
Overall it's still shorter due to the if statement being removed, in favour of this faster alternative, as well as faster because it doesn't waste time counting like 5000000...5000001...5000002, it goes 5000000...5111111...5111112

EDIT: replaced 'str(x).find("0") != -1' with 'while "0" in str(x)' as of M.I. Wright's request

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Script request thread

Post by dani » April 25th, 2018, 10:18 am

Hey, I'm having trouble with this:

Code: Select all

# powerz.py v1.99
# dani, 2018
# checks what number x gives when you multiply its digits, then compares it against what it gives when you multiply its square's digits. if they are equal, it prints and writes to results.txt (check that file for my search progress! <3)
# also special thanks to @schimatt for pretty much all of this, and helping me learn python in general c:
# this was my first real python script
with open("powerz_results.txt", "r") as res:
  x = int((list(res)[-1])) + 1
  num_res = sum(1 for line in open("powerz_results.txt"))
  print("last result:", x - 1,"is applicable. (#" + str(num_res) + ")")
# x = 0
while True:
  y = x ** 2
  cx = 1
  cy = 1
  for i in str(x):
    cx *= int(i)

  for i in str(y):
    cy *= int(i)

  if cx == cy:
    num_res += 1
    print(str(x) + " is applicable. (#" + str(num_res) + ", " + str(cx) + "==" + str(cy) + ")")
    with open("powerz_results.txt", "a") as res:
      res.write("\n" + str(x))
  x += 1
  while "0" in str(x):
    x += 10 ** (len(str(x)) - str(x).find("0") - 1)
    # print(x)
For example, with the number 53489358889, it counts by:

Code: Select all

last result: 53489358889 is applicable. (#1)
53489358890 is applicable. (#2, 0==0)
53489358910
53489358911
53489358921
53489358931
53489358941
53489358951
53489358961
53489358971
53489358981
53489358991
53489359100
53489359110
53489359111
Note the false positive 53489358890, and the fact that it counts by 10's when there are no 0's. It was working fine earlier, what gives?

EDIT: Oh, I'm stupid :( I set it only to print after incrementing by 10s, so it's working correctly. Disregard this post.
EDIT2: Added another loop to skip everything else if 0 in x ** 2.

User avatar
thorsilver
Posts: 13
Joined: March 31st, 2018, 9:43 pm
Location: Glasgow, Scotland

Re: Script request thread

Post by thorsilver » April 26th, 2018, 12:50 am

If I've generated long binary strings that equate to initial conditions for Wolfram Rule 110, is there a script out there that can convert this to an RLE or MC? Basically just translating a long binary string into a long line of on/off cells.

Naszvadi
Posts: 1248
Joined: May 7th, 2016, 8:53 am
Contact:

Re: Script request thread

Post by Naszvadi » April 26th, 2018, 6:14 am

thorsilver wrote:If I've generated long binary strings that equate to initial conditions for Wolfram Rule 110, is there a script out there that can convert this to an RLE or MC? Basically just translating a long binary string into a long line of on/off cells.
test it pls, might be buggy - in perl. I can rewrite it in any lang. like bash/ksh/sh, python etc:

Code: Select all

user@errorlevel:0:~$ echo ooooooobbbbbbbbbobobbbbbb | perl -pe 's((([ob])\2+))(length($1).$2)eg'
7o9bobo6b

User avatar
thorsilver
Posts: 13
Joined: March 31st, 2018, 9:43 pm
Location: Glasgow, Scotland

Re: Script request thread

Post by thorsilver » April 26th, 2018, 8:54 am

Naszvadi wrote:
thorsilver wrote:If I've generated long binary strings that equate to initial conditions for Wolfram Rule 110, is there a script out there that can convert this to an RLE or MC? Basically just translating a long binary string into a long line of on/off cells.
test it pls, might be buggy - in perl. I can rewrite it in any lang. like bash/ksh/sh, python etc:

Code: Select all

user@errorlevel:0:~$ echo ooooooobbbbbbbbbobobbbbbb | perl -pe 's((([ob])\2+))(length($1).$2)eg'
7o9bobo6b
Thanks for this -- sadly I can't get it to work, I know nothing at all about Perl :)

Any chance of a version in Python?

Naszvadi
Posts: 1248
Joined: May 7th, 2016, 8:53 am
Contact:

Re: Script request thread

Post by Naszvadi » April 26th, 2018, 12:00 pm

thorsilver wrote:
Naszvadi wrote:
thorsilver wrote:If I've generated long binary strings that equate to initial conditions for Wolfram Rule 110, is there a script out there that can convert this to an RLE or MC? Basically just translating a long binary string into a long line of on/off cells.
test it pls, might be buggy - in perl. I can rewrite it in any lang. like bash/ksh/sh, python etc:

Code: Select all

user@errorlevel:0:~$ echo ooooooobbbbbbbbbobobbbbbb | perl -pe 's((([ob])\2+))(length($1).$2)eg'
7o9bobo6b
Thanks for this -- sadly I can't get it to work, I know nothing at all about Perl :)

Any chance of a version in Python?
Here you are:

Code: Select all

# mandatory begin
import re
# mandatory end

# example variable
a='bbbbbbbboooooooooobobbbbbbbbbobbbbbbbb'

# mandatory begin
def squeezerepl(matchobj):
    return (str(len(matchobj.group(0))) + matchobj.group(0)[0])
# mandatory end

print "### DEBUG ### original a = '%s'" % (a)

# mandatory begin
b = re.sub(r'(([bo])\2+)', squeezerepl, a)
# mandatory end

print "### DEBUG ### modified a = '%s'" % (b)

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

Re: Script request thread

Post by dvgrn » April 26th, 2018, 12:27 pm

Naszvadi wrote:
thorsilver wrote:Any chance of a version in Python?
Here you are...
...
a='bbbbbbbboooooooooobobbbbbbbbbobbbbbbbb'
...
Once you have the string in that form with b's and o's, it's perfectly valid RLE already.

Here's a "Golly Python" implementation starting from a standard binary string like '00000000111111111101000000000100000000'. It might do slightly odd things if you have characters other than 0s and 1s in the input string, but Golly usually just tries to ignore non-standard characters so it will usually come out okay. Just copy the script below and choose File > Run Clipboard in Golly:

Code: Select all

import golly as g
a=g.getstring("Enter binary string for W110 initial conditions: ")
if len(a):
  g.new("W110") # or g.addlayer() if you don't want to overwrite the current layer's pattern
  g.setrule("W110")
  g.select([0,0,len(a),1])
  g.putcells(g.parse(a.replace("0","b").replace("1","o")))
  g.fit()
If you want to get RLE back out again as a string in canonical form, you could add another line to save the file:

Code: Select all

  g.save("C:/Temp/W110initial.rle","rle")
-- or modify Naszvadi's script, if you're just converting and storing lots of RLE strings and don't want to use Golly to simulate the patterns.

User avatar
thorsilver
Posts: 13
Joined: March 31st, 2018, 9:43 pm
Location: Glasgow, Scotland

Re: Script request thread

Post by thorsilver » April 26th, 2018, 2:59 pm

Thanks folks! :) Apologies for my cluelessness about RLE files. In the end I combined inspiration from both of your scripts. I found that both the g.getstring() and g.getclipstr() top out at about 125,000 characters, so I had to get something working on the command line. Then I found that I needed the RLE to constrain the width of the pattern, otherwise the left edge would expand and fire gliders off to the right, which would totally muck up the cyclic tag system implementations I was hoping to simulate with this!

So I fixed that and messed around a bit and this is the result:

Code: Select all

with open('initial_conditions.txt', 'r') as myfile:
    a=myfile.read()

b = a.replace("1", "o").replace("0", "b")

x = len(a)

with open("W110 initial conditions.rle", "w") as text_file:
    text_file.write("x = 0, y = 0, rule = W110:T{},0\n".format(x))
    text_file.write(b)
It takes a text file of binary digits and outputs an RLE that loads into Golly correctly, loads the W110 rule and constrains the pattern width to avoid spurious gliders. Perfect! 8)

Thank you both again!

As a bonus I've attached a W110 cyclic tag system pattern that this script just produced. This is a CTS taken from Wolfram's book which is defined by this rule: [{{1, 1, 0, 1, 0, 1}, {1, 1, 0, 0, 0, 0}}, {0, 1}]. The left side of the pattern is sparse, with just a few packs of gliders set up to modify the data which is a series of vertical gliders in the middle, with other gliders defining the CTS rule on the right side of the pattern. This simple CTS takes up 219,205 cells!

Of course I also generated a W110 unit cell version that runs in GoL -- I'll put that in my thread on W110 unit cell patterns in the Patterns forum, in case anyone wants to see it.

EDIT: Made a slight tweak to the script, so if you generate a W110 Cyclic Tag System initial condition in Mathematica as I did, you can save that output in a text file called 'initial_conditions_W110.txt' and run the script below in the same folder to generate an RLE for the corresponding W110 pattern. It's very quick-and-dirty and could be cleaned up a bunch, but it works. Made a different script as well that generates W110 unit cell patterns directly from the same Mathematica output, which I posted in the other thread.

Code: Select all

import re

with open('initial_conditions_W110.txt', 'r') as myfile:
    a=myfile.read()

b = re.sub('[{,} ]', '', a)
b = b.replace('\r', '')

b = a.replace("1", "o").replace("0", "b")

x = len(a)

with open("W110 initial conditions.rle", "w") as text_file:
    text_file.write("x = 0, y = 0, rule = W110:T{},0\n".format(x))
    text_file.write(b)
Attachments
W110 initial conditions CTS 1.rle
(214.1 KiB) Downloaded 297 times

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Script request thread

Post by dani » May 7th, 2018, 7:06 pm

Hi! I'm looking for some help on this:

Code: Select all

def isprime(m): # UGLY LOOP SANDWICH INCOMING
	prime = True
	for i in range(2, int(m ** .5) + 1):
		if m % i == 0:
			prime = i
			break
	return prime

n = 3 # NUMBER YOU WANT TO TEST
m = 50 # EXPONENT BEING USED

# ...

isprime(n ** m - (2 ** m - 1)) # basically just 3 ** 50
It returns a float error, for being too large. How can I remedy this? Can I remedy this? I've tried using Decimal, but not numpy yet. Does numpy work with big numbers like this?

User avatar
praosylen
Posts: 2443
Joined: September 13th, 2014, 5:36 pm
Location: Pembina University, Home of the Gliders
Contact:

Re: Script request thread

Post by praosylen » May 7th, 2018, 8:50 pm

dani wrote:Hi! I'm looking for some help on this:

Code: Select all

def isprime(m): # UGLY LOOP SANDWICH INCOMING
	prime = True
	for i in range(2, int(m ** .5) + 1):
		if m % i == 0:
			prime = i
			break
	return prime

n = 3 # NUMBER YOU WANT TO TEST
m = 50 # EXPONENT BEING USED

# ...

isprime(n ** m - (2 ** m - 1)) # basically just 3 ** 50
It returns a float error, for being too large. How can I remedy this? Can I remedy this? I've tried using Decimal, but not numpy yet. Does numpy work with big numbers like this?
I'm not sure, but I suspect the range function can't handle numbers larger than 2^64 for whatever reason. Replacing the for loop with a while loop works for me, and gives a dramatic speedup to boot:

Code: Select all

def isprime(m): # UGLY BLM (BOOLEAN OPERATOR, LOOP, AND MATH) SANDWICH INCOMING
   q = int(m ** .5) + 1
   i = 2
   while i < q and m % i:
       i += 1
   return i == q or i

n = 3 # NUMBER YOU WANT TO TEST
m = 50 # EXPONENT BEING USED

# ...

isprime(n ** m - (2 ** m - 1)) # basically just 3 ** 50
Obviously there are much faster primality testing algorithms out there, most of which I've probably never heard of. Also, for odd n the number you're testing will always be composite, since n^m-2^m+1 is always divisible by 2 for odd m.
Last edited by praosylen on April 23rd, 2020, 1:20 am, edited 1 time in total.
former username: A for Awesome
praosylen#5847 (Discord)

The only decision I made was made
of flowers, to jump universes to one of springtime in
a land of former winter, where no invisible walls stood,
or could stand for more than a few hours at most...

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Script request thread

Post by dani » May 7th, 2018, 9:26 pm

Thanks for that, although I should have clarified that this is the error I'm getting:

Code: Select all

  File "D:\Users\Dan\Desktop\python\pricol.py", line 6, in isprime
    q = int(m ** .5) + 1
OverflowError: int too large to convert to float

User avatar
calcyman
Moderator
Posts: 2936
Joined: June 1st, 2009, 4:32 pm

Re: Script request thread

Post by calcyman » May 7th, 2018, 9:59 pm

If it's too large to convert to a float, you want to be using Miller-Rabin (or AKS, but that's slower and harder) for primality testing.
What do you do with ill crystallographers? Take them to the mono-clinic!

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

Re: Script request thread

Post by dvgrn » May 8th, 2018, 10:37 am

calcyman wrote:If it's too large to convert to a float, you want to be using Miller-Rabin (or AKS, but that's slower and harder) for primality testing.
To clarify -- one of the biggest reasons why you want to use something other than the naive prime-testing algorithm is that you can't possibly run a Python for loop from 2 even as far as 10^19 in practice, let alone 10^38, even if the conversion worked.

9223372036854775807, about 10^19, is I think the maximum 64-bit integer that Python can store. Depending on the Python version, you might be able to get silent conversion to long or arbitrary precision ints, or you could use Decimal or some other arbitrary-precision type. But there's really no point because 10^19 is already too big.

(-- Yes, I use Golly as my experimental IDE...)

Code: Select all

import golly as g
import time
s = time.time()
prime = True

maximumint = 9223372036854775807
m=99999999977
for i in xrange(3,10**8,2):
  if m % i == 0:
    prime = i
    break
#  if i%10000==1: g.show(str(i))
g.note("Prime-testing maximumint will take " + str((time.time()-s)*10**11/60/60/24/365)+" years, give or take a few.")
On my system, according to the above Ultra Scientfic Test Script, testing even a small number like 9223372036854775807 for primality will take just under 30,000 years.

If you don't change "range" to "xrange" in Python 2, by the way, the script will never even get started, and it's worth looking up and understanding the reason why. Basically range builds a list of all the numbers in the range before it starts... Python 3 isn't that silly -- range in Python 3 is the same as xrange in Python 2.

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Script request thread

Post by dani » May 12th, 2018, 6:03 pm

Can I have some pointers on computing these numbers? A partial script would be nice, but I only really need pointers on how to do a brute force script

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Script request thread

Post by dani » July 4th, 2018, 2:07 am

Okay, this time I've computed everything pretty much right, but the problem is that it balloons to 1.6 GB of RAM for some reason. Can I fix this in any way or should I just choose a different algorithm?:

Code: Select all

# CALCULATES THE SUNRISE SEQUENCE FOR N
# VERY MESSY

from sympy.ntheory import factorint
import operator

m = 12

def readable(n): # changes script output
	# get all your ducks in a row
	# organize factors in order least to greatest to improve readability
	n = sorted(n.items(), key=operator.itemgetter(0))
	n = str(n)
	# replacements for output
	n = n.replace(", 1","")
	n = n.replace("), ("," * ")
	n = n.replace("[","")
	n = n.replace("]","")
	n = n.replace("(","")
	n = n.replace(")","")
	n = n.replace(", ","^")
	return n

def nextterm(n):
	# very nice method from my flamingo.py on repl.it
	y = ""
	n = str(n)
	for i in range(0,10):
		for j in range(0,n.count(str(9 - i))):
			y += str(9 - i)
	return y

while True:
	z = readable(factorint(int(m)))
	print(str(m) + " = " + z)
	with open("sunres.txt","a+") as sunres:
		sunres.write(str(m) + "	" + z + "\n")
	m = nextterm(z)
Apologies for future posters for turning this into script help thread, but it hasn't seen much use...

User avatar
KittyTac
Posts: 535
Joined: December 21st, 2017, 9:58 am

Re: Script request thread

Post by KittyTac » August 21st, 2018, 10:54 pm

Can anyone make me a script that rotates JvN patters, arrows included?

User avatar
Redstoneboi
Posts: 429
Joined: May 14th, 2018, 3:57 am

Re: Script request thread

Post by Redstoneboi » August 22nd, 2018, 4:59 am

And also flip them (even though it changes the behaviour, just do it)
c(>^w^<c)~*
This is 「Fluffy」
「Fluffy」is my sutando.
「Fluffy」has the ability to engineer r e p l i c a t o r s.
「Fluffy」likes to watch spaceship guns in Golly.
「Fluffy」knows Natsuki best girl.

User avatar
KittyTac
Posts: 535
Joined: December 21st, 2017, 9:58 am

Re: Script request thread

Post by KittyTac » August 22nd, 2018, 5:51 am

Redstoneboi wrote:And also flip them (even though it changes the behaviour, just do it)
I don't think that Hutton32 patterns would change behavior when rotated. And Hutton32 is the superior one anyways.

wildmyron
Posts: 1544
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Script request thread

Post by wildmyron » September 10th, 2018, 1:31 pm

dvgrn wrote:Maybe a script would be a good idea: when run in Golly when the current universe is a two-state isotropic rule on a bounded P{x,y} grid, the script could generate the equivalent custom rule with an extra "permanent dead" state, and line an (x,y) rectangle with those cells.
I misinterpreted what you were looking for because of the context in which it was asked. But I thought this was a nice idea anyway, so here is iso-permaOn-gen.py

It's a bit of a hack based on isotropic-rule.py and as such the comments are a bit dodgy and sparse.
It generates a 3 state rule with permaOn cells and then optionally draws a boundary of permaOn cells on the border of a bounded grid.

Code: Select all

# iso-permaOn-gen.py
#
# Derived from isotropic-rule.py / isotropicRulegen.py 
# which itself was derived from apgsearch v1.0
# 
# Generate a rule table for an isotropic rule using Alan Hensel's
# isotropic, non-totalistic rule format for CA on the Moore neighbourhood
# Adds a third state representing permanently On cells
# 
# Includes code written by: Eric Goldstein, Adam P. Goucher, Dave Green, Arie Paap, and Andrew Trevorrow

import golly as g
import os

# Generates the helper rules for apgsearch-isotropic, given a base isotropic
# rule in Hensel's notation.
class RuleGenerator:

    # notationdict adapted from Eric Goldstein's HenselNotation->Ruletable(1.3).py
    # Modified for neighbourhood2 version of Hensel's notation
    notationdict = {
        "0"  : [0,0,0,0,0,0,0,0],   #   
        "1e" : [1,0,0,0,0,0,0,0],   #   N
        "1c" : [0,1,0,0,0,0,0,0],   #   NE
        "2a" : [1,1,0,0,0,0,0,0],   #   N,  NE
        "2e" : [1,0,1,0,0,0,0,0],   #   N,  E
        "2k" : [1,0,0,1,0,0,0,0],   #   N,  SE
        "2i" : [1,0,0,0,1,0,0,0],   #   N,  S
        "2c" : [0,1,0,1,0,0,0,0],   #   NE, SE
        "2n" : [0,1,0,0,0,1,0,0],   #   NE, SW
        "3a" : [1,1,1,0,0,0,0,0],   #   N,  NE, E
        "3n" : [1,1,0,1,0,0,0,0],   #   N,  NE, SE
        "3r" : [1,1,0,0,1,0,0,0],   #   N,  NE, S
        "3q" : [1,1,0,0,0,1,0,0],   #   N,  NE, SW
        "3j" : [1,1,0,0,0,0,1,0],   #   N,  NE, W
        "3i" : [1,1,0,0,0,0,0,1],   #   N,  NE, NW
        "3e" : [1,0,1,0,1,0,0,0],   #   N,  E,  S
        "3k" : [1,0,1,0,0,1,0,0],   #   N,  E,  SW
        "3y" : [1,0,0,1,0,1,0,0],   #   N,  SE, SW
        "3c" : [0,1,0,1,0,1,0,0],   #   NE, SE, SW
        "4a" : [1,1,1,1,0,0,0,0],   #   N,  NE, E,  SE
        "4r" : [1,1,1,0,1,0,0,0],   #   N,  NE, E,  S
        "4q" : [1,1,1,0,0,1,0,0],   #   N,  NE, E,  SW
        "4i" : [1,1,0,1,1,0,0,0],   #   N,  NE, SE, S
        "4y" : [1,1,0,1,0,1,0,0],   #   N,  NE, SE, SW
        "4k" : [1,1,0,1,0,0,1,0],   #   N,  NE, SE, W
        "4n" : [1,1,0,1,0,0,0,1],   #   N,  NE, SE, NW
        "4z" : [1,1,0,0,1,1,0,0],   #   N,  NE, S,  SW
        "4j" : [1,1,0,0,1,0,1,0],   #   N,  NE, S,  W
        "4t" : [1,1,0,0,1,0,0,1],   #   N,  NE, S,  NW
        "4w" : [1,1,0,0,0,1,1,0],   #   N,  NE, SW, W
        "4e" : [1,0,1,0,1,0,1,0],   #   N,  E,  S,  W
        "4c" : [0,1,0,1,0,1,0,1],   #   NE, SE, SW, NW
        "5i" : [1,1,1,1,1,0,0,0],   #   N,  NE, E,  SE, S
        "5j" : [1,1,1,1,0,1,0,0],   #   N,  NE, E,  SE, SW
        "5n" : [1,1,1,1,0,0,1,0],   #   N,  NE, E,  SE, W
        "5a" : [1,1,1,1,0,0,0,1],   #   N,  NE, E,  SE, NW
        "5q" : [1,1,1,0,1,1,0,0],   #   N,  NE, E,  S,  SW
        "5c" : [1,1,1,0,1,0,1,0],   #   N,  NE, E,  S,  W
        "5r" : [1,1,0,1,1,1,0,0],   #   N,  NE, SE, S,  SW
        "5y" : [1,1,0,1,1,0,1,0],   #   N,  NE, SE, S,  W
        "5k" : [1,1,0,1,0,1,1,0],   #   N,  NE, SE, SW, W
        "5e" : [1,1,0,1,0,1,0,1],   #   N,  NE, SE, SW, NW
        "6a" : [1,1,1,1,1,1,0,0],   #   N,  NE, E,  SE, S,  SW
        "6c" : [1,1,1,1,1,0,1,0],   #   N,  NE, E,  SE, S,  W
        "6k" : [1,1,1,1,0,1,1,0],   #   N,  NE, E,  SE, SW, W
        "6e" : [1,1,1,1,0,1,0,1],   #   N,  NE, E,  SE, SW, NW
        "6n" : [1,1,1,0,1,1,1,0],   #   N,  NE, E,  S,  SW, W
        "6i" : [1,1,0,1,1,1,0,1],   #   N,  NE, SE, S,  SW, NW
        "7c" : [1,1,1,1,1,1,1,0],   #   N,  NE, E,  SE, S,  SW, W
        "7e" : [1,1,1,1,1,1,0,1],   #   N,  NE, E,  SE, S,  SW, NW
        "8"  : [1,1,1,1,1,1,1,1],   #   N,  NE, E,  SE, S,  SW, W,  NW
        }
   
    allneighbours = [ 
        ["0"],
        ["1e", "1c"],
        ["2a", "2e", "2k", "2i", "2c", "2n"],
        ["3a", "3n", "3r", "3q", "3j", "3i", "3e", "3k", "3y", "3c"],
        ["4a", "4r", "4q", "4i", "4y", "4k", "4n", "4z", "4j", "4t", "4w", "4e", "4c"],
        ["5i", "5j", "5n", "5a", "5q", "5c", "5r", "5y", "5k", "5e"],
        ["6a", "6c", "6k", "6e", "6n", "6i"],
        ["7c", "7e"],
        ["8"],
        ]
       
    allneighbours_flat = [n for x in allneighbours for n in x]
   
    numneighbours = len(notationdict)
   
    # Use dict to store rule elements, initialised by setrule():
    bee = {}
    ess = {}
    alphanumeric = ""
    rulename = ""
   
    # Save the isotropic rule
    def saveAllRules(self):   
        self.saveIsotropicRule()
   
    # Interpret birth or survival string
    def ruleparts(self, part):

        inverse = False
        nlist = []
        totalistic = True
        rule = { k: False for k, v in self.notationdict.iteritems() }
       
        # Reverse the rule string to simplify processing
        part = part[::-1]
       
        for c in part:
            if c.isdigit():
                d = int(c)
                if totalistic:
                    # Add all the neighbourhoods for this value
                    for neighbour in self.allneighbours[d]:
                        rule[neighbour] = True
                elif inverse:
                    # Add all the neighbourhoods not in nlist for this value
                    for neighbour in self.allneighbours[d]:
                        if neighbour[1] not in nlist:
                            rule[neighbour] = True
                else:
                    # Add all the neighbourhoods in nlist for this value
                    for n in nlist:
                        neighbour = c + n
                        if neighbour in rule:
                            rule[neighbour] = True
                        else:
                            # Error
                            return {}
                   
                inverse = False
                nlist = []
                totalistic = True

            elif (c == '-'):
                inverse = True

            else:
                totalistic = False
                nlist.append(c)
       
        return rule

    # Set isotropic, non-totalistic rule
    # Adapted from Eric Goldstein's HenselNotation->Ruletable(1.3).py
    def setrule(self, rulestring):
   
        # neighbours_flat = [n for x in neighbours for n in x]
        b = {}
        s = {}
        sep = ''
        birth = ''
        survive = ''
       
        rulestring = rulestring.lower()
       
        if '/' in rulestring:
            sep = '/'
        elif '_' in rulestring:
            sep = '_'
        elif (rulestring[0] == 'b'):
            sep = 's'
        else:
            sep = 'b'
       
        survive, birth = rulestring.split(sep)
        if (survive[0] == 'b'):
            survive, birth = birth, survive
        survive = survive.replace('s', '')
        birth = birth.replace('b', '')
       
        b = self.ruleparts(birth)
        s = self.ruleparts(survive)

        if b and s:
            self.alphanumeric = 'B' + birth + 'S' + survive
            self.rulename = 'B' + birth + 'S' + survive
            self.bee = b
            self.ess = s
        else:
            # Error
            g.note("Unable to process rule definition.\n" +
                    "b = " + str(b) + "\ns = " + str(s))
            g.exit()
           

    # Save a rule file:
    def saverule(self, name, comments, table, colours):
       
        ruledir = g.getdir("rules")
        filename = ruledir + name + ".rule"

        results = "@RULE " + name + "\n\n"
        results += "*** File autogenerated by saverule. ***\n\n"
        results += comments
        results += "\n\n@TABLE\n\n"
        results += table
        results += "\n\n@COLORS\n\n"
        results += colours
        results += "\n\n@ICONS\n\n"
        results += "circles\n"

        try:
            f = open(filename, 'w')
            f.write(results)
            f.close()
        except:
            g.warn("Unable to create rule table:\n" + filename)

    # Defines a variable:
    def newvar(self, name, vallist):

        line = "var "+name+"={"
        for i in xrange(len(vallist)):
            if (i > 0):
                line += ','
            line += str(vallist[i])
        line += "}\n"

        return line

    # Defines a block of equivalent variables:
    def newvars(self, namelist, vallist):

        block = "\n"

        for name in namelist:
            block += self.newvar(name, vallist)

        return block

    def scoline(self, chara, charb, left, right, amount):

        line = str(left) + ","

        for i in xrange(8):
            if (i < amount):
                line += chara
            else:
                line += charb
            line += chr(97 + i)
            line += ","

        line += str(right) + "\n"

        return line

    def isotropicline(self, chara, charb, left, right, n):

        line = str(left) + ","
        neighbours = self.notationdict[n]
       
        for i in xrange(8):
            if neighbours[i]:
                line += chara
            else:
                line += charb
            line += chr(97 + i)
            line += ","

        line += str(right) + "\n"

        return line
       
    def savePermaOnRule(self):
   
        comments = """
This is a two state, isotropic, non-totalistic rule on the Moore neighbourhood.
This rules add a third state which acts as a permanently On cell.
"""

        table = """
n_states:3
neighborhood:Moore
symmetries:rotate4reflect
"""

        table += self.newvars(["a","b","c","d","e","f","g","h"], [0, 1, 2])
        table += self.newvars(["da","db","dc","dd","de","df","dg","dh"], [0])
        table += self.newvars(["la","lb","lc","ld","le","lf","lg","lh"], [1, 2])

        table += "\n# Birth\n"
        for n in self.allneighbours_flat:
            if self.bee[n]:
                table += self.isotropicline('l', 'd', 0, 1, n)
       
        table += "\n# Survival\n"
        for n in self.allneighbours_flat:
            if self.ess[n]:
                table += self.isotropicline('l', 'd', 1, 1, n)

        table += "\n# Death\n"
        table += self.scoline("","",1,0,0)
       
        colours = """
1 255   0   0
2   0  64 255
"""
        self.saverule(self.rulename, comments, table, colours)


# Adapted from Golly's draw-lines.py by Andrew Trevorrow
def drawline(x1, y1, x2, y2, drawstate):
    # draw a line of cells from x1,y1 to x2,y2 using Bresenham's algorithm;
    if x1 == x2 and y1 == y2:
        return
    
    dx = x2 - x1
    ax = abs(dx) * 2
    sx = 1
    if dx < 0: sx = -1
    dy = y2 - y1
    ay = abs(dy) * 2
    sy = 1
    if dy < 0: sy = -1

    if ax > ay:
        d = ay - (ax / 2)
        while x1 != x2:
            g.setcell(x1, y1, drawstate)
            if d >= 0:
                y1 += sy
                d -= ax
            x1 += 2*sx
            d += ay
    else:
        d = ax - (ay / 2)
        while y1 != y2:
            g.setcell(x1, y1, drawstate)
            if d >= 0:
                x1 += sx
                d -= ay
            y1 += 2*sy
            d += ax

    g.setcell(x2, y2, drawstate)
    return

# Test the current rule to see if it can be used
bounds = ''
if g.getalgo() in ['QuickLife', 'HashLife']:
    rulestring = g.getrule()
    if ':' in rulestring:
        rulestring, bounds = rulestring.split(':')
else:
    rulestring = 'B3S23'
    
rulestring = g.getstring("This script generates a rule table for an isotropic 2-state CA\n" +
                         "with an added third state which act as permanently On cells\n" +
                         "Enter rule string in Alan Hensel's isotropic rule notation",
                         rulestring)

rg = RuleGenerator()
rg.setrule(rulestring)

rg.rulename = "PermaOn_" + rg.rulename
rg.savePermaOnRule()

if bounds and (bounds[0] == 'P'):
    wd, ht = map(int, bounds[1:].split(','))
    g.show(str([wd,ht]))
else:
    size = g.getstring("Enter size of bounded grid to create, 0 to skip:", "0")
    try:
        wd = int(size)
        ht = wd
    except:
        wd, ht = 0, 0

if wd and ht:
    g.setrule("%s:P%d,%d" % (rg.rulename, wd, ht))
    # Draw a boundary of PermaOn cells
    left, right, top, bottom = -wd/2+wd%2, wd/2-1+wd%2, -ht/2+ht%2, ht/2-1+ht%2
    drawline(left, top, right, top, 2)
    drawline(right, top, right, bottom, 2)
    drawline(right, bottom, left, bottom, 2)
    drawline(left, bottom, left, top, 2)
else:
    g.setrule(rg.rulename)
To modify this to get the originally requested script would probably only require a few changes:

In savePermOnRule(), change

Code: Select all

        table += self.newvars(["da","db","dc","dd","de","df","dg","dh"], [0])
        table += self.newvars(["la","lb","lc","ld","le","lf","lg","lh"], [1, 2])
to

Code: Select all

        table += self.newvars(["da","db","dc","dd","de","df","dg","dh"], [0, 2])
        table += self.newvars(["la","lb","lc","ld","le","lf","lg","lh"], [1])
and where the boundary is drawn, change

Code: Select all

    g.setrule("%s:P%d,%d" % (rg.rulename, wd, ht))
to

Code: Select all

    g.setrule(rg.rulename)
    wd = wd + 2
    ht = ht + 2
Some comments and other bits of text should also be updated if it were to be published, and then I realised it made more sense for the script to create a four state rule with both permaOn and permaOff cells. Then one script could simulate bounded grids with outer space being either all On cells or all Off cells without needing a bounded grid in Golly.
Last edited by wildmyron on October 7th, 2019, 3:38 am, edited 1 time in total.
The 5S project (Smallest Spaceships Supporting Specific Speeds) is now maintained by AforAmpere. The latest collection is hosted on GitHub and contains well over 1,000,000 spaceships.

Semi-active here - recovering from a severe case of LWTDS.

User avatar
KittyTac
Posts: 535
Joined: December 21st, 2017, 9:58 am

Re: Script request thread

Post by KittyTac » September 24th, 2018, 7:53 am

How about an Energetic-izer script? (Energetic is a rule in the rule request thread).

Basically, this script takes any Hensel-notation rule and makes an Energetic rule out of it, with two states of the Hensel rule and with normal energy states. Can you make it?

wildmyron
Posts: 1544
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Script request thread

Post by wildmyron » September 24th, 2018, 8:15 am

KittyTac wrote:Can you make it?
Was that directed at me?

Copying my response from PM
It's doable, but introduces a lot more complexity in the matter / antimatter interactions. I would probably use a rule generator to create the isotropic lines for matter and antimatter in nutshell format, and then combine it with the annihilation + photon generation + photon movement which would be fixed.

I really don't fancy working out how to write all the rules for matter/antimatter interaction though. Also, the photon interaction isn't complete yet as there are still asymmetries there.

I'm afraid this is not a project I can take on now. If no-one else picks it up I may have a go in a month or so, but I've already got a backlog of unfinished projects so I don't want to add another to the list.
The 5S project (Smallest Spaceships Supporting Specific Speeds) is now maintained by AforAmpere. The latest collection is hosted on GitHub and contains well over 1,000,000 spaceships.

Semi-active here - recovering from a severe case of LWTDS.

User avatar
KittyTac
Posts: 535
Joined: December 21st, 2017, 9:58 am

Re: Script request thread

Post by KittyTac » September 24th, 2018, 8:23 am

I guess someone else can make it.

Gamedziner
Posts: 795
Joined: May 30th, 2016, 8:47 pm
Location: Milky Way Galaxy: Planet Earth

Re: Script request thread

Post by Gamedziner » September 27th, 2018, 6:42 am

I would like a script that searches for a glider synthesis for a given input pattern, to try to find a synthesis for Sir Robin.

EDIT: Such a script should also output the synthesis, if one is found.
Last edited by Gamedziner on September 27th, 2018, 1:07 pm, edited 1 time in total.

Code: Select all

x = 81, y = 96, rule = LifeHistory
58.2A$58.2A3$59.2A17.2A$59.2A17.2A3$79.2A$79.2A2$57.A$56.A$56.3A4$27.
A$27.A.A$27.2A21$3.2A$3.2A2.2A$7.2A18$7.2A$7.2A2.2A$11.2A11$2A$2A2.2A
$4.2A18$4.2A$4.2A2.2A$8.2A!

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

Re: Script request thread

Post by dvgrn » September 27th, 2018, 8:21 am

Gamedziner wrote:I would like a script that searches for a glider synthesis for a given input pattern, to try to find a synthesis for Sir Robin.
So would I. Actually, I'd like a script that actually finds a synthesis, rather than just searching for it. While I'm wishing, I'd like it to be guaranteed that the synthesis is the most efficient one possible.

Post Reply