Page 1 of 1

Fuse finder scripts

Posted: January 4th, 2015, 7:49 am
by Scorbie
I thought there was too much talk in the script itself so I decided to make a thread mainly for the script.
Here's the code.

Code: Select all

# codeholic's FuseFinder script v1.02
#   Creates a new layer called "FuseTestLayer"
#   Writes to a FuseFinder subfolder in Golly's data directory.
#   Updates screen every hundred trials (why not?)
import golly as g
import os
import time
import copy

outpath=os.path.join(g.getdir("data"),"FuseFinder")
if not os.path.isdir(outpath): os.mkdir(outpath)

FUSENAME = "BeehiveFuse"
DEBRIS = g.parse('b2o$o2bo$b2o!')
WIDTH = 4
HEIGHT = 5
DX, DY = (0, -5) #For diagonal/oblique fuses
COPIES = 100
TESTRECT = [COPIES * DX, COPIES * DY, WIDTH, HEIGHT]
RNDSIZE = 16
RNDRECT = [-7, 0, RNDSIZE, RNDSIZE]
SOUPSPERDISPLAY = 100
SQRTSPP = 10
SPP = SQRTSPP ** 2
SOUPL = 4000

def patterns_identical(cells1, cells2):
  if len(cells1) != len(cells2):
    return False
  return set(zip(cells1[::2], cells1[1::2])) == set(zip(cells2[::2], cells2[1::2]))

for i in range(g.numlayers()):
  if g.getname(i)=="FuseTestLayer":
    r=g.getrect()
    if r is not []:
        g.select(r)
        g.clear(0)
    break
if not g.getname(i)=="FuseTestLayer":  g.addlayer()
g.setalgo('QuickLife')
for x in xrange(SQRTSPP):
    for y in xrange(SQRTSPP):
      for i in xrange(1, COPIES+1):
        g.putcells(DEBRIS, i * DX + x * SOUPL, i * DY + y * SOUPL)
      if x==0 and y==0:
        fuserect = g.getrect()
allcells=g.getcells(g.getrect())
cells = g.getcells(TESTRECT)

count, found = 0, 0

start_time = time.clock()

# For profiling
# 
#init_time = 0
#gen_time = 0
#check_time = 0


while True:
  #mark = time.clock()
  rndrect = copy.deepcopy(RNDRECT)
  testrect = copy.deepcopy(TESTRECT)
  foundindex = []
  g.new("FuseTestLayer")
  g.putcells(allcells)
  for x in xrange(SQRTSPP):
    for y in xrange(SQRTSPP):
      g.select(rndrect)
      g.randfill(50)
      rndrect[1] += SOUPL
    rndrect[0] += SOUPL
    rndrect[1] = RNDRECT[1]
  #init_time += time.clock()-mark
  #mark = time.clock()
  g.setbase(16)
  g.setstep(3)
  g.step()
  #gen_time +=time.clock()-mark
  #mark = time.clock()
  for x in xrange(SQRTSPP):
    for y in xrange(SQRTSPP):
      test = g.getcells(testrect)
      for i in xrange(0, len(test), 2):
        test[i] -= x * SOUPL
      for i in xrange(1, len(test), 2):
        test[i] -= y * SOUPL
      if not patterns_identical(test, cells):
        foundindex.append((x, y))
      testrect[1] += SOUPL
    testrect[0] += SOUPL
    testrect[1] = TESTRECT[1]
  if foundindex is not []:
    g.reset()
    for (x, y) in foundindex:
      rndrect = copy.deepcopy(RNDRECT)
      foundfuserect = copy.deepcopy(fuserect)
      rndrect[0] += x * SOUPL
      rndrect[1] += y * SOUPL
      foundfuserect[0] += x * SOUPL
      foundfuserect[1] += y * SOUPL
      foundpattern = g.getcells(rndrect) + g.getcells(foundfuserect)
      g.store(foundpattern, os.path.join(outpath, FUSENAME + str(count + 10 * y + x) + '.rle'))
      g.show("Saved fuse to " + outpath + FUSENAME + str(count + 10 * y + x) + ".rle")
      found += 1
  count += SPP
  
  if count % SOUPSPERDISPLAY == 0:
    end_time =time.clock()
    g.show('Count:' + str(count) + ' Found:' + str(found) + ' (' +\
        str(round(SOUPSPERDISPLAY/(end_time - start_time), 2)) + " soups per second)")
    #g.show('init: '+str(init_time*1000/count)+'ms gen: '+str(gen_time*1000/count)+'ms check: '+str(check_time*1000/count)+'ms')
    start_time = end_time
    g.fit()
    g.update()
  #check_time += time.clock()-mark
HartmutHolzwart wrote:the script does not close the current layer when terminated by "escape"...
I think we would have to use g.getevent() to do something like that. But before that, do you have problems with that unclosed layer? I think the layer would have been 'unclosed' since the original script.

Re: Fuse finder scripts

Posted: January 4th, 2015, 8:33 am
by simsim314
I've added
-Hashsoup implementation instead of randfill.
-Report to patterns folder.
-Seed to datetime.now

Code: Select all

# codeholic's FuseFinder script v1.02
#   Creates a new layer called "FuseTestLayer"
#   Writes to a FuseFinder subfolder in Golly's data directory.
#   Updates screen every hundred trials (why not?)
#   Uses g.new() as per simsim314's suggestion
#      (I noticed that Golly's Undo got very slow after running the old script) (?)
# hashsoup used instead randfill
# report to patterns folder
#SEED for datetime.now
import golly as g
import os
import time
import hashlib
import datetime

outpath=os.path.join(g.getdir("patterns"),"FuseFinder")
if not os.path.isdir(outpath): os.mkdir(outpath)

SEED =  str(datetime.datetime.now())
FUSENAME = "Diagpuffer"
DEBRIS = g.parse('b2o$o2bo$obo$bo!')
WIDTH = 5
HEIGHT = 10
DX, DY = (-7, 0) #For diagonal/oblique fuses
COPIES = 100
TESTRECT = [COPIES * DX, COPIES * DY, WIDTH, HEIGHT]
RNDSIZE = 15
RNDRECT = [0, 0, RNDSIZE, RNDSIZE]
SOUPSPERDISPLAY = 100

def hashsoup(instring):

    s = hashlib.sha256(instring).digest()

    thesoup = []

    for j in xrange(32):

        t = ord(s[j])

        for k in xrange(8):

            if (t & (1 << (7 - k))):

                thesoup.append(k + 8*(j % 2))
                thesoup.append(int(j / 2))

    return thesoup

def patterns_identical(cells1, cells2):
  if len(cells1) != len(cells2):
    return False
  return set(zip(cells1[::2], cells1[1::2])) == set(zip(cells2[::2], cells2[1::2]))

for i in range(g.numlayers()):
  if g.getname(i)=="FuseTestLayer":
    r=g.getrect()
    if r is not []:
        g.select(r)
        g.clear(0)
    break
if not g.getname(i)=="FuseTestLayer":  g.addlayer()
g.setalgo('QuickLife')
for i in xrange(1, COPIES+1):
  g.putcells(DEBRIS, i * DX, i * DY)
allcells=g.getcells(g.getrect())
cells = g.getcells(TESTRECT)

count, found = 0, 0

start_time = time.clock()

# For profiling
# 
#init_time = 0
#gen_time = 0
#check_time = 0


while True:
  #mark = time.clock()
  g.new("FuseTestLayer")
  g.putcells(allcells)
  g.select(RNDRECT)
  g.putcells(hashsoup(SEED + str(count)))
  #init_time += time.clock()-mark
  #mark = time.clock()
  g.setbase(16)
  g.setstep(3)
  g.step()
  #gen_time +=time.clock()-mark
  #mark = time.clock()
  test = g.getcells(TESTRECT)
  count += 1

  if not patterns_identical(test, cells):
    g.reset()
    g.save(os.path.join(outpath, FUSENAME + str(count) + '.rle'), 'rle')
    g.show("Saved fuse to " + outpath + FUSENAME + str(count) + ".rle")
    found += 1
  
  if count % SOUPSPERDISPLAY == 0:
    end_time =time.clock()
    g.show('Count:' + str(count) + ' Found:' + str(found) + ' (' +\
        str(round(SOUPSPERDISPLAY/(end_time - start_time), 2)) + " soups per second)")
    #g.show('init: '+str(init_time*1000/count)+'ms gen: '+str(gen_time*1000/count)+'ms check: '+str(check_time*1000/count)+'ms')
    start_time = end_time
    g.setmag(2)
    g.update()
    #check_time += time.clock()-mark

Re: Fuse finder scripts

Posted: January 4th, 2015, 11:06 am
by codeholic
I put it in GitHub.

https://github.com/conwaylife/fuse-finder

And I made UI a bit better, in my opinion. Now you don't need to change the source code if you want to search for another fuse. You just need to select a monomer you want to multiply and a horizontal offset (for diagonal and oblique fuses). Hope you like it.

Re: Fuse finder scripts

Posted: January 4th, 2015, 12:00 pm
by simsim314
I've added some more features (I didn't see codeholic's improvement):

1. Export into function - allows to loop on DX, DY.
2. Remove duplicates, to reduce spamming.
3. Extra verification to exclude false positive.
4. Allows periodic debris (like blinkers).

Here is a sample that loops on all blinkers of distance 5-20 (makes 5K checks):

Code: Select all

# codeholic's FuseFinder script v1.02
#	 Creates a new layer called "FuseTestLayer"
#	 Writes to a FuseFinder subfolder in Golly's data directory.
#	 Updates screen every hundred trials (why not?)
#	 Uses g.new() as per simsim314's suggestion
#			(I noticed that Golly's Undo got very slow after running the old script) (?)
# hashsoup used instead randfill
# report to patterns folder
#SEED for datetime.now
#Export to function with parameters - allow loops of DX, DY
#Removes redundant results 
#Better verification to remove false positive

import golly as g
import os
import time
import hashlib
import datetime

outpath=os.path.join(g.getdir("patterns"),"FuseFinder")
if not os.path.isdir(outpath): os.mkdir(outpath)


def hashsoup(instring):

		s = hashlib.sha256(instring).digest()

		thesoup = []

		for j in xrange(32):

				t = ord(s[j])

				for k in xrange(8):

						if (t & (1 << (7 - k))):

								thesoup.append(k + 8*(j % 2))
								thesoup.append(int(j / 2))

		return thesoup

def DebrisPattern(DX, DY, DEBRIS, COPIES, EVOLVE, TESTRECT):
	g.new("")
	for i in xrange(0, COPIES+1):
		g.putcells(g.evolve(DEBRIS, i * EVOLVE), i * DX, i * DY)
	allcells=g.getcells(g.getrect())
	cells = g.getcells(TESTRECT)

	return (allcells, cells)
	
def FindFuses(DX, DY, DEBRIS, EVOLVE, numTries):
	SEED =	str(datetime.datetime.now())
	FUSENAME = "Diagpuffer"
	
	WIDTH = 15
	HEIGHT = 15
	COPIES1 = 100
	TESTRECT1 = [COPIES1 * DX - WIDTH, COPIES1 * DY - HEIGHT, 2 * WIDTH, 2 * HEIGHT]
	COPIES2 = 200
	TESTRECT2 = [COPIES2 * DX - WIDTH, COPIES2 * DY - HEIGHT, 2 * WIDTH, 2 * HEIGHT]
	
	RNDSIZE = 15
	RNDRECT = [0, 0, RNDSIZE, RNDSIZE]
	SOUPSPERDISPLAY = 1000
	
	g.setalgo('HashLife')
	
	allcells1, cells1 = DebrisPattern(DX, DY, DEBRIS, COPIES1, EVOLVE, TESTRECT1)
	allcells2, cells2 = DebrisPattern(DX, DY, DEBRIS, COPIES2, EVOLVE, TESTRECT2)
	
	found = 0
	foundCells = []
	
	start_time = time.clock()

	for count in xrange(1, numTries + 1):

		g.new("FuseTestLayer")
		g.putcells(allcells1)
		g.setbase(16)
		g.setstep(3)
		g.putcells(hashsoup(SEED + str(count)))
		g.step()
		g.step()
		g.step()
		g.step()
		
		test = g.getcells(TESTRECT1)
		
		if  test != cells1 and not (test in foundCells):
			foundCells.append(test)
			#check again for larger fuse to remove false positive. 
			g.reset()
			g.putcells(allcells2)
			
			g.setstep(4)
			g.step()

			if  g.getcells(TESTRECT2) != cells2:
				g.reset()
				g.save(os.path.join(outpath, FUSENAME + "  " + str(found) + "  " + str(DX) + "  " + str(DY) + "  " + str(EVOLVE) + '.rle'), 'rle')
				g.show("Saved fuse to " + outpath + FUSENAME + "  " + str(found) + "  " + str(DX) + "  " + str(DY)  + "  " + str(EVOLVE) + '.rle')
				found += 1
				
		if count % SOUPSPERDISPLAY == 0:
			end_time =time.clock()
			g.show('DX = {0}, DY = {1} , Count = {2}, Found = {3}'.format(DX, DY, count, found) + ' (' + str(round(SOUPSPERDISPLAY/(end_time - start_time), 2)) + " soups per second)")

			start_time = end_time
			g.setmag(2)
			g.update()

DEBRIS = g.parse('3o!')
NUMTEST = 5000

for DX in xrange(5, 20):
	for ev in xrange(0, 2):
		FindFuses(-DX, 0, DEBRIS, ev, NUMTEST)

Re: Fuse finder scripts

Posted: January 4th, 2015, 12:07 pm
by dvgrn
Scorbie wrote:
HartmutHolzwart wrote:the script does not close the current layer when terminated by "escape"...
I think we would have to use g.getevent() to do something like that. But before that, do you have problems with that unclosed layer? I think the layer would have been 'unclosed' since the original script.
An escape key press counts as a general error for the script, so it can be caught by a try/finally block. Just wrap the whole main loop in a "try" block, and arrange to close the temporary layer in the "finally" section.

Only do that after you're sure there are no other errors in the script, of course -- a try/finally block can make it really annoying to troubleshoot syntax errors. Also make sure that the code in the "finally" block is guaranteed to terminate quickly, because otherwise I think it suddenly gets really hard to cancel the script...!

Re: Fuse finder scripts

Posted: January 4th, 2015, 12:44 pm
by Scorbie
Wow, Thank you all! At that time I really needed help because I'm not a programming major. Still learning Python and all those classy things, so I was hoping for someone with more programming knowledge to help me!
It's late at night in my time zone, which means I only took a glance at your scripts, but WOW. They're amazing!
codeholic wrote:And I made UI a bit better, in my opinion. Now you don't need to change the source code if you want to search for another fuse. You just need to select a monomer you want to multiply and a horizontal offset (for diagonal and oblique fuses). Hope you like it.
After a few searches I, as well, got a little tired pasting the rle and deleting the first line. Thanks for the UI!
simsim314 wrote:1. Export into function - allows to loop on DX, DY.
2. Remove duplicates, to reduce spamming.
3. Extra verification to exclude false positive.
4. Allows periodic debris (like blinkers).
This remarkable fuse might tell you how I feel about your new scripts.

Code: Select all

x = 1016, y = 17, rule = B3/S23
6bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo
9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo
9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo
9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo
9bo9bo9bo9bo9bo9bo9bo9bo$3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o3bo3b3o
3bo3b3o3bo3b3o3bo3b3o3bo3b3obo2bob2o4bo$6bo9bo9bo9bo9bo9bo9bo9bo9bo9bo
9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo
9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo
9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo
9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo9bo4bobo4b
obo2bobo$1001bob5ob2o2bobo$1003b4obo3b2obo$1000bo4bo6bob2o$1004b3o2bo
3bobo$1001b4obo2b4ob2o$1001bo4bobo$1000b6o2b2o3b3o$1002b10o2bo$1000bo
3bob2ob3o2b2o$1000bo2bo2b3o2bobobo$1007b3obo$1000bo3b2o4b3o$1002b3o2bo
bob2o2bo$1000bo3b2o2bob3o2bo!
dvgrn wrote:Just wrap the whole main loop in a "try" block, and arrange to close the temporary layer in the "finally" section.
Didn't think of that! I guess the try~finally block would be added after the code is stable.

Wouldn't it be nice if we incorporate all these features?

Re: Fuse finder scripts

Posted: January 4th, 2015, 12:47 pm
by Scorbie
I wrote:This remarkable fuse might tell you how I feel about your new scripts.
EDIT: This remarkable fuse of yours might tell you how I feel... which is... remarkable!