class Agent: """ A class that models the behavior of wild creatures. Args: x (float): The agents initial position. See :attr:`model2.Agent.x` id(int): An id used by the simulation class to keep track of agents. See :attr:`model2.Agent.id` iq (int): The number of neurons in every hidden layer of the agent's neural network that is responsible for movement. See :attr:`model2.Agent.iq` eq (int): The number of neurons in every hidden layer of the agent's neural network that is responsible for agent to agent interactions. See :attr:`model2.Agent.eq` mass(int): The mass of the agent represents its size. See :attr:`model2.Agent.mass` final_mass(int): The final mass of the agent. See :attr:`model2.Agent.final_mass` breed_mass_div(float): The initial mass to final mass ratio of the agents children. See :attr:`model2.Agent.breed_mass_div` breed_chance(float): The chance for the agent to breed at every step. See :attr:`model2.Agent.breed_chance` size_factor(float): A variable used by the sim's movement and collision engines to control the size of the simulation. See :attr:`model2.Sim.size_factor` move_brain(NeuralNetwork): The neural network the agent will use to decide where to move. This is either provided by the creature's parent (via a mutated brain) or is generated from scratch (for the initial population) See :attr:`model2.Agent.move_brain`. social_brain(NeuralNetwork): The neural network the agent will use to interact with other agents. This is either provided by the creature's parent (via a mutated brain) or is generated from scratch (for the initial population) See :attr:`model2.Agent.social_brain`. parent_id(int): This variable is used to detect close family by the simulation. It is then used as a variable in agent interactions. See :attr:`model2.Agent.parent_id` Attributes: energy(float): Tracks the amount of energy an agent has. energy is acquired by eating and lost by moving or thinking. Energy allows agents to grow and regenerate health. If an agent has low energy, he will take damage. See :meth:`model2.Agent.think`, :meth:`model2.Agent.move`, :meth:`model2.interact` speed(float): The maximum velocity at which the creature can move. Inversely related to mass (1/mass) health(float): How much damage can a creature sustain. When it reaches 0, the creature dies. x (float): The agents position. See :meth:`model2.Agent.move`. id(int): An id used by the simulation class to keep track of agents. See :attr:`model2.Agent.parent_id` for use case. iq (int): The number of neurons in every hidden layer of the agent's neural network that is responsible for movement. See :attr:`model2.Agent.move_brain` for use case. eq (int): The number of neurons in every hidden layer of the agent's neural network that is responsible for agent to agent interactions. See :attr:`model2.Agent.social_brain` for use case mass(int): The mass of the agent, represents its size. final_mass(int): The final mass of the agent. breed_mass_div(float): The initial mass to final mass ratio of the agent's children. See :meth:`model2.Agent.breed`. breed_chance(float): The chance for the agent to breed at every step. See :meth:`model2.Agent.breed` size_factor(float): A variable used by the sim's movement and collision engines to control the size of the simulation. See :attr:`model2.Sim.size_factor` move_brain(NeuralNetwork): The neural network the agent will use to decide where to move. Structure: [3, iq, iq, 1] See :meth:`model2.Agent.think` and :attr:`model2.Agent.iq`. social_brain(NeuralNetwork): The neural network the agent will use to interact with other agents. Structure [4, eq, eq, 2] See :meth:`model2.interact` and :attr:`model2.Agent.eq`. parent_id(int): This variable is used to detect close family by the simulation. It is then used as a variable in agent interactions See :attr:`model2.Agent.id`. """ def __init__(self, iq, eq, mass, x, id, final_mass, breed_mass_div, breed_chance, size_factor, move_brain=None, social_brain=None, parent_id=None): self.iq = iq self.eq = eq if move_brain is not None: self.move_brain = move_brain else: if iq == 1: # if the size of the hidden layers is 1, the amount of hidden layers doesn't matter self.move_brain = NeuralNetwork([3, 1]) else: self.move_brain = NeuralNetwork([3, iq, iq, 1]) if social_brain is not None: self.social_brain = social_brain else: if eq == 1: # if the size of the hidden layers is 1, the amount of hidden layers doesn't matter self.social_brain = NeuralNetwork([4, eq, 2]) else: self.social_brain = NeuralNetwork([4, eq, eq, 2]) self.parent_id = parent_id self.mass = mass self.energy = mass self.speed = (1 / mass) * size_factor * G_SPEED_FACTOR self.health = mass self.final_mass = final_mass self.breed_mass_div = breed_mass_div self.breed_chance = breed_chance self.x = x self.id = id self.size_factor = size_factor def age(self) -> None: """ Make the agent experience aging See Also: :attr:`model2.Agent.health` """ if self.mass > self.final_mass * AGING_TIME: self.health -= AGE_CONST def think(self, d_food: float, d_agent: float, s_agent: 'Agent') -> float: """ Trigger the agent's :attr:`model2.Agent.move_brain` and make it decide where to go. Energy is subtracted for thinking using :attr:`model2.Agent.iq`. See Also: :attr:`model2.Agent.move` Args: d_food(float): The distance to the nearest food item d_agent(float): The distance to the nearest agent s_agent(Agent): The strength of the nearest agent Returns: float: DX, fed into :meth:`model2.Agent.move` """ out = self.move_brain.feed_forward([ map_from_to(d_food, -1, 1, 0, 1), map_from_to(d_agent, -1, 1, 0, 1), int(s_agent.mass > self.mass) ]) self.energy -= self.iq * INT_CONST return map_from_to(float(out[0]), 0, 1, -self.speed, self.speed) def move(self, dx: float) -> None: """ Apply the velocity calculated in the think function to the agent's position Energy is subtracted for moving. See :attr:`model2.Agent.energy` Args: dx: Distance to travel See Also: :attr:`model2.Agent.think` """ self.x += dx if self.x > 1: # make the world round self.x = 1 - self.x if self.x < -1: self.x = -self.x - 1 self.energy -= MOV_CONST * dx def breed(self, nid: int) -> 'Agent': """ Make the agent have a child. Note that the child's mass is removed from the agent's mass on childbirth See Also: :attr:`model2.Agent.breed_chance` :attr:`model2.Agent.breed_mass_div` Args: nid(int): The id of the new child Returns: Agent: A child that is a mutated version of the agent, None if the agent died in childbirth """ nb = copy.deepcopy(self.move_brain) nb.mutate() nsb = copy.deepcopy(self.social_brain) nsb.mutate() nm = np.ceil( (self.mass + random.randrange(-10, 10)) * self.breed_mass_div) self.health -= nm self.mass -= nm if self.health <= 0 or self.mass < 1 or nm < 1: self.health = -1 return None # died in childbirth return Agent(self.iq, self.eq, nm, self.x + random.uniform(0.001, -0.001), nid, np.ceil(nm / self.breed_mass_div), self.breed_mass_div + random.uniform(0.01, -0.01), self.breed_chance + random.uniform(0.01, -0.01), self.size_factor, nb, nsb, self.id) def eat(self, food: int) -> None: """ Make the agent eat, increases agent energy (:attr:`model2.Agent.energy`) and mass (:attr:`model2.Agent.mass`) if the agent has not reached its final mass and has energy to spare Args: food(int): The amount of food the agent should eat """ if self.mass < self.final_mass and self.energy / self.mass > ENGB_CONST: self.mass += food # update mass and speed self.speed = (1 / self.mass) * self.size_factor * G_SPEED_FACTOR else: self.energy += food def __str__(self) -> str: return str(self.id)