fertig-classic-games/public/src/games/geniussquare/GeniusSquareAI.js

53 lines
1.7 KiB
JavaScript

// Genius Square AI — pre-solves the board at round start, then ticks through
// placements at a skill-scaled delay (mirrors the Nerts real-time pattern).
//
// The tick loop lives in GeniusSquareGame.js (Phaser's time.delayedCall).
// This module is pure JS with no Phaser dependency.
import { solveFromBlockers } from './GeniusSquareLogic.js';
const SKILL_PROFILES = {
1: { delay: [8000, 12000] }, // very slow — human wins easily
2: { delay: [4000, 7000] },
3: { delay: [2000, 4000] }, // balanced
4: { delay: [1000, 2000] }, // challenging
5: { delay: [400, 800] }, // expert
};
function profileFor(skill) {
return SKILL_PROFILES[Math.max(1, Math.min(5, skill | 0))] ?? SKILL_PROFILES[3];
}
/** Milliseconds until the AI's next piece placement, randomized within skill's band. */
export function nextThinkDelay(skill) {
const [lo, hi] = profileFor(skill).delay;
return lo + Math.random() * (hi - lo);
}
/**
* Create AI state. Pass in a pre-computed solution to avoid solving twice.
* solution: array of { pieceId, oriIdx, anchorR, anchorC, cells } | null
*/
export function createAIState(blockers, skill, solution) {
return {
skill,
solution: solution ?? solveFromBlockers(blockers),
stepIndex: 0,
done: false,
};
}
/**
* Return the next placement to visually execute, or null if done.
* Mutates aiState.stepIndex and aiState.done.
*/
export function getNextPlacement(aiState) {
if (aiState.done || !aiState.solution || aiState.stepIndex >= aiState.solution.length) {
return null;
}
const placement = aiState.solution[aiState.stepIndex];
aiState.stepIndex++;
if (aiState.stepIndex >= aiState.solution.length) aiState.done = true;
return placement;
}