/**
* @fileoverview Bridge zwischen numerischen Heuristiken und booleschen Regelbäumen.
* @author Alexander Wolf
*/
/**
* Adapterklasse von Heuristik-Scoring auf Rule-Engine-Entscheidungen.
*/
class HeuristicRuleAdapter extends AtomicRule {
/**
* @param {BaseHeuristic} heuristic
* @param {Object} [thresholds]
* @param {number} [thresholds.must=500]
* @param {number} [thresholds.should=100]
* @param {number} [thresholds.could=10]
* @param {string} [mode='best-of']
*/
constructor(heuristic, thresholds = {}, mode = 'best-of') {
super(
`H:${heuristic.name}`,
`Heuristik-Regel (${heuristic.name})`,
null
);
this.heuristic = heuristic;
this.thresholds = {
must: thresholds.must ?? 500,
should: thresholds.should ?? 100,
could: thresholds.could ?? 10
};
this.mode = mode;
this.logicFn = (gameState) => this._adaptedEvaluate(gameState);
}
/**
* @private
* @param {GameState} gameState
* @returns {(number|Object|null)}
*/
_adaptedEvaluate(gameState) {
const moves = gameState.getAllValidMoves ? gameState.getAllValidMoves() : [];
if (!moves || moves.length === 0) return null;
const player = gameState.currentPlayer;
const scoredMoves = [];
for (const move of moves) {
const sim = gameState.clone();
sim.makeMove(move);
const score = this.heuristic.evaluate(sim, player);
scoredMoves.push({ move, score, priority: this._getPriority(score) });
}
scoredMoves.sort((a, b) => b.score - a.score);
const best = scoredMoves[0];
DebugConfig.log(DEBUG_DOMAINS.AI_HEURISTICS, 'debug', 'HeuristicRuleAdapter evaluated moves', {
heuristic: this.heuristic.name,
mode: this.mode,
best,
thresholds: this.thresholds
});
if (!best) return null;
if (this.mode === 'first-above') {
const mustCandidate = scoredMoves.find((entry) => entry.score >= this.thresholds.must);
return mustCandidate ? mustCandidate.move : null;
}
if (this.mode === 'weighted-random') {
const candidates = scoredMoves.filter((entry) => entry.score >= this.thresholds.could);
if (candidates.length === 0) return null;
return candidates[Math.floor(Math.random() * candidates.length)].move;
}
return best.score >= this.thresholds.could ? best.move : null;
}
/**
* @private
* @param {number} score
* @returns {'MUST'|'SHOULD'|'COULD'|'SKIP'}
*/
_getPriority(score) {
if (score >= this.thresholds.must) return 'MUST';
if (score >= this.thresholds.should) return 'SHOULD';
if (score >= this.thresholds.could) return 'COULD';
return 'SKIP';
}
}