UNPKG

agentscape

Version:

Agentscape is a library for creating agent-based simulations. It provides a simple API for defining agents and their behavior, and for defining the environment in which the agents interact. Agentscape is designed to be flexible and extensible, allowing

419 lines (418 loc) 16.6 kB
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: <T extends Agent>(options?: TraitOptions) => (target: T, propertyKey: string) => void; export type GeneticTraitValue<T extends number | string | boolean | Color> = T | Array<T>; export type GeneticTraitOptions<T extends number | string | boolean | Color> = { 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<T>, rng: RandomGenerator, traitMutationRate: number) => GeneticTraitValue<T>; /** * 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<T>, value2: GeneticTraitValue<T>, rng: RandomGenerator) => GeneticTraitValue<T>; }; /** * 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: <T extends Agent, V extends number | string | boolean | Color>(options?: GeneticTraitOptions<V>) => (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<V, T extends Agent>(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<T extends { new (...args: any[]): {}; }>(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<string, GeneticTraitOptions<string | number | boolean | Color> & { value: GeneticTraitValue<string | number | boolean | Color>; }>; /** * 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<string, GeneticTraitValue<string | number | boolean | Color>>; 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<Cell>, agentSets: { [key: string]: AgentSet<Agent>; }, 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<U extends Cell>(world: CellGrid<U>, 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<U extends Cell>(world: CellGrid<U>, distance?: number): void; /** * Swaps the agent's position with another agent without effecting their energy level or rotation. */ swap<U extends Cell, T extends Agent>(world: CellGrid<U>, 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<U extends Cell>(world: CellGrid<U>, options?: { wiggle?: Angle; strideLength?: number; }): void; /** * Rotates the agent to face a specified cell. */ faceCell<U extends Cell>(world: CellGrid<Cell>, cell: U): void; /** * Rotates the agent to face a specified agent. */ faceAgent<T extends Agent>(world: CellGrid<Cell>, 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<T extends Agent | Cell>(world: CellGrid<Cell>, 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<T extends Agent>(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<T extends Cell>(world: CellGrid<T>): T | undefined; /** * Gets the cells adjacent to the agent. Optionally include diagonal cells. * Excludes the agent's location cell by default. */ getNeighborCells<T extends Cell>(world: CellGrid<T>, 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<T extends Cell>(world: CellGrid<T>, options?: { range?: number; includeOwnCell?: boolean; }): T[]; /** * Gets agents within this agent's adjacent cells. */ getNeighbors<T extends this, U extends Cell>(world: CellGrid<U>, options?: { includeDiagonals?: boolean; includeSelf?: boolean; }): AgentSet<T>; /** * Gets the agents within the some range of the agent. * Uses the agent's `sightRange` property by default. */ getAgentsWithinRange<T extends Agent>(world: CellGrid<Cell>, agents: AgentSet<T>, options?: { range?: number; includeSelf?: boolean; metric?: 'euclidean' | 'manhattan'; }): AgentSet<T>; /** * Gets the agents within the agent's field of view and range. * Uses agent's `fov` and `sightRange` properties by default. */ getAgentsWithinCone<T extends Agent>(world: CellGrid<Cell>, options?: { fov?: Angle; range?: number; includeSelf?: boolean; metric?: 'euclidean' | 'manhattan'; }): AgentSet<T>; /** * Gets any cells within the agent's field of view. * Uses agent's `fov` and `sightRange` properties by default. */ getCellsWithinCone<T extends Cell>(world: CellGrid<T>, 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<T extends Cell>(world: CellGrid<T>): 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<T extends Cell>(world: CellGrid<T>): 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<T extends Cell>(world: CellGrid<T>): T | undefined; /** * Serializes an agent as a JSON object. */ toJSON(): object; }