# Cellular Automata Driver # # Rajarshi Guha, 4th December 2002 # # # # T O D O # ------- #Currently the rules cannot have any memory of the previous #states of the grid. # #Redraws the grid for each generation - could/should be #optimized. # #There is no way to save a given initial grid #------------------ # Update 5/12/2002 # Added options for initial living population # Added option to load intial state from external file import sys, time, getopt, string from random import * from gtk import * try: import CARules except: print """ You must have the file CARules.py[c] in your PYTHONPATH or current working directory. You can download it from http://jijo.cjb.net/code/python/python.html """ sys.exit(0) # Global variables set to default values rule = 'CARules.flipflop' startiter = 1 grid = [] speed = .4 startpercent = .2 class CADrawingArea(GtkDrawingArea): def __init__(self,x = 20,y = 20): self.oldx = 0 self.oldy = 0 self.x = x self.y = y self.pixmap = None GtkDrawingArea.__init__(self) self.connect("expose_event", self.expose_event) self.connect("configure_event", self.configure_event) self.set_events(GDK.EXPOSURE_MASK) def configure_event(self, widget, event): win = widget.get_window() self.pixmap = create_pixmap(win, win.width, win.height, -1) draw_rectangle(self.pixmap, widget.get_style().white_gc, TRUE, 0 , 0, win.width, win.height) return TRUE def expose_event(self, widget, event): area = event.area win = widget.get_window() gc = widget.get_style().fg_gc[STATE_NORMAL] # Make the grid incr = win.width / self.x for i in range(0,win.width,incr): draw_line(self.pixmap, widget.get_style().black_gc,i,0,i,win.height) incr = win.height / self.y for i in range(0,win.height,incr): draw_line(self.pixmap, widget.get_style().black_gc,0,i,win.width,i) widget.draw_pixmap(gc, self.pixmap, area[0], area[1], area[0], area[1], area[2], area[3]) return FALSE def destroy(event): idle_remove(idlefunctag) mainquit() def CADriver(cada, load = 'random'): global startiter, grid, speed, startpercent incrx = cada.get_window().width / cada.x incry = cada.get_window().height / cada.y if startiter: if load == 'random': # Init the in memory grid for i in range(0,cada.x): m = [] for j in range(0,cada.y): m.append(0) grid.append(m) # Init the graphical grid with random blocks cl = [] for i in range(0,int(startpercent*cada.x*cada.y)): # Make sure to get unique random coordinates while 1: x = randrange(0,cada.x) y = randrange(0,cada.y) if (x,y) in cl: continue else: cl.append( (x,y) ) break grid[y][x] = 1 else: # we need to load the inital grid from a file f = open(load,'r') for line in f: l = [int(x) for x in string.split(line)] grid.append(l) f.close() # Draw the grid for x in range(0, cada.x): for y in range(0, cada.y): if grid[y][x] == 1: draw_rectangle(cada.pixmap, cada.get_style().black_gc, TRUE, x*incrx, y*incry, incrx, incry) cada.queue_draw() startiter = 0 else: # Iterate over the whole grid to get the new config c = 0 for x in range(0, cada.x): for y in range(0, cada.y): grid[y][x] = eval(rule)(grid, x,y, cada.x,cada.y) c += grid[y][x] # Check if everybody died :( if c == 0: print """ EVERYBODY WILL DIE :( """ x = raw_input("Press any key") sys.exit(0) # Draw the new grid for x in range(0, cada.x): for y in range(0, cada.y): if grid[y][x]: draw_rectangle(cada.pixmap, cada.get_style().black_gc, TRUE, x*incrx, y*incry, incrx, incry) cada.queue_draw() else: draw_rectangle(cada.pixmap, cada.get_style().white_gc, TRUE, x*incrx, y*incry, incrx, incry) cada.queue_draw() time.sleep(speed) return TRUE def usage(): print """ Usage: ca.py [OPTIONS] Where OPTIONS can be: -h,--help This message -x,--x Number of cells in the X direction (default = 10) -y,--y Number of cells in the Y direction (default = 10) -s,--speed Number of seconds between each generation (the default of .4 is slow enough to view the evolution of the cells -r,--rule The name of the rule to use (default is flipflop) -p,--percent The percentage of the total number of cells that are to be made 'alive' at the beginning (default is .2) -l,--load The argument can be 'random' or a file name. If 'random' the grid is initialized randomly. If a filename is specified the grid is loaded from the file. The file should have be in matrix form, 0 for dead cells, 1 for living cells. Each element of the matrix should be separated by a space. Example: 1 0 0 0 1 0 0 0 1 for a 3x3 grid with 3 cells initially alive """ sys.exit(0) ####################################### # # Main program # ####################################### if __name__ == '__main__': global idlefunctag maxx = 10 maxy = 10 load = 'random' try: opt, args = getopt.getopt(sys.argv[1:], "l:p:x:y:s:r:h", ["load=","percent=","x=","y=","speed=","rule=","help"]) except getopt.GetoptError: usage() for o,a in opt: if o in ('-h','--help'): usage() if o in ('-x', '--x'): maxx = int(a) if o in ('-y','--y'): maxy = int(a) if o in ('-s','--speed'): speed = float(a) if o in ('-p','--percent'): startpercent = float(a) if startpercent > 1: startpercent = 1 if o in ('-l','--load'): load = a # We should be able to get X and Y from the file contents if load != 'random': try: f = open(load,'r') except IOError: print """ Error: The file '%s' could'nt be opened """ % (load) sys.exit(0) maxx = len(string.split(f.readline())) maxy = 1 while 1: l = f.readline() if not l: break else: maxy += 1 f.close() if o in ('-r','--rule'): if not CARules.__dict__.has_key(a): print """ Error: The rules function you specified does not exit in CARules.py. Check to make sure you spelled it correctly """ sys.exit(0) rule = 'CARules.'+a # Set upthe GUI w = GtkWindow(WINDOW_TOPLEVEL,"CA Grid"); w.connect('destroy', destroy) w.show() sw = GtkScrolledWindow() sw.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC) sw.show() vp = GtkViewport() vp.show() cada = CADrawingArea(maxx,maxy) cada.set_usize(20*maxx, 20*maxy) cada.show() vp.add(cada) sw.add(vp) w.add(sw) # Set the driver function as an idle function idlefunctag = idle_add(CADriver,cada,load) mainloop()