Skip to content

Commit c0e03bc

Browse files
committed
Add rock-paper-scissors rule (ljvmiranda921#26)
Addresses ljvmiranda921#26. The current implemenation maintains separate 2D board matrices for each state to allow the use of existing lifeforms.
1 parent 8bad223 commit c0e03bc

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

seagull/rules.py

+49
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,55 @@ def life_rule(X: np.ndarray, rulestring: str) -> np.ndarray:
4848
return birth_rule | survival_rule
4949

5050

51+
def rps_life_rule(X: np.ndarray, cycle_thresholds: list) -> np.ndarray:
52+
"""A generalized rock-paper-scissors (RPS) life rule that accepts a rulestring in B/S notation
53+
54+
An RPS rule is a multistate rule in which states are arranged in a cycle
55+
e.g. red->green->blue->red. A red cell which has a certain number, N, of green neighbours
56+
becomes green. A green cell with N blue neighbours becomes blue, and so on.
57+
58+
Parameters
59+
----------
60+
X : np.ndarray
61+
A three-dimensional array of input board matrices, one for each state. The order of the
62+
matrices defines the cyclic ordering of the states.
63+
cycle_thresholds : list
64+
A list of integers, each of which is a number of appropriately coloured neighbours which
65+
will cause a state transition in a cell.
66+
67+
Returns
68+
-------
69+
np.ndarray
70+
Updated boards after applying the rule
71+
"""
72+
# Compute neighbors matrix for each state.
73+
neighbors = np.zeros_like(X)
74+
for state in range(len(X)):
75+
neighbors[state] = _count_neighbors(X[state])
76+
77+
updated = np.zeros_like(X)
78+
for state in range(len(X)):
79+
# A cell cycles into this state if a sufficient number of its
80+
# neighbors are in this state.
81+
cycles_in = np.logical_and(
82+
X[state - 1] == 1, np.isin(neighbors[state], cycle_thresholds)
83+
)
84+
# Given that it is alive, a cell cycles out of this state if a
85+
# sufficient number of its neighbors are in the next state.
86+
# It must also be in this state.
87+
cycles_out = np.isin(neighbors[(state + 1) % len(X)], cycle_thresholds)
88+
# A cell lives in this state in the next generation if it either is alive
89+
# and does not cycle out, or it cycles in.
90+
alive = np.logical_or(
91+
np.logical_and(X[state] == 1, np.logical_not(cycles_out)),
92+
cycles_in,
93+
)
94+
95+
updated[state] = alive
96+
97+
return updated
98+
99+
51100
def _parse_rulestring(r: str) -> Tuple[List[int], List[int]]:
52101
"""Parse a rulestring"""
53102
pattern = re.compile("B([0-8]+)?/S([0-8]+)?")

0 commit comments

Comments
 (0)