122 lines
3.4 KiB
TypeScript
122 lines
3.4 KiB
TypeScript
import type { Genome } from './genome';
|
|
import type { EvolutionConfig } from './evolution';
|
|
|
|
/**
|
|
* Export/Import system for trained genomes.
|
|
*
|
|
* Allows saving champion genomes as JSON files and loading them back
|
|
* for exhibition matches or continued training.
|
|
*/
|
|
|
|
export interface ExportedGenome {
|
|
version: string;
|
|
timestamp: number;
|
|
config: {
|
|
inputCount: number;
|
|
outputCount: number;
|
|
};
|
|
genome: Genome;
|
|
metadata?: {
|
|
generation?: number;
|
|
fitness?: number;
|
|
speciesCount?: number;
|
|
};
|
|
}
|
|
|
|
const EXPORT_VERSION = '1.0.0';
|
|
|
|
/**
|
|
* Export a genome to a downloadable JSON format
|
|
*/
|
|
export function exportGenome(
|
|
genome: Genome,
|
|
config: EvolutionConfig,
|
|
metadata?: ExportedGenome['metadata']
|
|
): ExportedGenome {
|
|
return {
|
|
version: EXPORT_VERSION,
|
|
timestamp: Date.now(),
|
|
config: {
|
|
inputCount: config.inputCount,
|
|
outputCount: config.outputCount,
|
|
},
|
|
genome: {
|
|
id: genome.id,
|
|
nodes: genome.nodes,
|
|
connections: genome.connections,
|
|
fitness: genome.fitness,
|
|
},
|
|
metadata,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Import a genome from JSON
|
|
*/
|
|
export function importGenome(exported: ExportedGenome): {
|
|
genome: Genome;
|
|
config: { inputCount: number; outputCount: number };
|
|
} {
|
|
// Version check
|
|
if (exported.version !== EXPORT_VERSION) {
|
|
console.warn(`Imported genome version ${exported.version} may be incompatible with current version ${EXPORT_VERSION}`);
|
|
}
|
|
|
|
return {
|
|
genome: exported.genome,
|
|
config: exported.config,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Download genome as JSON file
|
|
*/
|
|
export function downloadGenomeAsFile(exported: ExportedGenome, filename?: string): void {
|
|
const json = JSON.stringify(exported, null, 2);
|
|
const blob = new Blob([json], { type: 'application/json' });
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = filename || `neat-champion-${Date.now()}.json`;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
/**
|
|
* Upload and parse genome from file
|
|
*/
|
|
export function uploadGenomeFromFile(): Promise<ExportedGenome> {
|
|
return new Promise((resolve, reject) => {
|
|
const input = document.createElement('input');
|
|
input.type = 'file';
|
|
input.accept = 'application/json,.json';
|
|
|
|
input.onchange = (e) => {
|
|
const file = (e.target as HTMLInputElement).files?.[0];
|
|
if (!file) {
|
|
reject(new Error('No file selected'));
|
|
return;
|
|
}
|
|
|
|
const reader = new FileReader();
|
|
reader.onload = (event) => {
|
|
try {
|
|
const json = event.target?.result as string;
|
|
const exported = JSON.parse(json) as ExportedGenome;
|
|
resolve(exported);
|
|
} catch (err) {
|
|
reject(new Error('Failed to parse genome file'));
|
|
}
|
|
};
|
|
reader.onerror = () => reject(new Error('Failed to read file'));
|
|
reader.readAsText(file);
|
|
};
|
|
|
|
input.click();
|
|
});
|
|
}
|