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
JavaScript
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