# cellular automaton __version__ = "0.1" __author__ = "Jason Michael Adams (jmadams@cs.cmu.edu, http://mendicantbug.com)" __license__ = "http://creativecommons.org/licenses/BSD/" # please cite my website if you use or post this code (http://mendicantbug.com) import math import sys class CellularAutomaton(object): """Usage: CellularAutomaton(boardSize, ruleSize, automatonNumber) """ def __init__(self, size, rule_size, effects, initial_board=None,alpha=None): assert size % 2 == 1, "Size of board must be odd." self.size = size self.time = 0 self.rule_size = rule_size rules = list() for x in xrange(2 ** rule_size): rules.append(make_binary_rep(x, rule_size)) rules.reverse() ex = make_binary_rep(effects, 2 ** rule_size) self.rules = RuleSet(rules, ex) self.board = dict() if alpha is None: self.alpha = [0,1] else: self.alpha = alpha self.board[0] = self._init_board(initial_board) if initial_board is None: self.board[0][self.size / 2] = self.alpha[1] def increment(self): self.time += 1 self.board[self.time] = self._init_board() for x in xrange(self.size): context = list() rn = self.rule_size / 2 for y in xrange(self.rule_size): context.append(self.board[self.time - 1][(x + y - rn) % self.size]) self.board[self.time][x] = self.rules * context return self.board[self.time] def run(self, cycles): for x in xrange(cycles): tmp = self.increment() print self def _init_board(self, initial_board=None): if initial_board is None: board = list() for x in xrange(self.size): board.append(self.alpha[0]) board[self.size / 2] = self.alpha[1] else: assert len(initial_board) == size, "Size of initial board setting must be equal to size of board." board = initial_board return board def __repr__(self): keys = self.board.keys() keys.sort() tmps = list() for key in keys: tmps.append(self.__repb__(key)) return '\n'.join(tmps) def __repb__(self, b): board = self.board[b] tmp = "" for x in xrange(len(self.board[b])): tmp += str(self.board[b][x]) return tmp class Rule(object): """Usage: Rule(rule, effect) The rule parameter is a odd-length list indicating the prior pattern that will trigger the single value in effect. The effect parameter should be anything that has a string representation. """ def __init__(self, rule, effect): assert len(rule) % 2 == 1, "Size of rule must be odd." self.rule = rule self.effect = effect self.size = len(rule) def __mul__(self, context): assert len(context) == self.size, "Size of context must be equal to size of the rule (%d)." % (self.size) accept = True for x in xrange(len(context)): if self.rule[x] != context[x]: accept = False break if accept is True: return self.effect else: return None def __repr__(self): return "%s => %s" % (str(self.rule), self.effect) class RuleSet(object): """A set of Rule objects that can be applied to a given context. """ def __init__(self, rules, effects): self.rules = list() for rx in xrange(len(rules)): r = Rule(rules[rx], effects[rx]) self.rules.append(r) def __mul__(self, context): middle = context[len(context) / 2] for rule in self.rules: outp = rule * context if outp is not None: return outp return middle def __repr__(self): return str(self.rules) def make_binary_rep(num, size=0): outp = list() if size <= 0: size = int(round(math.log(num) / math.log(2))) for x in xrange(size - 1, -1, -1): if (num & (2 ** x)) > 0: outp.append(1) else: outp.append(0) return outp if __name__ == '__main__': if len(sys.argv) < 4: print "Usage: %s <# cycles>" % (sys.argv[0]) sys.exit(0) nhsize = int(sys.argv[1]) autonum = int(sys.argv[2]) cycles = int(sys.argv[3]) ca = CellularAutomaton(81, nhsize, autonum) ca.run(cycles)