This is an object-oriented Python implementation of a partial version of the Merchants & Marauders board game. The game simulates the economic and combat systems of 17th-century Caribbean trading and piracy. Players take on the roles of merchant captains or pirates, managing ships, completing missions, acquiring cargo, and engaging in combat on the high seas.
- Player Management: Create and manage multiple players with unique captains, ships, and resources (gold, cargo, glory points)
- Card Systems: Multiple card decks including captains, ships, missions, events, rumors, cargo, and glory cards
- Game Board: Tracks players across Caribbean ports and manages game state
- Combat System: Simulate naval combat between players with hit location mechanics and damage tracking
- Port Actions: Players can recruit crew, claim missions, and stash gold at various Caribbean ports
- Merchant Raiding: Non-pirate players can raid merchant ships for cargo
- Event System: Event cards trigger special game events
- Victory Condition: First player to acquire 10+ glory points wins
- Modular Design: Separate modules for cards, boards, players, game logic, and utilities
- Type Hints: Full type annotations using Python's typing module for better code clarity and IDE support
- Resource Management: Efficient memory usage with
__slots__and weak references - Immutable Constants: Centralized constant definitions (ports, merchants, nationalities, etc.)
The Game class implements the Flyweight pattern to ensure only one instance of each game exists per game ID. The __new__ method uses a WeakValueDictionary to store active games and return existing instances rather than creating duplicates, reducing memory overhead when managing multiple concurrent games.
__active_games: ClassVar[WeakValueDictionary[str, Game]] = WeakValueDictionary()The CardFactory generic class creates and manages shuffled decks of cards. Rather than modifying constant card definitions directly, it creates independent instances for each game, allowing modifications without affecting the original card sets.
The EventNotification class implements the Observer pattern, allowing different components (e.g., the game board) to subscribe to and receive notifications about game events. Currently used for:
glory_acquired: Triggered when a player earns glory pointscombat_declared: Triggered when a player initiates combat with another
Game behavior changes based on state (e.g., ActiveState, CombatState, EndGameState). Different states implement different behaviors for actions like creating players, drawing cards, and starting combat. This allows the game flow to be managed cleanly without extensive conditional logic.
The Player class acts as a façade, providing a simplified interface to the more complex PlayerBoard. It abstracts away the implementation details of the player board while exposing only the essential player actions and attributes.
game_oop/
├── __init__.py # Package initialization
├── game.py # Main Game class with Flyweight and State pattern
├── cards.py # Card classes and CardFactory (Factory pattern)
├── boards.py # GameBoard and PlayerBoard classes
├── user.py # Player class (Façade pattern)
├── utils.py # Constants, types, and EventNotification (Observer pattern)
├── pyproject.toml # Project configuration
└── README.md # This file
| File | Purpose |
|---|---|
game.py |
Core game logic, game state management, and card deck handling |
cards.py |
Card type definitions and the CardFactory for deck creation |
boards.py |
PlayerBoard class for tracking individual player state and GameBoard for global game tracking |
user.py |
Player class managing player actions, movement, and interactions |
utils.py |
Type aliases, constants, and the EventNotification observer implementation |
- Pattern: Flyweight
- Purpose: Manages overall game state, card decks, players, and game events
- Responsibilities: Create/manage games by ID, handle state transitions, manage event subscriptions
- Pattern: Façade
- Purpose: Simplified interface for player actions and data
- Responsibilities: Manage player attributes (crew, gold, state), execute port actions, manage combat
- Purpose: Detailed tracking of individual player state
- Responsibilities: Track captain, ship, cargo, bounties, hit locations, missions, glory points
- Purpose: Global game state tracker
- Responsibilities: Track active players, port occupation, merchant locations, monitor victory condition
- Pattern: Factory
- Purpose: Create shuffled, independent card decks
- Responsibilities: Draw cards from deck, maintain deck integrity
- Pattern: Observer
- Purpose: Event publishing and subscription system
- Responsibilities: Subscribe to events, publish events, notify subscribers
- Single Responsibility Principle: Each class has a focused purpose
- Dependency Injection: Game instance passed to components that need it
- Encapsulation: Private attributes with property accessors
- DRY (Don't Repeat Yourself): Shared constants and factory methods
- Type Safety: Full type annotations throughout codebase