| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Intelligence Operatives Optimization - Agent Simulation</title> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| body { |
| font-family: 'Courier New', monospace; |
| background: linear-gradient(135deg, #0a0e27 0%, #1a1f3a 100%); |
| color: #00ff41; |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| overflow-x: hidden; |
| } |
| |
| .header { |
| background: rgba(0, 0, 0, 0.7); |
| padding: 20px; |
| text-align: center; |
| border-bottom: 2px solid #00ff41; |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .header::before { |
| content: ""; |
| position: absolute; |
| top: 0; |
| left: -100%; |
| width: 100%; |
| height: 100%; |
| background: linear-gradient(90deg, transparent, rgba(0, 255, 65, 0.1), transparent); |
| animation: scan 3s infinite; |
| } |
| |
| @keyframes scan { |
| 0% { left: -100%; } |
| 100% { left: 100%; } |
| } |
| |
| h1 { |
| font-size: 2em; |
| text-shadow: 0 0 20px #00ff41; |
| margin-bottom: 10px; |
| letter-spacing: 2px; |
| } |
| |
| .subtitle { |
| color: #00cc33; |
| font-size: 0.9em; |
| opacity: 0.8; |
| } |
| |
| .main-container { |
| display: flex; |
| flex: 1; |
| padding: 20px; |
| gap: 20px; |
| } |
| |
| .left-panel { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| gap: 20px; |
| } |
| |
| .right-panel { |
| width: 350px; |
| display: flex; |
| flex-direction: column; |
| gap: 20px; |
| } |
| |
| .panel { |
| background: rgba(0, 0, 0, 0.6); |
| border: 1px solid #00ff41; |
| border-radius: 8px; |
| padding: 15px; |
| position: relative; |
| box-shadow: 0 0 20px rgba(0, 255, 65, 0.1); |
| } |
| |
| .panel-title { |
| font-size: 1.1em; |
| margin-bottom: 10px; |
| color: #00ff41; |
| text-transform: uppercase; |
| letter-spacing: 1px; |
| } |
| |
| #canvas { |
| width: 100%; |
| height: 500px; |
| border: 1px solid #00ff41; |
| background: radial-gradient(ellipse at center, rgba(0, 50, 20, 0.3) 0%, transparent 70%); |
| cursor: crosshair; |
| } |
| |
| .controls { |
| display: flex; |
| gap: 10px; |
| flex-wrap: wrap; |
| } |
| |
| button { |
| background: rgba(0, 255, 65, 0.1); |
| border: 1px solid #00ff41; |
| color: #00ff41; |
| padding: 10px 20px; |
| cursor: pointer; |
| border-radius: 4px; |
| font-family: inherit; |
| text-transform: uppercase; |
| transition: all 0.3s; |
| position: relative; |
| overflow: hidden; |
| } |
| |
| button:hover { |
| background: rgba(0, 255, 65, 0.2); |
| box-shadow: 0 0 15px rgba(0, 255, 65, 0.5); |
| transform: translateY(-2px); |
| } |
| |
| button:active { |
| transform: translateY(0); |
| } |
| |
| button.active { |
| background: rgba(0, 255, 65, 0.3); |
| box-shadow: 0 0 20px rgba(0, 255, 65, 0.7); |
| } |
| |
| .stats { |
| display: grid; |
| grid-template-columns: 1fr 1fr; |
| gap: 10px; |
| } |
| |
| .stat-item { |
| background: rgba(0, 255, 65, 0.05); |
| padding: 8px; |
| border-left: 3px solid #00ff41; |
| } |
| |
| .stat-label { |
| font-size: 0.8em; |
| color: #00cc33; |
| opacity: 0.7; |
| } |
| |
| .stat-value { |
| font-size: 1.1em; |
| font-weight: bold; |
| color: #00ff41; |
| } |
| |
| .parameter-control { |
| margin-bottom: 10px; |
| } |
| |
| .parameter-control label { |
| display: block; |
| margin-bottom: 5px; |
| font-size: 0.9em; |
| color: #00cc33; |
| } |
| |
| .parameter-control input[type="range"] { |
| width: 100%; |
| -webkit-appearance: none; |
| appearance: none; |
| height: 5px; |
| background: rgba(0, 255, 65, 0.1); |
| outline: none; |
| border-radius: 3px; |
| } |
| |
| .parameter-control input[type="range"]::-webkit-slider-thumb { |
| -webkit-appearance: none; |
| appearance: none; |
| width: 15px; |
| height: 15px; |
| background: #00ff41; |
| cursor: pointer; |
| border-radius: 50%; |
| box-shadow: 0 0 10px rgba(0, 255, 65, 0.5); |
| } |
| |
| .parameter-control span { |
| float: right; |
| color: #00ff41; |
| } |
| |
| select { |
| width: 100%; |
| padding: 8px; |
| background: rgba(0, 0, 0, 0.5); |
| border: 1px solid #00ff41; |
| color: #00ff41; |
| border-radius: 4px; |
| font-family: inherit; |
| } |
| |
| .trust-matrix { |
| max-height: 200px; |
| overflow-y: auto; |
| font-size: 0.8em; |
| background: rgba(0, 0, 0, 0.3); |
| padding: 10px; |
| border-radius: 4px; |
| } |
| |
| .trust-connection { |
| margin-bottom: 5px; |
| padding: 3px; |
| background: rgba(0, 255, 65, 0.05); |
| } |
| |
| .phase-indicator { |
| display: flex; |
| justify-content: space-between; |
| margin-top: 10px; |
| } |
| |
| .phase-item { |
| flex: 1; |
| text-align: center; |
| padding: 5px; |
| background: rgba(0, 255, 65, 0.05); |
| border: 1px solid transparent; |
| transition: all 0.3s; |
| } |
| |
| .phase-item.active { |
| background: rgba(0, 255, 65, 0.2); |
| border-color: #00ff41; |
| box-shadow: 0 0 10px rgba(0, 255, 65, 0.5); |
| } |
| |
| .convergence-chart { |
| height: 150px; |
| background: rgba(0, 0, 0, 0.3); |
| border-radius: 4px; |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .legend { |
| display: flex; |
| gap: 20px; |
| margin-top: 10px; |
| flex-wrap: wrap; |
| } |
| |
| .legend-item { |
| display: flex; |
| align-items: center; |
| gap: 5px; |
| } |
| |
| .legend-color { |
| width: 12px; |
| height: 12px; |
| border-radius: 50%; |
| } |
| |
| .alert { |
| position: fixed; |
| top: 20px; |
| right: 20px; |
| background: rgba(0, 0, 0, 0.9); |
| border: 2px solid #00ff41; |
| padding: 15px 20px; |
| border-radius: 8px; |
| color: #00ff41; |
| box-shadow: 0 0 30px rgba(0, 255, 65, 0.5); |
| animation: slideIn 0.5s ease-out; |
| z-index: 1000; |
| } |
| |
| @keyframes slideIn { |
| from { |
| transform: translateX(400px); |
| opacity: 0; |
| } |
| to { |
| transform: translateX(0); |
| opacity: 1; |
| } |
| } |
| |
| .operative-info { |
| position: absolute; |
| background: rgba(0, 0, 0, 0.9); |
| border: 1px solid #00ff41; |
| padding: 8px; |
| border-radius: 4px; |
| font-size: 0.8em; |
| pointer-events: none; |
| z-index: 100; |
| display: none; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="header"> |
| <h1>🔍 INTELLIGENCE OPERATIVES OPTIMIZATION</h1> |
| <div class="subtitle">Human Behavior-Inspired Metaheuristic | By Volkan Duran</div> |
| </div> |
| |
| <div class="main-container"> |
| <div class="left-panel"> |
| <div class="panel"> |
| <div class="panel-title">Mission Field</div> |
| <canvas id="canvas"></canvas> |
| <div class="legend"> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #00ff41;"></div> |
| <span>Operative</span> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #ff3333;"></div> |
| <span>High Deception</span> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #ffaa00;"></div> |
| <span>Global Best</span> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: rgba(0, 255, 65, 0.2);"></div> |
| <span>Trust Connection</span> |
| </div> |
| </div> |
| </div> |
| |
| <div class="panel"> |
| <div class="panel-title">Mission Control</div> |
| <div class="controls"> |
| <button id="startBtn" onclick="toggleSimulation()">▶ START MISSION</button> |
| <button onclick="resetSimulation()">↺ RESET</button> |
| <button id="trustBtn" onclick="toggleTrustLines()">📡 TRUST NETWORK</button> |
| <button id="trailBtn" onclick="toggleTrails()">🛸 TRAILS</button> |
| </div> |
| |
| <div class="phase-indicator"> |
| <div class="phase-item" id="phase-recon">RECON</div> |
| <div class="phase-item" id="phase-infil">INFILTRATE</div> |
| <div class="phase-item" id="phase-counter">COUNTER-INTEL</div> |
| </div> |
| </div> |
| |
| <div class="panel"> |
| <div class="panel-title">Convergence Analysis</div> |
| <canvas id="convergenceChart" class="convergence-chart"></canvas> |
| </div> |
| </div> |
| |
| <div class="right-panel"> |
| <div class="panel"> |
| <div class="panel-title">Mission Statistics</div> |
| <div class="stats"> |
| <div class="stat-item"> |
| <div class="stat-label">Iteration</div> |
| <div class="stat-value" id="iteration">0</div> |
| </div> |
| <div class="stat-item"> |
| <div class="stat-label">Best Value</div> |
| <div class="stat-value" id="bestValue">N/A</div> |
| </div> |
| <div class="stat-item"> |
| <div class="stat-label">Avg Trust</div> |
| <div class="stat-value" id="avgTrust">0.50</div> |
| </div> |
| <div class="stat-item"> |
| <div class="stat-label">Deception Risk</div> |
| <div class="stat-value" id="deceptionRisk">0.00</div> |
| </div> |
| <div class="stat-item"> |
| <div class="stat-label">Phase</div> |
| <div class="stat-value" id="currentPhase">IDLE</div> |
| </div> |
| <div class="stat-item"> |
| <div class="stat-label">Active Cells</div> |
| <div class="stat-value" id="activeCells">0</div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="panel"> |
| <div class="panel-title">Operation Parameters</div> |
| <div class="parameter-control"> |
| <label>Operatives: <span id="nAgentsValue">30</span></label> |
| <input type="range" id="nAgents" min="10" max="100" value="30" step="5"> |
| </div> |
| <div class="parameter-control"> |
| <label>Trust Decay λ⁻: <span id="trustDecayValue">0.10</span></label> |
| <input type="range" id="trustDecay" min="0.01" max="0.3" value="0.10" step="0.01"> |
| </div> |
| <div class="parameter-control"> |
| <label>Intel Weight α₀: <span id="alphaValue">1.2</span></label> |
| <input type="range" id="alpha" min="0.5" max="2.0" value="1.2" step="0.1"> |
| </div> |
| <div class="parameter-control"> |
| <label>Cell Size k: <span id="cellSizeValue">4</span></label> |
| <input type="range" id="cellSize" min="2" max="10" value="4" step="1"> |
| </div> |
| <div class="parameter-control"> |
| <label>Test Function:</label> |
| <select id="testFunction"> |
| <option value="rastrigin">Rastrigin</option> |
| <option value="ackley">Ackley</option> |
| <option value="rosenbrock">Rosenbrock</option> |
| <option value="sphere">Sphere</option> |
| <option value="schwefel">Schwefel</option> |
| </select> |
| </div> |
| </div> |
| |
| <div class="panel"> |
| <div class="panel-title">Trust Network Matrix</div> |
| <div class="trust-matrix" id="trustMatrix"> |
| <div style="color: #00cc33; opacity: 0.7;">Awaiting mission start...</div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="operative-info" id="operativeInfo"></div> |
|
|
| <script> |
| |
| class IntelligenceOperative { |
| constructor(id, dimensions) { |
| this.id = id; |
| this.dimensions = dimensions; |
| this.position = []; |
| this.velocity = []; |
| this.personalBest = []; |
| this.personalBestValue = Infinity; |
| this.trust = new Map(); |
| this.neighbors = []; |
| this.stagnation = 0; |
| this.deceptionRisk = 0; |
| this.history = []; |
| this.color = `hsl(${120 + Math.random() * 60}, 100%, 50%)`; |
| |
| |
| for (let i = 0; i < dimensions; i++) { |
| this.position.push(Math.random() * 10 - 5); |
| this.velocity.push(0); |
| this.personalBest.push(this.position[i]); |
| } |
| } |
| |
| updateTrust(neighborId, improvement) { |
| const currentTrust = this.trust.get(neighborId) || 0.5; |
| if (improvement) { |
| this.trust.set(neighborId, Math.min(1, currentTrust + 0.05)); |
| } else { |
| this.trust.set(neighborId, Math.max(0, currentTrust * 0.9)); |
| } |
| } |
| |
| getTrust(neighborId) { |
| return this.trust.get(neighborId) || 0.5; |
| } |
| |
| calculateDeceptionRisk(neighbors, globalBest) { |
| if (neighbors.length < 2) return 0; |
| |
| const values = neighbors.map(n => n.personalBestValue); |
| values.sort((a, b) => a - b); |
| const median = values[Math.floor(values.length / 2)]; |
| |
| const deviations = values.map(v => Math.abs(v - median)); |
| deviations.sort((a, b) => a - b); |
| const mad = deviations[Math.floor(deviations.length / 2)] + 1e-10; |
| |
| const risk = Math.abs(this.personalBestValue - median) / mad; |
| return Math.min(1, risk / (1 + risk)); |
| } |
| } |
| |
| class IOASimulation { |
| constructor(canvasId) { |
| this.canvas = document.getElementById(canvasId); |
| this.ctx = this.canvas.getContext('2d'); |
| this.operatives = []; |
| this.globalBest = null; |
| this.globalBestValue = Infinity; |
| this.iteration = 0; |
| this.running = false; |
| this.showTrustLines = true; |
| this.showTrails = false; |
| this.convergenceHistory = []; |
| this.maxHistory = 100; |
| this.testFunction = 'rastrigin'; |
| this.phase = 0; |
| this.missionPhase = 0.7; |
| |
| |
| this.nAgents = 30; |
| this.cellSize = 4; |
| this.alpha0 = 1.2; |
| this.beta0 = 0.8; |
| this.gamma0 = 0.6; |
| this.phi0 = 0.7; |
| this.trustDecay = 0.1; |
| |
| this.setupCanvas(); |
| this.bindEvents(); |
| this.updateParameterDisplays(); |
| } |
| |
| setupCanvas() { |
| const rect = this.canvas.getBoundingClientRect(); |
| this.canvas.width = rect.width; |
| this.canvas.height = rect.height; |
| } |
| |
| bindEvents() { |
| |
| document.getElementById('nAgents').addEventListener('input', (e) => { |
| this.nAgents = parseInt(e.target.value); |
| document.getElementById('nAgentsValue').textContent = this.nAgents; |
| if (!this.running) this.initialize(); |
| }); |
| |
| document.getElementById('trustDecay').addEventListener('input', (e) => { |
| this.trustDecay = parseFloat(e.target.value); |
| document.getElementById('trustDecayValue').textContent = this.trustDecay.toFixed(2); |
| }); |
| |
| document.getElementById('alpha').addEventListener('input', (e) => { |
| this.alpha0 = parseFloat(e.target.value); |
| document.getElementById('alphaValue').textContent = this.alpha0.toFixed(1); |
| }); |
| |
| document.getElementById('cellSize').addEventListener('input', (e) => { |
| this.cellSize = parseInt(e.target.value); |
| document.getElementById('cellSizeValue').textContent = this.cellSize; |
| }); |
| |
| document.getElementById('testFunction').addEventListener('change', (e) => { |
| this.testFunction = e.target.value; |
| if (!this.running) this.initialize(); |
| }); |
| |
| |
| this.canvas.addEventListener('mousemove', (e) => { |
| const rect = this.canvas.getBoundingClientRect(); |
| const x = e.clientX - rect.left; |
| const y = e.clientY - rect.top; |
| this.showOperativeInfo(x, y, e.clientX, e.clientY); |
| }); |
| |
| window.addEventListener('resize', () => this.setupCanvas()); |
| } |
| |
| updateParameterDisplays() { |
| document.getElementById('nAgentsValue').textContent = this.nAgents; |
| document.getElementById('trustDecayValue').textContent = this.trustDecay.toFixed(2); |
| document.getElementById('alphaValue').textContent = this.alpha0.toFixed(1); |
| document.getElementById('cellSizeValue').textContent = this.cellSize; |
| } |
| |
| |
| evaluateFunction(position) { |
| const x = position[0]; |
| const y = position.length > 1 ? position[1] : 0; |
| |
| switch(this.testFunction) { |
| case 'rastrigin': |
| return 20 + x*x + y*y - 10*(Math.cos(2*Math.PI*x) + Math.cos(2*Math.PI*y)); |
| case 'ackley': |
| const a = 20, b = 0.2, c = 2 * Math.PI; |
| return -a * Math.exp(-b * Math.sqrt(0.5 * (x*x + y*y))) - |
| Math.exp(0.5 * (Math.cos(c*x) + Math.cos(c*y))) + a + Math.E; |
| case 'rosenbrock': |
| return Math.pow(1 - x, 2) + 100 * Math.pow(y - x*x, 2); |
| case 'sphere': |
| return x*x + y*y; |
| case 'schwefel': |
| return 418.9829 * 2 - x * Math.sin(Math.sqrt(Math.abs(x))) - |
| y * Math.sin(Math.sqrt(Math.abs(y))); |
| default: |
| return x*x + y*y; |
| } |
| } |
| |
| initialize() { |
| this.operatives = []; |
| this.globalBest = null; |
| this.globalBestValue = Infinity; |
| this.iteration = 0; |
| this.convergenceHistory = []; |
| this.phase = 0; |
| this.missionPhase = this.phi0; |
| |
| |
| for (let i = 0; i < this.nAgents; i++) { |
| const operative = new IntelligenceOperative(i, 2); |
| const value = this.evaluateFunction(operative.position); |
| operative.personalBestValue = value; |
| |
| if (value < this.globalBestValue) { |
| this.globalBestValue = value; |
| this.globalBest = [...operative.position]; |
| } |
| |
| this.operatives.push(operative); |
| } |
| |
| |
| this.updateNeighborhoods(); |
| this.render(); |
| this.updateStats(); |
| } |
| |
| updateNeighborhoods() { |
| for (let i = 0; i < this.operatives.length; i++) { |
| const op = this.operatives[i]; |
| const distances = []; |
| |
| for (let j = 0; j < this.operatives.length; j++) { |
| if (i !== j) { |
| const other = this.operatives[j]; |
| const dist = Math.sqrt( |
| Math.pow(op.position[0] - other.position[0], 2) + |
| Math.pow(op.position[1] - other.position[1], 2) |
| ); |
| distances.push({id: j, dist: dist}); |
| } |
| } |
| |
| distances.sort((a, b) => a.dist - b.dist); |
| op.neighbors = distances.slice(0, this.cellSize).map(d => d.id); |
| } |
| } |
| |
| levyFlight(scale = 0.01) { |
| const beta = 1.5; |
| const sigma = Math.pow( |
| (Math.exp(Math.log(this.gamma(1 + beta)) + Math.log(Math.sin(Math.PI * beta / 2))) / |
| (Math.exp(Math.log(this.gamma((1 + beta) / 2)) + Math.log(beta) + Math.log(Math.pow(2, (beta - 1) / 2))))), |
| 1 / beta |
| ); |
| |
| const u = this.randomNormal() * sigma; |
| const v = this.randomNormal(); |
| const step = u / Math.pow(Math.abs(v), 1 / beta); |
| |
| return step * scale; |
| } |
| |
| gamma(n) { |
| const g = 7; |
| const p = [0.99999999999980993, 676.5203681218851, -1259.1392167224028, |
| 771.32342877765313, -176.61502916214059, 12.507343278686905, |
| -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7]; |
| |
| if (n < 0.5) { |
| return Math.PI / (Math.sin(Math.PI * n) * this.gamma(1 - n)); |
| } |
| |
| n--; |
| let x = p[0]; |
| for (let i = 1; i < g + 2; i++) { |
| x += p[i] / (n + i); |
| } |
| |
| const t = n + g + 0.5; |
| return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x; |
| } |
| |
| randomNormal() { |
| let u = 0, v = 0; |
| while(u === 0) u = Math.random(); |
| while(v === 0) v = Math.random(); |
| return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v); |
| } |
| |
| updateOperative(operative) { |
| |
| const neighbors = operative.neighbors.map(id => this.operatives[id]); |
| |
| |
| operative.deceptionRisk = operative.calculateDeceptionRisk(neighbors, this.globalBest); |
| |
| |
| this.missionPhase = this.phi0 * (1 - this.iteration / 1000); |
| if (this.missionPhase > 0.5) this.phase = 0; |
| else if (this.missionPhase > 0.2) this.phase = 1; |
| else this.phase = 2; |
| |
| |
| const alpha = this.alpha0 * (1 - this.missionPhase); |
| const beta = this.beta0; |
| const gamma = this.gamma0 * (this.missionPhase + operative.deceptionRisk); |
| |
| |
| const globalVector = []; |
| for (let d = 0; d < operative.dimensions; d++) { |
| globalVector[d] = alpha * Math.random() * (this.globalBest[d] - operative.position[d]); |
| } |
| |
| |
| const cellVector = []; |
| for (let d = 0; d < operative.dimensions; d++) { |
| let cellPull = 0; |
| let totalTrust = 0; |
| |
| for (const neighbor of neighbors) { |
| const trust = operative.getTrust(neighbor.id); |
| cellPull += trust * (neighbor.personalBest[d] - operative.position[d]); |
| totalTrust += trust; |
| } |
| |
| cellVector[d] = beta * (totalTrust > 0 ? cellPull / totalTrust : 0); |
| } |
| |
| |
| const counterVector = []; |
| for (let d = 0; d < operative.dimensions; d++) { |
| counterVector[d] = gamma * this.levyFlight(0.5); |
| } |
| |
| |
| const oldValue = this.evaluateFunction(operative.position); |
| const newPosition = []; |
| |
| for (let d = 0; d < operative.dimensions; d++) { |
| newPosition[d] = operative.position[d] + globalVector[d] + cellVector[d] + counterVector[d]; |
| |
| |
| if (newPosition[d] < -5) newPosition[d] = -5 + (-5 - newPosition[d]); |
| if (newPosition[d] > 5) newPosition[d] = 5 - (newPosition[d] - 5); |
| newPosition[d] = Math.max(-5, Math.min(5, newPosition[d])); |
| } |
| |
| const newValue = this.evaluateFunction(newPosition); |
| |
| |
| if (newValue < oldValue) { |
| operative.position = newPosition; |
| operative.stagnation = 0; |
| |
| |
| for (const neighbor of neighbors) { |
| operative.updateTrust(neighbor.id, true); |
| } |
| |
| |
| if (newValue < operative.personalBestValue) { |
| operative.personalBest = [...newPosition]; |
| operative.personalBestValue = newValue; |
| |
| |
| if (newValue < this.globalBestValue) { |
| this.globalBestValue = newValue; |
| this.globalBest = [...newPosition]; |
| this.showAlert(`New Global Best: ${this.globalBestValue.toFixed(6)}`); |
| } |
| } |
| } else { |
| operative.stagnation++; |
| |
| |
| for (const neighbor of neighbors) { |
| operative.updateTrust(neighbor.id, false); |
| } |
| |
| |
| if (operative.stagnation > 10 && Math.random() < 0.1) { |
| for (let d = 0; d < operative.dimensions; d++) { |
| operative.position[d] = -5 + 10 - operative.position[d] + this.randomNormal() * 0.1; |
| operative.position[d] = Math.max(-5, Math.min(5, operative.position[d])); |
| } |
| operative.stagnation = 0; |
| } |
| } |
| |
| |
| if (this.showTrails) { |
| operative.history.push([...operative.position]); |
| if (operative.history.length > 20) { |
| operative.history.shift(); |
| } |
| } |
| } |
| |
| step() { |
| if (!this.operatives.length) this.initialize(); |
| |
| |
| if (this.iteration % 10 === 0) { |
| this.updateNeighborhoods(); |
| } |
| |
| |
| for (const operative of this.operatives) { |
| this.updateOperative(operative); |
| } |
| |
| this.iteration++; |
| |
| |
| this.convergenceHistory.push(this.globalBestValue); |
| if (this.convergenceHistory.length > this.maxHistory) { |
| this.convergenceHistory.shift(); |
| } |
| |
| this.render(); |
| this.updateStats(); |
| this.updateConvergenceChart(); |
| this.updateTrustMatrix(); |
| } |
| |
| render() { |
| this.ctx.fillStyle = 'rgba(10, 14, 39, 0.1)'; |
| this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); |
| |
| |
| this.ctx.strokeStyle = 'rgba(0, 255, 65, 0.05)'; |
| this.ctx.lineWidth = 1; |
| for (let i = 0; i <= 10; i++) { |
| const x = (i / 10) * this.canvas.width; |
| const y = (i / 10) * this.canvas.height; |
| |
| this.ctx.beginPath(); |
| this.ctx.moveTo(x, 0); |
| this.ctx.lineTo(x, this.canvas.height); |
| this.ctx.stroke(); |
| |
| this.ctx.beginPath(); |
| this.ctx.moveTo(0, y); |
| this.ctx.lineTo(this.canvas.width, y); |
| this.ctx.stroke(); |
| } |
| |
| |
| if (this.showTrustLines) { |
| for (const operative of this.operatives) { |
| for (const neighborId of operative.neighbors) { |
| const neighbor = this.operatives[neighborId]; |
| const trust = operative.getTrust(neighborId); |
| |
| if (trust > 0.3) { |
| const x1 = (operative.position[0] + 5) / 10 * this.canvas.width; |
| const y1 = (operative.position[1] + 5) / 10 * this.canvas.height; |
| const x2 = (neighbor.position[0] + 5) / 10 * this.canvas.width; |
| const y2 = (neighbor.position[1] + 5) / 10 * this.canvas.height; |
| |
| this.ctx.strokeStyle = `rgba(0, 255, 65, ${trust * 0.2})`; |
| this.ctx.lineWidth = trust * 2; |
| this.ctx.beginPath(); |
| this.ctx.moveTo(x1, y1); |
| this.ctx.lineTo(x2, y2); |
| this.ctx.stroke(); |
| } |
| } |
| } |
| } |
| |
| |
| if (this.showTrails) { |
| for (const operative of this.operatives) { |
| if (operative.history.length > 1) { |
| this.ctx.strokeStyle = `rgba(0, 255, 65, 0.3)`; |
| this.ctx.lineWidth = 1; |
| this.ctx.beginPath(); |
| |
| for (let i = 0; i < operative.history.length; i++) { |
| const pos = operative.history[i]; |
| const x = (pos[0] + 5) / 10 * this.canvas.width; |
| const y = (pos[1] + 5) / 10 * this.canvas.height; |
| |
| if (i === 0) { |
| this.ctx.moveTo(x, y); |
| } else { |
| this.ctx.lineTo(x, y); |
| } |
| } |
| |
| this.ctx.stroke(); |
| } |
| } |
| } |
| |
| |
| if (this.globalBest) { |
| const x = (this.globalBest[0] + 5) / 10 * this.canvas.width; |
| const y = (this.globalBest[1] + 5) / 10 * this.canvas.height; |
| |
| this.ctx.fillStyle = '#ffaa00'; |
| this.ctx.shadowBlur = 20; |
| this.ctx.shadowColor = '#ffaa00'; |
| this.ctx.beginPath(); |
| this.ctx.arc(x, y, 8, 0, Math.PI * 2); |
| this.ctx.fill(); |
| this.ctx.shadowBlur = 0; |
| } |
| |
| |
| for (const operative of this.operatives) { |
| const x = (operative.position[0] + 5) / 10 * this.canvas.width; |
| const y = (operative.position[1] + 5) / 10 * this.canvas.height; |
| |
| |
| let color = '#00ff41'; |
| if (operative.deceptionRisk > 0.7) { |
| color = '#ff3333'; |
| } else if (operative.deceptionRisk > 0.4) { |
| color = '#ffaa00'; |
| } |
| |
| this.ctx.fillStyle = color; |
| this.ctx.shadowBlur = 10; |
| this.ctx.shadowColor = color; |
| this.ctx.beginPath(); |
| this.ctx.arc(x, y, 5, 0, Math.PI * 2); |
| this.ctx.fill(); |
| this.ctx.shadowBlur = 0; |
| |
| |
| this.ctx.fillStyle = color; |
| this.ctx.font = '9px monospace'; |
| this.ctx.fillText(operative.id.toString(), x + 7, y - 7); |
| } |
| } |
| |
| updateStats() { |
| document.getElementById('iteration').textContent = this.iteration; |
| document.getElementById('bestValue').textContent = this.globalBestValue.toFixed(6); |
| |
| |
| let totalTrust = 0; |
| let trustCount = 0; |
| for (const operative of this.operatives) { |
| for (const [_, trust] of operative.trust) { |
| totalTrust += trust; |
| trustCount++; |
| } |
| } |
| const avgTrust = trustCount > 0 ? totalTrust / trustCount : 0.5; |
| document.getElementById('avgTrust').textContent = avgTrust.toFixed(2); |
| |
| |
| const avgDeception = this.operatives.reduce((sum, op) => sum + op.deceptionRisk, 0) / this.operatives.length; |
| document.getElementById('deceptionRisk').textContent = avgDeception.toFixed(2); |
| |
| |
| const phases = ['RECONNAISSANCE', 'INFILTRATION', 'COUNTER-INTEL']; |
| document.getElementById('currentPhase').textContent = phases[this.phase]; |
| |
| |
| const activeCells = new Set(this.operatives.flatMap(op => op.neighbors)).size; |
| document.getElementById('activeCells').textContent = activeCells; |
| |
| |
| document.querySelectorAll('.phase-item').forEach(el => el.classList.remove('active')); |
| if (this.phase === 0) document.getElementById('phase-recon').classList.add('active'); |
| else if (this.phase === 1) document.getElementById('phase-infil').classList.add('active'); |
| else document.getElementById('phase-counter').classList.add('active'); |
| } |
| |
| updateConvergenceChart() { |
| const chartCanvas = document.getElementById('convergenceChart'); |
| const ctx = chartCanvas.getContext('2d'); |
| const width = chartCanvas.width = chartCanvas.offsetWidth; |
| const height = chartCanvas.height = chartCanvas.offsetHeight; |
| |
| ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; |
| ctx.fillRect(0, 0, width, height); |
| |
| if (this.convergenceHistory.length < 2) return; |
| |
| const padding = 10; |
| const graphWidth = width - 2 * padding; |
| const graphHeight = height - 2 * padding; |
| |
| const minVal = Math.min(...this.convergenceHistory); |
| const maxVal = Math.max(...this.convergenceHistory); |
| const range = maxVal - minVal || 1; |
| |
| |
| ctx.strokeStyle = 'rgba(0, 255, 65, 0.3)'; |
| ctx.lineWidth = 1; |
| ctx.beginPath(); |
| ctx.moveTo(padding, padding); |
| ctx.lineTo(padding, height - padding); |
| ctx.lineTo(width - padding, height - padding); |
| ctx.stroke(); |
| |
| |
| ctx.strokeStyle = '#00ff41'; |
| ctx.lineWidth = 2; |
| ctx.beginPath(); |
| |
| for (let i = 0; i < this.convergenceHistory.length; i++) { |
| const x = padding + (i / (this.convergenceHistory.length - 1)) * graphWidth; |
| const y = height - padding - ((this.convergenceHistory[i] - minVal) / range) * graphHeight; |
| |
| if (i === 0) { |
| ctx.moveTo(x, y); |
| } else { |
| ctx.lineTo(x, y); |
| } |
| } |
| |
| ctx.stroke(); |
| |
| |
| const lastIdx = this.convergenceHistory.length - 1; |
| const lastX = padding + graphWidth; |
| const lastY = height - padding - ((this.convergenceHistory[lastIdx] - minVal) / range) * graphHeight; |
| |
| ctx.fillStyle = '#ffaa00'; |
| ctx.beginPath(); |
| ctx.arc(lastX, lastY, 4, 0, Math.PI * 2); |
| ctx.fill(); |
| } |
| |
| updateTrustMatrix() { |
| const matrix = document.getElementById('trustMatrix'); |
| let html = ''; |
| |
| |
| const trustPairs = []; |
| for (const operative of this.operatives) { |
| for (const [neighborId, trust] of operative.trust) { |
| if (trust > 0.3) { |
| trustPairs.push({ |
| from: operative.id, |
| to: neighborId, |
| trust: trust |
| }); |
| } |
| } |
| } |
| |
| trustPairs.sort((a, b) => b.trust - a.trust); |
| const topPairs = trustPairs.slice(0, 10); |
| |
| for (const pair of topPairs) { |
| const trustPercent = (pair.trust * 100).toFixed(0); |
| const barWidth = pair.trust * 100; |
| html += ` |
| <div class="trust-connection"> |
| <span style="color: #00ff41;">OP${pair.from} → OP${pair.to}</span> |
| <span style="float: right; color: #00cc33;">${trustPercent}%</span> |
| <div style="background: rgba(0, 255, 65, 0.2); height: 2px; width: ${barWidth}%; margin-top: 2px;"></div> |
| </div> |
| `; |
| } |
| |
| matrix.innerHTML = html || '<div style="color: #00cc33; opacity: 0.7;">No significant trust connections</div>'; |
| } |
| |
| showOperativeInfo(mouseX, mouseY, clientX, clientY) { |
| const info = document.getElementById('operativeInfo'); |
| let found = false; |
| |
| for (const operative of this.operatives) { |
| const x = (operative.position[0] + 5) / 10 * this.canvas.width; |
| const y = (operative.position[1] + 5) / 10 * this.canvas.height; |
| |
| if (Math.sqrt(Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2)) < 10) { |
| info.innerHTML = ` |
| <div style="color: #00ff41; font-weight: bold;">Operative ${operative.id}</div> |
| <div>Position: [${operative.position[0].toFixed(2)}, ${operative.position[1].toFixed(2)}]</div> |
| <div>P.Best: ${operative.personalBestValue.toFixed(4)}</div> |
| <div>Deception: ${(operative.deceptionRisk * 100).toFixed(0)}%</div> |
| <div>Stagnation: ${operative.stagnation}</div> |
| `; |
| info.style.left = (clientX + 10) + 'px'; |
| info.style.top = (clientY - 60) + 'px'; |
| info.style.display = 'block'; |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) { |
| info.style.display = 'none'; |
| } |
| } |
| |
| showAlert(message) { |
| const alert = document.createElement('div'); |
| alert.className = 'alert'; |
| alert.textContent = '🎯 ' + message; |
| document.body.appendChild(alert); |
| |
| setTimeout(() => { |
| alert.style.animation = 'slideIn 0.5s ease-out reverse'; |
| setTimeout(() => alert.remove(), 500); |
| }, 2000); |
| } |
| |
| start() { |
| this.running = true; |
| this.animate(); |
| } |
| |
| stop() { |
| this.running = false; |
| } |
| |
| animate() { |
| if (!this.running) return; |
| |
| this.step(); |
| requestAnimationFrame(() => this.animate()); |
| } |
| } |
| |
| |
| const sim = new IOASimulation('canvas'); |
| sim.initialize(); |
| |
| |
| function toggleSimulation() { |
| const btn = document.getElementById('startBtn'); |
| if (sim.running) { |
| sim.stop(); |
| btn.textContent = '▶ START MISSION'; |
| btn.classList.remove('active'); |
| } else { |
| sim.start(); |
| btn.textContent = '⏸ PAUSE MISSION'; |
| btn.classList.add('active'); |
| } |
| } |
| |
| function resetSimulation() { |
| sim.stop(); |
| sim.initialize(); |
| document.getElementById('startBtn').textContent = '▶ START MISSION'; |
| document.getElementById('startBtn').classList.remove('active'); |
| } |
| |
| function toggleTrustLines() { |
| sim.showTrustLines = !sim.showTrustLines; |
| document.getElementById('trustBtn').classList.toggle('active'); |
| } |
| |
| function toggleTrails() { |
| sim.showTrails = !sim.showTrails; |
| document.getElementById('trailBtn').classList.toggle('active'); |
| if (!sim.showTrails) { |
| |
| for (const operative of sim.operatives) { |
| operative.history = []; |
| } |
| } |
| } |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Volkan36/intelligence-operatives-optimization" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |