Last active
October 10, 2019 09:28
-
-
Save apaap/f61eca796333c53f1e8e3e13601133e6 to your computer and use it in GitHub Desktop.
Random state distribution rule
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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('') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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