UNPKG

@zerospacegg/anrubic

Version:

Anrubic - ZeroSpace.gg MCP Server for AI agents to access game data

344 lines (331 loc) 11.6 kB
import createDebug from "debug"; // @ts-ignore - JavaScript module without TypeScript declarations import { PklDynamicEvaluator } from "../../scripts/pkl-utils/dynamic-evaluator.js"; const debugPkl = createDebug("anrubic:pkl"); let pklEvaluator = null; let pklAvailable = false; let initializationAttempted = false; /** * Initialize Pkl evaluator with configurable command * @param pklCommand - Pkl command to use (default: 'pkl') * @returns Initialization result */ export async function initializePkl(pklCommand = "pkl") { if (initializationAttempted && pklEvaluator) { return { success: true, pklAvailable }; } initializationAttempted = true; try { pklEvaluator = new PklDynamicEvaluator({ tempDir: "/tmp/pkl-eval-anrubic", cacheEnabled: true, pklCommand, }); // Test if Pkl is available with a simple evaluation const testTemplate = `result = "pkl-available"`; const testResult = await pklEvaluator.evaluateTemplate(testTemplate); if (testResult?.result === "pkl-available") { pklAvailable = true; debugPkl("Pkl integration ONLINE! Advanced combat analysis enabled!"); return { success: true, pklAvailable: true, version: "detected", }; } else { throw new Error("Pkl test evaluation failed"); } } catch (error) { pklAvailable = false; debugPkl("Pkl not available - falling back to JSON mode for basic analysis"); return { success: false, pklAvailable: false, error: error instanceof Error ? error.message : String(error), }; } } /** * Check if Pkl is available for advanced analysis */ export function isPklAvailable() { return pklAvailable; } /** * Get the Pkl evaluator instance (may be null if not available) */ export function getPklEvaluator() { return pklEvaluator; } /** * Execute combat analysis using Pkl API files * @param unitA - First unit identifier * @param unitB - Second unit identifier * @returns Combat analysis result */ export async function executeCombatAnalysis(unitA, unitB) { if (!pklEvaluator || !pklAvailable) { throw new Error("Pkl not available - cannot perform advanced combat analysis"); } const template = ` import "iolin/api/unit-combat-meta.pkl" as CombatMeta // Execute the combat analysis result = CombatMeta.result metadata = new { analysis = "pkl-powered-combat" version = "anrubic-v1" units = List("${escapeString(unitA)}", "${escapeString(unitB)}") } `; try { const result = await pklEvaluator.evaluateTemplate(template, { unitAId: unitA, unitBId: unitB, }, { timeout: 15000, format: "json", }); return { success: true, data: result, source: "pkl-api", units: [unitA, unitB], }; } catch (error) { throw new Error(`Combat analysis failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Execute unit comparison using Pkl API * @param units - Array of unit identifiers * @returns Unit comparison result */ export async function executeUnitComparison(units) { if (!pklEvaluator || !pklAvailable) { throw new Error("Pkl not available - cannot perform advanced unit comparison"); } const unitsListStr = units.map((u) => `"${escapeString(u)}"`).join("; "); const template = ` import "iolin/api/unit-comparison.pkl" as UnitComparison // Override the module's parameters by creating a new context result = (UnitComparison) { units = new Listing { ${unitsListStr} } analysis = "full" includeTiers = true includeUpgrades = false }.result metadata = new { analysis = "pkl-powered-comparison" version = "anrubic-v1" searchedUnits = new Listing { ${unitsListStr} } } `; try { const result = await pklEvaluator.evaluateTemplate(template, {}, { timeout: 20000, format: "json", }); return { success: true, data: result, source: "pkl-api", units, }; } catch (error) { throw new Error(`Unit comparison failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Find counters for a specific unit using custom Pkl analysis * @param targetUnit - Unit to find counters for * @param limit - Maximum number of counters to return * @returns Counter analysis result */ export async function findCounters(targetUnit, limit = 10) { if (!pklEvaluator || !pklAvailable) { throw new Error("Pkl not available - cannot perform counter analysis"); } const template = ` import "iolin/meta/all.pkl" as AllData // Hide intermediate variables to reduce output size local targetUnitName = "${escapeString(targetUnit)}" // Find the target unit local function findUnit(identifier: String) = AllData.all .filter((entity) -> entity.type == "unit") .find((unit) -> unit.name.toLowerCase() == identifier.toLowerCase() || (unit.slug != null && unit.slug.toLowerCase().contains(identifier.toLowerCase())) ) local targetUnit = findUnit(targetUnitName) // Get all combat units for analysis local allUnits = AllData.all .filter((entity) -> entity.type == "unit") .filter((unit) -> unit.name != targetUnitName) // Calculate who wins against target unit local function calculateWinners(target) = allUnits .map((challenger) -> let (targetAttack = target.ability?.keys?.map((key) -> target.ability[key])?.filter((a) -> a.subtype == "attack")?.firstOrNull) let (challengerAttack = challenger.ability?.keys?.map((key) -> challenger.ability[key])?.filter((a) -> a.subtype == "attack")?.firstOrNull) let (targetDamage = targetAttack?.damage ?? 0) let (challengerDamage = challengerAttack?.damage ?? 0) let (targetArmor = target.armor ?? 0) let (challengerArmor = challenger.armor ?? 0) let (targetArmorReduction = targetArmor / (targetArmor + 6)) let (challengerArmorReduction = challengerArmor / (challengerArmor + 6)) let (effectiveDamageToTarget = challengerDamage * (1 - targetArmorReduction)) let (effectiveDamageToChallenger = targetDamage * (1 - challengerArmorReduction)) let (shotsToKillTarget = if (effectiveDamageToTarget > 0) (target.hp / effectiveDamageToTarget).ceil else 999) let (shotsToKillChallenger = if (effectiveDamageToChallenger > 0) (challenger.hp / effectiveDamageToChallenger).ceil else 999) let (timeToKillTarget = shotsToKillTarget * (challengerAttack?.cooldown ?? 1)) let (timeToKillChallenger = shotsToKillChallenger * (targetAttack?.cooldown ?? 1)) let (challengerWins = timeToKillTarget < timeToKillChallenger) let (advantage = if (challengerWins) (timeToKillChallenger - timeToKillTarget) else 0) new Dynamic { name = challenger.name faction = challenger.id?.split("/")[1] ?? "unknown" tier = challenger.tier ?? "unknown" wins = challengerWins timeAdvantage = advantage costEfficiency = if ((challenger.hexiteCost ?? 0) > 0 && (target.hexiteCost ?? 0) > 0) ((target.hexiteCost ?? 0) / (challenger.hexiteCost ?? 0)) else 0 damageDealt = effectiveDamageToTarget.floor shotsNeeded = shotsToKillTarget timeToKill = timeToKillTarget } ) .filter((result) -> result.wins) .sortWith((a, b) -> if (b.timeAdvantage > a.timeAdvantage) 1 else if (b.timeAdvantage < a.timeAdvantage) -1 else 0) .take(${limit}) result = if (targetUnit != null) new Dynamic { target = new Dynamic { name = targetUnit.name faction = targetUnit.id?.split("/")[1] ?? "unknown" tier = targetUnit.tier ?? "unknown" health = targetUnit.hp ?? 0 armor = targetUnit.armor ?? 0 cost = targetUnit.hexiteCost ?? 0 } counters = calculateWinners(targetUnit) totalCountersFound = calculateWinners(targetUnit).length analysis = "Strong counters based on time-to-kill advantage" success = true } else new Dynamic { error = "Target unit not found: " + targetUnitName suggestions = allUnits.take(5).map((u) -> u.name) success = false } metadata = new Dynamic { analysis = "pkl-powered-counters" version = "anrubic-v1" targetUnit = targetUnitName } `; try { const result = await pklEvaluator.evaluateTemplate(template, {}, { timeout: 20000, format: "json", }); return { success: true, data: result, source: "pkl-counter-analysis", targetUnit, }; } catch (error) { throw new Error(`Counter analysis failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Execute Emperor sacrifice calculation using Pkl API * @param thralls - Number of Thralls to sacrifice * @param steelsworn - Number of Steelsworn to sacrifice * @param darkDisciples - Number of Dark Disciples to sacrifice * @returns Emperor calculation result */ export async function executeEmperorCalculation(thralls, steelsworn, darkDisciples) { if (!pklEvaluator || !pklAvailable) { throw new Error("Pkl not available - cannot perform Emperor calculation"); } const template = ` import "iolin/api/emperor-calculator.pkl" as EmperorCalc // Override the module's parameters by creating a new context result = (EmperorCalc) { thrallCount = ${thralls} steelswornCount = ${steelsworn} darkDiscipleCount = ${darkDisciples} }.result metadata = new { analysis = "pkl-powered-emperor-calculator" version = "anrubic-v1" sacrifices = new { thralls = ${thralls} steelsworn = ${steelsworn} darkDisciples = ${darkDisciples} } } `; try { const result = await pklEvaluator.evaluateTemplate(template, {}, { timeout: 15000, format: "json", }); return { success: true, data: result, source: "pkl-emperor-calculator", sacrifices: { thralls, steelsworn, darkDisciples }, }; } catch (error) { throw new Error(`Emperor calculation failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Get health status of Pkl integration */ export async function getPklHealth() { if (!pklEvaluator) { return { available: false, status: "not-initialized", message: "Pkl evaluator not initialized", }; } try { // Simple test evaluation to verify PKL is working const testTemplate = ` result = "pkl-health-ok" version = "test" `; const testResult = await pklEvaluator.evaluateTemplate(testTemplate, {}, { timeout: 5000 }); return { available: pklAvailable, status: "healthy", message: "Pkl evaluator is working correctly", testResult: testResult.result, }; } catch (error) { return { available: false, status: "error", error: error instanceof Error ? error.message : String(error), }; } } /** * Escape string for safe Pkl template insertion */ function escapeString(str) { return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); } //# sourceMappingURL=pkl-evaluator.js.map