Commit db11210a authored by Adam P. Goucher's avatar Adam P. Goucher

Tests for deficient life and extended generations

parent 5a476b2d
Pipeline #43460880 passed with stages
in 7 minutes and 5 seconds
'''
Common code and imports for 'genext' and 'deficient' genera.
'''
from .eightbit import *
from .eightbit import create_rule as make_header
from .rulefiles import rule2files
from .isotropic import str2tab
'''
The following code was written by M. I. Wright (for Python 3) and
subsequently modified to be compatible with both versions of Python.
'''
import sys
from os import path
N_HOODS = 'cekainyqjrtwz'
NAPKINS = {
k: dict(zip(N_HOODS, v)) for k, v in {
'0': '',
'1': ['00000001', '10000000'],
'2': ['01000001', '10000010', '00100001', '10000001', '10001000', '00010001'],
'3': ['01000101', '10100010', '00101001', '10000011', '11000001', '01100001', '01001001', '10010001', '10100001', '10001001'],
'4': ['01010101', '10101010', '01001011', '11100001', '01100011', '11000101', '01100101', '10010011', '10101001', '10100011', '11001001', '10110001', '10011001'],
'5': ['10111010', '01011101', '11010110', '01111100', '00111110', '10011110', '10110110', '01101110', '01011110', '01110110'],
'6': ['10111110', '01111101', '11011110', '01111110', '01110111', '11101110'],
'7': ['11111110', '01111111'],
'8': ''
}.items()
}
def order_segment(sz, segment):
if not segment or segment[0] == '-':
return [t for t in N_HOODS[:sz] if t not in segment]
return [t for t in N_HOODS if t in segment]
def combine_rstring(segment):
cop, last = {}, 0
for i, v in enumerate(segment, 1):
if v.isdigit():
after = next((idx for idx, j in enumerate(segment[i:], i) if j.isdigit()), len(segment))
cop[v] = order_segment(len(NAPKINS[v]), segment[i:after])
return cop
from .eightbit import *
from .eightbit import create_rule as make_header
from .rulefiles import rule2files
from .isotropic import str2tab
'''
The following code was written by M. I. Wright (for Python 3) and
subsequently modified to be compatible with both versions of Python.
'''
import sys
from os import path
N_HOODS = 'cekainyqjrtwz'
NAPKINS = {
k: dict(zip(N_HOODS, v)) for k, v in {
'0': '',
'1': ['00000001', '10000000'],
'2': ['01000001', '10000010', '00100001', '10000001', '10001000', '00010001'],
'3': ['01000101', '10100010', '00101001', '10000011', '11000001', '01100001', '01001001', '10010001', '10100001', '10001001'],
'4': ['01010101', '10101010', '01001011', '11100001', '01100011', '11000101', '01100101', '10010011', '10101001', '10100011', '11001001', '10110001', '10011001'],
'5': ['10111010', '01011101', '11010110', '01111100', '00111110', '10011110', '10110110', '01101110', '01011110', '01110110'],
'6': ['10111110', '01111101', '11011110', '01111110', '01110111', '11101110'],
'7': ['11111110', '01111111'],
'8': ''
}.items()
}
def order_segment(sz, segment):
if not segment or segment[0] == '-':
return [t for t in N_HOODS[:sz] if t not in segment]
return [t for t in N_HOODS if t in segment]
def combine_rstring(segment):
cop, last = {}, 0
for i, v in enumerate(segment, 1):
if v.isdigit():
after = next((idx for idx, j in enumerate(segment[i:], i) if j.isdigit()), len(segment))
cop[v] = order_segment(len(NAPKINS[v]), segment[i:after])
return cop
from ._wright import *
def replace_bind(transition, pre, sub='', count=0):
cop = []
......
'''
The following code was written by M. I. Wright (for Python 3) and
subsequently modified to be compatible with both versions of Python.
'''
from ._wright import *
ACTIVE, INACTIVE = 'active', 'inactive'
def unbind_vars(transition, start=0):
"""
Suffix varnames in transition with locally-unique numbers so that Golly
doesn't bind the identical names
"""
ret, seen = [], {}
for state in transition:
if isinstance(state, int) or state.isdigit():
ret.append(state)
else:
seen[state] = cur = seen.get(state, -1) + 1
ret.append('{}_{}'.format(state, cur))
return ret
def _lazy_tr(states):
"""
Take sequence of 2- or 3-tuples, interpreting them as
(
cellstate value to repeat+unbind,
number of times to repeat,
optional value to start bindings at,
)
and produce an expanded/chained form thereof
"""
for value in states:
if isinstance(value, tuple):
state = value[0]
count = value[1]
start_at = value[2:]
for uv in unbind_vars([state] * count, *start_at):
yield uv
else:
yield value
def tr(*states):
"""Generate a transition (as list) from varargs"""
return list(_lazy_tr(states))
def make_totalistic(birth, survival):
transitions = []
# Birth
transitions += [tr(0, (ACTIVE, n), (INACTIVE, 8 - n), 1) for n in map(int, birth)]
# Survival
transitions += [tr((ACTIVE, n + 1), (INACTIVE, 8 - n), ACTIVE+'_0') for n in map(int, survival)]
return 'permute', transitions
def make_nontot(birth, survival):
transitions = []
birth, survival = combine_rstring(birth), combine_rstring(survival)
# Birth
transitions += [unbind_vars((0,) + tuple(([INACTIVE, ACTIVE][int(c)] for c in NAPKINS[total][configuration])) + (1,))
for total, configurations in birth.items()
for configuration in configurations]
# Survival
transitions += [unbind_vars((ACTIVE,) + tuple(([INACTIVE, ACTIVE][int(c)] for c in NAPKINS[total][configuration]))) + [ACTIVE+'_0']
for total, configurations in survival.items()
for configuration in configurations]
return 'rotate4reflect', transitions
def make_rule(birth, survival, age_pattern):
active, inactive = [], [0]
n_states = 1 + sum(age_pattern)
tr_func = make_totalistic if (birth + survival).isdigit() else make_nontot
if n_states > 255:
raise ValueError("State count %d exceeds maximum of 255." % n_states)
symmetry_type, transitions = tr_func(birth, survival)
# Transition toward death where unspecified
transitions += [tr(state, ('all', 8), (state + 1) % n_states) for state in range(1, n_states)] # state 0 isn't included
lower = 1 # state 0 is always 'inactive', so we start at 1
for idx, upper in enumerate(age_pattern):
if (idx % 2):
inactive += list(range(lower, lower + upper))
else:
active += list(range(lower, lower + upper))
lower += upper
return symmetry_type, n_states, active, inactive, transitions
def write_table(fp, rulename, symmetries, n_states, active, inactive, transitions):
fp.write('@RULE {}\n@TABLE\n'.format(rulename))
fp.write('n_states:{}\nneighborhood:Moore\nsymmetries:{}\n'.format(n_states, symmetries))
# Variables
def define_var(name, var):
if not var:
raise SystemExit(
'ERROR: Var for {!r} states is coming up empty. '
'Are you sure this is a valid rule?'.format(name)
)
fp.write('\nvar {0}_0={1}\n'.format(name, list(var)).replace('[', '{').replace(']', '}'))
for n in range(8):
fp.write('var {0}_{1}={0}_0\n'.format(name, n + 1))
define_var(ACTIVE, active)
define_var(INACTIVE, inactive)
define_var('all', range(n_states))
# Transitions
for tr in transitions:
fp.write('\n' + ','.join(map(str, tr)))
def create_rule(rulestring):
transitions = []
bs, age_pattern = rulestring.split('d')
age_pattern = tuple(map(int, age_pattern.split('-')))
str2tab(bs) # Raise a NonCanonicalError if initial segment is non-canonical
birth, survival = tuple(map(str.strip, bs[1:].split('s')))
ruletable = make_rule(birth, survival, age_pattern)
# Write temporary .rule file:
with open((rulestring+'.rule'), 'w') as fp:
write_table(fp, rulestring, *ruletable)
# Convert .rule file into C/C++ code:
rule2files(rulestring + '.rule')
# Link code into lifelib:
make_header(rulestring)
......@@ -19,6 +19,7 @@ genus_list.append({'name': 'bsfkl', 'regex': 'b1?2?3?4?5?6?7?8?s0?1?2?3?4?5?6?7?
genus_list.append({'name': 'hrot', 'regex': 'r[2345]b[0-9a-f]*s[0-9a-f]*z?'})
genus_list.append({'name': 'ghrot', 'regex': 'g[1-9][0-9]*r[2345]b[0-9a-f]*s[0-9a-f]*z?'})
genus_list.append({'name': 'deficient', 'regex': 'b[1-8ceaiknjqrytwz-]*s[0-8ceaiknjqrytwz-]*dp?'})
genus_list.append({'name': 'genext', 'regex': 'b[1-8ceaiknjqrytwz-]*s[0-8ceaiknjqrytwz-]*d(0[-])?[1-9][0-9]*([-][1-9][0-9]*)*'})
# This one must always be last as a 'catch-all' for custom rules with
# no more than 256 states:
......
......@@ -5,6 +5,15 @@ import os
class TestRuleTrees(unittest.TestCase):
def test_wright(self):
sess = lifelib.load_rules('b3456s235678d1-7', 'b2s34678d', force_compile=True)
lt = sess.lifetree()
x = lt.pattern('6.F$3.A.GFG$.2ABA2H$A2BFA.G$ABDFH$2ADBHF$2.CAGD.H$2.2ADEH$3.2ADAHC$5.2BDB$5.3A2.H$10.H!', 'b3456s235678d1-7')
self.assertEqual(x.apgcode, 'xq24_0sc315ek8gzx1x2l1a_y0aagogzw11w2368e4_wgcco8zy223iu_xggz44x2')
y = lt.pattern('xq83_0h_4h0a', 'b2s34678d')
self.assertEqual(y.apgcode, 'xq83_0h_4h0a')
def test_langtons_loops(self):
filename = os.path.join(lifelib.lifelib_dir, 'rules', 'source', 'Langtons-Loops.table')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment