diff --git a/app.js b/app.js index 3abfd71..0a757f9 100644 --- a/app.js +++ b/app.js @@ -76,6 +76,473 @@ const orbitState = { let flyY = 2; // ═══ INIT ═══ + +// ═══ SOVEREIGN SYMBOLIC ENGINE (GOFAI) ═══ +class SymbolicEngine { + constructor() { + this.facts = new Map(); + this.factIndices = new Map(); + this.factMask = 0n; + this.rules = []; + this.reasoningLog = []; + } + + addFact(key, value) { + this.facts.set(key, value); + if (!this.factIndices.has(key)) { + this.factIndices.set(key, BigInt(this.factIndices.size)); + } + const bitIndex = this.factIndices.get(key); + if (value) { + this.factMask |= (1n << bitIndex); + } else { + this.factMask &= ~(1n << bitIndex); + } + } + + addRule(condition, action, description) { + this.rules.push({ condition, action, description }); + } + + reason() { + this.rules.forEach(rule => { + if (rule.condition(this.facts)) { + const result = rule.action(this.facts); + if (result) { + this.logReasoning(rule.description, result); + } + } + }); + } + + logReasoning(ruleDesc, outcome) { + const entry = { timestamp: Date.now(), rule: ruleDesc, outcome: outcome }; + this.reasoningLog.unshift(entry); + if (this.reasoningLog.length > 5) this.reasoningLog.pop(); + + const container = document.getElementById('symbolic-log-content'); + if (container) { + const logDiv = document.createElement('div'); + logDiv.className = 'symbolic-log-entry'; + logDiv.innerHTML = `[RULE] ${ruleDesc}→ ${outcome}`; + container.prepend(logDiv); + if (container.children.length > 5) container.lastElementChild.remove(); + } + } +} + +class AgentFSM { + constructor(agentId, initialState) { + this.agentId = agentId; + this.state = initialState; + this.transitions = {}; + } + + addTransition(fromState, toState, condition) { + if (!this.transitions[fromState]) this.transitions[fromState] = []; + this.transitions[fromState].push({ toState, condition }); + } + + update(facts) { + const possibleTransitions = this.transitions[this.state] || []; + for (const transition of possibleTransitions) { + if (transition.condition(facts)) { + console.log(`[FSM] Agent ${this.agentId} transitioning: ${this.state} -> ${transition.toState}`); + this.state = transition.toState; + return true; + } + } + return false; + } +} + +class KnowledgeGraph { + constructor() { + this.nodes = new Map(); + this.edges = []; + } + + addNode(id, type, metadata = {}) { + this.nodes.set(id, { id, type, ...metadata }); + } + + addEdge(from, to, relation) { + this.edges.push({ from, to, relation }); + } + + query(from, relation) { + return this.edges + .filter(e => e.from === from && e.relation === relation) + .map(e => this.nodes.get(e.to)); + } +} + +class Blackboard { + constructor() { + this.data = {}; + this.subscribers = []; + } + + write(key, value, source) { + const oldValue = this.data[key]; + this.data[key] = value; + this.notify(key, value, oldValue, source); + } + + read(key) { return this.data[key]; } + + subscribe(callback) { this.subscribers.push(callback); } + + notify(key, value, oldValue, source) { + this.subscribers.forEach(sub => sub(key, value, oldValue, source)); + const container = document.getElementById('blackboard-log-content'); + if (container) { + const entry = document.createElement('div'); + entry.className = 'blackboard-entry'; + entry.innerHTML = `[${source}] ${key}: ${JSON.stringify(value)}`; + container.prepend(entry); + if (container.children.length > 8) container.lastElementChild.remove(); + } + } +} + +class SymbolicPlanner { + constructor() { + this.actions = []; + this.currentPlan = []; + } + + addAction(name, preconditions, effects) { + this.actions.push({ name, preconditions, effects }); + } + + heuristic(state, goal) { + let h = 0; + for (let key in goal) { + if (state[key] !== goal[key]) { + h += Math.abs((state[key] || 0) - (goal[key] || 0)); + } + } + return h; + } + + findPlan(initialState, goalState) { + let openSet = [{ state: initialState, plan: [], g: 0, h: this.heuristic(initialState, goalState) }]; + let visited = new Map(); + visited.set(JSON.stringify(initialState), 0); + + while (openSet.length > 0) { + openSet.sort((a, b) => (a.g + a.h) - (b.g + b.h)); + let { state, plan, g } = openSet.shift(); + + if (this.isGoalReached(state, goalState)) return plan; + + for (let action of this.actions) { + if (this.arePreconditionsMet(state, action.preconditions)) { + let nextState = { ...state, ...action.effects }; + let stateStr = JSON.stringify(nextState); + let nextG = g + 1; + + if (!visited.has(stateStr) || nextG < visited.get(stateStr)) { + visited.set(stateStr, nextG); + openSet.push({ + state: nextState, + plan: [...plan, action.name], + g: nextG, + h: this.heuristic(nextState, goalState) + }); + } + } + } + } + return null; + } + + isGoalReached(state, goal) { + for (let key in goal) { + if (state[key] !== goal[key]) return false; + } + return true; + } + + arePreconditionsMet(state, preconditions) { + for (let key in preconditions) { + if (state[key] < preconditions[key]) return false; + } + return true; + } + + logPlan(plan) { + this.currentPlan = plan; + const container = document.getElementById('planner-log-content'); + if (container) { + container.innerHTML = ''; + if (!plan || plan.length === 0) { + container.innerHTML = '
NO ACTIVE PLAN
'; + return; + } + plan.forEach((step, i) => { + const div = document.createElement('div'); + div.className = 'planner-step'; + div.innerHTML = `${i+1}. ${step}`; + container.appendChild(div); + }); + } + } +} + +class HTNPlanner { + constructor() { + this.methods = {}; + this.primitiveTasks = {}; + } + + addMethod(taskName, preconditions, subtasks) { + if (!this.methods[taskName]) this.methods[taskName] = []; + this.methods[taskName].push({ preconditions, subtasks }); + } + + addPrimitiveTask(taskName, preconditions, effects) { + this.primitiveTasks[taskName] = { preconditions, effects }; + } + + findPlan(initialState, tasks) { + return this.decompose(initialState, tasks, []); + } + + decompose(state, tasks, plan) { + if (tasks.length === 0) return plan; + const [task, ...remainingTasks] = tasks; + if (this.primitiveTasks[task]) { + const { preconditions, effects } = this.primitiveTasks[task]; + if (this.arePreconditionsMet(state, preconditions)) { + const nextState = { ...state, ...effects }; + return this.decompose(nextState, remainingTasks, [...plan, task]); + } + return null; + } + const methods = this.methods[task] || []; + for (const method of methods) { + if (this.arePreconditionsMet(state, method.preconditions)) { + const result = this.decompose(state, [...method.subtasks, ...remainingTasks], plan); + if (result) return result; + } + } + return null; + } + + arePreconditionsMet(state, preconditions) { + for (const key in preconditions) { + if (state[key] < (preconditions[key] || 0)) return false; + } + return true; + } +} + +class CaseBasedReasoner { + constructor() { + this.caseLibrary = []; + } + + addCase(situation, action, outcome) { + this.caseLibrary.push({ situation, action, outcome, timestamp: Date.now() }); + } + + findSimilarCase(currentSituation) { + let bestMatch = null; + let maxSimilarity = -1; + this.caseLibrary.forEach(c => { + let similarity = this.calculateSimilarity(currentSituation, c.situation); + if (similarity > maxSimilarity) { + maxSimilarity = similarity; + bestMatch = c; + } + }); + return maxSimilarity > 0.7 ? bestMatch : null; + } + + calculateSimilarity(s1, s2) { + let score = 0, total = 0; + for (let key in s1) { + if (s2[key] !== undefined) { + score += 1 - Math.abs(s1[key] - s2[key]); + total += 1; + } + } + return total > 0 ? score / total : 0; + } + + logCase(c) { + const container = document.getElementById('cbr-log-content'); + if (container) { + const div = document.createElement('div'); + div.className = 'cbr-entry'; + div.innerHTML = ` +
SIMILAR CASE FOUND (${(this.calculateSimilarity(symbolicEngine.facts, c.situation) * 100).toFixed(0)}%)
+
SUGGESTED: ${c.action}
+
PREVIOUS OUTCOME: ${c.outcome}
+ `; + container.prepend(div); + if (container.children.length > 3) container.lastElementChild.remove(); + } + } +} + +class NeuroSymbolicBridge { + constructor(symbolicEngine, blackboard) { + this.engine = symbolicEngine; + this.blackboard = blackboard; + this.perceptionLog = []; + } + + perceive(rawState) { + const concepts = []; + if (rawState.stability < 0.4 && rawState.energy > 60) concepts.push('UNSTABLE_OSCILLATION'); + if (rawState.energy < 30 && rawState.activePortals > 2) concepts.push('CRITICAL_DRAIN_PATTERN'); + concepts.forEach(concept => { + this.engine.addFact(concept, true); + this.logPerception(concept); + }); + return concepts; + } + + logPerception(concept) { + const container = document.getElementById('neuro-bridge-log-content'); + if (container) { + const div = document.createElement('div'); + div.className = 'neuro-bridge-entry'; + div.innerHTML = `🧠 ${concept}`; + container.prepend(div); + if (container.children.length > 5) container.lastElementChild.remove(); + } + } +} + +class MetaReasoningLayer { + constructor(planner, blackboard) { + this.planner = planner; + this.blackboard = blackboard; + this.reasoningCache = new Map(); + this.performanceMetrics = { totalReasoningTime: 0, calls: 0 }; + } + + getCachedPlan(stateKey) { + const cached = this.reasoningCache.get(stateKey); + if (cached && (Date.now() - cached.timestamp < 10000)) return cached.plan; + return null; + } + + cachePlan(stateKey, plan) { + this.reasoningCache.set(stateKey, { plan, timestamp: Date.now() }); + } + + reflect() { + const avgTime = this.performanceMetrics.totalReasoningTime / (this.performanceMetrics.calls || 1); + const container = document.getElementById('meta-log-content'); + if (container) { + container.innerHTML = ` +
CACHE SIZE: ${this.reasoningCache.size}
+
AVG LATENCY: ${avgTime.toFixed(2)}ms
+
STATUS: ${avgTime > 50 ? 'OPTIMIZING' : 'NOMINAL'}
+ `; + } + } + + track(startTime) { + const duration = performance.now() - startTime; + this.performanceMetrics.totalReasoningTime += duration; + this.performanceMetrics.calls++; + } +} + +// ═══ ADAPTIVE CALIBRATOR (LOCAL EFFICIENCY) ═══ +class AdaptiveCalibrator { + constructor(modelId, initialParams) { + this.model = modelId; + this.weights = { + 'input_tokens': 0.0, + 'complexity_score': 0.0, + 'task_type_indicator': 0.0, + 'bias': initialParams.base_rate || 0.0 + }; + this.learningRate = 0.01; + this.history = []; + } + + predict(features) { + let prediction = this.weights['bias']; + for (let feature in features) { + if (this.weights[feature] !== undefined) { + prediction += this.weights[feature] * features[feature]; + } + } + return Math.max(0, prediction); + } + + update(features, actualCost) { + const predicted = this.predict(features); + const error = actualCost - predicted; + for (let feature in features) { + if (this.weights[feature] !== undefined) { + this.weights[feature] += this.learningRate * error * features[feature]; + } + } + this.history.push({ predicted, actual: actualCost, timestamp: Date.now() }); + + const container = document.getElementById('calibrator-log-content'); + if (container) { + const div = document.createElement('div'); + div.className = 'calibrator-entry'; + div.innerHTML = `CALIBRATED: ${predicted.toFixed(4)} ERR: ${error.toFixed(4)}`; + container.prepend(div); + if (container.children.length > 5) container.lastElementChild.remove(); + } + } +} + +let metaLayer, neuroBridge, cbr, symbolicPlanner, knowledgeGraph, blackboard, symbolicEngine, calibrator; +let agentFSMs = {}; + +function setupGOFAI() { + knowledgeGraph = new KnowledgeGraph(); + blackboard = new Blackboard(); + symbolicEngine = new SymbolicEngine(); + symbolicPlanner = new SymbolicPlanner(); + cbr = new CaseBasedReasoner(); + neuroBridge = new NeuroSymbolicBridge(symbolicEngine, blackboard); + metaLayer = new MetaReasoningLayer(symbolicPlanner, blackboard); + calibrator = new AdaptiveCalibrator('nexus-v1', { base_rate: 0.05 }); + + // Setup initial facts + symbolicEngine.addFact('energy', 100); + symbolicEngine.addFact('stability', 1.0); + + // Setup FSM + agentFSMs['timmy'] = new AgentFSM('timmy', 'IDLE'); + agentFSMs['timmy'].addTransition('IDLE', 'ANALYZING', (facts) => facts.get('activePortals') > 0); + + // Setup Planner + symbolicPlanner.addAction('Stabilize Matrix', { energy: 50 }, { stability: 1.0 }); +} + +function updateGOFAI(delta, elapsed) { + const startTime = performance.now(); + + // Simulate perception + neuroBridge.perceive({ stability: 0.3, energy: 80, activePortals: 1 }); + + // Run reasoning + if (Math.floor(elapsed * 2) > Math.floor((elapsed - delta) * 2)) { + symbolicEngine.reason(); + metaLayer.reflect(); + + // Simulate calibration update + calibrator.update({ input_tokens: 100, complexity_score: 0.5 }, 0.06); + } + + metaLayer.track(startTime); +} + async function init() { clock = new THREE.Clock(); playerPos = new THREE.Vector3(0, 2, 12); @@ -95,6 +562,7 @@ async function init() { scene = new THREE.Scene(); scene.fog = new THREE.FogExp2(0x050510, 0.012); + setupGOFAI(); camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.copy(playerPos); diff --git a/index.html b/index.html index 39eb7d1..5ea207e 100644 --- a/index.html +++ b/index.html @@ -65,6 +65,38 @@