/**
* @fileoverview Zentrale Registry für Heuristiken.
* Kapselt Registrierung und Lookup von Heuristik-Instanzen.
* @author Alexander Wolf
*/
class HeuristicRegistry {
static _registry = new Map();
static _constructors = new Map();
/**
* @param {BaseHeuristic} heuristic
*/
static register(heuristic) {
if (!(heuristic instanceof BaseHeuristic)) {
throw new Error('Nur BaseHeuristic-Instanzen können registriert werden.');
}
if (HeuristicRegistry._registry.has(heuristic.id)) {
DebugConfig.log(DEBUG_DOMAINS.AI_HEURISTICS, 'warn', 'Heuristic already registered, overwriting', {
id: heuristic.id
});
}
HeuristicRegistry._registry.set(heuristic.id, heuristic);
DebugConfig.log(DEBUG_DOMAINS.AI_HEURISTICS, 'debug', 'Heuristic registered', {
id: heuristic.id
});
}
/**
* Ruft eine registrierte Heuristik ab.
* Unterstützt sowohl `get('ttt', 'regular')` als auch `get('ttt:regular:v2_positional')`.
* @param {string} game - Spielname oder vollständiger Composite-Key.
* @param {string} [variant] - Variante (optional bei Composite-Key).
* @returns {BaseHeuristic}
*/
static get(game, variant) {
// Composite-Key: Wenn game bereits ':' enthält und kein variant explizit übergeben, direkt verwenden
const id = (variant !== undefined) ? `${game}:${variant}` : (game.includes(':') ? game : `${game}:regular`);
const heuristic = HeuristicRegistry._registry.get(id);
if (!heuristic) {
DebugConfig.log(DEBUG_DOMAINS.AI_HEURISTICS, 'error', 'Heuristic not found', {
id,
available: [...HeuristicRegistry._registry.keys()]
});
throw new Error(`Heuristik '${id}' nicht registriert.`);
}
return heuristic;
}
/**
* Prüft ob eine Heuristik registriert ist.
* Unterstützt sowohl `has('ttt', 'regular')` als auch `has('ttt:regular:v2_positional')`.
* @param {string} game - Spielname oder vollständiger Composite-Key.
* @param {string} [variant] - Variante (optional bei Composite-Key).
* @returns {boolean}
*/
static has(game, variant) {
const id = (variant !== undefined) ? `${game}:${variant}` : (game.includes(':') ? game : `${game}:regular`);
return HeuristicRegistry._registry.has(id);
}
/**
* @param {string} game
* @returns {BaseHeuristic[]}
*/
static getByGame(game) {
return [...HeuristicRegistry._registry.values()].filter((heuristic) => heuristic.game === game);
}
/**
* @returns {BaseHeuristic[]}
*/
static getAll() {
return [...HeuristicRegistry._registry.values()];
}
/**
* @param {Object} config
* @returns {BaseHeuristic}
*/
static createFromConfig(config) {
const Constructor = HeuristicRegistry._constructors.get(config.game);
if (!Constructor) {
throw new Error(`Kein Heuristik-Konstruktor für Spiel '${config.game}' registriert.`);
}
const instance = new Constructor(config);
HeuristicRegistry.register(instance);
return instance;
}
/**
* @param {string} game
* @param {Function} Constructor - A BaseHeuristic subclass constructor
*/
static registerConstructor(game, Constructor) {
HeuristicRegistry._constructors.set(game, Constructor);
}
}
/**
* Generic fallback heuristic for terminal-only evaluation.
*/
class GenericWinLossHeuristic extends BaseHeuristic {
constructor(config = {}) {
super({ game: 'generic', variant: 'winLoss', ...config });
}
evaluate(gameState, player) {
const terminal = this.checkTerminal(gameState, player);
return terminal !== null ? terminal : 0;
}
}
if (!HeuristicRegistry.has('generic', 'winLoss')) {
HeuristicRegistry.register(new GenericWinLossHeuristic());
}