119 lines
4.0 KiB
Python
119 lines
4.0 KiB
Python
"""
|
|
Top level module representing a Cellular Automata Machine.
|
|
|
|
The CAM consists of any number of cell planes that allow for increasingly complex cellular automata.
|
|
This is the top-level module that should be used by anyone wanting to work with fifth, and provides
|
|
all methods needed (i.e. supported) to interact/configure the cellular automata as desired.
|
|
|
|
@author: jrpotter
|
|
@date: June 01, 2015
|
|
"""
|
|
import time
|
|
import copy
|
|
|
|
import ruleset as rs
|
|
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.animation as ani
|
|
|
|
|
|
class CAM:
|
|
"""
|
|
Represents a Cellular Automata Machine (CAM).
|
|
|
|
A CAM consists of a series of "cell planes" which represent a separate numpy grid instance. There
|
|
should always be at least one cell plane, dubbed the "master", since all other planes cannot be handled
|
|
directly, but instead mirror the master plane, and reflect these changes after a given number of
|
|
"ticks."
|
|
|
|
A tick represents an interval of time after which all states should be updated, and, therefore, all
|
|
cell planes should be updated. Certain planes may or may not change every tick, but instead on every
|
|
nth tick, allowing for more sophisticated views such as ECHOing and TRACE-ing.
|
|
"""
|
|
|
|
def __init__(self, cps=1, states=100, dimen=2):
|
|
"""
|
|
|
|
@cps: Cell planes. By default this is 1, but can be any positive number. Any non-positive number
|
|
is assumed to be 1.
|
|
|
|
@states: The number of cells that should be included in any dimension. The number of total states
|
|
will be cps * states^dimen
|
|
|
|
@dimen: The dimensions of the cellular automata. For example, for an N-tuple array, the dimension is N.
|
|
|
|
"""
|
|
plane_count = max(cps, 1)
|
|
grid_dimen = (states,) * dimen
|
|
|
|
self.planes = np.zeros((plane_count,) + grid_dimen, dtype='int32')
|
|
self.master = self.planes[0]
|
|
|
|
|
|
def randomize(self, propagate=True):
|
|
"""
|
|
Set the master grid to a random configuration.
|
|
|
|
If propagate is set to True, also immediately change all other cell planes to match.
|
|
"""
|
|
self.master[:] = np.random.random_integers(0, 1, self.master.shape)
|
|
for plane in self.planes[1:]:
|
|
plane[:] = self.master
|
|
|
|
|
|
def tick(self, rules, *args):
|
|
"""
|
|
Modify all states in a given CAM "simultaneously".
|
|
|
|
The tick function should be called whenever we want to change the current status of the grid.
|
|
Every time the tick is called, the ruleset is applied to each cell and the next set of states
|
|
is placed into the master grid. Depending on the timing specifications set by the user, this
|
|
may also change secondary cell planes (the master is always updated on each tick).
|
|
"""
|
|
tmp = np.copy(self.master)
|
|
for i in range(len(self.master.flat)):
|
|
tmp.flat[i] = rules.applyTo(i, self.master, *args)
|
|
|
|
self.master[:] = tmp
|
|
|
|
|
|
def start_plot(self, clock, rules, *args):
|
|
"""
|
|
Initiates main graphical loop.
|
|
|
|
The following function displays the graphical component (through use of matplotlib), and triggers the
|
|
next tick for every "clock" milliseconds. This should only be called if the automata is 2 or 3 dimensional.
|
|
"""
|
|
fig, ax = plt.subplots()
|
|
|
|
ax.set_frame_on(False)
|
|
ax.get_xaxis().set_visible(False)
|
|
ax.get_yaxis().set_visible(False)
|
|
|
|
mshown = plt.matshow(self.master, fig.number)
|
|
|
|
def animate(frame):
|
|
self.tick(rules, *args)
|
|
mshown.set_array(self.master)
|
|
fig.canvas.draw()
|
|
|
|
ani.FuncAnimation(fig, animate, interval=clock)
|
|
plt.axis('off')
|
|
plt.show()
|
|
|
|
|
|
def start_console(self, clock, rules, *args):
|
|
"""
|
|
Initates main console loop.
|
|
|
|
Works similarly to start_plot but prints out to the console.
|
|
TODO: Incorporate curses, instead of just printing repeatedly.
|
|
"""
|
|
while True:
|
|
print(self.master)
|
|
time.sleep(clock / 1000)
|
|
self.tick(rules, *args)
|
|
|
|
|