UNPKG

agentjs-core

Version:

A comprehensive agent-based modeling framework with built-in p5.js visualization

1,880 lines 648 kB
var Qo = Object.defineProperty; var Ko = (s, t, e) => t in s ? Qo(s, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[t] = e; var M = (s, t, e) => (Ko(s, typeof t != "symbol" ? t + "" : t, e), e); import * as j from "@tensorflow/tfjs"; function Zo(s) { return s && s.__esModule && Object.prototype.hasOwnProperty.call(s, "default") ? s.default : s; } var Us = { exports: {} }; (function(s) { var t = Object.prototype.hasOwnProperty, e = "~"; function i() { } Object.create && (i.prototype = /* @__PURE__ */ Object.create(null), new i().__proto__ || (e = !1)); function n(c, l, h) { this.fn = c, this.context = l, this.once = h || !1; } function o(c, l, h, u, d) { if (typeof h != "function") throw new TypeError("The listener must be a function"); var f = new n(h, u || c, d), g = e ? e + l : l; return c._events[g] ? c._events[g].fn ? c._events[g] = [c._events[g], f] : c._events[g].push(f) : (c._events[g] = f, c._eventsCount++), c; } function r(c, l) { --c._eventsCount === 0 ? c._events = new i() : delete c._events[l]; } function a() { this._events = new i(), this._eventsCount = 0; } a.prototype.eventNames = function() { var l = [], h, u; if (this._eventsCount === 0) return l; for (u in h = this._events) t.call(h, u) && l.push(e ? u.slice(1) : u); return Object.getOwnPropertySymbols ? l.concat(Object.getOwnPropertySymbols(h)) : l; }, a.prototype.listeners = function(l) { var h = e ? e + l : l, u = this._events[h]; if (!u) return []; if (u.fn) return [u.fn]; for (var d = 0, f = u.length, g = new Array(f); d < f; d++) g[d] = u[d].fn; return g; }, a.prototype.listenerCount = function(l) { var h = e ? e + l : l, u = this._events[h]; return u ? u.fn ? 1 : u.length : 0; }, a.prototype.emit = function(l, h, u, d, f, g) { var p = e ? e + l : l; if (!this._events[p]) return !1; var m = this._events[p], y = arguments.length, b, x; if (m.fn) { switch (m.once && this.removeListener(l, m.fn, void 0, !0), y) { case 1: return m.fn.call(m.context), !0; case 2: return m.fn.call(m.context, h), !0; case 3: return m.fn.call(m.context, h, u), !0; case 4: return m.fn.call(m.context, h, u, d), !0; case 5: return m.fn.call(m.context, h, u, d, f), !0; case 6: return m.fn.call(m.context, h, u, d, f, g), !0; } for (x = 1, b = new Array(y - 1); x < y; x++) b[x - 1] = arguments[x]; m.fn.apply(m.context, b); } else { var v = m.length, w; for (x = 0; x < v; x++) switch (m[x].once && this.removeListener(l, m[x].fn, void 0, !0), y) { case 1: m[x].fn.call(m[x].context); break; case 2: m[x].fn.call(m[x].context, h); break; case 3: m[x].fn.call(m[x].context, h, u); break; case 4: m[x].fn.call(m[x].context, h, u, d); break; default: if (!b) for (w = 1, b = new Array(y - 1); w < y; w++) b[w - 1] = arguments[w]; m[x].fn.apply(m[x].context, b); } } return !0; }, a.prototype.on = function(l, h, u) { return o(this, l, h, u, !1); }, a.prototype.once = function(l, h, u) { return o(this, l, h, u, !0); }, a.prototype.removeListener = function(l, h, u, d) { var f = e ? e + l : l; if (!this._events[f]) return this; if (!h) return r(this, f), this; var g = this._events[f]; if (g.fn) g.fn === h && (!d || g.once) && (!u || g.context === u) && r(this, f); else { for (var p = 0, m = [], y = g.length; p < y; p++) (g[p].fn !== h || d && !g[p].once || u && g[p].context !== u) && m.push(g[p]); m.length ? this._events[f] = m.length === 1 ? m[0] : m : r(this, f); } return this; }, a.prototype.removeAllListeners = function(l) { var h; return l ? (h = e ? e + l : l, this._events[h] && r(this, h)) : (this._events = new i(), this._eventsCount = 0), this; }, a.prototype.off = a.prototype.removeListener, a.prototype.addListener = a.prototype.on, a.prefixed = e, a.EventEmitter = a, s.exports = a; })(Us); var Jo = Us.exports; const et = /* @__PURE__ */ Zo(Jo); let Le; const tr = new Uint8Array(16); function er() { if (!Le && (Le = typeof crypto < "u" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto), !Le)) throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported"); return Le(tr); } const G = []; for (let s = 0; s < 256; ++s) G.push((s + 256).toString(16).slice(1)); function ir(s, t = 0) { return G[s[t + 0]] + G[s[t + 1]] + G[s[t + 2]] + G[s[t + 3]] + "-" + G[s[t + 4]] + G[s[t + 5]] + "-" + G[s[t + 6]] + G[s[t + 7]] + "-" + G[s[t + 8]] + G[s[t + 9]] + "-" + G[s[t + 10]] + G[s[t + 11]] + G[s[t + 12]] + G[s[t + 13]] + G[s[t + 14]] + G[s[t + 15]]; } const nr = typeof crypto < "u" && crypto.randomUUID && crypto.randomUUID.bind(crypto), Sn = { randomUUID: nr }; function sr(s, t, e) { if (Sn.randomUUID && !t && !s) return Sn.randomUUID(); s = s || {}; const i = s.random || (s.rng || er)(); if (i[6] = i[6] & 15 | 64, i[8] = i[8] & 63 | 128, t) { e = e || 0; for (let n = 0; n < 16; ++n) t[e + n] = i[n]; return t; } return ir(i); } class qs extends et { constructor(t = sr(), e = {}, i = { x: 0, y: 0 }) { super(), this.state = "active", this.id = t, this.properties = new Map(Object.entries(e)), this._position = i, this.createdAt = Date.now(), this.lastUpdated = this.createdAt, this.validateInitialState(), this.emitLifecycleEvent("agentCreated"); } /** * Get agent property with type safety */ getProperty(t) { return this.properties.get(t); } /** * Set agent property with validation and event emission */ setProperty(t, e) { this.validateProperty(t, e); const i = this.properties.get(t); this.properties.set(t, e), this.lastUpdated = Date.now(), this.emitPropertyChanged(t, i ?? null, e); } /** * Get all agent properties as an object */ getProperties() { return Object.fromEntries(this.properties.entries()); } /** * Get all properties as readonly record */ getAllProperties() { return Object.fromEntries(this.properties.entries()); } /** * Check if agent has a specific property */ hasProperty(t) { return this.properties.has(t); } /** * Remove a property from the agent */ removeProperty(t) { const e = this.properties.has(t); if (e) { const i = this.properties.get(t); this.properties.delete(t), this.lastUpdated = Date.now(), this.emitPropertyChanged(t, i ?? null, null); } return e; } /** * Get current position */ get position() { return this._position; } /** * Get current position (method form for compatibility) */ getPosition() { return this._position; } /** * Set position with validation and event emission */ setPosition(t) { this.validatePosition(t); const e = this._position; this._position = t, this.lastUpdated = Date.now(), this.emitPositionChanged(e, t); } /** * Translate position by offset */ translate(t, e) { const i = { x: this._position.x + t, y: this._position.y + e }; this.setPosition(i); } /** * Get current agent state */ get currentState() { return this.state; } /** * Activate agent (make it active in simulation) */ activate() { this.state !== "active" && (this.state = "active", this.emitLifecycleEvent("agentActivated")); } /** * Deactivate agent (remove from simulation but keep in memory) */ deactivate() { this.state === "active" && (this.state = "dormant", this.emitLifecycleEvent("agentDeactivated")); } /** * Destroy agent (mark for removal from simulation) */ destroy() { this.state !== "destroyed" && (this.state = "destroyed", this.emitLifecycleEvent("agentDestroyed"), this.removeAllListeners()); } /** * Check if agent is active */ isActive() { return this.state === "active"; } /** * Check if agent is destroyed */ isDestroyed() { return this.state === "destroyed"; } /** * Serialize agent to JSON for persistence */ toJSON() { return { id: this.id, state: this.state, position: this._position, properties: Object.fromEntries(this.properties.entries()), createdAt: this.createdAt, lastUpdated: this.lastUpdated }; } /** * Get string representation of agent */ toString() { return `Agent(${this.id}, ${this.state}, ${this._position.x}, ${this._position.y})`; } /** * Validate property value before setting * Override in subclasses for custom validation */ validateProperty(t, e) { if (t === "" || t === null) throw new Error("Property key cannot be empty or null"); if (e !== null && typeof e == "object" && !Array.isArray(e)) try { JSON.stringify(e); } catch { throw new Error("Property value must be JSON serializable"); } } /** * Validate position before setting * Override in subclasses for custom validation */ validatePosition(t) { if (!isFinite(t.x) || !isFinite(t.y)) throw new Error("Position coordinates must be finite numbers"); } /** * Validate initial agent state */ validateInitialState() { if (!this.id || this.id.length === 0) throw new Error("Agent ID cannot be empty"); } /** * Emit property changed event */ emitPropertyChanged(t, e, i) { const n = { type: "propertyChanged", timestamp: Date.now(), agent: this, property: t, oldValue: e, newValue: i }; this.emit("propertyChanged", n); } /** * Emit position changed event */ emitPositionChanged(t, e) { const i = { type: "positionChanged", timestamp: Date.now(), agent: this, oldPosition: t, newPosition: e }; this.emit("positionChanged", i); } /** * Emit lifecycle event */ emitLifecycleEvent(t) { const e = { type: t, timestamp: Date.now(), agent: this }; this.emit(t, e); } } class ai extends qs { constructor(t, e = {}, i = { x: 0, y: 0 }) { super(t, e, i); } /** * Basic step implementation - override for custom behavior * By default, does nothing (static agent) */ step() { } /** * Simple position update with validation */ moveTo(t) { this.setPosition(t); } /** * Move relative to current position */ moveBy(t, e) { this.translate(t, e); } /** * Set agent energy level (common property) */ setEnergy(t) { if (t < 0) throw new Error("Energy cannot be negative"); this.setProperty("energy", t); } /** * Get agent energy level */ getEnergy() { return this.getProperty("energy") ?? 100; } /** * Set agent activity level (0-1) */ setActivity(t) { if (t < 0 || t > 1) throw new Error("Activity must be between 0 and 1"); this.setProperty("activity", t); } /** * Get agent activity level */ getActivity() { return this.getProperty("activity") ?? 0.5; } /** * Check if agent is at a specific position */ isAt(t) { return this.position.x === t.x && this.position.y === t.y; } /** * Calculate distance to another position */ distanceTo(t) { const e = this.position.x - t.x, i = this.position.y - t.y; return Math.sqrt(e * e + i * i); } /** * Calculate distance to another agent */ distanceToAgent(t) { return this.distanceTo(t.position); } /** * Check if agent is within a certain distance of a position */ isNear(t, e) { return this.distanceTo(t) <= e; } /** * Check if agent is within a certain distance of another agent */ isNearAgent(t, e) { return this.distanceToAgent(t) <= e; } } class or extends ai { constructor(t, e = {}, i = { x: 0, y: 0 }, n = {}) { super(t, e, i), this.velocity = { vx: 0, vy: 0 }, this.previousPosition = i, e.x !== void 0 && (i = { ...i, x: e.x }), e.y !== void 0 && (i = { ...i, y: e.y }), this.setProperty("x", i.x), this.setProperty("y", i.y), this.movementConfig = { maxSpeed: 5, acceleration: 1, friction: 0.95, bounceOnBoundary: !0, ...n }; } /** * Update agent position based on velocity */ step() { this.previousPosition = this.position; const t = this.getProperty("velocityX"), e = this.getProperty("velocityY"); t !== void 0 && e !== void 0 && (this.velocity = { vx: t, vy: e }), this.velocity = this.applyFriction(this.velocity), this.velocity = this.limitSpeed(this.velocity); const i = { x: this.position.x + this.velocity.vx, y: this.position.y + this.velocity.vy }; this.setPosition(i), this.setProperty("x", i.x), this.setProperty("y", i.y); } /** * Set velocity directly */ setVelocity(t, e) { this.velocity = { vx: t, vy: e }; } /** * Get current velocity */ getVelocity() { return { ...this.velocity }; } /** * Add force to velocity (acceleration) */ addForce(t, e) { const i = this.movementConfig.acceleration; this.velocity = { vx: this.velocity.vx + t * i, vy: this.velocity.vy + e * i }; } /** * Apply impulse (instant velocity change) */ applyImpulse(t, e) { this.velocity = { vx: this.velocity.vx + t, vy: this.velocity.vy + e }; } /** * Stop agent movement */ stop() { this.velocity = { vx: 0, vy: 0 }; } /** * Set target speed in current direction */ setSpeed(t) { const e = this.getSpeed(); if (e > 0) { const i = t / e; this.velocity = { vx: this.velocity.vx * i, vy: this.velocity.vy * i }; } } /** * Get current speed (magnitude of velocity) */ getSpeed() { return Math.sqrt( this.velocity.vx * this.velocity.vx + this.velocity.vy * this.velocity.vy ); } /** * Get movement direction in radians */ getDirection() { return Math.atan2(this.velocity.vy, this.velocity.vx); } /** * Move towards a target position */ moveToward(t, e = 1) { const i = t.x - this.position.x, n = t.y - this.position.y, o = Math.sqrt(i * i + n * n); if (o > 0) { const r = i / o * e, a = n / o * e; this.addForce(r, a); } } /** * Move away from a position */ moveAway(t, e = 1) { const i = this.position.x - t.x, n = this.position.y - t.y, o = Math.sqrt(i * i + n * n); if (o > 0) { const r = i / o * e, a = n / o * e; this.addForce(r, a); } } /** * Apply random movement (wandering behavior) */ wander(t = 0.5) { const e = Math.random() * 2 * Math.PI, i = Math.cos(e) * t, n = Math.sin(e) * t; this.addForce(i, n); } /** * Get movement configuration */ getMovementConfig() { return { ...this.movementConfig }; } /** * Update movement configuration */ updateMovementConfig(t) { this.movementConfig = { ...this.movementConfig, ...t }; } /** * Check if agent is moving */ isMoving() { return this.getSpeed() > 1e-3; } /** * Handle boundary collision with environment */ handleBoundaryCollision(t) { let { vx: e, vy: i } = this.velocity; (this.position.x <= 0 || this.position.x >= t.width) && (this.movementConfig.bounceOnBoundary ? e = -e * 0.8 : e = 0), (this.position.y <= 0 || this.position.y >= t.height) && (this.movementConfig.bounceOnBoundary ? i = -i * 0.8 : i = 0), this.velocity = { vx: e, vy: i }; } /** * Apply friction to velocity */ applyFriction(t) { const e = this.movementConfig.friction; return { vx: t.vx * e, vy: t.vy * e }; } /** * Limit velocity to maximum speed */ limitSpeed(t) { const e = Math.sqrt( t.vx * t.vx + t.vy * t.vy ), i = this.movementConfig.maxSpeed; if (e > i) { const n = i / e; return { vx: t.vx * n, vy: t.vy * n }; } return t; } } class Zt extends ai { constructor(t, e = {}, i = { x: 0, y: 0 }) { super(t, e, i), this.connections = /* @__PURE__ */ new Map(), this.incomingConnections = /* @__PURE__ */ new Set(), this.influence = 1, this.trustLevels = /* @__PURE__ */ new Map(); } /** * Basic step - can be overridden for network-specific behaviors */ step() { this.decayConnections(), this.updateInfluence(); } /** * Create connection to another agent */ connect(t, e = "default", i = 1, n = {}) { if (t.id === this.id) throw new Error("Agent cannot connect to itself"); if (i < 0 || i > 1) throw new Error("Connection strength must be between 0 and 1"); const o = { target: t, type: e, strength: i, createdAt: Date.now(), lastInteraction: Date.now(), metadata: { ...n } }; this.connections.set(t.id, o), t instanceof Zt && t.addIncomingConnection(this.id), this.emitConnectionEvent("connectionFormed", t, e, i); } /** * Remove connection to another agent */ disconnect(t) { const e = this.connections.has(t.id); if (e) { const i = this.connections.get(t.id); this.connections.delete(t.id), t instanceof Zt && t.removeIncomingConnection(this.id), this.emitConnectionEvent( "connectionBroken", t, i.type, i.strength ); } return e; } /** * Check if connected to another agent */ isConnectedTo(t) { return this.connections.has(t.id); } /** * Get connection to specific agent */ getConnection(t) { return this.connections.get(t.id); } /** * Get all connections */ getAllConnections() { return Array.from(this.connections.values()); } /** * Get connections of specific type */ getConnectionsByType(t) { return Array.from(this.connections.values()).filter( (e) => e.type === t ); } /** * Get connection count */ getConnectionCount() { return this.connections.size; } /** * Get strong connections (strength > 0.7) */ getStrongConnections() { return Array.from(this.connections.values()).filter( (t) => t.strength > 0.7 ); } /** * Get weak connections (strength <= 0.3) */ getWeakConnections() { return Array.from(this.connections.values()).filter( (t) => t.strength <= 0.3 ); } /** * Update connection strength */ updateConnectionStrength(t, e) { if (e < 0 || e > 1) throw new Error("Connection strength must be between 0 and 1"); const i = this.connections.get(t.id); if (!i) throw new Error(`No connection to agent ${t.id}`); const n = { ...i, strength: e, lastInteraction: Date.now() }; this.connections.set(t.id, n), this.emitConnectionEvent( "connectionModified", t, i.type, e ); } /** * Strengthen connection through interaction */ strengthenConnection(t, e = 0.1) { const i = this.connections.get(t.id); if (i) { const n = Math.min(1, i.strength + e); this.updateConnectionStrength(t, n); } } /** * Weaken connection */ weakenConnection(t, e = 0.1) { const i = this.connections.get(t.id); if (i) { const n = Math.max(0, i.strength - e); n === 0 ? this.disconnect(t) : this.updateConnectionStrength(t, n); } } /** * Set trust level for another agent */ setTrust(t, e) { if (e < 0 || e > 1) throw new Error("Trust level must be between 0 and 1"); this.trustLevels.set(t.id, e); } /** * Get trust level for another agent */ getTrust(t) { return this.trustLevels.get(t.id) ?? 0.5; } /** * Get network influence */ getInfluence() { return this.influence; } /** * Set network influence */ setInfluence(t) { if (t < 0) throw new Error("Influence cannot be negative"); this.influence = t; } /** * Get network neighbors (connected agents) */ getNeighbors() { return Array.from(this.connections.values()).map((t) => t.target); } /** * Get neighbors within a certain trust threshold */ getTrustedNeighbors(t = 0.7) { return this.getNeighbors().filter( (e) => this.getTrust(e) >= t ); } /** * Calculate network statistics */ getNetworkStats() { const t = Array.from(this.connections.values()), e = t.filter((r) => r.strength > 0.7), i = t.filter((r) => r.strength <= 0.3), n = t.length > 0 ? t.reduce((r, a) => r + a.strength, 0) / t.length : 0, o = this.calculateClusteringCoefficient(); return { connectionCount: t.length, strongConnections: e.length, weakConnections: i.length, averageStrength: n, mostConnectedAgent: this.findMostConnectedNeighbor(), clusteringCoefficient: o }; } /** * Add incoming connection reference */ addIncomingConnection(t) { this.incomingConnections.add(t); } /** * Remove incoming connection reference */ removeIncomingConnection(t) { this.incomingConnections.delete(t); } /** * Get incoming connection count */ getIncomingConnectionCount() { return this.incomingConnections.size; } /** * Calculate degree centrality (total connections) */ getDegreeCentrality() { return this.connections.size + this.incomingConnections.size; } /** * Decay connection strengths over time */ decayConnections() { const e = Date.now(), i = 24 * 60 * 60 * 1e3; for (const [n, o] of this.connections.entries()) { const r = e - o.lastInteraction; if (r > i) { const a = Math.min(1e-3 * r / i, 0.1), c = Math.max(0, o.strength - a); if (c === 0) this.connections.delete(n), this.emitConnectionEvent( "connectionBroken", o.target, o.type, 0 ); else { const l = { ...o, strength: c }; this.connections.set(n, l); } } } } /** * Update influence based on network position */ updateInfluence() { const t = Array.from(this.connections.values()).reduce( (i, n) => i + n.strength, 0 ), e = this.incomingConnections.size * 0.5; this.influence = Math.max( 0.1, 1 + t * 0.1 + e * 0.1 ); } /** * Calculate clustering coefficient */ calculateClusteringCoefficient() { const t = this.getNeighbors(); if (t.length < 2) return 0; let e = 0, i = 0; for (let n = 0; n < t.length; n++) for (let o = n + 1; o < t.length; o++) { i++; const r = t[n], a = t[o]; r instanceof Zt && a && r.isConnectedTo(a) && e++; } return i > 0 ? e / i : 0; } /** * Find the most connected neighbor */ findMostConnectedNeighbor() { let t = -1, e = null; for (const i of this.getNeighbors()) if (i instanceof Zt) { const n = i.getConnectionCount(); n > t && (t = n, e = i.id); } return e; } /** * Emit connection event */ emitConnectionEvent(t, e, i, n) { const o = { type: t, timestamp: Date.now(), source: this, target: e, connectionType: i, strength: n }; this.emit(t, o); } } class Gs extends et { constructor(t) { super(), this.validateDimensions(t), this.dimensions = { ...t }, this.agents = /* @__PURE__ */ new Set(), this.agentPositions = /* @__PURE__ */ new Map(); } /** * Add agent to environment at specified position */ addAgent(t, e) { if (this.agents.has(t)) throw new Error(`Agent ${t.id} is already in this environment`); const i = e ?? t.position; this.validatePosition(i), this.agents.add(t), this.agentPositions.set(t, i), e && t.setPosition(i), this.onAgentAdded(t, i), this.emit("agentAdded", { agent: t, position: i }); } /** * Remove agent from environment */ removeAgent(t) { return this.agents.has(t) ? (this.agents.delete(t), this.agentPositions.delete(t), this.onAgentRemoved(t), this.emit("agentRemoved", { agent: t }), !0) : !1; } /** * Move agent to new position within environment */ moveAgent(t, e) { if (!this.agents.has(t)) throw new Error(`Agent ${t.id} is not in this environment`); const i = this.applyBoundaryConditions(e), n = this.agentPositions.get(t); this.agentPositions.set(t, i), t.setPosition(i), this.onAgentMoved(t, n, i), this.emit("agentMoved", { agent: t, oldPosition: n, newPosition: i }); } /** * Get agent's current position in environment */ getAgentPosition(t) { return this.agentPositions.get(t); } /** * Get all agents in environment */ getAllAgents() { return Array.from(this.agents); } /** * Get number of agents in environment */ getAgentCount() { return this.agents.size; } /** * Check if position is within environment bounds */ isValidPosition(t) { return t.x >= 0 && t.x <= this.dimensions.width && t.y >= 0 && t.y <= this.dimensions.height; } /** * Apply boundary conditions to position */ applyBoundaryConditions(t) { switch (this.dimensions.boundaryType) { case "periodic": return this.applyPeriodicBoundary(t); case "reflective": return this.applyReflectiveBoundary(t); case "absorbing": return this.applyAbsorbingBoundary(t); default: throw new Error( `Unknown boundary type: ${this.dimensions.boundaryType}` ); } } /** * Calculate distance between two positions */ distance(t, e) { const i = t.x - e.x, n = t.y - e.y; return Math.sqrt(i * i + n * n); } /** * Get environment dimensions */ getDimensions() { return { ...this.dimensions }; } /** * Hook for subclasses when agent is added */ onAgentAdded(t, e) { } /** * Hook for subclasses when agent is removed */ onAgentRemoved(t) { } /** * Hook for subclasses when agent is moved */ onAgentMoved(t, e, i) { } /** * Validate environment dimensions */ validateDimensions(t) { if (t.width <= 0 || t.height <= 0) throw new Error("Environment dimensions must be positive"); if (!isFinite(t.width) || !isFinite(t.height)) throw new Error("Environment dimensions must be finite"); } /** * Validate position coordinates */ validatePosition(t) { if (!isFinite(t.x) || !isFinite(t.y)) throw new Error("Position coordinates must be finite numbers"); } /** * Apply periodic boundary conditions (wrap around) */ applyPeriodicBoundary(t) { const e = (t.x % this.dimensions.width + this.dimensions.width) % this.dimensions.width, i = (t.y % this.dimensions.height + this.dimensions.height) % this.dimensions.height; return { x: e, y: i }; } /** * Apply reflective boundary conditions (bounce back) */ applyReflectiveBoundary(t) { let e = t.x, i = t.y; return e < 0 && (e = -e), e > this.dimensions.width && (e = 2 * this.dimensions.width - e), i < 0 && (i = -i), i > this.dimensions.height && (i = 2 * this.dimensions.height - i), { x: e, y: i }; } /** * Apply absorbing boundary conditions (clamp to bounds) */ applyAbsorbingBoundary(t) { const e = Math.max(0, Math.min(this.dimensions.width, t.x)), i = Math.max(0, Math.min(this.dimensions.height, t.y)); return { x: e, y: i }; } } class rr extends Gs { constructor(t, e) { let i; typeof t == "number" && e !== void 0 ? i = { width: t, height: e, boundaryType: "absorbing", enableSpatialIndex: !0, maxObjectsPerNode: 10, maxTreeDepth: 6 } : i = t, super(i), this.config = { ...i }, this.quadtree = null, this.distanceFunction = (n, o) => { const r = n.x - o.x, a = n.y - o.y; return Math.sqrt(r * r + a * a); }, this.initializeQuadtree(); } /** * Find agents within radius of position */ findNeighbors(t, e, i = {}) { const n = performance.now(), o = i.maxResults ?? Number.MAX_SAFE_INTEGER, r = i.includeDistance ?? !1, a = this.config.enableSpatialIndex && this.quadtree ? this.queryQuadtree(t, e) : Array.from(this.agents), c = [], l = []; for (const h of a) { if (c.length >= o) break; const u = this.agentPositions.get(h); if (!u) continue; const d = this.distanceFunction(t, u); if (d <= e) { if (i.filterFn && !i.filterFn(h)) continue; c.push(h), r && l.push(d); } } if (r && l.length > 0) { const h = c.map((u, d) => ({ agent: u, distance: l[d] ?? 0 })); h.sort((u, d) => u.distance - d.distance), c.length = 0, l.length = 0; for (const u of h) c.push(u.agent), l.push(u.distance); } return { items: c, distances: r ? l : [], queryTime: performance.now() - n }; } /** * Get agents at specific position (within small tolerance) */ getAgentsAt(t) { return this.findNeighbors(t, 1e-3).items; } /** * Set custom distance function */ setDistanceFunction(t) { this.distanceFunction = t; } /** * Get Manhattan distance function */ static getManhattanDistance() { return (t, e) => Math.abs(t.x - e.x) + Math.abs(t.y - e.y); } /** * Get Euclidean distance function (default) */ static getEuclideanDistance() { return (t, e) => { const i = t.x - e.x, n = t.y - e.y; return Math.sqrt(i * i + n * n); }; } /** * Get Chebyshev distance function (max of x,y differences) */ static getChebyshevDistance() { return (t, e) => Math.max(Math.abs(t.x - e.x), Math.abs(t.y - e.y)); } /** * Get current configuration */ getConfig() { return { ...this.config }; } /** * Update spatial configuration */ updateConfig(t) { Object.assign(this.config, t), ("enableSpatialIndex" in t || "maxObjectsPerNode" in t || "maxTreeDepth" in t) && this.rebuildQuadtree(); } /** * Get quadtree statistics */ getQuadtreeStats() { return this.quadtree ? this.calculateQuadtreeStats(this.quadtree) : { nodeCount: 0, maxDepth: 0, agentCount: 0 }; } /** * Force rebuild of spatial index */ rebuildQuadtree() { if (this.config.enableSpatialIndex) { this.initializeQuadtree(); for (const [t, e] of this.agentPositions.entries()) this.insertIntoQuadtree(t, e); } } /** * Check if quadtree needs rebalancing */ needsRebalancing() { if (!this.quadtree || !this.config.enableSpatialIndex) return !1; const t = this.getQuadtreeStats(), e = t.agentCount / t.nodeCount; return t.maxDepth > this.config.maxTreeDepth * 1.5 || e < this.config.maxObjectsPerNode * 0.2; } /** * Rebalance quadtree for optimal performance */ rebalanceQuadtree() { if (!this.needsRebalancing()) return; const t = []; for (const [i, n] of this.agentPositions.entries()) t.push({ agent: i, position: n }); this.initializeQuadtree(); const e = this.sortByMortonCode(t); for (const { agent: i, position: n } of e) this.insertIntoQuadtree(i, n); } /** * Sort agents by Morton code (Z-order) for better spatial locality */ sortByMortonCode(t) { return t.sort((e, i) => { const n = this.calculateMortonCode(e.position), o = this.calculateMortonCode(i.position); return n - o; }); } /** * Calculate Morton code (Z-order) for a position */ calculateMortonCode(t) { const e = Math.floor(t.x / this.dimensions.width * 65535), i = Math.floor(t.y / this.dimensions.height * 65535); let n = 0; for (let o = 0; o < 16; o++) n |= (e & 1 << o) << o | (i & 1 << o) << o + 1; return n; } /** * Merge quadtree nodes if they're underutilized */ tryMergeNode(t) { if (!t.children) return !1; let e = 0; for (const i of t.children) { if (i.children) return !1; e += i.agents.length; } if (e <= this.config.maxObjectsPerNode) { t.agents = []; for (const i of t.children) t.agents.push(...i.agents); return t.children = null, !0; } return !1; } /** * Optimize quadtree by merging underutilized nodes */ optimizeQuadtree() { !this.quadtree || !this.config.enableSpatialIndex || this.optimizeNode(this.quadtree); } /** * Recursively optimize quadtree nodes */ optimizeNode(t) { if (t.children) { for (const e of t.children) this.optimizeNode(e); this.tryMergeNode(t); } } /** * Hook: Handle agent addition */ onAgentAdded(t, e) { this.config.enableSpatialIndex && this.quadtree && this.insertIntoQuadtree(t, e); } /** * Hook: Handle agent removal */ onAgentRemoved(t) { this.config.enableSpatialIndex && this.quadtree && this.removeFromQuadtree(t); } /** * Hook: Handle agent movement */ onAgentMoved(t, e, i) { this.config.enableSpatialIndex && this.quadtree && (this.removeFromQuadtree(t), this.insertIntoQuadtree(t, i)); } /** * Initialize quadtree structure */ initializeQuadtree() { if (!this.config.enableSpatialIndex) { this.quadtree = null; return; } this.quadtree = { bounds: { x: 0, y: 0, width: this.dimensions.width, height: this.dimensions.height }, agents: [], children: null, level: 0 }; } /** * Insert agent into quadtree */ insertIntoQuadtree(t, e) { this.quadtree && this.insertIntoNode(this.quadtree, t, e); } /** * Remove agent from quadtree */ removeFromQuadtree(t) { this.quadtree && this.removeFromNode(this.quadtree, t); } /** * Insert agent into specific quadtree node */ insertIntoNode(t, e, i) { if (this.pointInBounds(i, t.bounds)) { if (t.children) { for (const n of t.children) this.insertIntoNode(n, e, i); return; } t.agents.push(e), t.agents.length > this.config.maxObjectsPerNode && t.level < this.config.maxTreeDepth && this.splitNode(t); } } /** * Remove agent from quadtree node */ removeFromNode(t, e) { const i = t.agents.indexOf(e); if (i >= 0) return t.agents.splice(i, 1), !0; if (t.children) { for (const n of t.children) if (this.removeFromNode(n, e)) return !0; } return !1; } /** * Split quadtree node into four children */ splitNode(t) { const { x: e, y: i, width: n, height: o } = t.bounds, r = n / 2, a = o / 2; t.children = [ // Top-left { bounds: { x: e, y: i, width: r, height: a }, agents: [], children: null, level: t.level + 1 }, // Top-right { bounds: { x: e + r, y: i, width: r, height: a }, agents: [], children: null, level: t.level + 1 }, // Bottom-left { bounds: { x: e, y: i + a, width: r, height: a }, agents: [], children: null, level: t.level + 1 }, // Bottom-right { bounds: { x: e + r, y: i + a, width: r, height: a }, agents: [], children: null, level: t.level + 1 } ]; for (const c of t.agents) { const l = this.agentPositions.get(c); if (l) { for (const h of t.children) if (this.pointInBounds(l, h.bounds)) { this.insertIntoNode(h, c, l); break; } } } t.agents = []; } /** * Query quadtree for agents within radius */ queryQuadtree(t, e) { if (!this.quadtree) return []; const i = []; return this.queryNode(this.quadtree, t, e, i), i; } /** * Query specific quadtree node */ queryNode(t, e, i, n) { if (this.circleIntersectsBounds(e, i, t.bounds) && (n.push(...t.agents), t.children)) for (const o of t.children) this.queryNode(o, e, i, n); } /** * Check if point is within bounds */ pointInBounds(t, e) { return t.x >= e.x && t.x < e.x + e.width && t.y >= e.y && t.y < e.y + e.height; } /** * Check if circle intersects with bounds */ circleIntersectsBounds(t, e, i) { const n = Math.max( i.x, Math.min(t.x, i.x + i.width) ), o = Math.max( i.y, Math.min(t.y, i.y + i.height) ), r = t.x - n, a = t.y - o; return r * r + a * a <= e * e; } /** * Calculate quadtree statistics */ calculateQuadtreeStats(t) { let e = 1, i = t.level, n = t.agents.length; if (t.children) for (const o of t.children) { const r = this.calculateQuadtreeStats(o); e += r.nodeCount, i = Math.max(i, r.maxDepth), n += r.agentCount; } return { nodeCount: e, maxDepth: i, agentCount: n }; } } class Tu extends Gs { constructor(t, e, i) { let n; if (typeof t == "number" && e !== void 0) { const o = t, r = i || 1; n = { width: e * r, height: o * r, boundaryType: "absorbing", rows: o, cols: e, neighborhoodType: "moore", allowMultipleOccupancy: !0 }; } else n = t; super(n), this.config = { ...n }, this.agentCoordinates = /* @__PURE__ */ new Map(), this.grid = this.initializeGrid(), this.validateConfiguration(); } /** * Find neighbors within radius (in grid cells) */ findNeighbors(t, e, i = {}) { const n = performance.now(), o = i.maxResults ?? Number.MAX_SAFE_INTEGER, r = this.positionToGrid(t); if (!this.isValidCoordinate(r)) return { items: [], distances: [], queryTime: performance.now() - n }; const a = [], c = [], l = Math.floor(e); for (let h = -l; h <= l; h++) for (let u = -l; u <= l && !(a.length >= o); u++) { const d = { row: r.row + h, col: r.col + u }; if (!this.isValidCoordinate(d)) continue; const f = this.gridDistance(r, d); if (f > e) continue; const g = this.grid[d.row]?.[d.col]; if (g) for (const p of g.agents) { if (a.length >= o) break; i.filterFn && !i.filterFn(p) || (a.push(p), i.includeDistance && c.push(f)); } } if (i.includeDistance && c.length > 0) { const h = a.map((u, d) => ({ agent: u, distance: c[d] ?? 0 })); h.sort((u, d) => u.distance - d.distance), a.length = 0, c.length = 0; for (const u of h) a.push(u.agent), c.push(u.distance); } return { items: a, distances: i.includeDistance ? c : [], queryTime: performance.now() - n }; } /** * Get agents at specific position */ getAgentsAt(t) { const e = this.positionToGrid(t); if (!this.isValidCoordinate(e)) return []; const i = this.grid[e.row]?.[e.col]; return i ? Array.from(i.agents) : []; } /** * Get agents in specific grid cell */ getAgentsAtCell(t) { if (!this.isValidCoordinate(t)) return []; const e = this.grid[t.row]?.[t.col]; return e ? Array.from(e.agents) : []; } /** * Get grid coordinate for agent */ getAgentGridCoordinate(t) { return this.agentCoordinates.get(t); } /** * Get neighbors of a grid cell */ getCellNeighbors(t) { const e = []; if (this.config.neighborhoodType === "moore") for (let i = -1; i <= 1; i++) for (let n = -1; n <= 1; n++) { if (i === 0 && n === 0) continue; const o = { row: t.row + i, col: t.col + n }; this.isValidCoordinate(o) && e.push(o); } else { const i = [ { row: -1, col: 0 }, // North { row: 1, col: 0 }, // South { row: 0, col: -1 }, // West { row: 0, col: 1 } // East ]; for (const n of i) { const o = { row: t.row + n.row, col: t.col + n.col }; this.isValidCoordinate(o) && e.push(o); } } return e; } /** * Check if cell can accommodate more agents */ canPlaceAgent(t) { if (!this.isValidCoordinate(t)) return !1; const e = this.grid[t.row]?.[t.col]; return e ? this.config.allowMultipleOccupancy ? e.agents.size < e.maxOccupancy : e.agents.size === 0 : !1; } /** * Get empty cells in grid */ getEmptyCells() { const t = []; for (let e = 0; e < this.config.rows; e++) for (let i = 0; i < this.config.cols; i++) { const n = { row: e, col: i }; this.canPlaceAgent(n) && t.push(n); } return t; } /** * Get grid occupancy statistics */ getOccupancyStats() { let t = 0, e = 0, i = 0; for (let o = 0; o < this.config.rows; o++) for (let r = 0; r < this.config.cols; r++) { const a = this.grid[o]?.[r]; if (!a) continue; const c = a.agents.size; c > 0 && (t++, e += c, i = Math.max(i, c)); } const n = this.config.rows * this.config.cols; return { totalCells: n, occupiedCells: t, emptyCells: n - t, averageOccupancy: t > 0 ? e / t : 0, maxOccupancy: i }; } /** * Convert position to grid coordinate */ positionToGrid(t) { const e = this.dimensions.width / this.config.cols, i = this.dimensions.height / this.config.rows; return { row: Math.floor(t.y / i), col: Math.floor(t.x / e) }; } /** * Convert grid coordinate to position (center of cell) */ gridToPosition(t) { const e = this.dimensions.width / this.config.cols, i = this.dimensions.height / this.config.rows; return { x: (t.col + 0.5) * e, y: (t.row + 0.5) * i }; } /** * Calculate distance between grid coordinates */ gridDistance(t, e) { return this.config.neighborhoodType === "moore" ? Math.max(Math.abs(t.row - e.row), Math.abs(t.col - e.col)) : Math.abs(t.row - e.row) + Math.abs(t.col - e.col); } /** * Get current grid configuration */ getGridConfig() { return { ...this.config }; } /** * Place agent at specific grid coordinate */ placeAgentAtCell(t, e) { if (!this.canPlaceAgent(e)) return !1; this.removeAgentFromGrid(t); const i = this.grid[e.row]?.[e.col]; if (!i) return !1; i.agents.add(t), this.agentCoordinates.set(t, e); const n = this.gridToPosition(e); return t.setPosition(n), !0; } /** * Hook: Handle agent addition */ onAgentAdded(t, e) { const i = this.positionToGrid(e); if (this.isValidCoordinate(i) && this.canPlaceAgent(i)) { const n = this.grid[i.row]?.[i.col]; if (!n) throw new Error( `Invalid grid cell at ${i.row}, ${i.col}` ); n.agents.add(t), this.agentCoordinates.set(t, i); const o = this.gridToPosition(i); t.setPosition(o); } else throw new Error( `Cannot place agent at position ${e.x}, ${e.y} - cell occupied or invalid` ); } /** * Hook: Handle agent removal */ onAgentRemoved(t) { this.removeAgentFromGrid(t); } /** * Hook: Handle agent movement */ onAgentMoved(t, e, i) { const n = this.positionToGrid(i); if (!this.isValidCoordinate(n)) throw new Error( `Invalid grid position: ${n.row}, ${n.col}` ); const o = this.agentCoordinates.get(t); if (!o || o.row !== n.row || o.col !== n.col) { if (!this.canPlaceAgent(n)) throw new Error( `Cannot move agent to occupied cell: ${n.row}, ${n.col}` ); if (o && this.isValidCoordinate(o)) { const c = this.grid[o.row]?.[o.col]; c && c.agents.delete(t); } const r = this.grid[n.row]?.[n.col]; if (!r) throw new Error( `Invalid grid cell at ${n.row}, ${n.col}` ); r.agents.add(t), this.agentCoordinates.set(t, n); const a = this.gridToPosition(n); t.setPosition(a); } } /** * Initialize grid structure */ initializeGrid() { const t = []; for (let e = 0; e < this.config.rows; e++) { t[e] = []; for (let i = 0; i < this.config.cols; i++) t[e][i] = { coordinate: { row: e, col: i }, agents: /* @__PURE__ */ new Set(), maxOccupancy: this.config.allowMultipleOccupancy ? 10 : 1 // Default max occupancy }; } return t; } /** * Check if grid coordinate is valid */ isValidCoordinate(t) { return t.row >= 0 && t.row < this.config.rows && t.col >= 0 && t.col < this.config.cols; } /** * Remove agent from grid */ removeAgentFromGrid(t) { const e = this.agentCoordinates.get(t); if (e && this.isValidCoordinate(e)) { const i = this.grid[e.row]?.[e.col]; i && i.agents.delete(t); } this.agentCoordinates.delete(t); } /** * Validate grid configuration */ validateConfiguration() { if (this.config.rows <= 0 || this.config.cols <= 0) throw new Error("Grid dimensions must be positive"); if (this.dimensions.width / this.config.cols < 1 || this.dimensions.height / this.config.rows < 1) throw new Error("Grid cells are too small for given dimensions"); } } class Qs extends et { constructor(t) { super(), this.agents = /* @__PURE__ */ new Set(), this.stepNumber = 0, this.lastStepTime = 0, this.totalSteps = 0, this.randomSeed = t, this.randomSeed !== void 0 && this.initializeRandom(this.randomSeed); } /** * Add agent to scheduling */ addAgent(t) { if (this.agents.has(t)) throw new Error(`Agent ${t.id} is already scheduled`); this.agents.add(t), this.onAgentAdded(t), this.emit("agentAdded", { agent: t, schedulerSize: this.agents.size }); } /** * Remove agent from scheduling */ removeAgent(t) { return this.agents.has(t) ? (this.agents.delete(t), this.onAgentRemoved(t), this.emit("agentRemoved", { agent: t, schedulerSize: this.agents.size }), !0) : !1; } /** * Get all scheduled agents */ getAgents() { return Array.from(this.agents); } /** * Get number of scheduled agents */ getAgentCount() { return this.agents.size; } /** * Execute one simulation step */ step() { const t = performance.now(), i = this.getActivationOrder().filter((n) => n.isActive()); this.emit("stepStarted", { stepNumber: this.stepNumber, agentCount: i.length }); for (const n of i) try { n.step(); } catch (o) { this.emit("agentStepError", { agent: n, error: o, stepNumber: this.stepNumber }), console.warn(`Agent ${n.id} step failed:`, o); } this.stepNumber++, this.totalSteps++, this.lastStepTime = performance.now() - t, this.emit("stepCompleted", { stepNumber: this.stepNumber, executionTime: this.lastStepTime, agentCount: i.length }); } /** * Reset scheduler state */ reset() { this.stepNumber = 0, this.totalSteps = 0, this.lastStepTime = 0, this.randomSeed !== void 0 && this.initializeRandom(this.randomSeed), this.emit("schedulerReset"); } /** * Get current step number */ getCurrentStep() { return this.stepNumber; } /** * Get total steps executed */ getTotalSteps() { return this.totalSteps; } /** * Get last step execution time in milliseconds */ getLastStepTime() { return this.lastStepTime; } /** * Get scheduling statistics */ getStatistics() { const t = this.totalSteps > 0 ? this.lastStepTime / this.totalSteps : 0; return { totalAgents: this.agents.size, activeAgents: Array.from(this.agents).filter((e) => e.isActive()).length, currentStep: this.stepNumber, totalSteps: this.totalSteps, lastStepTime: this.lastStepTime, averageStepTime: t, schedulerType: this.getSchedulerType() }; } /** * Hook for subclasses when agent is added */ onAgentAdded(t) { } /** * Hook for subclasses when agent is removed */ onAgentRemoved(t) { } /** * Initialize random number generator with seed */ initializeRandom(t) { let e = t; Math.random = () => (e = (e * 1664525 + 1013904223) % 4294967296, e / 4294967296); } } class ar extends Qs { constructor(t) { super(), this.seed = t, this.rngState = t ?? Date.now(); } /** * Get agent activation order (implementing abstract method) */ getActivationOrder() { return this.schedule(this.getAgents()); } /** * Get scheduler type (implementing abstract method) */ getSchedulerType() { return this.getType(); } /** * Schedule agents in random order */ schedule(t) { const e = t.filter((n) => n.isActive()); if (e.length === 0) return []; const i = [...e]; return this.shuffleArray(i), i; } /** * Set random seed for reproducible results */ setSeed(t) { this.seed = t, this.rngState = t; } /** * Get current seed */ getSeed() { return this.seed; } /** * Reset RNG state to original seed */ resetSeed() { this.rngState = this.seed ?? Date.now(); } /** * Get scheduler type */ getType() { return "random"; } /** * Get scheduler configuration */ getConfiguration() { return { type: this.getType(), seed: this.seed, currentRngState: this.rngState, repr