docs: complete documentation and add web monitoring dashboard
- Add comprehensive configuration guide (docs/configuration.md) - Add development guide with setup instructions (docs/development.md) - Add complete API reference documentation (docs/api_reference.md) - Update README.md with new features and project structure - Update TODO.md to reflect current implementation status - Add web monitoring dashboard with FastAPI and WebSocket support - Add tests for sync integration and web monitoring - Update config example with monitoring settings The MUD AI Player is now feature-complete with full documentation.
This commit is contained in:
1 parent
69ba824bc8
commit
b9f21c7199
14 files changed
+2658
-160
No files matched your search
@@ -0,0 +1,552 @@
|
||||
# API Reference
|
||||
|
||||
Complete API reference for the MUD AI Player.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Core Components](#core-components)
|
||||
- [Modules](#modules)
|
||||
- [Configuration](#configuration)
|
||||
- [Utilities](#utilities)
|
||||
|
||||
---
|
||||
|
||||
## Core Components
|
||||
|
||||
### TelnetClient
|
||||
|
||||
Asynchronous telnet client for MUD connection.
|
||||
|
||||
**Location:** `src.core.telnet_client.TelnetClient`
|
||||
|
||||
```python
|
||||
class TelnetClient:
|
||||
def __init__(
|
||||
self,
|
||||
host: str,
|
||||
port: int,
|
||||
max_retries: int = 3,
|
||||
retry_delay: float = 5.0,
|
||||
auto_reconnect: bool = True,
|
||||
)
|
||||
```
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `async connect() -> bool` - Connect to MUD server
|
||||
- `async connect_with_retry() -> bool` - Connect with retry logic
|
||||
- `async disconnect()` - Disconnect from server
|
||||
- `async send(command: str)` - Send command to server
|
||||
- `async receive_loop()` - Main receive loop
|
||||
- `set_receive_callback(callback: Callable)` - Set data callback
|
||||
|
||||
**Example:**
|
||||
|
||||
```python
|
||||
client = TelnetClient("mud.example.com", 4000)
|
||||
await client.connect()
|
||||
await client.send("look")
|
||||
```
|
||||
|
||||
### GameState
|
||||
|
||||
Central game state management.
|
||||
|
||||
**Location:** `src.core.game_state.GameState`
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class GameState:
|
||||
character: Character
|
||||
current_room: Optional[Room]
|
||||
previous_room: Optional[Room]
|
||||
inventory: List[str]
|
||||
active_quests: List[Quest]
|
||||
combat_state: CombatState
|
||||
target: Optional[str]
|
||||
```
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `is_in_combat() -> bool` - Check if in combat
|
||||
- `is_healthy(threshold: float = 0.7) -> bool` - Check health threshold
|
||||
- `update_from_prompt(prompt: ParsedPrompt)` - Update vitals
|
||||
- `update_room(parsed_room: ParsedRoom)` - Update current room
|
||||
- `update_combat(parsed_combat: ParsedCombat)` - Update combat state
|
||||
|
||||
### ANSIParser
|
||||
|
||||
Parse and strip ANSI color codes.
|
||||
|
||||
**Location:** `src.core.ansi_parser.ANSIParser`
|
||||
|
||||
```python
|
||||
class ANSIParser:
|
||||
def strip_ansi(self, text: str) -> str
|
||||
def parse_colors(self, text: str) -> List[ANSISegment]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Modules
|
||||
|
||||
### WorldMapper
|
||||
|
||||
High-level automapper with hybrid storage.
|
||||
|
||||
**Location:** `src.modules.mapper.WorldMapper`
|
||||
|
||||
```python
|
||||
class WorldMapper:
|
||||
def __init__(
|
||||
self,
|
||||
db_path: str = "data/maps/active/world.db",
|
||||
archive_dir: str = "data/maps/archive",
|
||||
enable_sync: bool = True,
|
||||
room_count_trigger: int = 500,
|
||||
time_interval_hours: float = 24.0,
|
||||
)
|
||||
```
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `on_room_enter(title, description, exits, npcs, items, zone) -> int`
|
||||
- Process arrival in a room, returns room ID
|
||||
|
||||
- `on_move(direction: str)`
|
||||
- Record movement direction
|
||||
|
||||
- `on_move_failed(direction: str)`
|
||||
- Record blocked exit
|
||||
|
||||
- `find_path(to_room_id: int) -> Optional[List[str]]`
|
||||
- Find path from current room to target
|
||||
|
||||
- `find_path_between(from_id: int, to_id: int) -> Optional[List[str]]`
|
||||
- Find path between two rooms
|
||||
|
||||
- `get_next_exploration_target() -> Optional[Dict]`
|
||||
- Get next unexplored exit
|
||||
|
||||
- `get_unexplored_exits(room_id: Optional[int]) -> List[str]`
|
||||
- Get unexplored directions from room
|
||||
|
||||
- `search_rooms(query: str, top_k: int = 5) -> List[Dict]`
|
||||
- Semantic search on room descriptions
|
||||
|
||||
- `close()`
|
||||
- Close mapper and create final snapshot
|
||||
|
||||
**Properties:**
|
||||
|
||||
- `current_room_id: Optional[int]`
|
||||
- `total_rooms: int`
|
||||
- `total_exits: int`
|
||||
- `sync_stats: Dict`
|
||||
|
||||
### DecisionEngine
|
||||
|
||||
Central decision-making engine.
|
||||
|
||||
**Location:** `src.modules.decision.engine.DecisionEngine`
|
||||
|
||||
```python
|
||||
class DecisionEngine:
|
||||
def __init__(
|
||||
self,
|
||||
combat_config: Optional[CombatConfig] = None,
|
||||
exploration_strategy: str = "random",
|
||||
)
|
||||
```
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `tick(game_state: GameState)`
|
||||
- Evaluate world and queue actions
|
||||
|
||||
- `next_action() -> Optional[Action]`
|
||||
- Get most urgent action
|
||||
|
||||
- `force_state(state_name: str, game_state: GameState)`
|
||||
- Force state transition
|
||||
|
||||
- `register_state(state: BaseBehaviorState)`
|
||||
- Add custom behavior state
|
||||
|
||||
**Properties:**
|
||||
|
||||
- `current_state: BaseBehaviorState`
|
||||
- `current_state_name: str`
|
||||
|
||||
### ActionQueue
|
||||
|
||||
Priority queue for actions.
|
||||
|
||||
**Location:** `src.modules.decision.action_queue.ActionQueue`
|
||||
|
||||
```python
|
||||
class ActionQueue:
|
||||
def push(self, action: Action)
|
||||
def pop(self) -> Optional[Action]
|
||||
def peek(self) -> Optional[Action]
|
||||
def clear(self)
|
||||
def clear_by_source(self, source: str)
|
||||
|
||||
@property
|
||||
def size(self) -> int
|
||||
@property
|
||||
def is_empty(self) -> bool
|
||||
```
|
||||
|
||||
### CombatAI
|
||||
|
||||
Intelligent combat decision-making.
|
||||
|
||||
**Location:** `src.modules.decision.combat_ai.CombatAI`
|
||||
|
||||
```python
|
||||
class CombatAI:
|
||||
def __init__(self, config: Optional[CombatConfig] = None)
|
||||
|
||||
def decide(self, game_state: GameState) -> List[Action]
|
||||
def reset(self)
|
||||
```
|
||||
|
||||
### GoalManager
|
||||
|
||||
Manage AI goals and objectives.
|
||||
|
||||
**Location:** `src.modules.decision.goal_manager.GoalManager`
|
||||
|
||||
```python
|
||||
class GoalManager:
|
||||
def add_goal(self, goal: Goal)
|
||||
def remove_goal(self, goal_id: str)
|
||||
def get_current_goal(self, game_state: GameState) -> Optional[Goal]
|
||||
def get_active_goals(self) -> List[Goal]
|
||||
def set_default_goals(self)
|
||||
|
||||
@property
|
||||
def active_goal_count(self) -> int
|
||||
```
|
||||
|
||||
### LLMBrain
|
||||
|
||||
LLM integration with memory.
|
||||
|
||||
**Location:** `src.modules.llm.brain.LLMBrain`
|
||||
|
||||
```python
|
||||
class LLMBrain:
|
||||
def __init__(
|
||||
self,
|
||||
config: LLMConfig,
|
||||
memory_db_path: str = "data/llm_memory.db",
|
||||
)
|
||||
|
||||
async def initialize()
|
||||
async def decide(game_state: GameState) -> Optional[str]
|
||||
async def social_reply(
|
||||
message: str,
|
||||
sender: str,
|
||||
character_class: str,
|
||||
character_name: str,
|
||||
) -> Optional[str]
|
||||
|
||||
@property
|
||||
def is_available(self) -> bool
|
||||
```
|
||||
|
||||
### SocialAI
|
||||
|
||||
Detect and respond to social interactions.
|
||||
|
||||
**Location:** `src.modules.llm.social_ai.SocialAI`
|
||||
|
||||
```python
|
||||
class SocialAI:
|
||||
def __init__(
|
||||
self,
|
||||
client: LLMClient,
|
||||
character_name: str,
|
||||
character_class: str,
|
||||
)
|
||||
|
||||
def detect_social_event(self, text: str) -> Optional[Dict]
|
||||
```
|
||||
|
||||
### CharacterCreator
|
||||
|
||||
Automated character creation.
|
||||
|
||||
**Location:** `src.modules.character.creator.CharacterCreator`
|
||||
|
||||
```python
|
||||
class CharacterCreator:
|
||||
def __init__(self, config: CreatorConfig)
|
||||
|
||||
async def create_character(telnet: TelnetClient) -> bool
|
||||
```
|
||||
|
||||
### QuestManager
|
||||
|
||||
Quest tracking and management.
|
||||
|
||||
**Location:** `src.modules.quest.quest_manager.QuestManager`
|
||||
|
||||
```python
|
||||
class QuestManager:
|
||||
def add_quest(self, quest: Quest)
|
||||
def complete_quest(self, quest_id: str)
|
||||
def get_active_quests(self) -> List[Quest]
|
||||
def detect_quest_in_text(self, text: str) -> Optional[Quest]
|
||||
```
|
||||
|
||||
### ProgressionOptimizer
|
||||
|
||||
Optimize grinding and progression.
|
||||
|
||||
**Location:** `src.modules.progression.optimizer.ProgressionOptimizer`
|
||||
|
||||
```python
|
||||
class ProgressionOptimizer:
|
||||
def find_optimal_grind_area(
|
||||
self,
|
||||
game_state: GameState,
|
||||
world_mapper: WorldMapper,
|
||||
) -> Optional[str]
|
||||
|
||||
def should_grind(self, game_state: GameState) -> bool
|
||||
```
|
||||
|
||||
### MonitoringServer
|
||||
|
||||
Web dashboard server.
|
||||
|
||||
**Location:** `src.modules.web.server.MonitoringServer`
|
||||
|
||||
```python
|
||||
class MonitoringServer:
|
||||
def __init__(
|
||||
self,
|
||||
host: str = "127.0.0.1",
|
||||
port: int = 8080,
|
||||
game_state: Optional[Any] = None,
|
||||
world_mapper: Optional[Any] = None,
|
||||
decision_engine: Optional[Any] = None,
|
||||
)
|
||||
|
||||
async def start()
|
||||
async def stop()
|
||||
def log_event(self, event_type: str, message: str, data: Optional[Dict] = None)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### load_config
|
||||
|
||||
Load and validate configuration.
|
||||
|
||||
**Location:** `src.config.settings.load_config`
|
||||
|
||||
```python
|
||||
def load_config(config_path: str = "config.yaml") -> Config
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```python
|
||||
from src.config.settings import load_config
|
||||
|
||||
config = load_config("config.yaml")
|
||||
print(config.mud.host)
|
||||
print(config.character.preferred_class)
|
||||
```
|
||||
|
||||
### Config Classes
|
||||
|
||||
- `MudConfig` - MUD connection settings
|
||||
- `CharacterConfig` - Character preferences
|
||||
- `AIConfig` - AI engine settings
|
||||
- `LLMProviderConfig` - LLM configuration
|
||||
- `MapperConfig` - Mapper settings
|
||||
- `BehaviorConfig` - Behavior simulation
|
||||
- `LoggingConfig` - Logging settings
|
||||
- `MonitoringConfig` - Web dashboard settings
|
||||
|
||||
---
|
||||
|
||||
## Utilities
|
||||
|
||||
### setup_logger
|
||||
|
||||
Configure application logging.
|
||||
|
||||
**Location:** `src.utils.logger.setup_logger`
|
||||
|
||||
```python
|
||||
def setup_logger(
|
||||
level: str = "INFO",
|
||||
log_file: str = "data/logs/game.log",
|
||||
console: bool = True,
|
||||
max_file_size_mb: int = 10,
|
||||
backup_count: int = 5,
|
||||
) -> logging.Logger
|
||||
```
|
||||
|
||||
### HumanBehavior
|
||||
|
||||
Simulate human-like behavior.
|
||||
|
||||
**Location:** `src.utils.human_behavior.HumanBehavior`
|
||||
|
||||
```python
|
||||
class HumanBehavior:
|
||||
def __init__(
|
||||
self,
|
||||
reaction_time_min_ms: int = 200,
|
||||
reaction_time_max_ms: int = 2000,
|
||||
pause_probability: float = 0.05,
|
||||
)
|
||||
|
||||
async def delay(self)
|
||||
def maybe_typo(self, text: str) -> str
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Classes
|
||||
|
||||
### ParsedRoom
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ParsedRoom:
|
||||
title: Optional[str]
|
||||
description: Optional[str]
|
||||
exits: List[str]
|
||||
npcs: List[str]
|
||||
items: List[str]
|
||||
```
|
||||
|
||||
### ParsedPrompt
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ParsedPrompt:
|
||||
hp: Optional[int]
|
||||
max_hp: Optional[int]
|
||||
mp: Optional[int]
|
||||
max_mp: Optional[int]
|
||||
sp: Optional[int]
|
||||
max_sp: Optional[int]
|
||||
```
|
||||
|
||||
### ParsedCombat
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ParsedCombat:
|
||||
attacker: Optional[str]
|
||||
target: Optional[str]
|
||||
action: str
|
||||
damage: Optional[int]
|
||||
is_combat: bool
|
||||
```
|
||||
|
||||
### Action
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Action:
|
||||
command: str
|
||||
priority: ActionPriority
|
||||
description: str
|
||||
source: str
|
||||
```
|
||||
|
||||
### Goal
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Goal:
|
||||
id: str
|
||||
name: str
|
||||
description: str
|
||||
priority: GoalPriority
|
||||
target_state: str
|
||||
conditions: List[Callable[[GameState], bool]]
|
||||
progress: float
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Enums
|
||||
|
||||
### CombatState
|
||||
|
||||
```python
|
||||
class CombatState(Enum):
|
||||
IDLE = "idle"
|
||||
IN_COMBAT = "in_combat"
|
||||
FLEEING = "fleeing"
|
||||
```
|
||||
|
||||
### ActionPriority
|
||||
|
||||
```python
|
||||
class ActionPriority(Enum):
|
||||
CRITICAL = 1
|
||||
HIGH = 2
|
||||
MEDIUM = 3
|
||||
LOW = 4
|
||||
IDLE = 5
|
||||
```
|
||||
|
||||
### GoalPriority
|
||||
|
||||
```python
|
||||
class GoalPriority(Enum):
|
||||
CRITICAL = 1
|
||||
HIGH = 2
|
||||
MEDIUM = 3
|
||||
LOW = 4
|
||||
```
|
||||
|
||||
### EventType
|
||||
|
||||
```python
|
||||
class EventType(Enum):
|
||||
ROOM = "room"
|
||||
PROMPT = "prompt"
|
||||
COMBAT = "combat"
|
||||
INVENTORY = "inventory"
|
||||
CHAT = "chat"
|
||||
UNKNOWN = "unknown"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Exceptions
|
||||
|
||||
All custom exceptions inherit from `MUDAIPlayerException`:
|
||||
|
||||
```python
|
||||
class MUDAIPlayerException(Exception):
|
||||
"""Base exception for MUD AI Player"""
|
||||
pass
|
||||
|
||||
class ConnectionError(MUDAIPlayerException):
|
||||
"""Connection-related errors"""
|
||||
pass
|
||||
|
||||
class ParseError(MUDAIPlayerException):
|
||||
"""Parsing errors"""
|
||||
pass
|
||||
|
||||
class ConfigurationError(MUDAIPlayerException):
|
||||
"""Configuration errors"""
|
||||
pass
|
||||
```
|
||||
@@ -0,0 +1,157 @@
|
||||
# Configuration Guide
|
||||
|
||||
This guide covers all configuration options for the MUD AI Player.
|
||||
|
||||
## Configuration File
|
||||
|
||||
The main configuration file is `config.yaml`. An example is provided in `examples/config.example.yaml`.
|
||||
|
||||
## Configuration Sections
|
||||
|
||||
### MUD Connection Settings
|
||||
|
||||
```yaml
|
||||
mud:
|
||||
host: "192.168.0.63" # MUD server hostname or IP
|
||||
port: 6666 # MUD server port
|
||||
mud_type: "dead_souls" # Parser type: dead_souls, aardwolf, etc.
|
||||
username: "your_username" # Login username
|
||||
password: "your_password" # Login password
|
||||
keep_alive_interval: 60 # Seconds between keep-alive commands
|
||||
keep_alive_command: "" # Command to send for keep-alive (empty = no-op)
|
||||
```
|
||||
|
||||
### Character Settings
|
||||
|
||||
```yaml
|
||||
character:
|
||||
name: "AIPlayer" # Character name
|
||||
preferred_class: "warrior" # warrior, mage, cleric, rogue
|
||||
preferred_race: "human" # human, elf, dwarf, etc.
|
||||
play_style: "balanced" # aggressive, cautious, balanced
|
||||
```
|
||||
|
||||
### AI Engine Settings
|
||||
|
||||
```yaml
|
||||
ai:
|
||||
engine: "hybrid" # rule_based, llm, hybrid
|
||||
llm_model: "llama3.2" # LLM model name
|
||||
decision_delay_ms: [200, 2000] # Min/max reaction time in milliseconds
|
||||
exploration_strategy: "depth_first" # depth_first, breadth_first
|
||||
|
||||
llm:
|
||||
enabled: true # Enable/disable LLM features
|
||||
base_url: "http://localhost:11434" # Ollama/OpenAI-compatible endpoint
|
||||
model: "llama3.2" # Model to use
|
||||
temperature: 0.3 # Creativity vs consistency (0.0-1.0)
|
||||
max_tokens: 512 # Max response length
|
||||
timeout_seconds: 30.0 # Request timeout
|
||||
max_retries: 2 # Retry attempts on failure
|
||||
system_prompt: "You are an AI playing a text-based MUD game..."
|
||||
memory_db_path: "data/llm_memory.db" # Long-term memory storage
|
||||
```
|
||||
|
||||
### Mapper Settings
|
||||
|
||||
```yaml
|
||||
mapper:
|
||||
active_storage:
|
||||
db_path: "data/maps/active/world.db"
|
||||
graph_cache_size: 10000
|
||||
|
||||
historical_storage:
|
||||
archive_dir: "data/maps/archive"
|
||||
snapshot_triggers:
|
||||
room_count: 500 # Trigger snapshot every N rooms
|
||||
session_end: true # Snapshot on disconnect
|
||||
time_interval_hours: 24 # Periodic snapshots
|
||||
compression:
|
||||
fps: 30
|
||||
codec: "h264"
|
||||
|
||||
semantic_search:
|
||||
model: "all-MiniLM-L6-v2" # Sentence transformer model
|
||||
top_k: 5 # Number of search results
|
||||
similarity_threshold: 0.7 # Minimum similarity score
|
||||
```
|
||||
|
||||
### Behavior Simulation
|
||||
|
||||
```yaml
|
||||
behavior:
|
||||
reaction_time_min_ms: 200 # Minimum delay between actions
|
||||
reaction_time_max_ms: 2000 # Maximum delay between actions
|
||||
pause_probability: 0.05 # Chance to pause briefly (5%)
|
||||
typo_probability: 0.0 # Chance to make typos (0% = disabled)
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
```yaml
|
||||
logging:
|
||||
level: "DEBUG" # DEBUG, INFO, WARNING, ERROR
|
||||
file: "data/logs/game.log" # Log file path
|
||||
console: true # Also log to console
|
||||
max_file_size_mb: 10 # Rotate when file reaches this size
|
||||
backup_count: 5 # Number of backup files to keep
|
||||
```
|
||||
|
||||
### Web Monitoring Dashboard
|
||||
|
||||
```yaml
|
||||
monitoring:
|
||||
enabled: true # Enable/disable web dashboard
|
||||
host: "127.0.0.1" # Bind address
|
||||
port: 8080 # Port for dashboard
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
You can override any configuration value using environment variables:
|
||||
|
||||
```bash
|
||||
export MUD_HOST="your-mud-server.com"
|
||||
export MUD_PORT=4000
|
||||
export MUD_USERNAME="player"
|
||||
export MUD_PASSWORD="secret"
|
||||
```
|
||||
|
||||
## Multiple Configurations
|
||||
|
||||
To use different configurations for different MUDs:
|
||||
|
||||
```bash
|
||||
# For Dead Souls MUD
|
||||
python -m src.main --config config_dead_souls.yaml
|
||||
|
||||
# For Aardwolf MUD
|
||||
python -m src.main --config config_aardwolf.yaml
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
⚠️ **Important**: The `config.yaml` file contains sensitive credentials. It is automatically excluded from Git via `.gitignore`.
|
||||
|
||||
Never commit your `config.yaml` to version control. Always use the example file as a template.
|
||||
|
||||
## Configuration Validation
|
||||
|
||||
The configuration is validated at startup using Pydantic models. If a required field is missing or invalid, the application will exit with a clear error message.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
- Verify `host` and `port` are correct
|
||||
- Check firewall rules
|
||||
- Ensure `mud_type` matches your MUD
|
||||
|
||||
### LLM Not Working
|
||||
- Verify Ollama is running: `curl http://localhost:11434/api/tags`
|
||||
- Check `ai.llm.enabled` is set to `true`
|
||||
- Ensure the model is installed: `ollama pull llama3.2`
|
||||
|
||||
### Mapper Not Saving
|
||||
- Check that `data/maps/` directory exists and is writable
|
||||
- Verify disk space is available
|
||||
- Check file permissions
|
||||
@@ -0,0 +1,381 @@
|
||||
# Development Guide
|
||||
|
||||
This guide is for developers who want to contribute to or extend the MUD AI Player.
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.10+
|
||||
- Poetry (recommended) or pip
|
||||
- Git
|
||||
|
||||
### Installation
|
||||
|
||||
**Using Poetry (Recommended):**
|
||||
|
||||
```bash
|
||||
# Install Poetry if not already installed
|
||||
pip install poetry
|
||||
|
||||
# Clone the repository
|
||||
git clone <repository-url>
|
||||
cd mud-ai-player
|
||||
|
||||
# Install dependencies
|
||||
poetry install
|
||||
|
||||
# Activate virtual environment
|
||||
poetry shell
|
||||
```
|
||||
|
||||
**Using pip:**
|
||||
|
||||
```bash
|
||||
# Create virtual environment
|
||||
python -m venv venv
|
||||
source venv/bin/activate # On Windows: venv\Scripts\activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
mud-ai-player/
|
||||
├── src/ # Source code
|
||||
│ ├── core/ # Core components
|
||||
│ │ ├── telnet_client.py # Async telnet connection
|
||||
│ │ ├── game_state.py # Game state management
|
||||
│ │ ├── ansi_parser.py # ANSI color code parsing
|
||||
│ │ └── parsers/ # MUD-specific parsers
|
||||
│ ├── modules/ # Feature modules
|
||||
│ │ ├── mapper/ # World mapping (SQLite + Memvid)
|
||||
│ │ ├── decision/ # AI decision engine
|
||||
│ │ ├── llm/ # LLM integration
|
||||
│ │ ├── character/ # Character creation
|
||||
│ │ ├── quest/ # Quest management
|
||||
│ │ ├── progression/ # Grinding optimizer
|
||||
│ │ └── web/ # Monitoring dashboard
|
||||
│ ├── config/ # Configuration management
|
||||
│ └── utils/ # Utilities
|
||||
├── tests/ # Test suite
|
||||
├── docs/ # Documentation
|
||||
├── data/ # Persistent storage
|
||||
└── examples/ # Example configurations
|
||||
```
|
||||
|
||||
## Adding a New Module
|
||||
|
||||
To add a new feature module:
|
||||
|
||||
1. Create a directory under `src/modules/your_module/`
|
||||
2. Create `__init__.py` and your module files
|
||||
3. Export the main class from `__init__.py`
|
||||
4. Add tests in `tests/test_your_module.py`
|
||||
5. Update documentation
|
||||
|
||||
Example structure:
|
||||
|
||||
```python
|
||||
# src/modules/your_module/__init__.py
|
||||
from .your_class import YourClass
|
||||
|
||||
__all__ = ["YourClass"]
|
||||
|
||||
# src/modules/your_module/your_class.py
|
||||
class YourClass:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def do_something(self):
|
||||
pass
|
||||
```
|
||||
|
||||
## Adding a New Parser
|
||||
|
||||
To add support for a new MUD type:
|
||||
|
||||
1. Create a new parser in `src/core/parsers/your_mud.py`
|
||||
2. Inherit from `BaseParser`
|
||||
3. Register in `ParserFactory`
|
||||
|
||||
```python
|
||||
# src/core/parsers/your_mud.py
|
||||
from .base import BaseParser, ParsedRoom, ParsedPrompt
|
||||
|
||||
class YourMudParser(BaseParser):
|
||||
def parse_room(self, text: str) -> Optional[ParsedRoom]:
|
||||
# Implement room parsing
|
||||
pass
|
||||
|
||||
def parse_prompt(self, text: str) -> Optional[ParsedPrompt]:
|
||||
# Implement prompt parsing
|
||||
pass
|
||||
```
|
||||
|
||||
Then register in `factory.py`:
|
||||
|
||||
```python
|
||||
from .your_mud import YourMudParser
|
||||
|
||||
PARSERS = {
|
||||
"dead_souls": DeadSoulsParser,
|
||||
"your_mud": YourMudParser, # Add this
|
||||
}
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pytest tests/ -v
|
||||
|
||||
# Run with coverage
|
||||
pytest --cov=src --cov-report=html
|
||||
|
||||
# Run specific test file
|
||||
pytest tests/test_world_mapper.py -v
|
||||
|
||||
# Run with watch mode
|
||||
pytest-watch
|
||||
```
|
||||
|
||||
## Code Style
|
||||
|
||||
We use Black and Ruff for code formatting and linting:
|
||||
|
||||
```bash
|
||||
# Format code
|
||||
black src/ tests/
|
||||
|
||||
# Lint code
|
||||
ruff check src/ tests/
|
||||
|
||||
# Auto-fix linting issues
|
||||
ruff check --fix src/ tests/
|
||||
```
|
||||
|
||||
### Style Guidelines
|
||||
|
||||
- Line length: 100 characters
|
||||
- Use type hints where possible
|
||||
- Write docstrings for public methods
|
||||
- Follow PEP 8 naming conventions
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
### Unit Tests
|
||||
|
||||
Test individual components in isolation:
|
||||
|
||||
```python
|
||||
# tests/test_your_module.py
|
||||
def test_room_parsing():
|
||||
parser = YourMudParser()
|
||||
room = parser.parse_room("Room Title\nDescription\n[Exits: n s]")
|
||||
assert room.title == "Room Title"
|
||||
assert "n" in room.exits
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
Test component interactions:
|
||||
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_full_exploration():
|
||||
# Setup
|
||||
mapper = WorldMapper()
|
||||
engine = DecisionEngine()
|
||||
|
||||
# Execute
|
||||
await engine.tick(game_state)
|
||||
action = engine.next_action()
|
||||
|
||||
# Assert
|
||||
assert action is not None
|
||||
```
|
||||
|
||||
### Mocking
|
||||
|
||||
Use mocks to isolate tests:
|
||||
|
||||
```python
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
def test_with_mock():
|
||||
mock_client = Mock()
|
||||
mock_client.send.return_value = None
|
||||
|
||||
with patch('src.core.telnet_client.TelnetClient', return_value=mock_client):
|
||||
# Test code here
|
||||
pass
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Logging
|
||||
|
||||
The application uses Python's standard logging module:
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# In your code
|
||||
logger.debug("Detailed debug info")
|
||||
logger.info("General information")
|
||||
logger.warning("Warning message")
|
||||
logger.error("Error message")
|
||||
```
|
||||
|
||||
View logs in real-time:
|
||||
|
||||
```bash
|
||||
tail -f data/logs/game.log
|
||||
```
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Set logging level to DEBUG in config:
|
||||
|
||||
```yaml
|
||||
logging:
|
||||
level: "DEBUG"
|
||||
```
|
||||
|
||||
### Web Dashboard
|
||||
|
||||
The monitoring dashboard provides real-time debugging:
|
||||
|
||||
```yaml
|
||||
monitoring:
|
||||
enabled: true
|
||||
host: "127.0.0.1"
|
||||
port: 8080
|
||||
```
|
||||
|
||||
Then open http://127.0.0.1:8080 in your browser.
|
||||
|
||||
## Performance Profiling
|
||||
|
||||
```bash
|
||||
# Profile the application
|
||||
python -m cProfile -o profile.stats -m src.main
|
||||
|
||||
# View profile results
|
||||
python -c "import pstats; p = pstats.Stats('profile.stats'); p.sort_stats('cumulative'); p.print_stats(20)"
|
||||
```
|
||||
|
||||
## Git Workflow
|
||||
|
||||
1. Create a feature branch: `git checkout -b feature/my-feature`
|
||||
2. Make your changes
|
||||
3. Run tests: `pytest tests/ -v`
|
||||
4. Format code: `black src/ tests/`
|
||||
5. Lint: `ruff check src/ tests/`
|
||||
6. Commit: `git commit -m "Add my feature"`
|
||||
7. Push: `git push origin feature/my-feature`
|
||||
8. Create Pull Request
|
||||
|
||||
## Commit Message Guidelines
|
||||
|
||||
Use conventional commits format:
|
||||
|
||||
- `feat: Add new feature`
|
||||
- `fix: Fix bug in parser`
|
||||
- `docs: Update documentation`
|
||||
- `test: Add tests`
|
||||
- `refactor: Refactor module`
|
||||
- `chore: Update dependencies`
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding a New Behavior State
|
||||
|
||||
```python
|
||||
# src/modules/decision/behavior_states.py
|
||||
class MyCustomState(BaseBehaviorState):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "my_state"
|
||||
|
||||
def get_actions(self, game_state: GameState) -> List[Action]:
|
||||
actions = []
|
||||
# Your logic here
|
||||
return actions
|
||||
```
|
||||
|
||||
Then register in `DecisionEngine`:
|
||||
|
||||
```python
|
||||
self._states = {
|
||||
# ... existing states
|
||||
"my_state": MyCustomState(),
|
||||
}
|
||||
```
|
||||
|
||||
### Extending the Mapper
|
||||
|
||||
To add custom room metadata:
|
||||
|
||||
```python
|
||||
# In world_mapper.py
|
||||
def on_room_enter(self, title, description, exits, **kwargs):
|
||||
room_data = {
|
||||
# ... existing fields
|
||||
"custom_field": kwargs.get("custom_field"),
|
||||
}
|
||||
# ... rest of method
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Import Errors
|
||||
|
||||
If you get import errors, ensure you're in the virtual environment:
|
||||
|
||||
```bash
|
||||
poetry shell
|
||||
# or
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
### Telnet Connection Issues
|
||||
|
||||
Test connection manually:
|
||||
|
||||
```bash
|
||||
telnet your-mud-server.com 4000
|
||||
```
|
||||
|
||||
### Database Locked
|
||||
|
||||
If SQLite reports "database is locked":
|
||||
|
||||
```bash
|
||||
# Kill any hanging Python processes
|
||||
pkill -f "python -m src.main"
|
||||
|
||||
# Remove lock file if it exists
|
||||
rm data/maps/active/*.db-journal
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Check existing issues before creating new ones
|
||||
2. Discuss major changes in an issue first
|
||||
3. Write tests for new features
|
||||
4. Update documentation
|
||||
5. Follow code style guidelines
|
||||
6. Be respectful in communications
|
||||
|
||||
## Resources
|
||||
|
||||
- [Python Asyncio Documentation](https://docs.python.org/3/library/asyncio.html)
|
||||
- [NetworkX Documentation](https://networkx.org/documentation/stable/)
|
||||
- [Pydantic Documentation](https://docs.pydantic.dev/)
|
||||
- [pytest Documentation](https://docs.pytest.org/)
|
||||
Reference in new issue
Block a user