This script runs CA with the following rules:

-The 'heat' or neighbour count of a cell is the total sum of all reciprocals of quarted(^4) euclidean distances to other live cells.

-It chooses to birth or survive cells based on thresholds specified by the rule. For example, ECA:B(1,2)(3,4)/S(3,6) would birth if the heat was between 1 and 2 (including both of those), or 3 and 4, and survive if between 3 and 6.

Code: Select all

`--This is a preliminary version. If sufficiently interesting behaviours show`

--up, it would be nice to get a full simulating algorithm in better-written

--softawre like Golly. Speaking of Golly, make sure you have a version that

--supports Lua, so you can run this script. Stinky.

--TERMS TO KNOW:

--Heat - The number returned when the previous equation is ran on all of the

-- other cells in relation to the given cell (basically what count()

-- does)

local g = golly()

local gp = require "gplus"

local rulesB = {.01, 7}

local rulesS = {}

function count(x1, y1) --determine cell's heat

local pat

local total = 0

if g.getcell(x1, y1) == 1 then --ok i know it's ugly but it saves a lot of processing time than doing a conditional

g.setcell(x1, y1, 0)

pat = g.getcells(g.getrect())

g.setcell(x1, y1, 1)

else

pat = g.getcells(g.getrect())

end

for i = 1, #pat, 2 do

local x2 = pat[i]

local y2 = pat[i+1] --ugly

total = total+1 / ((x2-x1)^2+(y2-y1)^2)^2 --i know i said i was using fourth powers, however the square root and fourth power cancel out leaving just a stray ^2

end

return total

end

function ring(r) --basically, it returns a 'outer moore birth ring' of the given size

local pat = g.getcells(g.getrect())

local x, y, w, h = table.unpack(r) --there a better way to do this?

g.select({x-1, y-1, w+2, h+2})

g.randfill(100)

g.select({x, y, w, h})

g.clear(0)

local nring = g.getcells(g.getrect())

g.putcells(pat)

g.clear(1)

g.select({})

return nring

end

function step()

--hoooo boy i hated writing this

--don't read it if you don't want to get lost

local pat = g.getcells(g.getrect())

if g.getpop() == "0" then

g.exit("All cells are dead.")

return

end

local rulesBclone = rulesB

table.sort(rulesBclone)

local minrulesB = rulesBclone[1] --math.min but with an array

local lx, ly, lw, lh = table.unpack(g.getrect())

while true do --this loop determines how far to check for births

local localmax = 0

local localring = ring({lx, ly, lw, lh})

for i = 1, #localring, 2 do

local localcount = count(localring[i], localring[i+1])

if localcount > localmax then

localmax = localcount

end

end

if localmax < minrulesB then

break

end

lx = lx - 1

ly = ly - 1

lw = lw + 2

lh = lh + 2

end

--gosh that took me months to make, now onto processing

g.select({lx, ly, lw, lh})

g.randfill(100)

g.putcells(pat, 0, 0, 1, 0, 0, 1, --[[ugh why]] "xor")

local area = g.getcells(g.getrect())

g.clear(0)

g.putcells(pat)

--NOW LET'S FINALLY PROCESS BIRTH, aka the reason that hellhole of lines above had to be done

local localcells = {}

for i = 1, #area, 2 do

local localcount = count(area[i], area[i+1])

for j = 1, #rulesB, 2 do

if localcount >= rulesB[j] and localcount <= rulesB[j+1] then

table.insert(localcells, area[i])

table.insert(localcells, area[i+1]) --can't think of a way to insert two at once

end

end

end

--and survival, the easy part

for i = 1, #pat, 2 do

local localcount = count(pat[i], pat[i+1])

for j = 1, #rulesS, 2 do

if localcount >= rulesS[j] and localcount <= rulesS[j+1] then

table.insert(localcells, pat[i])

table.insert(localcells, pat[i+1])

end

end

end

g.select(g.getrect())

g.clear(0)

g.putcells(localcells)

g.select({})

g.setgen(tonumber(g.getgen())+1)

end

while true do

step()

g.update()

step()

There are optimizations, but I will briefly explain some properties I have noticed.

Thresholds

A cell with maximum heat would be surrounded by infinitely many cells. This 'maximum heat' number is finite, but I do not yet know its value. A 511x511 block of cells yields the number 6.0267726589419..., but that's not even close to a good estimate. I have settled on using 7 when engineering day and night-esque rules.

Another threshold that isn't yet known is being able to expand outside its bounding box.

EDIT 11/30/2018: Thanks to Caenbe and Freywa for pointing out that the maximum heat is (2/3)*pi^2*C, where C = Catalan's constant. Wolfram says this is the value to 1000 digits:

Code: Select all

`6.0268120396919401235462601927282855839417908072537241239789823989168652037814250418764946630938418433906127747493598517914519884043536868495100966503449541972750061912676710610851774101700255398481455670182612361266898875483352921070069449008114281527829434299322822560251114200149117706817994764236876636117413174159952976496849741988010861076557834927010493267788390209735731100040920526016251643211225971568622986661712279082923986446238998997768676990659832983376483731474723643953842741351597605714161337207790707042631552431695921352276400390580604568489239229700788071861541187775637812405539300590870406433494222017883382946074250172136911832633689703956037954481037629038480262769680362849773653130682557719791223453092147760706480221366950515486440933396727770173048557378012773452636411632552957204199179226582314343628445920394854336019550159007480469325289524410543403938235788421982263661511011524075413030449483389003139678799601958658704861812673964630612466034108439517982559502176303...`

Lonely patterns

Ah, yes, a big problem. I had to choose between either having patterns that can only exist alone on a grid, or having patterns behave way differently when alone. Yes, there are patterns that, due to having maximized or minimized heat within a range, can only exist alone. You could engineer this for any pattern, really. For example, here is a glider (quite vast in its rule range) that can exist only by itself in this rule:

Code: Select all

`#C rule = ECA:B(1.15,1.5625)/S(1.6,3)`

x = 2, y = 3, rule = B/S012345678

bo$2o$bo!

Code: Select all

`#C rule = ECA:B(1.15,1.5625)/S(1.6,3)`

x = 9, y = 3, rule = B/S012345678

bo$2o6bo$bo!

The speed of light

This one may bother calcyman and Apple Bottom and the like. The speed of light in this rule is infinite, so it is way more comfortable to use one cell per generation. To demonstrate it is infinite, a single cell in the rule ECA:B(.01,7)/S (I've nicknamed this family of quite redundant rules 'Target', this would be 'Target.01') turns into this monstrosity in 5 generations:

Code: Select all

`#C rule = ECA:B(.01,7)/S`

x = 57, y = 57, rule = B/S012345678

23b11o$19b19o$17b23o$15b27o$13b31o$12b33o$10b37o$9b15o9b15o$8b13o15b

13o$7b12o19b12o$6b11o23b11o$6b10o25b10o$5b9o29b9o$4b9o31b9o$4b8o12b9o

12b8o$3b9o10b13o10b9o$3b8o9b17o9b8o$2b8o9b19o9b8o$2b8o8b21o8b8o$b8o8b

23o8b8o$b8o7b10o5b10o7b8o$b7o8b8o9b8o8b7o$b7o7b8o11b8o7b7o$8o7b7o13b7o

7b8o$7o7b7o15b7o7b7o$7o7b7o6b3o6b7o7b7o$7o7b6o6b5o6b6o7b7o$7o7b6o5b7o

5b6o7b7o$7o7b6o5b3ob3o5b6o7b7o$7o7b6o5b7o5b6o7b7o$7o7b6o6b5o6b6o7b7o$

7o7b7o6b3o6b7o7b7o$7o7b7o15b7o7b7o$8o7b7o13b7o7b8o$b7o7b8o11b8o7b7o$b

7o8b8o9b8o8b7o$b8o7b10o5b10o7b8o$b8o8b23o8b8o$2b8o8b21o8b8o$2b8o9b19o

9b8o$3b8o9b17o9b8o$3b9o10b13o10b9o$4b8o12b9o12b8o$4b9o31b9o$5b9o29b9o$

6b10o25b10o$6b11o23b11o$7b12o19b12o$8b13o15b13o$9b15o9b15o$10b37o$12b

33o$13b31o$15b27o$17b23o$19b19o$23b11o!

Rules & Other miscellany

This rulespace is infinitely big. To start, here's a rule with a rhomblicator and the aforementioned T-glider:

Code: Select all

`#C rule = B(1.15,1.2)(1.3,1.4)(1.5,1.6)/S(1.603,2.5)(3,4)`

x = 2, y = 13, rule = B/S012345678

bo$2o$bo8$o$2o$bo!

I guess I'll post more rules later once I've done more research.