-
Notifications
You must be signed in to change notification settings - Fork 0
Description
/**
- ArgOS Analytics Module
- Collects and analyzes simulation data to measure agent performance
- and environmental dynamics
*/
import {
Actions,
SensoryData,
Memory,
Goals,
CognitiveState,
Learning,
Social,
RealityFlux,
Environmental,
Position
} from './argos-framework.js';
export class ArgOSAnalytics {
/**
-
Initialize analytics for an ArgOS simulation
-
@param {Object} world - The BitECS world object
*/
constructor(world) {
this.world = world;
this.data = {
time: 0,// Agent metrics
agentCount: 0,
successRates: [], // Success rate history
avgSuccessRate: 0,
avgReward: 0,
avgAdaptability: 0,
avgEmotionalState: 0,// Environmental metrics
resourceCount: 0,
obstacleCount: 0,
hazardCount: 0,// Social metrics
cooperationCount: 0,
avgTrustLevel: 0,
alliances: 0,
rivalries: 0,// Reality metrics
realityShifts: 0,
activeFluxEffects: 0,
teleports: 0,
phases: 0,
transforms: 0,// Historical data (for charting)
successHistory: [],
rewardHistory: [],
cooperationHistory: [],
resourceHistory: [],
fluxHistory: []
};
// Sampling rate (how often to record history)
this.samplingRate = 10; // Every 10 ticks
}
/**
- Update analytics data based on current world state
- Should be called every simulation tick
*/
update() {
const time = this.world.time;
this.data.time = time;
// Count entities by type
let agentCount = 0;
let resourceCount = 0;
let obstacleCount = 0;
let hazardCount = 0;
// Agent metrics
let totalSuccessRate = 0;
let totalReward = 0;
let totalAdaptability = 0;
let totalEmotionalState = 0;
// Social metrics
let totalTrustLevel = 0;
let totalCooperationCount = 0;
let totalAlliances = 0;
let totalRivalries = 0;
// Reality metrics
let activeFluxEffects = 0;
let teleports = 0;
let phases = 0;
let transforms = 0;
// Analyze all entities
for (let i = 0; i < this.world.entities.length; i++) {
const entity = i;
// Count agent types
if (SensoryData[entity] && Memory[entity] && Goals[entity]) {
agentCount++;
// Track action success rate
if (Actions[entity]) {
totalSuccessRate += Actions.successRate[entity];
}
// Track learning rewards
if (Learning[entity]) {
totalReward += Learning.rewardAccumulator[entity];
}
// Track cognitive metrics
if (CognitiveState[entity]) {
totalAdaptability += CognitiveState.adaptability[entity];
totalEmotionalState += CognitiveState.emotionalState[entity];
}
// Track social metrics
if (Social[entity]) {
totalTrustLevel += Social.trustLevel[entity];
totalCooperationCount += Social.cooperationCount[entity];
// Count alliances
for (let j = 0; j < 5; j++) {
if (Social.allies[entity * 5 + j] !== 0) {
totalAlliances++;
}
if (Social.rivals[entity * 5 + j] !== 0) {
totalRivalries++;
}
}
}
}
// Count environmental entities
if (Environmental[entity]) {
switch (Environmental.type[entity]) {
case 0: resourceCount++; break;
case 1: obstacleCount++; break;
case 2: hazardCount++; break;
}
}
// Count reality flux effects
if (RealityFlux[entity] && RealityFlux.effectType[entity] > 0) {
activeFluxEffects++;
switch (RealityFlux.effectType[entity]) {
case 1: teleports++; break;
case 2: phases++; break;
case 3: transforms++; break;
}
}
}
// Calculate averages
this.data.agentCount = agentCount;
this.data.avgSuccessRate = agentCount > 0 ? totalSuccessRate / agentCount : 0;
this.data.avgReward = agentCount > 0 ? totalReward / agentCount : 0;
this.data.avgAdaptability = agentCount > 0 ? totalAdaptability / agentCount : 0;
this.data.avgEmotionalState = agentCount > 0 ? totalEmotionalState / agentCount : 0;
this.data.resourceCount = resourceCount;
this.data.obstacleCount = obstacleCount;
this.data.hazardCount = hazardCount;
this.data.cooperationCount = totalCooperationCount;
this.data.avgTrustLevel = agentCount > 0 ? totalTrustLevel / agentCount : 0;
this.data.alliances = totalAlliances;
this.data.rivalries = totalRivalries;
this.data.activeFluxEffects = activeFluxEffects;
this.data.teleports = teleports;
this.data.phases = phases;
this.data.transforms = transforms;
// Count reality shifts based on time
if (time % 150 === 0 && time > 0) {
this.data.realityShifts++;
}
// Record historical data (at sampling rate)
if (time % this.samplingRate === 0) {
this.data.successHistory.push({
time: time,
value: this.data.avgSuccessRate
});
this.data.rewardHistory.push({
time: time,
value: this.data.avgReward
});
this.data.cooperationHistory.push({
time: time,
value: this.data.cooperationCount
});
this.data.resourceHistory.push({
time: time,
value: this.data.resourceCount
});
this.data.fluxHistory.push({
time: time,
value: this.data.activeFluxEffects
});
// Limit history size to prevent memory issues
const maxHistoryLength = 100;
if (this.data.successHistory.length > maxHistoryLength) {
this.data.successHistory.shift();
this.data.rewardHistory.shift();
this.data.cooperationHistory.shift();
this.data.resourceHistory.shift();
this.data.fluxHistory.shift();
}
}
return this.data;
}
/**
- Render analytics to HTML container
- @param {HTMLElement} container - The HTML element to render analytics into
*/
render(container) {
if (!container) return;
// Format data for display
const data = this.data;
const html = `
<h3>ArgOS Analytics</h3>
<div class="metrics-grid">
<div class="metric-card">
<h4>Simulation</h4>
<p>Time: <span class="value">${data.time}</span></p>
<p>Reality Shifts: <span class="value">${data.realityShifts}</span></p>
</div>
<div class="metric-card">
<h4>Agents</h4>
<p>Count: <span class="value">${data.agentCount}</span></p>
<p>Success Rate: <span class="value">${data.avgSuccessRate.toFixed(1)}%</span></p>
<p>Avg Reward: <span class="value">${data.avgReward.toFixed(1)}</span></p>
</div>
<div class="metric-card">
<h4>Cognitive</h4>
<p>Adaptability: <span class="value">${data.avgAdaptability.toFixed(1)}</span></p>
<p>Emotional: <span class="value">${data.avgEmotionalState.toFixed(1)}</span></p>
</div>
<div class="metric-card">
<h4>Social</h4>
<p>Cooperation: <span class="value">${data.cooperationCount}</span></p>
<p>Trust Level: <span class="value">${data.avgTrustLevel.toFixed(1)}</span></p>
<p>Alliances: <span class="value">${data.alliances}</span></p>
</div>
<div class="metric-card">
<h4>Environment</h4>
<p>Resources: <span class="value">${data.resourceCount}</span></p>
<p>Obstacles: <span class="value">${data.obstacleCount}</span></p>
<p>Hazards: <span class="value">${data.hazardCount}</span></p>
</div>
<div class="metric-card">
<h4>Reality Flux</h4>
<p>Active Effects: <span class="value">${data.activeFluxEffects}</span></p>
<p>Teleports: <span class="value">${data.teleports}</span></p>
<p>Phases/Transforms: <span class="value">${data.phases}/${data.transforms}</span></p>
</div>
</div>
<canvas id="analytics-chart" width="300" height="150"></canvas>
`;
container.innerHTML = html;
// Render chart if canvas and chart library available
this.renderChart();
}
/**
- Render analytics chart using Chart.js if available
*/
renderChart() {
const canvas = document.getElementById('analytics-chart');
if (!canvas) return;
// Check if Chart is available (we're assuming Chart.js is loaded)
if (typeof Chart !== 'undefined') {
// Destroy previous chart if it exists
if (this.chart) {
this.chart.destroy();
}
const ctx = canvas.getContext('2d');
// Create chart with history data
this.chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: 'Success Rate',
data: this.data.successHistory.map(item => ({ x: item.time, y: item.value })),
borderColor: 'rgba(52, 152, 219, 1)',
backgroundColor: 'rgba(52, 152, 219, 0.2)',
tension: 0.3
},
{
label: 'Avg Reward',
data: this.data.rewardHistory.map(item => ({ x: item.time, y: item.value })),
borderColor: 'rgba(46, 204, 113, 1)',
backgroundColor: 'rgba(46, 204, 113, 0.2)',
tension: 0.3
},
{
label: 'Cooperation',
data: this.data.cooperationHistory.map(item => ({ x: item.time, y: item.value })),
borderColor: 'rgba(155, 89, 182, 1)',
backgroundColor: 'rgba(155, 89, 182, 0.2)',
tension: 0.3
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'linear',
title: {
display: true,
text: 'Simulation Time'
}
},
y: {
beginAtZero: true
}
}
}
});
} else {
// Fallback to simple text if Chart.js is not available
canvas.style.display = 'none';
}
}
/**
- Get a simplified data summary for export
- @returns {Object} Data summary
*/
getDataSummary() {
return {
simulationTime: this.data.time,
realityShifts: this.data.realityShifts,
agentMetrics: {
count: this.data.agentCount,
avgSuccessRate: this.data.avgSuccessRate,
avgReward: this.data.avgReward,
avgAdaptability: this.data.avgAdaptability
},
socialMetrics: {
cooperationCount: this.data.cooperationCount,
avgTrustLevel: this.data.avgTrustLevel,
alliances: this.data.alliances
},
environmentMetrics: {
resources: this.data.resourceCount,
hazards: this.data.hazardCount
},
realityMetrics: {
activeFluxEffects: this.data.activeFluxEffects
}
};
}
/**
- Export analytics data as CSV
- @returns {string} CSV data
*/
exportAsCSV() {
const headers = [
'Time',
'AgentCount',
'AvgSuccessRate',
'AvgReward',
'ResourceCount',
'CooperationCount',
'ActiveFluxEffects'
].join(',');
// Combine history data
const rows = [];
for (let i = 0; i < this.data.successHistory.length; i++) {
const time = this.data.successHistory[i].time;
const row = [
time,
this.data.agentCount,
this.data.successHistory[i].value.toFixed(2),
this.data.rewardHistory[i].value.toFixed(2),
this.data.resourceHistory[i].value,
this.data.cooperationHistory[i].value,
this.data.fluxHistory[i].value
].join(',');
rows.push(row);
}
return `${headers}\n${rows.join('\n')}`;
}
/**
- Download analytics data as CSV file
*/
downloadCSV() {
const csvContent = this.exportAsCSV();
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', `argos-analytics-${Date.now()}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
/**
- Generate a detailed analytics report
- @returns {Object} Detailed report with analysis
*/
generateReport() {
const data = this.data;
// Calculate trends
let successTrend = 'stable';
let rewardTrend = 'stable';
if (this.data.successHistory.length > 5) {
const recentSuccess = this.data.successHistory.slice(-5);
const firstValue = recentSuccess[0].value;
const lastValue = recentSuccess[4].value;
if (lastValue > firstValue * 1.1) {
successTrend = 'improving';
} else if (lastValue < firstValue * 0.9) {
successTrend = 'declining';
}
const recentReward = this.data.rewardHistory.slice(-5);
const firstReward = recentReward[0].value;
const lastReward = recentReward[4].value;
if (lastReward > firstReward * 1.1) {
rewardTrend = 'improving';
} else if (lastReward < firstReward * 0.9) {
rewardTrend = 'declining';
}
}
// Generate insights
const insights = [];
if (data.avgSuccessRate > 80) {
insights.push('Agents are performing exceptionally well with high success rates.');
} else if (data.avgSuccessRate < 40) {
insights.push('Agents are struggling to achieve their goals.');
}
if (data.cooperationCount > data.agentCount * 3) {
insights.push('High levels of cooperation observed among agents.');
} else if (data.rivalries > data.alliances) {
insights.push('Competitive behavior dominates over cooperation.');
}
if (data.activeFluxEffects > data.agentCount * 0.5) {
insights.push('Significant reality distortion affecting agent behavior.');
}
if (successTrend === 'improving' && rewardTrend === 'improving') {
insights.push('Agents show clear learning and adaptation over time.');
}
return {
summary: {
time: data.time,
agentCount: data.agentCount,
resourceCount: data.resourceCount,
realityShifts: data.realityShifts
},
performance: {
avgSuccessRate: data.avgSuccessRate.toFixed(1),
avgReward: data.avgReward.toFixed(1),
successTrend,
rewardTrend
},
social: {
cooperationCount: data.cooperationCount,
alliances: data.alliances,
rivalries: data.rivalries
},
reality: {
activeEffects: data.activeFluxEffects,
totalShifts: data.realityShifts
},
insights
};
}
}