Skip to content

Instantly share code, notes, and snippets.

@apaap
Last active October 10, 2019 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apaap/f61eca796333c53f1e8e3e13601133e6 to your computer and use it in GitHub Desktop.
Save apaap/f61eca796333c53f1e8e3e13601133e6 to your computer and use it in GitHub Desktop.
Random state distribution rule
# Random State Distribution
A rule generator for Golly to create a random state distributor rule
The rule is intended to convert a two state random soup pattern into an n-state random soup. The rule has (n+1)-states where the additional state is state 1. All state 1 cells will become cells with state 2 -> n after the first generation, with the resulting state being dependent on the neighbourhood configuration of the cell.
This could potentially be used with apgsearch to generate multi-state soups in rules where 2-sate soups don't show the full dynamics of the rule.
This project contains three separate files:
- rand_state_distribution.py
A Golly Python script to generate a rule file (can be run from clipboard).
- test_rand_state_distribution.py
A Golly Python script to test a random state distribution rule (can be run from clipboard).
- plot_rand_state_distribution.py
Python code to plot the results, exported from a Jupyter Notebook.
Could perhaps be run standalone with little modification.
# Usage:
- Edit rand_state_distribution.py to set the number of states in the original rule.
- Run rand_state_distribution.py in Golly to generate a rule file (rand_states.rule)
- Run test_rand_state_distribution.py in Golly to collect statistics about the distribution of states and neighbour states in the multi-state soups created by hashsoup() and run for 1 generation with rand_states
- Import the Python code from plot_rand_state_distribution.py to a Jupyter Notebook. Copy the statistics from test_rand_state_distribution.py to the Notebook and run the Notebook.
#plot_rand_state_distribution.py
# Python code to plot results - exported from Jupyter notebook
# **Test the distribution of random states using rand_state rule**
#
# Data collected from 10000 soups using a Golly Python script
# In[1]:
# Setup
import numpy as np
import matplotlib.pyplot as plt
# Data:
n_states = 7
states = list(range(2, n_states+1))
statecounts = {2: [204342, [993565, 88442, 87026, 121801, 123419, 116922, 103561]],
3: [193909, [920961, 87026, 84806, 110104, 125962, 113511, 108902]],
4: [226041, [939934, 121801, 110104, 164692, 148763, 185151, 137883]],
5: [249731, [1130849, 123419, 125962, 148763, 161104, 164197, 143554]],
6: [216125, [853827, 116922, 113511, 185151, 164197, 151296, 144096]],
7: [189802, [752442, 103561, 108902, 137883, 143554, 144096, 127978]], }
# In[2]:
# Count of each state generated
counts = np.array([statecounts[s][0] for s in statecounts])
fig, ax = plt.subplots()
ax.plot(states, counts/1000, '*-')
ax.set_ylim([0,300])
ax.set_xlabel('State')
ax.set_ylabel('Counts (/1000)')
plt.title('Counts of each state')
plt.tight_layout()
plt.savefig('counts.png')
# In[3]:
# Normalised count of each state in neighbours
neighcounts = np.array([np.array(statecounts[s][1]) / statecounts[s][0] for s in statecounts]).transpose()
fig, ax = plt.subplots()
plt.plot([0]+states, neighcounts, '*-')
ax.set_ylim([0,5])
ax.set_xlabel('Neighbour States')
ax.set_ylabel('Normalised Counts')
plt.title('Normalised counts of each state in neighbours')
plt.legend(['State %d' % s for s in states])
plt.tight_layout()
plt.savefig('neighbour_counts.png')
#rand_state_distribution.py
# Rule generator for Golly to create a random state distributor rule
import random
import os
import golly as g
# Create a rule file
rulename = 'rand_states'
# Number of states in the original rule
n_states = 7
# Generate the transitions
# Table will map state 1 to the (n_states - 1) states - [2:n_states]
table = 'n_states:{}\n'.format(n_states + 1)
table += 'neighborhood:Moore\n'
table += 'symmetries:none\n\n'
# Generate random transitions for all survival conditions
for t in range(256):
r = random.randint(2, n_states)
table += '1{:08b}{}\n'.format(t, r)
# Create the rule file
results = '@RULE ' + rulename + '\n\n'
results += 'A rule to generate soups with n_states from 2 states\n'
results += '\n@TABLE\n\n'
results += table
results += '\n@COLORS\n\n'
results += '1 255 255 255'
ruledir = g.getdir("rules")
rulefile = os.path.join(ruledir, rulename + '.rule')
# Save the rule
with open(rulefile, 'w') as ruleF:
ruleF.write(results)
g.setrule(rulename)
g.new('')
#test_rand_state_distribution.py
# Golly Python script to test the random state distributor rule
import hashlib
import golly as g
# Takes approximately 350 microseconds to construct a 16-by-16 soup based
# on a SHA-256 cryptographic hash in the obvious way.
def hashsoup(instring):
s = hashlib.sha256(instring).digest()
thesoup = []
for j in xrange(32):
t = ord(s[j])
for k in xrange(8):
x = k + 8*(j % 2)
y = int(j / 2)
if (t & (1 << (7 - k))):
thesoup.append(x)
thesoup.append(y)
return thesoup
def chunks(celllist):
l = len(celllist)
if l % 2:
size = 3
if l % 3:
l += -1
else:
size = 2
for x in range(0, l, size):
yield celllist[x:(x + size)]
# Initialisation
rulename = 'rand_states'
n_states = 7
statecounts = {}
neighbours = [[-1, -1], [0, -1], [1, -1], [-1, 0], [1, 0], [-1, 1], [0, 1], [1, 1]]
# The statecounts dict records the count of each state generated, and the
# count of neighbours of state for each of the generated states
# state: (count, [neighbour_counts])
for s in range(2, n_states+1):
statecounts[s] = [0, [0]*(n_states)]
seed = 's'
g.setrule(rulename)
g.new('')
# Create a soup
for ii in range(10000):
g.select([-1,-1,1,1])
g.clear(1)
g.putcells(hashsoup(seed+str(ii)))
g.run(1)
cells = g.getcells(g.getrect())
# Count states and neighbours
# for s in range(n_states):
# if s > 0: s += 1
for cell in chunks(cells):
try:
x, y, v = cell
except:
g.note('%d/3 cells, current cell: %s\n\n' % (len(cells), cell) + str(cells))
# Add the statecount
statecounts[v][0] += 1
# And now the neighbours
for n in neighbours:
nv = g.getcell(x + n[0], y + n[1])
sv = max(0, nv-1)
statecounts[v][1][sv] += 1
g.note(str(statecounts))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment