automata-metaverse
Version:
Automaton execution engines for self-referential CanvasL/JSONL systems
133 lines (110 loc) • 3.82 kB
text/typescript
/**
* A₂: Face Binder Automaton
*
* Role: C₂ document/face management (GeoJSON format)
* Uses meta-log-db: r5rs:export-2d
*/
import { BaseAutomaton } from './types.js';
import type { AutomatonId, SwarmContext, AutomatonMessage } from './types.js';
import type { MetaLogDb } from 'meta-log-db';
export interface A2FaceBinderState {
faces: Array<{ id: string; edges: string[]; data?: any }>;
initialized: boolean;
}
/**
* A₂: Face Binder Automaton
*
* Manages C₂ documents/faces (2-cells) and binds faces from edges
*/
export class A2_FaceBinder extends BaseAutomaton {
readonly id: 2 = 2;
readonly name = 'A₂ Face Binder';
readonly role = 'C₂ document/face management (GeoJSON format)';
state: A2FaceBinderState = {
faces: [],
initialized: false
};
constructor(private db?: MetaLogDb) {
super();
}
async tick(swarm: SwarmContext): Promise<void> {
if (!this.state.initialized) {
await this.initialize(swarm);
}
// Face binding happens based on edges from A₁
const a1 = swarm.get(1);
if (a1 && 'getEdges' in a1 && typeof a1.getEdges === 'function') {
const edges = (a1 as any).getEdges();
await this.bindFaces(edges);
}
}
private async initialize(swarm: SwarmContext): Promise<void> {
this.state.initialized = true;
console.log('A₂: Face Binder initialized');
}
private async bindFaces(edges: Array<{ id: string; from: string; to: string }>): Promise<void> {
if (!this.db || edges.length < 3) {
return;
}
try {
// Create faces from closed loops of edges
// Simple implementation: create a face from every 3+ edge cycle
const newFaces: Array<{ id: string; edges: string[] }> = [];
// Find cycles (simplified: every 3 consecutive edges form a face)
for (let i = 0; i < edges.length - 2; i++) {
const edge1 = edges[i];
const edge2 = edges[i + 1];
const edge3 = edges[i + 2];
// Check if edges form a cycle (edge1.to === edge2.from, edge2.to === edge3.from, edge3.to === edge1.from)
if (edge1.to === edge2.from && edge2.to === edge3.from && edge3.to === edge1.from) {
const faceId = `face-${edge1.id}-${edge2.id}-${edge3.id}`;
if (!this.state.faces.find(f => f.id === faceId)) {
newFaces.push({
id: faceId,
edges: [edge1.id, edge2.id, edge3.id]
});
}
}
}
if (newFaces.length === 0) {
return;
}
// Create C₂ cells (faces) using meta-log-db
const cells = await Promise.all(
newFaces.map(face =>
this.db!.executeR5RS('r5rs:create-cell', [
2, // dimension (2-cell)
face.id,
face.edges, // boundary: edges that form the face
{ edges: face.edges, type: 'face' }
])
)
);
// Build chain complex with faces
const complex = await this.db.executeR5RS('r5rs:build-chain-complex', [cells]);
// Export to 2D (GeoJSON)
const geojson = await this.db.executeR5RS('r5rs:export-2d', [complex]);
const geo = JSON.parse(geojson);
// Update state
this.state.faces.push(...newFaces.map((f, i) => ({
id: f.id,
edges: f.edges,
data: geo.features[i] || {}
})));
console.log(`A₂: Bound ${newFaces.length} new faces`);
} catch (error) {
console.error('A₂: Error binding faces:', error);
}
}
async receive(from: AutomatonId, message: AutomatonMessage): Promise<void> {
if (message.type === 'request-faces') {
// Could send faces back to requester
}
}
/**
* Get all faces
*/
getFaces(): Array<{ id: string; edges: string[]; data?: any }> {
return this.state.faces;
}
}