# Snake CLI Game - Development Guidelines ## Overview This project demonstrates clean Python architecture with separation of concerns, comprehensive type hints, robust error handling, and user-friendly design. These guidelines capture the principles and patterns established during the refactor. ## Architecture & Design ### Modular Structure Separate concerns into dedicated modules: - **Game Logic** (`snake.py`) - Core game loop and UI interaction - **Constants** (`constants.py`) - Game configuration and magic numbers - **Themes** (`theme.py`) - Color and UI styling - **Sound** (`sound_manager.py`) - Audio effects and music (runs in separate process) - **Persistence** (`high_score.py`, `game_config.py`) - Data storage and retrieval **Pattern**: Each module has a single responsibility. Avoid mixing config, logic, and I/O. **Example**: Don't hardcode speed values in the game loop—define them in `constants.py` and import them. ### Configuration Management Use JSON files in the home directory for user preferences and data: - `~/.snake_config.json` - User settings (speed, length, theme, effects) - `~/.snake_scores.json` - High scores and game history **Pattern**: Create dedicated manager classes (`GameConfig`, `HighScoreManager`) to handle persistence. **Benefits**: Settings persist across sessions, users can manually edit configs, no directory pollution. ## Code Style & Quality ### Type Hints Use comprehensive type hints on all functions: ```python def ask_question( self, question: str, value_type: type, value_default, value_min: int = 0, value_max: int = 0 ): """Ask user a question and validate the response.""" ``` **When**: Every function parameter and return type. **Why**: Enables better IDE support, catches bugs early, improves readability. ### Docstrings Add docstrings to all modules, classes, and methods: ```python def _handle_input(self, current_key: int, next_key: int) -> Optional[int]: """Handle player input and return new direction, or None if quitting.""" ``` **Pattern**: One-liner for simple functions; paragraph for complex logic explaining intent and side effects. ### Error Handling & Graceful Degradation When optional features fail (missing files, audio init), gracefully continue: ```python def _load_effects(self) -> None: """Load all sound effects.""" for effect_name, filename in effect_files.items(): effect_path = self.effects_dir / filename try: if effect_path.exists(): self.sounds[effect_name] = pygame.mixer.Sound(str(effect_path)) except pygame.error as e: print(f"Warning: Could not load effect {filename}: {e}") ``` **Pattern**: Try to load optional features; warn but don't crash if they're missing. ### File Paths Always use absolute paths with `pathlib.Path`: ```python from pathlib import Path self.effects_dir = Path(__file__).parent / 'effects' self.music_dir = Path(__file__).parent / 'music' ``` **Why**: Works regardless of where the script is invoked from. Relative paths break when run from different directories. ### Input Validation Use loops instead of recursion to avoid stack overflow: ```python while True: # ... get input ... if value_type == int: try: answer = int(answer) if value_min <= answer <= value_max: return answer except ValueError: pass # Show error and retry (not recursive call) self.print_center(f"ERROR: Must be {value_min}-{value_max}", 1, color) time.sleep(2) ``` **Why**: Recursive validation can stack overflow on repeated bad input. ### Code Cleanliness - Remove unnecessary semicolons (Python style) - Remove commented-out code—if needed, use version control - Use f-strings instead of `.format()` or `%` formatting - Avoid `Any` type hints—be specific: `List[int]`, `Optional[str]`, etc. ## CLI Arguments & Configuration When adding user-facing options, use `argparse`: ```python def parse_args() -> argparse.Namespace: """Parse command-line arguments.""" parser = argparse.ArgumentParser(description='Snake CLI Game') parser.add_argument( '--music', action='store_true', help='Enable background music' ) return parser.parse_args() ``` **Pattern**: Arguments override saved preferences; save user choices back to config. ## Process Management When spawning background processes (audio, workers): 1. Make processes **daemons** (`p.daemon = True`) so they don't block shutdown 2. Always **clean up** on exit: ```python queue.put('quit') p.join(timeout=2) if p.is_alive(): p.terminate() ``` 3. Use **queues** for inter-process communication (not globals) ## Testing & Documentation ### Integration Testing For features involving file I/O, configuration, or multi-component interaction, write integration tests: ```python def test_high_score_persistence(): """Test that high scores are saved and loaded correctly.""" manager = HighScoreManager() manager.save_game(score=100, length=15, speed=7) # Reload from disk manager2 = HighScoreManager() assert manager2.high_score == 100 ``` **When**: Testing config loader, score persistence, sound initialization **Why**: Ensures data survives across restarts and different processes load correctly **Pattern**: 1. Create temporary test data or use fixtures 2. Verify both save and load operations 3. Test error cases (missing files, corrupted JSON) 4. Clean up test files Run with: `pytest tests/` or `python -m pytest` ### Manual Testing Checklist For CLI games, manual testing covers behavior integration better than unit tests: - [ ] Start game, verify colors/characters render - [ ] Change speed at menu, verify timing in-game - [ ] Hit pause (ESC), verify time display, resume works - [ ] Lose game, verify high score shows and persists - [ ] Check `~/.snake_config.json` was created with settings - [ ] Test with terminal <20x10, verify error message - [ ] Test with missing audio files, verify graceful fallback ### README Always include a comprehensive `README.md` with: - **Features**: What the project does - **Installation**: Step-by-step setup - **Usage**: Examples with common options - **Controls/API**: Reference for users - **Configuration**: How to customize - **Troubleshooting**: Common issues and solutions - **Architecture**: Project structure and design decisions - **Changelog**: Version history See [README.md](../README.md) for the pattern used here. ### Game Statistics & Telemetry Track meaningful stats for debugging and user feedback: - Game duration (tracks performance) - Scores and history (user engagement) - High scores (replayability metric) Store in JSON with timestamps for later analysis. ## Performance Considerations ### Avoid Blocking - Don't run long operations on the main game loop - Move audio processing to separate process with queues - Use `time.sleep()` for delays, not busy-wait loops ### Efficient Rendering - Clear screen once per frame, not multiple times - Update only changed elements when possible - In curses, use `refresh()` once per iteration ## Refactoring & Maintenance When improving existing code: 1. **Identify code smells**: Magic numbers, repeated patterns, unclear responsibility 2. **Extract modules**: One class/function per file when it exceeds 20-30 lines 3. **Add type hints**: Progressively add even to old code 4. **Fix bugs first**: Don't refactor and add features simultaneously 5. **Test manually**: For CLI games, run through gameplay scenarios 6. **Document changes**: Add to CHANGELOG, update README ## Example: Adding a New Feature If adding a difficulty mode: 1. Add constants to `constants.py` 2. Create `DifficultyManager` class in new `difficulty.py` 3. Add config options to `GameConfig` 4. Update `Game` class to use the manager 5. Add CLI argument in `parse_args()` 6. Update README with new feature 7. Test with: `python snake.py --difficulty hard` ### Future Feature Architecture Patterns When implementing planned features, follow these patterns: **Obstacles On Map**: Create `obstacles.py` with `ObstacleManager` handling collision detection and serialization to config **Power-ups**: Add `PowerUpManager` class, integrate with `game_config.py` for user selection and persistence **Game Modes**: Add mode enum to `constants.py`, create mode-specific logic in separate modules (not inline) **Leaderboard** (online): Create `leaderboard.py` with API integration, keep local `high_score.py` as fallback **Custom Themes**: Extend `theme.py` with `ThemeLoader` that reads themes from `~/.snake_themes/` directory **Replay System**: Add `GameRecorder` class that logs moves to JSON for playback/analysis **Key principle**: Never inline new feature code into existing modules. Always create a dedicated module + manager class for substantial features (>50 lines of code). ## Common Patterns in This Project | Pattern | Use Case | Example | |---------|----------|---------| | Manager Classes | Persistence and state | `HighScoreManager`, `GameConfig` | | Dataclasses | Structured data | `GameRecord`, `ColorPair` | | Type hints | All functions | `def load() -> None:` | | Queues | Inter-process comms | Sound process queue | | Graceful degradation | Optional features | Missing audio files | | JSON config | User preferences | `~/.snake_config.json` | | Absolute paths | Cross-directory execution | `Path(__file__).parent` | | CLI args | User customization | `--music`, `--no-effects` | ## Anti-patterns to Avoid ❌ **Hardcoded values** → Use `constants.py` ❌ **Relative file paths** → Use `pathlib.Path(__file__).parent` ❌ **Recursive input validation** → Use loops ❌ **Mixing concerns** → Separate config, logic, UI, persistence ❌ **Unhandled exceptions** → Catch and warn, don't crash ❌ **Magic numbers** → Extract to constants ❌ **Missing docstrings** → Document all functions ❌ **Unused imports or code** → Remove or commit properly ## Resources - **Type Hints**: [PEP 484](https://www.python.org/dev/peps/pep-0484/) and [Python typing docs](https://docs.python.org/3/library/typing.html) - **Pathlib**: [Python pathlib docs](https://docs.python.org/3/library/pathlib.html) - **Curses**: [Curses documentation](https://docs.python.org/3/library/curses.html) - **Argparse**: [Argparse tutorial](https://docs.python.org/3/library/argparse.html) - **Python Style**: [PEP 8](https://www.python.org/dev/peps/pep-0008/) ## Questions? Refer to the project structure, examples in existing code, or ask the agent to reference this document when making changes.