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

153 lines 4.77 kB
import Cell from '../entities/Cell'; export default class CellGrid { static default(width, options) { const { height = width, boundaryCondition = 'finite' } = options !== null && options !== void 0 ? options : {}; return new CellGrid({ width, height, boundaryCondition, cellFactory: (location) => new Cell({ location }) }); } constructor(opts) { this.cells = []; this.width = 0; this.height = 0; this.boundaryCondition = 'finite'; const { width, height = width, cellFactory, boundaryCondition = 'finite' } = opts; this.width = width; this.height = height; this.boundaryCondition = boundaryCondition; for (let x = 0; x < width; x++) { this.cells[x] = []; for (let y = 0; y < height; y++) { this.cells[x][y] = cellFactory([x, y]); } } } *[Symbol.iterator]() { for (const row of this.cells) { for (const cell of row) { yield cell; } } } map(callback) { let i = 0; const values = []; for (const row of this.cells) { for (const cell of row) { values.push(callback(cell, i)); i++; } } return values; } forEach(callback) { for (const row of this.cells) { for (const cell of row) { callback(cell); } } } filter(callback) { const filtered = []; for (const row of this.cells) { for (const cell of row) { if (callback(cell)) { filtered.push(cell); } } } return filtered; } reduce(callback, initialValue) { let accumulator = initialValue; for (const row of this.cells) { for (const cell of row) { accumulator = callback(accumulator, cell); } } return accumulator; } /** * Gets the cell at the set's [x,y] location. * Returns undefined if the location is out of bounds. */ getCell(location) { var _a; if (!this.isInBounds(location)) { // throw new Error(`Location ${location} is out of bounds.`) return undefined; } const cell = (_a = this.cells[location[0]]) === null || _a === void 0 ? void 0 : _a[location[1]]; return cell; } /** * Gets the cell nearest to the given location. * * There are two boundary conditions: * - **FINITE** Returns undefined if the location is out of bounds. * - **PERIODIC** Returns the cell at the periodic location. */ getCellNearest(location) { if (this.boundaryCondition === 'finite') { if (!this.isInBounds(location)) { return undefined; } const x = Math.round(location[0]); const y = Math.round(location[1]); return this.getCell([x, y]); } if (this.boundaryCondition === 'periodic') { let x = location[0] % (this.width - 1); let y = location[1] % (this.height - 1); if (x < 0) { x += this.width - 1; } if (y < 0) { y += this.height - 1; } x = Math.round(x); y = Math.round(y); return this.getCell([x, y]); } throw new Error('Invalid boundary condition'); } /** * Picks a cell at random from the cell set. */ random(rng) { const x = rng.uniformInt(0, this.width - 1); const y = rng.uniformInt(0, this.width - 1); return this.cells[x][y]; } /** * Picks N random cells from the cell set without replacement */ randomSample(rng, N) { const cells = [...this.cells.flat()]; const randomCells = []; for (let i = 0; i < N; i++) { const index = rng.uniformInt(0, cells.length - 1); randomCells.push(cells[index]); cells.splice(index, 1); } return randomCells; } /** * Returns true if the given location is within the grid bounds. */ isInBounds(location) { const [x, y] = location.map(Math.floor); return x >= 0 && x < this.width && y >= 0 && y < this.height; } insertAgent(agent) { const location = agent.position.components.map(Math.floor); if (this.isInBounds(location)) { const cell = this.getCell(location); cell.tenantAgents.add(agent); } } } //# sourceMappingURL=CellGrid.js.map