Exemplo n.º 1
0
class Simulation:
	def __init__(self):
		"""
			Initializes a simulation creating the world (which is a grid),
			creating a dictionary of critters, the keys are tuples representing the positions
			and the values are the critters, spawning food and then syncing everything with the
			world
		"""
		self.world = Grid(Environment_s.width, Environment_s.height)
		self.world_age = 0

		self.population = {}
		for i in range(Simulation_s.n_crit):
			c = Critter(Critter_s.randpos())
			self.population[c.pos] = c

		self.food = Food(Simulation_s.n_food)
		self.sync()

	def sync(self):
		"""
			Overlaps the current grid world with the updated one
		"""
		self.world = Grid(Environment_s.width, Environment_s.height)

		for pos in self.food.food:
			self.world.set_value(pos[0], pos[1], Environment_s.grid_food)

		for pos in self.population.keys():
			self.world.set_value(pos[0], pos[1], Environment_s.grid_crit)

	def step(self):
		"""
			Makes the population move by one step, reproduce according to the constraints and
			will, increase world's age, spawn new food and update the world
		"""
		children = []
		critters = self.population.values()
		old_pos  = self.population.keys()
		
		for crit in critters:
			self.world.set_value(crit.pos[0], crit.pos[1], Environment_s.grid_void)	# clear the space on the world 
			
			sub = self.world.sub_grid(crit.pos)						# critter's view
			partners_pos = sub.get_type(Environment_s.grid_crit)	# relative position of the potential partners
			partners = []											# potential partners

			for pos in partners_pos:
				pos = (crit.pos[0]+pos[0] - Critter_s.sight, crit.pos[1]+pos[1] - Critter_s.sight)	# absolute position of the partner
				partners.append(self.population[pos])

			old_pos  = crit.pos
			children += crit.process(self.world, partners)

			if self.food.food.has_key(crit.pos):
				crit.eat(self.food.food.pop(crit.pos))	# removes the food from the food list and increments the energy of the critter
				self.world.set_value(crit.pos[0], crit.pos[1], Environment_s.grid_void) # removes the food from the world

			self.population.pop(old_pos)	# removes critter from old position
			if not crit.is_dead():
				if self.population.has_key(crit.pos):
					crit.pos = old_pos				# if the new position is occupied by another critter don't move
				self.population[crit.pos] = crit 	# set critter in the new (or old) position
				self.world.set_value(crit.pos[0], crit.pos[1], Environment_s.grid_crit)	# updates the world 

		self.add_children(children)				# add children in the population list
		self.world_age += 1						# increments world's age
		self.food.add(1, None, self.world_age)	# spawns randomly 1 food
		self.sync()

	def add_children(self, children):
		"""
			Adds the given children on the population list of the simulation

			Parameter children is a list of critters
		"""
		for child in children:
			if not self.population.has_key(child.pos):
				self.population[child.pos] = child

	def min_stat(self, field):
		"""
			Returns the critter with the lowest value of field
			or None if the population is empty

			Parameter field is a string indicating the attribute to compare
		"""
		l = [(getattr(c, field), c) for c in self.population.values()] # tuples with (value, critter)
		return min((l, [(None, None)])[l == []])[1]

	def max_stat(self, field):
		"""
			Returns the critter with the highest value of field
			or None if the population is empty

			Parameter field is a string indicating the attribute to compare
		"""
		l = [(getattr(c, field), c) for c in self.population.values()] # tuples with (value, critter)
		return max((l, [(None, None)])[l == []])[1]
		
	def sum_stat(self, field):
		"""
			Returns the sum of the values in field of the whole population 
			or 0 if the population is empty

			Parameter field is a string indicating the attribute to sum
		"""
		l = [getattr(c, field) for c in self.population.values()] # list of values
		return sum((l, [0])[l == []])
		
	def average_stat(self, field):
		"""
			Returns the average of the values in field of the whole population
			or 0 if the population is empty

			Parameter field is a string indicating the attribute to average
		"""
		return float(self.sum_stat(field))/(len(self.population), 1)[len(self.population) == 0]

#########temporary until new gui#############
	def stats(self):
		"""
			Statistics sample
			Returns a string with some informations about the world and about the population
		"""
		max_energy = self.max_stat("energy")
		min_energy = self.min_stat("energy")
		oldest = self.max_stat("age")
		average_age = self.average_stat("age")
		average_energy = self.average_stat("energy")
	
		if max_energy == None or min_energy == None or oldest == None:
			return ["World age: " + str(self.world_age), 
				 	"Food quantity: " + str(len(self.food.food))]
		stats = [str(len(self.population)) + " critters.",
				 "World age: " + str(self.world_age), 
				 "Food quantity: " + str(len(self.food.food)),
				 "Max energy critter[" + str(max_energy) + "]",
				 "Min energy critter[" + str(min_energy) + "]",
				 "Oldest critter[" + str(oldest) + "]",
				 "Average age: " + str(average_age),
				 "Average energy: " + str(average_energy)
				 ]
		return stats