##PyPond ##A digital life simulator inspired by NanoPond, Avida, Tierra, etc. ## Made by Paras Chopra (http://paraschopra.com/) INST_NUMBER = 21 ## Total number of instructions from random import * import pygame, sys from pygame.locals import * class Cell: #cell_id is the id of the cell #location_x and location_y are the location of the cell in the pond #genome is an array containing genome of the cell #register is the variable holding value of the register #self_ptr is the pointer to the self genome #neigh_ptr is the pointer to the neighbour genome #parents is the array containing (ids of the parents and time) in order of ascending time #generation is the number of generations the cell has been through. A generation is increased everytime other cell writes into it #facing points to direction the cell is facing: 0 top, 1 right, 2 down, 3 left def __init__(self, cell_id=0, energy=0, location_x=0,location_y=0, list_parents=[], genome=[], register=0, self_ptr=0, neigh_ptr=0, facing=0,color=0): self.cell_id=cell_id self.location_x=location_x self.location_y=location_y self.genome=genome self.register=register self.self_ptr=self_ptr self.neigh_ptr=neigh_ptr self.facing=facing self.list_parents=list_parents self.energy=energy self.color=color def add_parent(self,parent): self.list_parents[0:0]=[parent] class Pond: #width is width of the pond #height is the height of the pond #pond_depth is the length of genome #initial_population is the initial population of the pong #mutation_rate is the rate of mutation while executing/reading/writing #permission_rate is the probability of granting permsision to read/write neighbours #mean_energy is alloted initially and after each run #variation_energy is the variation in energy alloted. If variation energy>mean_energy, this would mean that even negative energy can be allotted def __init__(self, width=500, height=500, pond_depth=100, initial_population=100, mutation_rate=0.005, permission_rate=0.01, mean_energy=5000, variation_energy=10000, mean_program_length=50, variation_program_length=20, visual=True, dump=True, filename='test.csv'): self.width=width self.height=height self.initial_population=initial_population self.mutation_rate=mutation_rate self.permission_rate=permission_rate self.mean_energy=mean_energy self.variation_energy=variation_energy self.mean_program_length=mean_program_length self.variation_program_length=variation_program_length self.not_none=[] ## array to keep track of all cells present in the pond self.pond_depth=pond_depth self.cell_counter=1 ## counter to track cell's id self.pond=[] self.visual=visual self.dump=dump if self.dump: self.filename=filename self.file=open(filename,'w') self.file.write(str(width)+"|"+str(height)+"|"+str(pond_depth)+"|"+str(mutation_rate)+"|"+str(permission_rate)+"|"+str(mean_energy)+"|"+str(variation_energy)+"|"+str(mean_program_length)+"|"+str(variation_program_length)+"\n") ##create blank cells for i in range(width): self.pond.append([]) self.pond[i]=[None]*height for j in range(height): location_x=i location_y=j cell_id=self.cell_counter facing=randint(0,3) genome=[0]*self.pond_depth ## default genome temp=Cell(location_x=location_x,location_y=location_y,energy=0,cell_id=cell_id,facing=facing,genome=genome,list_parents=[]) temp.energy=self.mean_energy+randint(-1*self.variation_energy,1*self.variation_energy) if temp.energy<0: temp.energy=0 self.generate_random_genome(temp) self.pond[location_x][location_y]=temp if self.dump: self.dump_cell(temp, 0) self.cell_counter=self.cell_counter+1 ##generate cells at random locations for i in range(initial_population): location_x=randint(0,width-1) location_y=randint(0,height-1) cell=self.pond[location_x][location_y] while [cell.location_x,cell.location_y] in self.not_none: location_x=randint(0,width-1) location_y=randint(0,height-1) cell=self.pond[location_x][location_y] #self.generate_random_genome(cell) #cell.energy=self.mean_energy+randint(-1*self.variation_energy,1*self.variation_energy) #if cell.energy<0: #cell.energy=0 #if self.dump: #self.dump_cell(cell, 0) self.not_none.append([location_x,location_y]) ##initialize graphics if visual: pygame.init() self.window=pygame.display.set_mode((width,height)) pygame.display.set_caption('Simulating Life...') self.screen = pygame.display.get_surface() self.screen.fill((0,0,0)) def run(self,execution_runs=10000): if execution_runs>0: for i in range(1,execution_runs+1): self.run_once(i) else: i=1 running=True while running: for event in pygame.event.get(): if event.type == QUIT: running=False else: self.run_once(i) i=i+1 if self.dump: self.file.close() #execution runs is the number of times execution should run.. at one time, only one program is executed def run_once(self,i=0): rand_cell=None neigh=None rand_cell=randint(0,len(self.not_none)-1) # pick a random cell rand_cell=self.pond[self.not_none[rand_cell][0]][self.not_none[rand_cell][1]] rand_cell.register=0 rand_cell.self_ptr=0 rand_cell.neigh_ptr=0 permission=self.get_permission_status(rand_cell) neigh=self.get_neighbour(rand_cell) print i,rand_cell.cell_id,neigh.cell_id #execute it stop=False j=0 modified_cells=[] while j=0: inside_inst=rand_cell.genome[k] if inside_inst==18: ##another REP stack.append(k) elif inside_inst==17: ## JUMP stack.pop() if not stack: #empty stack break k=k-1 j=k+1 elif inst==19: #CMPSELF compares self_ptr instruction with register and returns 0 if equal if rand_cell.genome[rand_cell.self_ptr]==rand_cell.register: rand_cell.register=0 elif inst==20: #CMPNEIGH compares neigh_ptr instruction with register and returns 0 if equal if neigh.genome[rand_cell.neigh_ptr]==rand_cell.register: rand_cell.register=0 j=j+1 rand_cell.energy=rand_cell.energy+randint(-1*self.variation_energy,1*self.variation_energy) if self.visual: color=(rand_cell.color*1000) % (255*3+1) r=abs(color - 255)%256 g = abs(color-r-255)%256 b = abs(color-r-g-255)%256 self.screen.set_at((rand_cell.location_x, rand_cell.location_y), (r, g, b)) print rand_cell.color,color,r,g,b pygame.display.flip() if rand_cell.energy<0: rand_cell.energy=0 if self.dump: self.dump_cell(rand_cell,i) for location in modified_cells: self.dump_cell(self.pond[location[0]][location[1]],i) #print rand_cell.genome, rand_cell.register, neigh.genome def dump_cell(self,cell,time): self.file.write(str(time)+"|"+str(cell.cell_id)+"|"+str(cell.genome)+"|"+str(cell.location_x)+"|"+str(cell.location_y)+"|"+str(cell.list_parents)+"|"+str(cell.energy)+"|"+str(cell.facing)+"|"+str(cell.color)+"\n") def generate_random_genome(self,cell): program_length=self.mean_program_length+randint(-1*self.variation_program_length,1*self.variation_program_length) for i in range(program_length): cell.genome[i]=randint(0,INST_NUMBER-1) cell.color=cell.color+cell.genome[i] #used to check if permission is there def get_permission_status(self,cell): perm = False neigh=self.get_neighbour(cell) #check for common parents for parent in cell.list_parents: if perm==True: break parent_id = parent[0] for j in neigh.list_parents: if j[0]==parent_id: perm=True return (perm or (random() <= self.permission_rate) or (neigh.genome[0]==0) or (neigh.energy==0)) def get_neighbour(self,cell): if cell.facing==0: return self.pond[cell.location_x][cell.location_y-1] elif cell.facing==1: return self.pond[(cell.location_x+1)% self.width][cell.location_y] elif cell.facing==2: return self.pond[cell.location_x][(cell.location_y+1)%self.height] else: return self.pond[cell.location_x-1][cell.location_y] if __name__=="__main__": a=Pond(100,100,visual=True,filename='test.dat') a.run(1000)