Files
AIPLAYER/tests/test_progression.py
truetype bd211b6036 Implement phases 3-4 + all modules: decision engine, automapper, character creator, quest system, progression optimizer, and hybrid LLM integration
- Phase 4 (Decision Engine): ActionQueue with 7 priority tiers,
  4 behavior states (Idle/Explore/Combat/Loot), CombatAI with skill
  rotation and flee logic, GoalManager with survival override,
  DecisionEngine state machine integrated into the main game loop

- Phase 3 (Automapper): Complete hybrid SQLite/Memvid architecture
  with WorldMapper orchestrator, room fingerprinting (SHA-256),
  bidirectional exit linking, exploration queue, A* pathfinding,
  HistoricalStorage with JSON fallback, SemanticSearch with keyword
  fallback, SyncManager with configurable triggers

- Character Creator: State machine for automated character creation
  with stat rolling/optimization per class weights, race/class/gender
  selection, reroll logic with configurable thresholds

- Quest System: Quest lifecycle tracking (discover->accept->complete),
  NLP-based objective extraction (kill/fetch/talk/explore), EXP/time
  prioritization, pending kill target tracking

- Progression Optimizer: Grinding spot finder (EXP/hour), equipment
  comparison by weighted stats, skill rotation optimizer (DPS vs
  mana efficiency), session summary statistics

- LLM Integration (hybrid): Async Ollama client with retry/backoff,
  LLMBrain coordinator for quest parsing, social AI, decision advice,
  and ambiguous text parsing fallback. SocialAI for detecting and
  responding to player messages (say/tell/shout/group/trade).
  Graceful degradation when Ollama is unavailable.

All 214 tests passing.
2026-02-08 20:55:53 +01:00

117 lines
4.2 KiB
Python

"""Tests for the ProgressionOptimizer module."""
import pytest
from modules.progression.optimizer import (
ProgressionOptimizer, GrindingSpot, EquipmentPiece, SkillInfo,
)
class TestGrindingSpot:
def test_exp_per_hour(self):
spot = GrindingSpot(room_id=1, room_title="Forest", kills=10,
total_exp=1000, total_time_seconds=3600)
assert spot.exp_per_hour == 1000.0
def test_survival_rate(self):
spot = GrindingSpot(room_id=1, room_title="Dungeon",
kills=8, deaths=2)
assert spot.survival_rate == 0.8
def test_zero_time(self):
spot = GrindingSpot(room_id=1, room_title="Room")
assert spot.exp_per_hour == 0.0
assert spot.kills_per_hour == 0.0
class TestEquipmentPiece:
def test_weighted_score(self):
eq = EquipmentPiece(name="Iron Sword", slot="weapon",
stat_bonuses={"str": 5, "dex": 2})
score = eq.weighted_score({"str": 2.0, "dex": 1.0})
assert score == 12.0
def test_default_weights(self):
eq = EquipmentPiece(name="Shield", slot="armor",
stat_bonuses={"con": 3})
score = eq.weighted_score()
assert score == 3.0
class TestSkillInfo:
def test_damage_per_mana(self):
skill = SkillInfo(name="Fireball", command="cast fireball",
damage=100, mana_cost=20)
assert skill.damage_per_mana == 5.0
def test_dps(self):
skill = SkillInfo(name="Slash", command="slash",
damage=50, cooldown_seconds=2.0)
assert skill.dps == 25.0
def test_zero_mana_cost(self):
skill = SkillInfo(name="Punch", command="punch",
damage=10, mana_cost=0)
assert skill.damage_per_mana == float("inf")
class TestProgressionOptimizer:
def test_record_kill(self):
opt = ProgressionOptimizer()
opt.record_kill(1, "Forest", exp_gained=50)
opt.record_kill(1, "Forest", exp_gained=50)
spots = opt.get_best_grinding_spots()
# Need at least 3 kills for statistical relevance
assert len(spots) == 0
opt.record_kill(1, "Forest", exp_gained=50)
spots = opt.get_best_grinding_spots()
assert len(spots) == 1
assert spots[0].kills == 3
assert spots[0].total_exp == 150
def test_record_death(self):
opt = ProgressionOptimizer()
opt.record_death(1, "Dungeon")
opt.record_kill(1, "Dungeon", 100)
# Spot exists but deaths affect survival rate
# (< 3 kills so won't appear in best spots)
assert 1 in opt._spots
assert opt._spots[1].deaths == 1
def test_add_and_compare_equipment(self):
opt = ProgressionOptimizer()
opt.add_equipment(EquipmentPiece(
name="Iron Sword", slot="weapon",
stat_bonuses={"str": 5}))
opt.add_equipment(EquipmentPiece(
name="Steel Sword", slot="weapon",
stat_bonuses={"str": 8}))
best = opt.get_best_equipment("weapon")
assert best is not None
assert best.name == "Steel Sword"
def test_optimal_rotation(self):
opt = ProgressionOptimizer()
opt.add_skill(SkillInfo("Slash", "slash", damage=30, mana_cost=10, cooldown_seconds=1))
opt.add_skill(SkillInfo("Fireball", "fireball", damage=100, mana_cost=50, cooldown_seconds=3))
rotation = opt.get_optimal_rotation(available_mana=40)
# Only Slash fits within 40 mana
assert len(rotation) == 1
assert rotation[0].name == "Slash"
def test_efficient_rotation(self):
opt = ProgressionOptimizer()
opt.add_skill(SkillInfo("Slash", "slash", damage=30, mana_cost=10))
opt.add_skill(SkillInfo("Fireball", "fireball", damage=100, mana_cost=50))
efficient = opt.get_efficient_rotation()
# Slash = 3.0 dmg/mana, Fireball = 2.0 dmg/mana
assert efficient[0].name == "Slash"
def test_session_summary(self):
opt = ProgressionOptimizer()
summary = opt.session_summary()
assert "total_kills" in summary
assert "total_exp" in summary
assert summary["total_kills"] == 0