A digital implementation of the Unmatched board game in C++17, built around a data-driven architecture, a rule-based AI opponent, and raylib rendering.
The game currently includes Dracula, Sherlock Holmes, and The Invisible Man. Heroes, cards, maps, and effects are loaded from JSON rather than hardcoded into the C++ code.
- 🎮 Playable digital implementation of Unmatched
- 🧩 Data-driven game content using JSON
- 🤖 Rule-based AI opponent for Player 2
- 💾 Save/load with 3 save slots
- ↩️ Undo using serialized game-state snapshots
- 🏭 Factory-based creation of heroes, cards, maps, effects, and more
- 👀 Observer-based game events between the rules and rendering layers
- ⚔️ Card combat, movement, maneuvers, abilities, and pending player choices
- 🛡️ Custom exception hierarchy with contextual error propagation
- 🖥️ raylib-based rendering
The project is organized into five main layers:
| Layer | Responsibility |
|---|---|
| Model | Game state and data classes such as Card, Hero, Sidekick, Deck, Map, and Tile. |
| Engine | Reusable rule building blocks: Effect, Condition, Query, and shared game data. |
| Factory | Converts JSON data into game objects and isolates the on-disk data format. |
| Controller | Game rules, turn state, AI, scene management, and resource managers. |
| View | raylib scenes and UI. Reads game state without containing game rules. |
The main game-data flow is:
JSON → Factory → Model → Engine → GameManager → View
This keeps game content, rules, application control, and rendering separated from each other.
Several design patterns are used where they fit the project:
- Factory — creates data-driven objects from JSON. Adding a new hero or card can be done through data without changing the core game logic.
- Strategy —
Effect,Condition, andQueryprovide interchangeable rule components selected from JSON. - Observer —
IGameObserverallows the view to react to game events without couplingGameManagerto rendering. - Memento — serialized game state is used to implement Undo as well as save/load.
- Dependency Injection — application-owned managers are passed to the parts of the program that need them instead of relying on globals.
- Singleton — limited to the player/load-selection state that needs to survive scene transitions.
Player 2 can be controlled by a simple rule-based AI through AIController.
The AI interacts with the same public GameManager operations used by the human player. It can:
- Resolve pending tile, fighter, card, and effect choices
- Defend during combat
- Attack reachable enemy fighters
- Play useful event cards
- Maneuver toward opponents
- Manage its hand and end its turn
The AI intentionally uses greedy heuristics rather than lookahead or full board planning. It is designed as a playable starting point rather than a strong competitive opponent.
GameManager supports 3 save slots. The complete game state is serialized to JSON and restored when loading a slot.
Before meaningful actions, the game stores serialized state snapshots in an undo stack. Undo is disabled during intermediate states such as active combat or pending choices to prevent invalid state transitions.
The project uses a custom exception hierarchy instead of relying on unexplained crashes:
AppException
├── FileException
├── JsonParseException
├── DataFormatException
└── FactoryException
Errors gain additional context as they move upward through the application. Scene construction can recover from application-level exceptions and return to the main menu, while main() acts as the final safety net.
- CMake 3.16+
- C++17-compatible compiler
- Linux X11/OpenGL development headers required by raylib
raylib and nlohmann/json are fetched automatically through CMake FetchContent.
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j$(nproc)
./CardGameEngineThe build also copies the required assets/ and data/ directories next to the executable.
.
├── CMakeLists.txt
├── main.cpp
├── data/ # Heroes, cards, maps, and other JSON data
├── include/
│ ├── controller/ # Application and game control
│ ├── engine/ # Effects, conditions, queries, game data
│ ├── factory/ # JSON → game objects
│ ├── model/ # Core game data
│ ├── utility/ # File and exception utilities
│ └── view/ # Scenes and UI
└── src/
├── controller/
├── factory/
├── model/
├── utility/
└── view/
- 3 playable heroes: Dracula, Sherlock Holmes, The Invisible Man
- 2 maps
- Player vs. AI gameplay
- Data-driven cards, effects, conditions, queries, and maps
- Only Player 2 can currently be controlled by the AI.
- AI has no multi-turn lookahead or hero-specific strategy.
- Only three heroes and two maps are currently implemented.
- The Settings and Collection scenes/buttons are present but not implemented.
- The How to Play scene is not implemented yet.
- There is currently no automated test suite.
Possible next steps include:
- Smarter AI with deeper board evaluation and lookahead
- More heroes, cards, and maps
- Automated tests for game rules and data loading
- Completing the remaining UI scenes
- Improving extensibility and gameplay polish
- kiarash habibi
This project is developed for educational and portfolio purposes.