# 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 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/)