A Minesweeper game built in Unity as an architecture-focused showcase project. The project demonstrates a strict separation between domain logic, presentation, and infrastructure layers, with modular composition powered by dependency injection through VContainer.
Tech Stack: Unity (uGUI) · VContainer (DI) · UniTask (async) · Unity Input System · TextMeshPro · NUnit (Edit Mode Tests).
Game loop: first click → generate mines → reveal safe area → open/flag cells → win or lose → restart.
Gameplay
- Classic Minesweeper rules with fair first-click generation: mines are placed after the first move and never occupy the clicked cell or its neighbours.
- Flood-fill reveal of connected empty cells, stopping at numbered cells.
- Flags with a live remaining-flags counter, full mine reveal on loss, win detection, a running timer, and a saved best time.
Cross-platform input
- A single input pipeline with platform-specific handlers, selected automatically at startup:
- Desktop: left-click → open, right-click → flag
- Mobile: tap → open, long-press → flag
- Restart and Pause shortcuts use Unity Input System, while cell interactions are handled through Unity's uGUI event system.
UI & presentation
- Main Menu, HUD, and Pause screens follow a Presenter–View split and are driven by the game state.
- Pooled cell views and an auto-scaling grid that fits any viewport without changing the layout logic.
- Open the project under
Minesweeper-Unity/in Unity. - Open the
Bootstrapscene and press Play.
The game is split into small interface-based systems, composed via dependency injection (VContainer). Two composition roots organize the application: BootstrapScope registers infrastructure services and loads the Core scene, while CoreScope registers and initializes the gameplay systems, UI, input, and game assets.
Starting a new game doesn't reload the scene — GameSessionSystem simply replaces the current GameSession with a fresh instance via CreateNewSession().
| System | Responsibility | Unity |
|---|---|---|
GameSystem |
Top-level app state (MainMenu / Playing / Paused) |
No — pure C# |
GameSessionSystem / GameSession |
Owns the current session and its state machine | No — pure C# |
MinesGenerator |
Safe-zone mine placement and adjacency calculation | No — pure C# |
FloodFiller |
Iterative stack-based reveal of connected empty cells | No — pure C# |
RulesSystem |
Centralized game rules and validation | No — pure C# |
RandomProvider |
Deterministic random source for gameplay and tests | No — pure C# |
GameAssetsSystem |
Provides game configs and prefabs | Yes |
CellInputSystem |
Pointer/tap abstraction and keyboard shortcuts | Yes |
TimerSystem / SaveSystem |
Timer tracking and best-time persistence via PlayerPrefs |
Yes |
UISystem |
Switches Main Menu / HUD / Pause presenters and views | Yes |
The application flow is split between global state and session state: GameSystem manages the app lifecycle (MainMenu, Playing, Paused), while GameSession owns the Minesweeper lifecycle (WaitingFirstOpen → Playing → Won/Lost). RulesSystem keeps the game rules centralized, validating actions and determining win/loss conditions.
The domain layer (GameSession, RulesSystem, MinesGenerator, FloodFiller, CellData, GridData, Grid<T>) is pure C# and independent from Unity, making it easy to test in isolation. Internal domain types are exposed to EditMode tests through InternalsVisibleTo.
CellInputSystem hides platform differences behind interchangeable pointer handlers: DesktopPointerHandler and MobilePointerHandler implement the same interface (PointerDown, PointerUp, Tick), keeping the rest of the game platform-agnostic.
The core logic is covered by EditMode unit tests: mine generation and adjacency counts, flood-fill expansion, rules predicates, and the game-session state machine.
- Difficulty settings: Beginner/Intermediate/Expert presets, plus a settings screen for configuring grid size and mine count instead of relying on a single baked-in
GridConfig. - Quick reveal: clicking a revealed number with the required adjacent flags automatically opens its remaining neighbours.
- File-based save system: replace
PlayerPrefswith file-based saves and persist game snapshots, allowing unfinished games to be resumed after restarting the app. - Game feel: add reveal and explosion animations instead of instant sprite swaps, along with basic sound effects.
- Input testing: extend test coverage with PlayMode/integration tests for the mobile drag/hold input path.
