@holoscript/framework 6.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ALL-test-results.json +1 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/ROADMAP.md +175 -0
- package/dist/AgentManifest-CB4xM-Ma.d.cts +704 -0
- package/dist/AgentManifest-CB4xM-Ma.d.ts +704 -0
- package/dist/BehaviorTree-BrBFECv5.d.cts +103 -0
- package/dist/BehaviorTree-BrBFECv5.d.ts +103 -0
- package/dist/InvisibleWallet-BB6tFvRA.d.cts +1732 -0
- package/dist/InvisibleWallet-rtRrBOA8.d.ts +1732 -0
- package/dist/OrchestratorAgent-BvWgf9uw.d.cts +798 -0
- package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +798 -0
- package/dist/agents/index.cjs +4790 -0
- package/dist/agents/index.d.cts +1788 -0
- package/dist/agents/index.d.ts +1788 -0
- package/dist/agents/index.js +4695 -0
- package/dist/ai/index.cjs +5347 -0
- package/dist/ai/index.d.cts +1753 -0
- package/dist/ai/index.d.ts +1753 -0
- package/dist/ai/index.js +5244 -0
- package/dist/behavior.cjs +449 -0
- package/dist/behavior.d.cts +130 -0
- package/dist/behavior.d.ts +130 -0
- package/dist/behavior.js +407 -0
- package/dist/economy/index.cjs +3659 -0
- package/dist/economy/index.d.cts +747 -0
- package/dist/economy/index.d.ts +747 -0
- package/dist/economy/index.js +3617 -0
- package/dist/implementations-D9T3un9D.d.cts +236 -0
- package/dist/implementations-D9T3un9D.d.ts +236 -0
- package/dist/index.cjs +24550 -0
- package/dist/index.d.cts +1729 -0
- package/dist/index.d.ts +1729 -0
- package/dist/index.js +24277 -0
- package/dist/learning/index.cjs +219 -0
- package/dist/learning/index.d.cts +104 -0
- package/dist/learning/index.d.ts +104 -0
- package/dist/learning/index.js +189 -0
- package/dist/negotiation/index.cjs +970 -0
- package/dist/negotiation/index.d.cts +610 -0
- package/dist/negotiation/index.d.ts +610 -0
- package/dist/negotiation/index.js +931 -0
- package/dist/skills/index.cjs +1118 -0
- package/dist/skills/index.d.cts +289 -0
- package/dist/skills/index.d.ts +289 -0
- package/dist/skills/index.js +1079 -0
- package/dist/swarm/index.cjs +5268 -0
- package/dist/swarm/index.d.cts +2433 -0
- package/dist/swarm/index.d.ts +2433 -0
- package/dist/swarm/index.js +5221 -0
- package/dist/training/index.cjs +2745 -0
- package/dist/training/index.d.cts +1734 -0
- package/dist/training/index.d.ts +1734 -0
- package/dist/training/index.js +2687 -0
- package/extract-failures.js +10 -0
- package/package.json +82 -0
- package/src/__tests__/bounty-marketplace.test.ts +374 -0
- package/src/__tests__/delegation.test.ts +144 -0
- package/src/__tests__/distributed-claimer.test.ts +147 -0
- package/src/__tests__/done-log-audit.test.ts +342 -0
- package/src/__tests__/framework.test.ts +865 -0
- package/src/__tests__/goal-synthesizer.test.ts +236 -0
- package/src/__tests__/presence.test.ts +223 -0
- package/src/__tests__/protocol-agent.test.ts +254 -0
- package/src/__tests__/revenue-splitter.test.ts +114 -0
- package/src/__tests__/scenario-driven-todo.test.ts +197 -0
- package/src/__tests__/self-improve.test.ts +349 -0
- package/src/__tests__/service-lifecycle.test.ts +237 -0
- package/src/__tests__/skill-router.test.ts +121 -0
- package/src/agents/AgentManifest.ts +493 -0
- package/src/agents/AgentRegistry.ts +475 -0
- package/src/agents/AgentTypes.ts +585 -0
- package/src/agents/AgentWalletRegistry.ts +83 -0
- package/src/agents/AuthenticatedCRDT.ts +388 -0
- package/src/agents/CapabilityMatcher.ts +453 -0
- package/src/agents/CrossRealityHandoff.ts +305 -0
- package/src/agents/CulturalMemory.ts +454 -0
- package/src/agents/FederatedRegistryAdapter.ts +429 -0
- package/src/agents/NormEngine.ts +450 -0
- package/src/agents/OrchestratorAgent.ts +414 -0
- package/src/agents/SkillWorkflowEngine.ts +472 -0
- package/src/agents/TaskDelegationService.ts +551 -0
- package/src/agents/__tests__/AgentManifest.prod.test.ts +134 -0
- package/src/agents/__tests__/AgentManifest.test.ts +182 -0
- package/src/agents/__tests__/AgentModule.test.ts +864 -0
- package/src/agents/__tests__/AgentRegistry.prod.test.ts +125 -0
- package/src/agents/__tests__/AgentRegistry.test.ts +148 -0
- package/src/agents/__tests__/AgentTypes.test.ts +534 -0
- package/src/agents/__tests__/AgentWalletRegistry.test.ts +152 -0
- package/src/agents/__tests__/AuthenticatedCRDT.test.ts +558 -0
- package/src/agents/__tests__/CapabilityMatcher.prod.test.ts +117 -0
- package/src/agents/__tests__/CapabilityMatcher.test.ts +178 -0
- package/src/agents/__tests__/CrossRealityHandoff.test.ts +402 -0
- package/src/agents/__tests__/CulturalMemory.test.ts +200 -0
- package/src/agents/__tests__/FederatedRegistryAdapter.test.ts +409 -0
- package/src/agents/__tests__/NormEngine.test.ts +276 -0
- package/src/agents/__tests__/OrchestratorAgent.test.ts +182 -0
- package/src/agents/__tests__/SkillWorkflowEngine.test.ts +357 -0
- package/src/agents/__tests__/TaskDelegationService.test.ts +446 -0
- package/src/agents/index.ts +107 -0
- package/src/agents/spatial-comms/Layer1RealTime.ts +621 -0
- package/src/agents/spatial-comms/Layer2A2A.ts +661 -0
- package/src/agents/spatial-comms/Layer3MCP.ts +651 -0
- package/src/agents/spatial-comms/ProtocolTypes.ts +543 -0
- package/src/agents/spatial-comms/SpatialCommClient.ts +483 -0
- package/src/agents/spatial-comms/__tests__/performance-benchmark.test.ts +465 -0
- package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +409 -0
- package/src/agents/spatial-comms/index.ts +66 -0
- package/src/ai/AIAdapter.ts +313 -0
- package/src/ai/AICopilot.ts +331 -0
- package/src/ai/AIOutputValidator.ts +203 -0
- package/src/ai/BTNodes.ts +239 -0
- package/src/ai/BehaviorSelector.ts +135 -0
- package/src/ai/BehaviorTree.ts +153 -0
- package/src/ai/Blackboard.ts +165 -0
- package/src/ai/GenerationAnalytics.ts +461 -0
- package/src/ai/GenerationCache.ts +265 -0
- package/src/ai/GoalPlanner.ts +165 -0
- package/src/ai/HoloScriptGenerator.ts +580 -0
- package/src/ai/InfluenceMap.ts +180 -0
- package/src/ai/NavMesh.ts +168 -0
- package/src/ai/PerceptionSystem.ts +178 -0
- package/src/ai/PromptTemplates.ts +453 -0
- package/src/ai/SemanticSearchService.ts +80 -0
- package/src/ai/StateMachine.ts +196 -0
- package/src/ai/SteeringBehavior.ts +150 -0
- package/src/ai/SteeringBehaviors.ts +244 -0
- package/src/ai/TrainingDataGenerator.ts +1082 -0
- package/src/ai/UtilityAI.ts +145 -0
- package/src/ai/__tests__/AIAdapter.prod.test.ts +259 -0
- package/src/ai/__tests__/AIAdapter.test.ts +109 -0
- package/src/ai/__tests__/AICopilot.prod.test.ts +341 -0
- package/src/ai/__tests__/AICopilot.test.ts +178 -0
- package/src/ai/__tests__/AIOutputValidator.prod.test.ts +226 -0
- package/src/ai/__tests__/AIOutputValidator.test.ts +138 -0
- package/src/ai/__tests__/BTNodes.prod.test.ts +391 -0
- package/src/ai/__tests__/BTNodes.test.ts +263 -0
- package/src/ai/__tests__/BehaviorSelector.prod.test.ts +129 -0
- package/src/ai/__tests__/BehaviorSelector.test.ts +132 -0
- package/src/ai/__tests__/BehaviorTree.prod.test.ts +266 -0
- package/src/ai/__tests__/BehaviorTree.test.ts +216 -0
- package/src/ai/__tests__/Blackboard.prod.test.ts +339 -0
- package/src/ai/__tests__/Blackboard.test.ts +183 -0
- package/src/ai/__tests__/GenerationAnalytics.prod.test.ts +141 -0
- package/src/ai/__tests__/GenerationAnalytics.test.ts +165 -0
- package/src/ai/__tests__/GenerationCache.prod.test.ts +144 -0
- package/src/ai/__tests__/GenerationCache.test.ts +171 -0
- package/src/ai/__tests__/GoalPlanner.prod.test.ts +189 -0
- package/src/ai/__tests__/GoalPlanner.test.ts +137 -0
- package/src/ai/__tests__/GoalPlannerDepth.prod.test.ts +217 -0
- package/src/ai/__tests__/HoloScriptGenerator.test.ts +125 -0
- package/src/ai/__tests__/InfluenceMap.prod.test.ts +146 -0
- package/src/ai/__tests__/InfluenceMap.test.ts +149 -0
- package/src/ai/__tests__/NavMesh.prod.test.ts +141 -0
- package/src/ai/__tests__/NavMesh.test.ts +159 -0
- package/src/ai/__tests__/PerceptionSystem.prod.test.ts +135 -0
- package/src/ai/__tests__/PerceptionSystem.test.ts +250 -0
- package/src/ai/__tests__/PromptTemplates.prod.test.ts +313 -0
- package/src/ai/__tests__/PromptTemplates.test.ts +146 -0
- package/src/ai/__tests__/SemanticSearch.test.ts +37 -0
- package/src/ai/__tests__/StateMachine.prod.test.ts +162 -0
- package/src/ai/__tests__/StateMachine.test.ts +163 -0
- package/src/ai/__tests__/SteeringBehavior.prod.test.ts +251 -0
- package/src/ai/__tests__/SteeringBehavior.test.ts +135 -0
- package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +133 -0
- package/src/ai/__tests__/SteeringBehaviors.test.ts +151 -0
- package/src/ai/__tests__/TrainingDataGenerator.prod.test.ts +286 -0
- package/src/ai/__tests__/TrainingDataGenerator.test.ts +286 -0
- package/src/ai/__tests__/UtilityAI.prod.test.ts +207 -0
- package/src/ai/__tests__/UtilityAI.test.ts +155 -0
- package/src/ai/__tests__/adapters.prod.test.ts +263 -0
- package/src/ai/__tests__/adapters.test.ts +320 -0
- package/src/ai/adapters.ts +1585 -0
- package/src/ai/index.ts +130 -0
- package/src/behavior/BehaviorPresets.ts +140 -0
- package/src/behavior/BehaviorTree.ts +236 -0
- package/src/behavior/StateMachine.ts +176 -0
- package/src/behavior/StateTrait.ts +67 -0
- package/src/behavior/index.ts +8 -0
- package/src/behavior.ts +8 -0
- package/src/board/audit.ts +284 -0
- package/src/board/board-ops.ts +336 -0
- package/src/board/board-types.ts +302 -0
- package/src/board/index.ts +69 -0
- package/src/define-agent.ts +46 -0
- package/src/define-team.ts +33 -0
- package/src/delegation.ts +265 -0
- package/src/distributed-claimer.ts +228 -0
- package/src/economy/AgentBudgetEnforcer.ts +464 -0
- package/src/economy/BountyManager.ts +185 -0
- package/src/economy/CreatorRevenueAggregator.ts +460 -0
- package/src/economy/InvisibleWallet.ts +82 -0
- package/src/economy/KnowledgeMarketplace.ts +193 -0
- package/src/economy/PaymentWebhookService.ts +512 -0
- package/src/economy/RevenueSplitter.ts +156 -0
- package/src/economy/SubscriptionManager.ts +546 -0
- package/src/economy/UnifiedBudgetOptimizer.ts +635 -0
- package/src/economy/UsageMeter.ts +440 -0
- package/src/economy/_core-stubs.ts +219 -0
- package/src/economy/index.ts +100 -0
- package/src/economy/x402-facilitator.ts +1978 -0
- package/src/index.ts +348 -0
- package/src/knowledge/__tests__/knowledge-consolidator.test.ts +444 -0
- package/src/knowledge/__tests__/knowledge-store-vector.test.ts +291 -0
- package/src/knowledge/brain.ts +167 -0
- package/src/knowledge/consolidation.ts +581 -0
- package/src/knowledge/knowledge-consolidator.ts +510 -0
- package/src/knowledge/knowledge-store.ts +616 -0
- package/src/learning/MemoryConsolidator.ts +102 -0
- package/src/learning/MemoryScorer.ts +69 -0
- package/src/learning/ProceduralCompiler.ts +45 -0
- package/src/learning/SemanticClusterer.ts +66 -0
- package/src/learning/index.ts +8 -0
- package/src/llm/llm-adapter.ts +159 -0
- package/src/mesh/index.ts +309 -0
- package/src/negotiation/NegotiationProtocol.ts +694 -0
- package/src/negotiation/NegotiationTypes.ts +473 -0
- package/src/negotiation/VotingMechanisms.ts +691 -0
- package/src/negotiation/index.ts +49 -0
- package/src/protocol/goal-synthesizer.ts +317 -0
- package/src/protocol/implementations.ts +474 -0
- package/src/protocol/micro-phase-decomposer.ts +299 -0
- package/src/protocol/micro-step-decomposer.test.ts +306 -0
- package/src/protocol-agent.test.ts +353 -0
- package/src/protocol-agent.ts +670 -0
- package/src/self-improve/absorb-scanner.ts +252 -0
- package/src/self-improve/evolution-engine.ts +149 -0
- package/src/self-improve/framework-absorber.ts +214 -0
- package/src/self-improve/index.ts +50 -0
- package/src/self-improve/prompt-optimizer.ts +212 -0
- package/src/self-improve/test-generator.ts +175 -0
- package/src/skill-router.ts +186 -0
- package/src/skills/index.ts +5 -0
- package/src/skills/skill-md-bridge.ts +1699 -0
- package/src/swarm/ACOEngine.ts +261 -0
- package/src/swarm/CollectiveIntelligence.ts +383 -0
- package/src/swarm/ContributionSynthesizer.ts +481 -0
- package/src/swarm/LeaderElection.ts +393 -0
- package/src/swarm/PSOEngine.ts +206 -0
- package/src/swarm/QuorumPolicy.ts +173 -0
- package/src/swarm/SwarmCoordinator.ts +335 -0
- package/src/swarm/SwarmManager.ts +442 -0
- package/src/swarm/SwarmMembership.ts +456 -0
- package/src/swarm/VotingRound.ts +255 -0
- package/src/swarm/__tests__/ACOEngine.prod.test.ts +164 -0
- package/src/swarm/__tests__/ACOEngine.test.ts +117 -0
- package/src/swarm/__tests__/CollectiveIntelligence.prod.test.ts +296 -0
- package/src/swarm/__tests__/CollectiveIntelligence.test.ts +457 -0
- package/src/swarm/__tests__/ContributionSynthesizer.prod.test.ts +269 -0
- package/src/swarm/__tests__/ContributionSynthesizer.test.ts +254 -0
- package/src/swarm/__tests__/LeaderElection.prod.test.ts +196 -0
- package/src/swarm/__tests__/LeaderElection.test.ts +151 -0
- package/src/swarm/__tests__/PSOEngine.prod.test.ts +162 -0
- package/src/swarm/__tests__/PSOEngine.test.ts +106 -0
- package/src/swarm/__tests__/QuorumPolicy.prod.test.ts +216 -0
- package/src/swarm/__tests__/QuorumPolicy.test.ts +177 -0
- package/src/swarm/__tests__/SwarmCoordinator.prod.test.ts +186 -0
- package/src/swarm/__tests__/SwarmCoordinator.test.ts +167 -0
- package/src/swarm/__tests__/SwarmManager.prod.test.ts +308 -0
- package/src/swarm/__tests__/SwarmManager.test.ts +373 -0
- package/src/swarm/__tests__/SwarmMembership.prod.test.ts +273 -0
- package/src/swarm/__tests__/SwarmMembership.test.ts +264 -0
- package/src/swarm/__tests__/VotingRound.prod.test.ts +233 -0
- package/src/swarm/__tests__/VotingRound.test.ts +174 -0
- package/src/swarm/analytics/SwarmInspector.ts +476 -0
- package/src/swarm/analytics/SwarmMetrics.ts +449 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.prod.test.ts +366 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.test.ts +454 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.prod.test.ts +254 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.test.ts +370 -0
- package/src/swarm/analytics/index.ts +7 -0
- package/src/swarm/index.ts +69 -0
- package/src/swarm/messaging/BroadcastChannel.ts +509 -0
- package/src/swarm/messaging/GossipProtocol.ts +565 -0
- package/src/swarm/messaging/SwarmEventBus.ts +443 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +331 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.test.ts +333 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +356 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.test.ts +437 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +191 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +247 -0
- package/src/swarm/messaging/index.ts +8 -0
- package/src/swarm/spatial/FlockingBehavior.ts +462 -0
- package/src/swarm/spatial/FormationController.ts +500 -0
- package/src/swarm/spatial/Vector3.ts +170 -0
- package/src/swarm/spatial/ZoneClaiming.ts +509 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.prod.test.ts +239 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.test.ts +298 -0
- package/src/swarm/spatial/__tests__/FormationController.prod.test.ts +240 -0
- package/src/swarm/spatial/__tests__/FormationController.test.ts +297 -0
- package/src/swarm/spatial/__tests__/Vector3.prod.test.ts +283 -0
- package/src/swarm/spatial/__tests__/Vector3.test.ts +224 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.prod.test.ts +246 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.test.ts +374 -0
- package/src/swarm/spatial/index.ts +28 -0
- package/src/team.ts +1245 -0
- package/src/training/LRScheduler.ts +377 -0
- package/src/training/QualityScoringPipeline.ts +139 -0
- package/src/training/SoftDedup.ts +461 -0
- package/src/training/SparsityMonitor.ts +685 -0
- package/src/training/SparsityMonitorTypes.ts +209 -0
- package/src/training/SpatialTrainingDataGenerator.ts +1526 -0
- package/src/training/SpatialTrainingDataTypes.ts +216 -0
- package/src/training/TrainingPipelineConfig.ts +215 -0
- package/src/training/constants.ts +94 -0
- package/src/training/index.ts +138 -0
- package/src/training/schema.ts +147 -0
- package/src/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
- package/src/training/scripts/generate-spatial-dataset.ts +521 -0
- package/src/training/training/data/novel-use-cases.jsonl +153 -0
- package/src/training/training/data/spatial-reasoning-10k.jsonl +9354 -0
- package/src/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
- package/src/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
- package/src/training/trainingmonkey/index.ts +26 -0
- package/src/training/trait-mappings.ts +157 -0
- package/src/types/core-stubs.d.ts +113 -0
- package/src/types.ts +304 -0
- package/test-output.txt +0 -0
- package/test-result.json +1 -0
- package/tsc-errors.txt +4 -0
- package/tsc_output.txt +0 -0
- package/tsconfig.json +14 -0
- package/tsup-learning-esm.config.ts +12 -0
- package/tsup.config.ts +21 -0
- package/typescript-errors-2.txt +0 -0
- package/typescript-errors.txt +22 -0
- package/vitest-log-utf8.txt +268 -0
- package/vitest-log.txt +0 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { FlockingBehavior } from '../FlockingBehavior';
|
|
3
|
+
import { Vector3 } from '../Vector3';
|
|
4
|
+
|
|
5
|
+
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
function mkFlock(cfg?: ConstructorParameters<typeof FlockingBehavior>[0]) {
|
|
8
|
+
return new FlockingBehavior(cfg);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function v(x: number, y = 0, z = 0) {
|
|
12
|
+
return new Vector3(x, y, z);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ─── tests ───────────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
describe('FlockingBehavior — defaultConfig', () => {
|
|
18
|
+
it('separationRadius = 25', () => expect(mkFlock().getConfig().separationRadius).toBe(25));
|
|
19
|
+
it('alignmentRadius = 50', () => expect(mkFlock().getConfig().alignmentRadius).toBe(50));
|
|
20
|
+
it('cohesionRadius = 50', () => expect(mkFlock().getConfig().cohesionRadius).toBe(50));
|
|
21
|
+
it('maxSpeed = 4', () => expect(mkFlock().getConfig().maxSpeed).toBe(4));
|
|
22
|
+
it('maxForce = 0.1', () => expect(mkFlock().getConfig().maxForce).toBe(0.1));
|
|
23
|
+
it('boundaryMode = wrap', () => expect(mkFlock().getConfig().boundaryMode).toBe('wrap'));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('FlockingBehavior — boid CRUD', () => {
|
|
27
|
+
it('addBoid stores boid', () => {
|
|
28
|
+
const flock = mkFlock();
|
|
29
|
+
flock.addBoid('b1', v(0));
|
|
30
|
+
expect(flock.getBoid('b1')).toBeDefined();
|
|
31
|
+
});
|
|
32
|
+
it('addBoid returns boid object', () => {
|
|
33
|
+
const flock = mkFlock();
|
|
34
|
+
const b = flock.addBoid('b2', v(1, 2, 3));
|
|
35
|
+
expect(b.id).toBe('b2');
|
|
36
|
+
});
|
|
37
|
+
it('addBoid sets position correctly', () => {
|
|
38
|
+
const flock = mkFlock();
|
|
39
|
+
flock.addBoid('b3', v(5, 6, 7));
|
|
40
|
+
const b = flock.getBoid('b3')!;
|
|
41
|
+
expect(b.position.x).toBe(5);
|
|
42
|
+
expect(b.position.y).toBe(6);
|
|
43
|
+
});
|
|
44
|
+
it('addBoid with explicit velocity uses it', () => {
|
|
45
|
+
const flock = mkFlock();
|
|
46
|
+
flock.addBoid('b4', v(0), v(1, 0, 0));
|
|
47
|
+
expect(flock.getBoid('b4')!.velocity.x).toBe(1);
|
|
48
|
+
});
|
|
49
|
+
it('removeBoid returns true', () => {
|
|
50
|
+
const flock = mkFlock();
|
|
51
|
+
flock.addBoid('b5', v(0));
|
|
52
|
+
expect(flock.removeBoid('b5')).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it('removeBoid deletes boid', () => {
|
|
55
|
+
const flock = mkFlock();
|
|
56
|
+
flock.addBoid('b6', v(0));
|
|
57
|
+
flock.removeBoid('b6');
|
|
58
|
+
expect(flock.getBoid('b6')).toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
it('removeBoid on missing id returns false', () => {
|
|
61
|
+
expect(mkFlock().removeBoid('nope')).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
it('getAllBoids returns all boids', () => {
|
|
64
|
+
const flock = mkFlock();
|
|
65
|
+
flock.addBoid('a', v(0));
|
|
66
|
+
flock.addBoid('b', v(1));
|
|
67
|
+
expect(flock.getAllBoids()).toHaveLength(2);
|
|
68
|
+
});
|
|
69
|
+
it('setBoidPosition moves boid', () => {
|
|
70
|
+
const flock = mkFlock();
|
|
71
|
+
flock.addBoid('m', v(0));
|
|
72
|
+
flock.setBoidPosition('m', v(99, 0, 0));
|
|
73
|
+
expect(flock.getBoid('m')!.position.x).toBe(99);
|
|
74
|
+
});
|
|
75
|
+
it('setBoidPosition no-op for missing id', () => {
|
|
76
|
+
expect(() => mkFlock().setBoidPosition('none', v(1))).not.toThrow();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('FlockingBehavior — findNeighbors', () => {
|
|
81
|
+
it('finds boid within radius', () => {
|
|
82
|
+
const flock = mkFlock();
|
|
83
|
+
const b1 = flock.addBoid('A', v(0), v(0));
|
|
84
|
+
flock.addBoid('B', v(10), v(0));
|
|
85
|
+
const neighbors = flock.findNeighbors(b1, 20);
|
|
86
|
+
expect(neighbors.some((n) => n.id === 'B')).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
it('excludes boid outside radius', () => {
|
|
89
|
+
const flock = mkFlock();
|
|
90
|
+
const b1 = flock.addBoid('A', v(0), v(0));
|
|
91
|
+
flock.addBoid('B', v(100), v(0));
|
|
92
|
+
const neighbors = flock.findNeighbors(b1, 20);
|
|
93
|
+
expect(neighbors.some((n) => n.id === 'B')).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
it('excludes self from neighbors', () => {
|
|
96
|
+
const flock = mkFlock();
|
|
97
|
+
const b1 = flock.addBoid('self', v(0), v(0));
|
|
98
|
+
const neighbors = flock.findNeighbors(b1, 999);
|
|
99
|
+
expect(neighbors.every((n) => n.id !== 'self')).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('FlockingBehavior — seek / flee / arrive', () => {
|
|
104
|
+
it('seek returns non-zero force toward target', () => {
|
|
105
|
+
const flock = mkFlock();
|
|
106
|
+
const boid = flock.addBoid('s', v(0), v(0));
|
|
107
|
+
const force = flock.seek(boid, v(100, 0, 0));
|
|
108
|
+
expect(force.x).toBeGreaterThan(0);
|
|
109
|
+
});
|
|
110
|
+
it('flee returns opposite direction from seek', () => {
|
|
111
|
+
const flock = mkFlock();
|
|
112
|
+
const boid = flock.addBoid('f', v(0), v(0));
|
|
113
|
+
const seekF = flock.seek(boid, v(100, 0, 0));
|
|
114
|
+
const fleeF = flock.flee(boid, v(100, 0, 0));
|
|
115
|
+
expect(Math.sign(fleeF.x)).toBe(-Math.sign(seekF.x));
|
|
116
|
+
});
|
|
117
|
+
it('arrive returns zero force when at target', () => {
|
|
118
|
+
const flock = mkFlock({ maxForce: 1 });
|
|
119
|
+
const boid = flock.addBoid('a', v(0), v(0));
|
|
120
|
+
const force = flock.arrive(boid, v(0, 0, 0), 10);
|
|
121
|
+
expect(force.magnitude()).toBeCloseTo(0, 5);
|
|
122
|
+
});
|
|
123
|
+
it('arrive slows within slowingRadius', () => {
|
|
124
|
+
const flock = mkFlock({ maxSpeed: 4, maxForce: 1 });
|
|
125
|
+
// Place boid exactly at slowingRadius/2 from target
|
|
126
|
+
const boid = flock.addBoid('slow', v(5), v(0));
|
|
127
|
+
const fullSeekForce = flock.seek(boid, v(10)).magnitude();
|
|
128
|
+
const arriveForce = flock.arrive(boid, v(10), 10).magnitude(); // inside slowing zone
|
|
129
|
+
// Arrive magnitude should be <= full seek magnitude when slowing
|
|
130
|
+
expect(arriveForce).toBeLessThanOrEqual(fullSeekForce + 0.001);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('FlockingBehavior — separate / align / cohere', () => {
|
|
135
|
+
it('separate returns zero when no close neighbors', () => {
|
|
136
|
+
const flock = mkFlock({ separationRadius: 5 });
|
|
137
|
+
const boid = flock.addBoid('me', v(0), v(0));
|
|
138
|
+
const far = flock.addBoid('far', v(100), v(0));
|
|
139
|
+
const force = flock.separate(boid, [boid, far]);
|
|
140
|
+
expect(force.magnitude()).toBeCloseTo(0, 5);
|
|
141
|
+
});
|
|
142
|
+
it('separate returns force away from close neighbor', () => {
|
|
143
|
+
const flock = mkFlock({ separationRadius: 30, maxSpeed: 4, maxForce: 1 });
|
|
144
|
+
const boid = flock.addBoid('me', v(0), v(0));
|
|
145
|
+
const close = flock.addBoid('close', v(5), v(0));
|
|
146
|
+
const force = flock.separate(boid, [boid, close]);
|
|
147
|
+
// Should push left (away from +x)
|
|
148
|
+
expect(force.x).toBeLessThan(0);
|
|
149
|
+
});
|
|
150
|
+
it('align returns zero when no in-radius neighbors', () => {
|
|
151
|
+
const flock = mkFlock({ alignmentRadius: 5 });
|
|
152
|
+
const boid = flock.addBoid('me', v(0), v(0));
|
|
153
|
+
const far = flock.addBoid('far', v(100), v(0));
|
|
154
|
+
const force = flock.align(boid, [boid, far]);
|
|
155
|
+
expect(force.magnitude()).toBeCloseTo(0, 5);
|
|
156
|
+
});
|
|
157
|
+
it('cohere steers toward center of nearby neighbors', () => {
|
|
158
|
+
const flock = mkFlock({ cohesionRadius: 200, maxSpeed: 4, maxForce: 1 });
|
|
159
|
+
const boid = flock.addBoid('me', v(0), v(0));
|
|
160
|
+
flock.addBoid('r1', v(100), v(0));
|
|
161
|
+
flock.addBoid('r2', v(100), v(0));
|
|
162
|
+
const all = flock.getAllBoids();
|
|
163
|
+
const force = flock.cohere(boid, all);
|
|
164
|
+
// Center of other boids is at x=100, so cohere should push +x
|
|
165
|
+
expect(force.x).toBeGreaterThan(0);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('FlockingBehavior — update', () => {
|
|
170
|
+
it('update() runs without throwing', () => {
|
|
171
|
+
const flock = mkFlock();
|
|
172
|
+
flock.addBoid('u1', v(0), v(1, 0, 0));
|
|
173
|
+
flock.addBoid('u2', v(2), v(-1, 0, 0));
|
|
174
|
+
expect(() => flock.update()).not.toThrow();
|
|
175
|
+
});
|
|
176
|
+
it('update() moves boid positions', () => {
|
|
177
|
+
const flock = mkFlock();
|
|
178
|
+
flock.addBoid('m', v(0), v(1, 0, 0));
|
|
179
|
+
const before = flock.getBoid('m')!.position.x;
|
|
180
|
+
flock.update();
|
|
181
|
+
expect(flock.getBoid('m')!.position.x).not.toBe(before);
|
|
182
|
+
});
|
|
183
|
+
it('applyForce adds to acceleration', () => {
|
|
184
|
+
const flock = mkFlock();
|
|
185
|
+
flock.addBoid('af', v(0), v(0));
|
|
186
|
+
flock.applyForce('af', v(5, 0, 0));
|
|
187
|
+
expect(flock.getBoid('af')!.acceleration.x).toBe(5);
|
|
188
|
+
});
|
|
189
|
+
it('applyForceToAll affects all boids', () => {
|
|
190
|
+
const flock = mkFlock();
|
|
191
|
+
flock.addBoid('x1', v(0), v(0));
|
|
192
|
+
flock.addBoid('x2', v(5), v(0));
|
|
193
|
+
flock.applyForceToAll(v(0, 0, 2));
|
|
194
|
+
flock.getAllBoids().forEach((b) => expect(b.acceleration.z).toBe(2));
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe('FlockingBehavior — analytics', () => {
|
|
199
|
+
it('getFlockCenter returns zero for empty flock', () => {
|
|
200
|
+
const c = mkFlock().getFlockCenter();
|
|
201
|
+
expect(c.magnitude()).toBeCloseTo(0, 5);
|
|
202
|
+
});
|
|
203
|
+
it('getFlockCenter returns centroid', () => {
|
|
204
|
+
const flock = mkFlock();
|
|
205
|
+
flock.addBoid('a', v(0), v(0));
|
|
206
|
+
flock.addBoid('b', v(10), v(0));
|
|
207
|
+
expect(flock.getFlockCenter().x).toBeCloseTo(5, 5);
|
|
208
|
+
});
|
|
209
|
+
it('getFlockSpread = 0 for single boid', () => {
|
|
210
|
+
const flock = mkFlock();
|
|
211
|
+
flock.addBoid('solo', v(0), v(0));
|
|
212
|
+
expect(flock.getFlockSpread()).toBe(0);
|
|
213
|
+
});
|
|
214
|
+
it('getFlockSpread > 0 for spread boids', () => {
|
|
215
|
+
const flock = mkFlock();
|
|
216
|
+
flock.addBoid('a', v(0), v(0));
|
|
217
|
+
flock.addBoid('b', v(100), v(0));
|
|
218
|
+
expect(flock.getFlockSpread()).toBeGreaterThan(0);
|
|
219
|
+
});
|
|
220
|
+
it('getFlockDirection returns unit vector', () => {
|
|
221
|
+
const flock = mkFlock();
|
|
222
|
+
flock.addBoid('d', v(0), v(3, 0, 0));
|
|
223
|
+
const dir = flock.getFlockDirection();
|
|
224
|
+
expect(dir.magnitude()).toBeCloseTo(1, 5);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('FlockingBehavior — config', () => {
|
|
229
|
+
it('setConfig updates a field', () => {
|
|
230
|
+
const flock = mkFlock();
|
|
231
|
+
flock.setConfig({ maxSpeed: 10 });
|
|
232
|
+
expect(flock.getConfig().maxSpeed).toBe(10);
|
|
233
|
+
});
|
|
234
|
+
it('setConfig preserves unchanged fields', () => {
|
|
235
|
+
const flock = mkFlock();
|
|
236
|
+
flock.setConfig({ maxSpeed: 10 });
|
|
237
|
+
expect(flock.getConfig().separationRadius).toBe(25);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FlockingBehavior Tests
|
|
3
|
+
* HoloScript v3.2 - Autonomous Agent Swarms
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
7
|
+
import { FlockingBehavior } from '../FlockingBehavior';
|
|
8
|
+
import { Vector3 } from '../Vector3';
|
|
9
|
+
|
|
10
|
+
describe('FlockingBehavior', () => {
|
|
11
|
+
let flock: FlockingBehavior;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
flock = new FlockingBehavior({
|
|
15
|
+
separationRadius: 25,
|
|
16
|
+
alignmentRadius: 50,
|
|
17
|
+
cohesionRadius: 50,
|
|
18
|
+
maxSpeed: 4,
|
|
19
|
+
maxForce: 0.1,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('boid management', () => {
|
|
24
|
+
it('should add a boid', () => {
|
|
25
|
+
const boid = flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
26
|
+
expect(boid.id).toBe('boid-1');
|
|
27
|
+
expect(boid.position.x).toBe(0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should add boid with velocity', () => {
|
|
31
|
+
const boid = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(1, 0, 0));
|
|
32
|
+
expect(boid.velocity.x).toBe(1);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should generate random velocity if not provided', () => {
|
|
36
|
+
const boid = flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
37
|
+
const mag = boid.velocity.magnitude();
|
|
38
|
+
expect(mag).toBeGreaterThan(0);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should remove a boid', () => {
|
|
42
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
43
|
+
expect(flock.removeBoid('boid-1')).toBe(true);
|
|
44
|
+
expect(flock.getBoid('boid-1')).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should get all boids', () => {
|
|
48
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
49
|
+
flock.addBoid('boid-2', new Vector3(10, 0, 0));
|
|
50
|
+
expect(flock.getAllBoids()).toHaveLength(2);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should set boid position', () => {
|
|
54
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
55
|
+
flock.setBoidPosition('boid-1', new Vector3(100, 100, 100));
|
|
56
|
+
const boid = flock.getBoid('boid-1');
|
|
57
|
+
expect(boid?.position.x).toBe(100);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('separation', () => {
|
|
62
|
+
it('should steer away from close neighbors', () => {
|
|
63
|
+
const boid1 = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
64
|
+
const boid2 = flock.addBoid('boid-2', new Vector3(10, 0, 0), new Vector3(0, 0, 0));
|
|
65
|
+
|
|
66
|
+
const steer = flock.separate(boid1, [boid2]);
|
|
67
|
+
// Should steer away (negative x direction)
|
|
68
|
+
expect(steer.x).toBeLessThan(0);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should return zero if no neighbors in separation radius', () => {
|
|
72
|
+
const boid1 = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
73
|
+
const boid2 = flock.addBoid('boid-2', new Vector3(100, 0, 0), new Vector3(0, 0, 0));
|
|
74
|
+
|
|
75
|
+
const steer = flock.separate(boid1, [boid2]);
|
|
76
|
+
expect(steer.magnitude()).toBe(0);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('alignment', () => {
|
|
81
|
+
it('should steer toward average heading of neighbors', () => {
|
|
82
|
+
const boid1 = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
83
|
+
const boid2 = flock.addBoid('boid-2', new Vector3(10, 0, 0), new Vector3(1, 0, 0));
|
|
84
|
+
const boid3 = flock.addBoid('boid-3', new Vector3(-10, 0, 0), new Vector3(1, 0, 0));
|
|
85
|
+
|
|
86
|
+
const steer = flock.align(boid1, [boid2, boid3]);
|
|
87
|
+
// Should align with positive x direction
|
|
88
|
+
expect(steer.x).toBeGreaterThan(0);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should return zero if no neighbors in alignment radius', () => {
|
|
92
|
+
const boid1 = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
93
|
+
const boid2 = flock.addBoid('boid-2', new Vector3(100, 0, 0), new Vector3(1, 0, 0));
|
|
94
|
+
|
|
95
|
+
const steer = flock.align(boid1, [boid2]);
|
|
96
|
+
expect(steer.magnitude()).toBe(0);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('cohesion', () => {
|
|
101
|
+
it('should steer toward center of neighbors', () => {
|
|
102
|
+
const boid1 = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
103
|
+
const boid2 = flock.addBoid('boid-2', new Vector3(30, 0, 0), new Vector3(0, 0, 0));
|
|
104
|
+
const boid3 = flock.addBoid('boid-3', new Vector3(30, 30, 0), new Vector3(0, 0, 0));
|
|
105
|
+
|
|
106
|
+
const steer = flock.cohere(boid1, [boid2, boid3]);
|
|
107
|
+
// Center is at (30, 15, 0), so should steer positive x and y
|
|
108
|
+
expect(steer.x).toBeGreaterThan(0);
|
|
109
|
+
expect(steer.y).toBeGreaterThan(0);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should return zero if no neighbors in cohesion radius', () => {
|
|
113
|
+
const boid1 = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
114
|
+
const boid2 = flock.addBoid('boid-2', new Vector3(100, 0, 0), new Vector3(0, 0, 0));
|
|
115
|
+
|
|
116
|
+
const steer = flock.cohere(boid1, [boid2]);
|
|
117
|
+
expect(steer.magnitude()).toBe(0);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('steering behaviors', () => {
|
|
122
|
+
it('should seek target', () => {
|
|
123
|
+
const boid = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
124
|
+
const target = new Vector3(100, 0, 0);
|
|
125
|
+
|
|
126
|
+
const steer = flock.seek(boid, target);
|
|
127
|
+
expect(steer.x).toBeGreaterThan(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should flee from target', () => {
|
|
131
|
+
const boid = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
132
|
+
const target = new Vector3(100, 0, 0);
|
|
133
|
+
|
|
134
|
+
const steer = flock.flee(boid, target);
|
|
135
|
+
expect(steer.x).toBeLessThan(0);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should arrive and slow down near target', () => {
|
|
139
|
+
// Use high maxForce to avoid clamping
|
|
140
|
+
const arrivalFlock = new FlockingBehavior({ maxSpeed: 4, maxForce: 10 });
|
|
141
|
+
const boid = arrivalFlock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
142
|
+
const nearTarget = new Vector3(2, 0, 0); // Very close, within slowingRadius
|
|
143
|
+
const farTarget = new Vector3(100, 0, 0);
|
|
144
|
+
|
|
145
|
+
const nearSteer = arrivalFlock.arrive(boid, nearTarget, 20);
|
|
146
|
+
const farSteer = arrivalFlock.arrive(boid, farTarget, 20);
|
|
147
|
+
|
|
148
|
+
// Near target should have smaller force due to slowing behavior
|
|
149
|
+
expect(nearSteer.magnitude()).toBeLessThan(farSteer.magnitude());
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('neighbors', () => {
|
|
154
|
+
it('should find neighbors within radius', () => {
|
|
155
|
+
const boid1 = flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
156
|
+
flock.addBoid('boid-2', new Vector3(10, 0, 0));
|
|
157
|
+
flock.addBoid('boid-3', new Vector3(100, 0, 0));
|
|
158
|
+
|
|
159
|
+
const neighbors = flock.findNeighbors(boid1, 50);
|
|
160
|
+
expect(neighbors).toHaveLength(1);
|
|
161
|
+
expect(neighbors[0].id).toBe('boid-2');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should exclude self from neighbors', () => {
|
|
165
|
+
const boid1 = flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
166
|
+
flock.addBoid('boid-2', new Vector3(0, 0, 0)); // Same position
|
|
167
|
+
|
|
168
|
+
const neighbors = flock.findNeighbors(boid1, 50);
|
|
169
|
+
expect(neighbors).toHaveLength(1);
|
|
170
|
+
expect(neighbors[0].id).toBe('boid-2');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('update', () => {
|
|
175
|
+
it('should update all boids', () => {
|
|
176
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(1, 0, 0));
|
|
177
|
+
flock.addBoid('boid-2', new Vector3(10, 0, 0), new Vector3(1, 0, 0));
|
|
178
|
+
|
|
179
|
+
const initialPos = flock.getBoid('boid-1')!.position.clone();
|
|
180
|
+
flock.update();
|
|
181
|
+
const newPos = flock.getBoid('boid-1')!.position;
|
|
182
|
+
|
|
183
|
+
expect(newPos.equals(initialPos)).toBe(false);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should respect max speed', () => {
|
|
187
|
+
const boid = flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(100, 0, 0));
|
|
188
|
+
flock.update();
|
|
189
|
+
|
|
190
|
+
expect(boid.velocity.magnitude()).toBeLessThanOrEqual(flock.getConfig().maxSpeed);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('external forces', () => {
|
|
195
|
+
it('should apply force to single boid', () => {
|
|
196
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
197
|
+
flock.applyForce('boid-1', new Vector3(1, 0, 0));
|
|
198
|
+
|
|
199
|
+
const boid = flock.getBoid('boid-1')!;
|
|
200
|
+
expect(boid.acceleration.x).toBe(1);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should apply force to all boids', () => {
|
|
204
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(0, 0, 0));
|
|
205
|
+
flock.addBoid('boid-2', new Vector3(10, 0, 0), new Vector3(0, 0, 0));
|
|
206
|
+
flock.applyForceToAll(new Vector3(0, 1, 0));
|
|
207
|
+
|
|
208
|
+
expect(flock.getBoid('boid-1')!.acceleration.y).toBe(1);
|
|
209
|
+
expect(flock.getBoid('boid-2')!.acceleration.y).toBe(1);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe('flock statistics', () => {
|
|
214
|
+
it('should calculate flock center', () => {
|
|
215
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
216
|
+
flock.addBoid('boid-2', new Vector3(10, 0, 0));
|
|
217
|
+
flock.addBoid('boid-3', new Vector3(20, 0, 0));
|
|
218
|
+
|
|
219
|
+
const center = flock.getFlockCenter();
|
|
220
|
+
expect(center.x).toBe(10);
|
|
221
|
+
expect(center.y).toBe(0);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should return zero for empty flock center', () => {
|
|
225
|
+
const center = flock.getFlockCenter();
|
|
226
|
+
expect(center.magnitude()).toBe(0);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should calculate flock direction', () => {
|
|
230
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0), new Vector3(1, 0, 0));
|
|
231
|
+
flock.addBoid('boid-2', new Vector3(10, 0, 0), new Vector3(1, 0, 0));
|
|
232
|
+
|
|
233
|
+
const direction = flock.getFlockDirection();
|
|
234
|
+
expect(direction.x).toBeCloseTo(1);
|
|
235
|
+
expect(direction.y).toBeCloseTo(0);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should calculate flock spread', () => {
|
|
239
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
240
|
+
flock.addBoid('boid-2', new Vector3(10, 0, 0));
|
|
241
|
+
flock.addBoid('boid-3', new Vector3(-10, 0, 0));
|
|
242
|
+
|
|
243
|
+
const spread = flock.getFlockSpread();
|
|
244
|
+
expect(spread).toBe(10);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('should return zero spread for single boid', () => {
|
|
248
|
+
flock.addBoid('boid-1', new Vector3(0, 0, 0));
|
|
249
|
+
expect(flock.getFlockSpread()).toBe(0);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe('boundaries', () => {
|
|
254
|
+
it('should wrap positions', () => {
|
|
255
|
+
const boundedFlock = new FlockingBehavior({
|
|
256
|
+
boundaryMode: 'wrap',
|
|
257
|
+
worldBounds: {
|
|
258
|
+
min: new Vector3(0, 0, 0),
|
|
259
|
+
max: new Vector3(100, 100, 100),
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const boid = boundedFlock.addBoid('boid-1', new Vector3(99, 50, 50), new Vector3(10, 0, 0));
|
|
264
|
+
boundedFlock.update();
|
|
265
|
+
|
|
266
|
+
// Should wrap to low x values
|
|
267
|
+
expect(boid.position.x).toBeLessThan(100);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should bounce off boundaries', () => {
|
|
271
|
+
const boundedFlock = new FlockingBehavior({
|
|
272
|
+
boundaryMode: 'bounce',
|
|
273
|
+
worldBounds: {
|
|
274
|
+
min: new Vector3(0, 0, 0),
|
|
275
|
+
max: new Vector3(100, 100, 100),
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const boid = boundedFlock.addBoid('boid-1', new Vector3(99, 50, 50), new Vector3(10, 0, 0));
|
|
280
|
+
boundedFlock.update();
|
|
281
|
+
|
|
282
|
+
// Velocity should be reversed
|
|
283
|
+
expect(boid.velocity.x).toBeLessThan(0);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('configuration', () => {
|
|
288
|
+
it('should update configuration', () => {
|
|
289
|
+
flock.setConfig({ maxSpeed: 10 });
|
|
290
|
+
expect(flock.getConfig().maxSpeed).toBe(10);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should preserve existing config values', () => {
|
|
294
|
+
flock.setConfig({ maxSpeed: 10 });
|
|
295
|
+
expect(flock.getConfig().separationRadius).toBe(25);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
});
|