dvgrn wrote: ↑August 7th, 2021, 8:56 am

The big problem with many of these kinds of problems is not so much that they can't be turned into runnable code. Often the real problem is getting a good estimate of the amount of CPU time or memory resources needed to complete the work -- and in many cases the estimate turns out to be "sometime after the Sun expands as far as Earth's orbit". [...] If it can be done in a way that allows searches to be run and time/resource estimates to be generated in the process, then it will definitely be very useful.

I'm not sure why producing time estimates would be that important. I never had automatically generated time estimates when I solved the problems I mentioned above. If you don't know whether a problem is tractable to brute force or not, then the amount of effort it takes to write a Golly script might compel you to not even bother, since you don't know if it'll work anyway. If that burden of effort is reduced, though, and you only need to write a few lines, you're much more likely to try it, and thus more likely to discover something you otherwise wouldn't have.

Also, you mentioned memory, which to me implies something like gfind. But in my opinion, if you want to invoke a gfind-style algorithm, you should just run gfind (or something like it).

Here's a demo of what I'm imagining:

Code: Select all

```
--[[ grammar:
program = iterator '|' selector
iterator = statement*
statement = number 'x' number
statement = '(' number ',' number ')'
statement = 'o'
selector = criterion*
criterion = 'pop' {'>'|'='} number
]]
local g=golly()
local program = g.getstring("Enter your CADSL program.\n"..
"NOTE THAT THIS IS A PROOF OF CONCEPT; NOTHING IS FINAL.")
----------------------------------------------------------------------- ITERATOR
-- All the code in this section deals with candidate pattern generation.
function resetCursor()
cursorX, cursorY = 0, 0
end
function newPattern()
resetCursor()
g.new("")
end
-- STATEMENTS:
function soup(x, y)
g.select{cursorX, cursorY, x, y}
g.randfill(50)
cursorX = cursorX + math.floor(x)
end
function go(x, y)
cursorX = x
cursorY = y
end
function dot()
g.setcell(cursorX, cursorY, 1)
cursorX = cursorX + 1
end
function doStatement(s)
-- figure out which kind of statement it is and run it
local _, _, num1, num2 = s:find("^(%d+)x(%d+)$")
if _ then soup(num1, num2) end
_, _, num1, num2 = s:find("^%((%-?%d+),(%-?%d+)%)$")
if _ then go(num1, num2) end
if s == "o" then dot() end
end
function doIterator(s)
for statement in s:gmatch("[^ \t\n;]+") do
doStatement(statement)
end
end
----------------------------------------------------------------------- SELECTOR
-- All the code in this section deals with detecting valid results.
function doCriterion(s)
s = s:gsub("pop",g.getpop())
local _, _, arg1, comparison, arg2 = s:find("(%w+)([>=])(%w+)")
arg1 = tonumber(arg1)
arg2 = tonumber(arg2)
if comparison == ">" then
return arg1 > arg2
elseif comparison == "=" then
return arg1 == arg2
end
end
function doSelector(s)
local shouldShow = true
for criterion in s:gmatch("[^ \t\n&]+") do
shouldShow = shouldShow and doCriterion(criterion)
end
return shouldShow
end
--------------------------------------------------------------------------------
pipe = program:find("|")
iterator = program:sub(1, pipe - 1)
selector = program:sub(pipe + 1)
-- main loop
i = 0
while true do
i = i + 1
newPattern()
doIterator(iterator:gsub("i",i))
g.select(g.getrect()) g.copy()
g.run(1000)
if doSelector(selector) then
g.new("") g.paste(0,0,"copy")
g.warn("Result found! :D ("..i.." tested)")
end
end
```

It's like a reverse awk; the program is broken into two halves: The "iterator", which generates patterns, and the "selector", which filters out everything which isn't wanted. In this limited demo, the iterator can include these commands, separated by spaces, with no spaces within them:

(width)x(height): Generate soup rectangle.

((number),(number)): Move cursor to specified coordinates.

o: Draw dot.

The character "i" is replaced with a number that's incremented each time the program is run. The selector can consist of "=" or ">" tests (but not "<"). The only variable that exists is "pop" (and I guess "i"), which is the population after 1000 steps.

Example programs:

"10x10 | pop=0" finds 10x10 diehards.

"o o (1,1) o o (1,2) o (4,i) o o o |" creates combinations of rpents and blinkers. (See where I'm going with this? I think a fleshed out version of this functionality could solve the 3rd of the 4 problems I mentioned in my last post, about reflecting spaceships.)

"4x4 | pop=55" finds pi predecessors.

Here's a soup found with "4x4 (-16,-16) 4x4 | pop>100 200>pop":

Code: Select all

```
x = 20, y = 20, rule = B3/S23
3o$o$2b2o$2bo13$16b3o$16b2obo$17b3o$16bo2bo!
```

A diehard found with "16x16 | pop=0":

Code: Select all

```
x = 16, y = 16, rule = B3/S23
2b2obo4b2o2bo$o2bo2bo5bob2o$3o2b2ob2ob5o$6b2obobo2bo$2bob4o2bo3bo$4bo
3bobob3o$2bo2bobo2b3obo$bo2b2o3bo2bob2o$2bo3bobo2b2o2bo$ob2o3bob4obo$b
obo2bo2bo2b4o$5o3bo2bo3bo$2b2ob3o2b4obo$5o4bobob3o$4b2o3b2o$o4bob3ob2o
!
```

My program is a little under a hundred lines long. My fantasy is to have something more like ten thousand lines; the iterator would allow imperative constructs like if/for/while blocks, functions, variables, etc. One example problem I want it to be able to solve is enumerating polyominoes, for example.

I have other things I'd rather work on, so I'll leave the script as it is. If anyone wants to add to the script, especially the selector part, please do so and post it here.