コード例 #1
0
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)