import sys, pygame, random, time
def random_color():
  color1 = random.randrange(0,255)
  color2 = random.randrange(0,255)
  color3 = random.randrange(0,255)
  return color1,color2,color3

class Creature:
    "A simple creature"
    def __init__(self):        
      self.skin_color = random_color()
    def fitness(self,background_color):
        fitness0 = self.skin_color[0]-background_color[0] 
        fitness1 = self.skin_color[1]-background_color[1] 
        fitness2 = self.skin_color[2]-background_color[2]
        if fitness0 < 0 : fitness0 += 255
        if fitness1 < 0 : fitness1 += 255
        if fitness2 < 0 : fitness2 += 255
        return fitness0 + fitness1 + fitness2
    def mate(self,creature):
      new_color = (self.skin_color[0] + creature.skin_color[0] ) / 2 , (self.skin_color[1] + creature.skin_color[1] ) / 2 , (self.skin_color[2] + creature.skin_color[2] ) / 2
      child = Creature()
      child.skin_color= new_color
      return child
    def mutate(self):
      new_color = (self.skin_color[0] - random.randrange(-15,15)) % 255,  (self.skin_color[1] - random.randrange(-20,20)) % 255 , (self.skin_color[2] - random.randrange(-20,20)) % 255 
      self.skin_color = new_color

def create_new_population(population,background_color):
  new_population = []
  population_size = len(population)
  population.sort(lambda x, y: cmp(x.fitness(background_color), y.fitness(background_color)))
  
  #the least fit die:
  population = population[0:len(population)-len(population)/4]
  
  #survival of the fittest:
  for i in range(len(new_population),len(new_population)+population_size*10/15):
    new_population.append(population[i])  
  
  #some fornicate:
  for i in range(len(new_population),len(new_population)+population_size*4/15):
      parent1 = population[random.randrange(0,len(population)/3)]
      parent2 = population[random.randrange(0,len(population)/3)]
      child = parent1.mate(parent2)
      new_population.append(child) 
  
  #others mutate
  for i in range(len(new_population),population_size):
    mutant = population[random.randrange(0,len(population))]
    mutant.mutate()
    new_population.append(mutant) 
  
  random.shuffle(new_population)  
  return new_population 
  
def population_fitness(population,background_color):
  total_fitness = 0
  for i in range(0,len(population)):
    total_fitness += population[i].fitness(background_color)
  #print total_fitness
  return total_fitness;
 
size = width, height = 1024, 768
population_size=10000
spacing = 2
creature_size = ( width * height / population_size - 4) ** 0.5 
population = []
background = random_color() 

pygame.init()
screen = pygame.display.set_mode(size,pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )
screen.fill(background)

for x in range(0,population_size):
    creature = Creature()
    population.append(creature)

pygame.display.flip()

while 1:
  screen.fill(background)
  population = create_new_population(population,background)
  population_fitness(population,background)
  
  
  for event in pygame.event.get():
    if event.type == pygame.QUIT: sys.exit()
    elif event.type == pygame.MOUSEBUTTONDOWN : background = random_color() 
    elif event.type == pygame.VIDEORESIZE:
      screen=pygame.display.set_mode(event.dict['size'],pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE)
      pygame.display.flip()
      width, height = screen.get_width() , screen.get_height()
      creature_size = ( width * height / population_size - 4) ** 0.5 
      screen.fill(background)
      pygame.display.flip()   
  
  y = 0
  x = 0  
  for i in range(0,len(population)):    
    creature = population[i]
    pygame.draw.rect(screen,creature.skin_color, (x,y, creature_size, creature_size))
    x +=  spacing + creature_size
    if x + creature_size + spacing> width : 
      x = 0
      y += creature_size + spacing
  pygame.display.flip()    
  
  new_background =( background[0] + random.randrange(-1,2)  )% 255, (background[1] + random.randrange(-2,2))%255 ,( background[2] + random.randrange(-1,2)) % 255
  background = new_background
  time.sleep(0.1)
  
 
  
  
  

