import { AgentSet, CellGrid } from '../structures'; import { RandomGenerator, Vector2, Angle, Color } from '../numbers'; import type Cell from './Cell'; import { BoundaryCondition } from '../structures/CellGrid'; export declare enum AgentStyle { CIRCLE = "circle", SQUARE = "square", TRIANGLE = "triangle" } export type TraitOptions = { min?: number; max?: number; }; /** * Traits represent some custom property of the agent. * Traits are inherited by children via the Agent's `reproduce` method * and are initialized with the parent's current value. Traits are constants * and do not change independently over time or by reproduction. * * For genetic traits that are inherited with mutation, see {@link GeneticTraitValue}. * For dynamic traits that are a function of the agent's current state, see {@link DynamicTrait}. */ export declare const Trait: (options?: TraitOptions) => (target: T, propertyKey: string) => void; export type GeneticTraitValue = T | Array; export type GeneticTraitOptions = { min?: number; max?: number; /** * A function that mutates the trait value slightly. * @param value The current value of the trait * @param rng A reference to the agent's random number generator * @param traitMutationRate The agent's average mutation rate for genetic traits * @returns New value of the trait */ mutationFunction?: (value: GeneticTraitValue, rng: RandomGenerator, traitMutationRate: number) => GeneticTraitValue; /** * A function that recombines the trait value with another agent's trait value. * @param value1 The current value of the trait * @param value2 The other agent's value of the trait * @param rng A reference to this agent's random number generator * @returns The new value of the trait */ recombinantFunction?: (value1: GeneticTraitValue, value2: GeneticTraitValue, rng: RandomGenerator) => GeneticTraitValue; }; /** * Genetic traits are inherited by children and initialized * with the parent's current value plus some mutation. * This can be used with new properties or to override existing properties. * * Default mutation functions are provided for numbers, booleans, and colors. * * - Numbers: The value is mutated by a random amount between `-traitMutationRate` and `traitMutationRate`. * - Booleans: The value has a `traitMutationRate` chance of flipping. * - Colors: The color's RGB values are mutated by a normally distributed random amount with mean `traitMutationRate`. * * Default recombinant functions are provided for numbers, booleans, and colors. * * - Numbers: The value is the average of the two parent values. * - Booleans: The value is randomly chosen from the two parent values. * - Colors: The color is a blend of the two parent colors. * * These defaults can be overridden by providing custom mutation and recombinant functions. * See {@link GeneticTraitOptions} for more information. * * See also: {@link Trait} */ export declare const GeneticTrait: (options?: GeneticTraitOptions) => (target: T, propertyKey: string) => void; /** * Dynamic traits are `Traits` which are a function of the agent's current state. * This can be used with new properties or to override existing properties. * * See also: {@link Trait} */ export declare function DynamicTrait(f: (agent: T) => V): (target: T, propertyKey: string) => void; /** * An Agent-class decorator that enables the agent's energy to decrease as it moves. * By default, the agent's energy decreases by `metabolism` units per cell moved. * If the agent's energy drops below 0, the agent dies. * * Can also be enabled by setting `this.enableHunger = true` in the agent's constructor. */ export declare function Hungry(constructor: T): { new (...args: any[]): { enableHunger: boolean; }; } & T; export interface AgentConstructor { initialPosition: [number, number]; rotation?: Angle; initialEnergy?: number; maxEnergy?: number; metabolism?: number; reproductionThreshold?: number; sightRange?: number; fov?: Angle; randomSeed?: number; radius?: number; id?: string; color?: Color; strokeColor?: Color; style?: AgentStyle; traitMutationRate?: number; traitCrossoverRate?: number; enableHunger?: boolean; } /** * Base class for all agents in the simulation. * The agent comes with the following defaults: * * - energy = 1 * - maxEnergy = 5 * - metabolism = 0.01 * - reproductionThreshold = maxEnergy / 2 * - sightRange = 10 * - fov = 90 degrees * - radius = 1 * - color = blue * - strokeColor = black * - style = circle * - traitMutationRate = 0.01 * - traitCrossoverRate = 0.5 * * By default, all traits are stable and do not change by reproduction. * This behavior can be overridden by using the `GeneticTrait` decorator. * Moreover, the agent's energy will not decrease as it moves. * This behavior can be enabled using the `Hungry` class decorator. * * No agent should be instantiated directly. Instead, create a subclass that implements the `act` method. * Any custom fields which are to be inherited by children using the `reproduce` method * should be decorated with the `Trait` decorator. */ export default abstract class Agent { /** * The agent's position in the world. * Setting this value directly instead of using the agent's movement methods * will not update the agent's energy level, rotation, and other side effects * applied by the movement methods. * * See also: {@link moveTo()}, {@link move()}, {@link wander()} */ position: Vector2; /** * The agent's previous position in the world */ previousPosition: Vector2; /** * The agent's rotation in radians */ rotation: Angle; /** * The agent's energy level. If the energy level drops below 0, * the agent is considered dead. */ energy: number; /** * The agent's maximum energy level. * The agent's energy level will not increase beyond this value. */ maxEnergy: number; /** * A boolean flag indicating whether the agent is alive or dead. */ isAlive: boolean; /** * The amount of energy the agent loses per cell of distance moved. */ metabolism: number; /** * The energy level at which the agent can reproduce. */ reproductionThreshold: number; /** * The range of the agent's vision measured in cells. */ sightRange: number; /** * The agent's field of view. */ fov: Angle; /** * The radius of the agent, measured in cells. */ radius: number; /** * The agent's color when rendered. */ color: Color; /** * The agent's stroke color when rendered. */ strokeColor: Color; /** * The shape of the agent when rendered. */ style: AgentStyle; /** * The average mutation rate for genetic traits * passed down to children. */ traitMutationRate: number; /** * The probability that a child inherits a * new genetic trait from a parent. */ traitCrossoverRate: number; /** * The random number generator used by the agent. */ rng: RandomGenerator; /** * The unique identifier for the agent */ readonly id: string; /** * Used as a time step for the agent's behavior. */ dt: number; /** * The generation of the agent. * Children created by this agent will have their `generation` * value incremented by 1. */ generation: number; /** * The agent's genetic traits. These traits are inherited by children with some mutation. * Genetic traits should be decorated with the `GeneticTrait` decorator. * See also: {@link GeneticTraitValue} */ geneticTraits: Map & { value: GeneticTraitValue; }>; /** * The agent's inherited traits. These traits are inherited by children without mutation. * Inherited traits should be decorated with the `Trait` or `DynamicTrait` decorator. * See also: {@link Trait}, {@link DynamicTrait} */ inheritedTraits: Map>; className: string; enableHunger: boolean; /** * Agent Constructor * @constructor * @param {AgentConstructor} opts - The options for the agent */ constructor(opts: AgentConstructor); /** * The agent's main behavior. This method should be implemented by subclasses. * It is used to update the agent's state and interact with the world. */ abstract act(grid: CellGrid, agentSets: { [key: string]: AgentSet; }, tick: number): void; /** * Moves the agent to a specified location while updating related properties * such as the agent's energy level and rotation. * * If the next location is outside the world bounds: * - **FINITE** An error will be thrown. * - **PERIODIC** The next location will be recalculated to wrap the agent around to the other side of the world. */ moveTo(world: CellGrid, nextLocation: [number, number]): void; /** * Moves the agent in the direction it is facing. * The agent's default time step is used to determine the distance moved. Else, a specified distance may be used. * * Depending on the world's boundary condition, the agent will behave differently: * - **FINITE** If the next location is outside the world bounds, the agent will move to the edge of the world instead. * - **PERIODIC** If the next location is outside the world bounds, the agent will wrap around to the other side of the world. * * See {@link moveTo()} for Agent side effects. */ move(world: CellGrid, distance?: number): void; /** * Swaps the agent's position with another agent without effecting their energy level or rotation. */ swap(world: CellGrid, other: T): void; /** * The agent moves in a random direction with a specified wiggle angle (default 90 deg). * The agent's `dt` property is used to determine the default distance moved. */ wander(world: CellGrid, options?: { wiggle?: Angle; strideLength?: number; }): void; /** * Rotates the agent to face a specified cell. */ faceCell(world: CellGrid, cell: U): void; /** * Rotates the agent to face a specified agent. */ faceAgent(world: CellGrid, agent: T): void; /** * The agent consumes an environmental resource and gains energy. The source of the energy can be another agent or a cell. * The agent's net energy gain is determined by the source's (Agent or Cell) energy level and the `efficiencyFunction` and `greed` options. * An agent eats by following these rules in order: * 1. Agents are greedy and will take 100% of the energy of the source. This can be modified by the `greed` option. * 2. Agents are perfectly efficient and will add 100% of the taken energy to their energy. This can be modified by the `efficiencyFunction` option. * * Both options are functions that take an energy level at a stage in the process and return a new energy level. By default, both functions are the identity function * of their input. */ eat(source: Agent | Cell, options?: { greedFunction?: (sourceEnergy: number) => number; efficiencyFunction?: (energyTakenFromSource: number) => number; }): void; /** * Sets `isAlive` to `false`. * An agent dies when it runs out of energy or is eaten by another agent. */ die(): void; /** * Gives the distance between the agent and another agent or cell. */ distanceTo(world: CellGrid, other: T, options?: { metric?: 'euclidean' | 'manhattan'; boundaryCondition?: BoundaryCondition; }): number; /** * Reproduces the agent by creating a new agent with half the energy of the parent. * The parent agent loses half of its energy in the process. * * If a maximum population is set and the current population is at the maximum, the agent will not reproduce * and this function will return `undefined`. * * The new agent inherits all of the parent's traits. Genetic traits are mutated according to their `mutationFunction`. * If another agent is provided, the new agent will inherit traits that both agents share and a * `traitCrossoverRate` chance of inheriting new traits that the other agent does not have. * Genetic traits are recombined according to their `recombinantFunction`. * * A new random seed is generated for the new agent using the parent's random number generator. */ reproduce(opts?: { childPosition?: [number, number]; other?: T; }): T | undefined; /** * Gets the cell the agent is currently occupying. * If the agent's position is not an integer, it will be rounded to the nearest integer. * If the agent's position is not a number, an error will be thrown. * If the agent's position is outside the world bounds, undefined will be returned. */ getCell(world: CellGrid): T | undefined; /** * Gets the cells adjacent to the agent. Optionally include diagonal cells. * Excludes the agent's location cell by default. */ getNeighborCells(world: CellGrid, options?: { includeDiagonals?: boolean; includeOwnCell?: boolean; }): T[]; /** * Gets the neighboring cells within the agent's vision range. * Uses the agent's `sightRange` property by default. * Excludes the agent's own cell by default. * * If the agent is outside the world bounds, an empty array will be returned. */ getCellsWithinRange(world: CellGrid, options?: { range?: number; includeOwnCell?: boolean; }): T[]; /** * Gets agents within this agent's adjacent cells. */ getNeighbors(world: CellGrid, options?: { includeDiagonals?: boolean; includeSelf?: boolean; }): AgentSet; /** * Gets the agents within the some range of the agent. * Uses the agent's `sightRange` property by default. */ getAgentsWithinRange(world: CellGrid, agents: AgentSet, options?: { range?: number; includeSelf?: boolean; metric?: 'euclidean' | 'manhattan'; }): AgentSet; /** * Gets the agents within the agent's field of view and range. * Uses agent's `fov` and `sightRange` properties by default. */ getAgentsWithinCone(world: CellGrid, options?: { fov?: Angle; range?: number; includeSelf?: boolean; metric?: 'euclidean' | 'manhattan'; }): AgentSet; /** * Gets any cells within the agent's field of view. * Uses agent's `fov` and `sightRange` properties by default. */ getCellsWithinCone(world: CellGrid, options?: { fov?: Angle; range?: number; }): T[]; /** * Gets the cell in front of the agent according to its current * position and rotation, and world boundary conditions. */ getCellInFront(world: CellGrid): T | undefined; /** * Gets the cell in front and to the left of the agent according to its current * position and rotation, and world boundary conditions. */ getCellInFrontAndLeft(world: CellGrid): T | undefined; /** * Gets the cell in front and to the right of the agent according to its current * position and rotation, and world boundary conditions. */ getCellInFrontAndRight(world: CellGrid): T | undefined; /** * Serializes an agent as a JSON object. */ toJSON(): object; }