/** * Adaptive Tactics - Adaptation Tests * Tests for memory ledger, trigger evaluation, package selection */ import { evaluateMajorPackages, evaluateMinorModifiers, applyPackageEffects, applyModifierEffects, updateMemoryAfterCycle, getAdaptationHint } from '../js/adaptation.js'; // Test utilities function assert(condition, message) { if (!condition) { throw new Error(`FAILED: ${message}`); } console.log(`PASSED: ${message}`); } function createFreshMemory() { return { primary_lane_history: [], eilisia_kill_leads: 0, mirella_high_heal_cycles: 0, fast_clear_count: 0, fracture_line_cycles: 0, bait_pull_cycles: 0, throne_rush_count: 0, last_carry_unit: null, turns_to_clear_history: [] }; } const mockPackages = { western_denial: { id: 'western_denial', narrativeHint: 'The western path grows treacherous.', effect: { spawnBias: ['W1', 'W2'], preferredUnits: ['hollow_archer', 'fallen_soldier_spear'] } }, choke_hold: { id: 'choke_hold', narrativeHint: 'The center will not break so easily.', effect: { spawnBias: ['C1'], preferredUnits: ['fallen_soldier_spear', 'enslaved'] } }, mirellas_price: { id: 'mirellas_price', narrativeHint: 'His eyes turn toward your healer.', effect: { targetPriorityIncrease: 'mirella' } }, fast_pressure: { id: 'fast_pressure', narrativeHint: 'He will not wait for you this time.', effect: { engagementRadiusBonus: 1, forwardFlexPlacement: true } } }; const mockModifiers = { spear_wall: { id: 'spear_wall', availableFromCycle: 5, narrativeHint: 'The crossings bristle with spears.', effect: { spawnBias: 'crossings', preferredUnits: ['fallen_soldier_spear'] } }, cruel_recall: { id: 'cruel_recall', availableFromCycle: 5, narrativeHint: 'He remembers who struck hardest.', effect: { carryTargetBiasMultiplier: 1.5 } } }; // Test: No Package in Early Cycles function testNoPackageEarlyCycles() { const memory = createFreshMemory(); const package1 = evaluateMajorPackages(memory, mockPackages, 1); assert(package1 === null, 'Cycle 1 should have no package'); const package2 = evaluateMajorPackages(memory, mockPackages, 2); assert(package2 === null, 'Cycle 2 should have no package'); } // Test: Western Denial Trigger function testWesternDenialTrigger() { const memory = createFreshMemory(); memory.primary_lane_history = ['west', 'west']; const pkg = evaluateMajorPackages(memory, mockPackages, 3); assert(pkg === 'western_denial', `Should trigger western_denial, got ${pkg}`); } // Test: Choke Hold Trigger function testChokeHoldTrigger() { const memory = createFreshMemory(); memory.primary_lane_history = ['center', 'center']; const pkg = evaluateMajorPackages(memory, mockPackages, 3); assert(pkg === 'choke_hold', `Should trigger choke_hold, got ${pkg}`); } // Test: Mirella's Price Trigger function testMirellasPriceTrigger() { const memory = createFreshMemory(); memory.mirella_high_heal_cycles = 1; const pkg = evaluateMajorPackages(memory, mockPackages, 3); assert(pkg === 'mirellas_price', `Should trigger mirellas_price, got ${pkg}`); } // Test: Fast Pressure Trigger function testFastPressureTrigger() { const memory = createFreshMemory(); memory.fast_clear_count = 2; const pkg = evaluateMajorPackages(memory, mockPackages, 3); assert(pkg === 'fast_pressure', `Should trigger fast_pressure, got ${pkg}`); } // Test: No Modifier Before Cycle 5 function testNoModifierBeforeCycle5() { const memory = createFreshMemory(); memory.last_carry_unit = 'eilisia'; const mod3 = evaluateMinorModifiers(memory, mockModifiers, 3); assert(mod3 === null, 'Cycle 3 should have no modifier'); const mod4 = evaluateMinorModifiers(memory, mockModifiers, 4); assert(mod4 === null, 'Cycle 4 should have no modifier'); } // Test: Modifier Available at Cycle 5 function testModifierAtCycle5() { const memory = createFreshMemory(); memory.last_carry_unit = 'eilisia'; const mod = evaluateMinorModifiers(memory, mockModifiers, 5); assert(mod !== null, `Cycle 5 should have a modifier, got ${mod}`); } // Test: Package Effects Application function testPackageEffectsApplication() { const spawnConfig = {}; const aiConfig = {}; applyPackageEffects('western_denial', mockPackages, spawnConfig, aiConfig); assert(spawnConfig.biasNodes !== undefined, 'Should set spawn bias nodes'); assert(spawnConfig.biasNodes.includes('W1'), 'Should include W1 in bias'); assert(spawnConfig.preferredUnits !== undefined, 'Should set preferred units'); } // Test: Target Priority Effect function testTargetPriorityEffect() { const spawnConfig = {}; const aiConfig = {}; applyPackageEffects('mirellas_price', mockPackages, spawnConfig, aiConfig); assert(aiConfig.targetPriority !== undefined, 'Should set target priority'); assert(aiConfig.targetPriority.mirella > 1, 'Mirella should have elevated priority'); } // Test: Modifier Effects Application function testModifierEffectsApplication() { const spawnConfig = {}; const aiConfig = {}; applyModifierEffects('cruel_recall', mockModifiers, spawnConfig, aiConfig); assert(aiConfig.carryBias === 1.5, `Should set carry bias to 1.5, got ${aiConfig.carryBias}`); } // Test: Memory Update After Cycle function testMemoryUpdateAfterCycle() { const memory = createFreshMemory(); const cycleMetrics = { playerLaneMovement: { west: 5, center: 2, east: 1 }, eilisiaKills: 3, mirellaHealCount: 4, turnsElapsed: 7 }; updateMemoryAfterCycle(memory, cycleMetrics); assert(memory.primary_lane_history[0] === 'west', 'Should record west as primary lane'); assert(memory.eilisia_kill_leads === 1, 'Should increment Eilisia kill leads'); assert(memory.mirella_high_heal_cycles === 1, 'Should increment Mirella heal cycles'); assert(memory.fast_clear_count === 1, 'Should increment fast clear (7 <= 8)'); assert(memory.turns_to_clear_history[0] === 7, 'Should record turn count'); } // Test: Adaptation Hint Generation function testAdaptationHintGeneration() { const hint = getAdaptationHint('western_denial', null, mockPackages, mockModifiers); assert(hint.includes('western'), `Hint should mention western, got: ${hint}`); const combinedHint = getAdaptationHint('fast_pressure', 'cruel_recall', mockPackages, mockModifiers); assert(combinedHint.includes('wait') || combinedHint.includes('remembers'), `Combined hint should have both package and modifier content, got: ${combinedHint}`); } // Test: Priority of First Triggered Package function testPackagePriority() { const memory = createFreshMemory(); // Trigger multiple conditions memory.primary_lane_history = ['west', 'west']; memory.mirella_high_heal_cycles = 1; memory.fast_clear_count = 2; const pkg = evaluateMajorPackages(memory, mockPackages, 3); // Should return first triggered (western_denial comes first in code) assert(pkg === 'western_denial', `First triggered package should win, got ${pkg}`); } // Run all tests function runAllTests() { console.log('=== Running Adaptation Tests ===\n'); try { testNoPackageEarlyCycles(); testWesternDenialTrigger(); testChokeHoldTrigger(); testMirellasPriceTrigger(); testFastPressureTrigger(); testNoModifierBeforeCycle5(); testModifierAtCycle5(); testPackageEffectsApplication(); testTargetPriorityEffect(); testModifierEffectsApplication(); testMemoryUpdateAfterCycle(); testAdaptationHintGeneration(); testPackagePriority(); console.log('\n=== All Adaptation Tests Passed ==='); } catch (e) { console.error('\n=== Test Failed ==='); console.error(e.message); throw e; } } // Export for use in browser or Node if (typeof window !== 'undefined') { window.runAdaptationTests = runAllTests; } export { runAllTests };