The classic 2048 game built with .NET MAUI.
This is a fully-featured implementation of the classic 2048 puzzle game, built with .NET MAUI for cross-platform support (Android, iOS, Mac Catalyst, Windows). The project follows a clean architecture with a testable core engine and MVVM pattern for the UI.
- ๐ฎ Classic 2048 gameplay with smooth animations
- ๐ Undo functionality
- ๐พ Auto-save and resume game state
- ๐ Best score tracking
- ๐งฉ Multiple board sizes (3x3 through 8x8)
- ๐งฑ Multiple game modes (Modern + Classic + Walltastrophy + Adversarial)
- ๐ง Optional Move Coach (recommended direction + reason)
- ๐ Coach Nudges when you're stuck (optional)
- ๐ Swipe preview (slow-drag previews a move before committing)
- ๐ Game Center integration on iOS + Mac Catalyst (leaderboards and achievements)
- ๐จ Light and dark theme support
- โ๏ธ Gameplay settings (coach, nudges, haptics, undo button visibility)
- โฟ Accessibility features: semantic descriptions + screen reader announcements
- ๐ฃ๏ธ Voice Control friendly directional buttons (shown only when Voice Control / Narrator / TalkBack is enabled)
- โจ๏ธ Keyboard support (arrow keys + WASD)
- ๐ฎ Gamepad support (where supported)
- ๐ Touch gestures (swipe to move)
- ๐ฑ Responsive layout for phones, tablets, and desktops
- .NET 10 SDK
- .NET MAUI workload
Platform notes:
- iOS / Mac Catalyst: requires macOS + Xcode
- Android: requires Android SDK + emulators/device
-
Install .NET MAUI workload:
dotnet workload install maui
-
Restore dependencies:
dotnet restore
-
Build:
dotnet build
Tip: MAUI builds can be slow if you build multiple platforms. For a faster loop, build a specific target framework (examples in the next section), or set
MAUI_TARGET_PLATFORMwhen building the solution (this is what CI uses). -
Run tests:
dotnet test
This repo includes launch/task configs for debugging:
- macOS: Mac Catalyst debug/run
- Windows: Windows debug/run
- Android: Android debug/run
See .vscode/launch.json and .vscode/tasks.json.
dotnet build src/TwentyFortyEight.Maui/TwentyFortyEight.Maui.csproj -f net10.0-windows10.0.19041.0
dotnet run --project src/TwentyFortyEight.Maui/TwentyFortyEight.Maui.csproj -f net10.0-windows10.0.19041.0dotnet build src/TwentyFortyEight.Maui/TwentyFortyEight.Maui.csproj -f net10.0-android
dotnet run --project src/TwentyFortyEight.Maui/TwentyFortyEight.Maui.csproj -f net10.0-androiddotnet build src/TwentyFortyEight.Maui/TwentyFortyEight.Maui.csproj -f net10.0-maccatalyst -c Debug
dotnet run --project src/TwentyFortyEight.Maui/TwentyFortyEight.Maui.csproj -f net10.0-maccatalyst -c DebugNote: Building for iOS requires a Mac with Xcode installed.
dotnet build src/TwentyFortyEight.Maui/TwentyFortyEight.Maui.csproj -f net10.0-ios -c Debug
dotnet run --project src/TwentyFortyEight.Maui/TwentyFortyEight.Maui.csproj -f net10.0-ios -c DebugThe game includes Game Center integration on iOS and Mac Catalyst with leaderboards and achievements.
- Sign in to App Store Connect
- Navigate to your app (Bundle ID:
com.dappermagna.twentyfortyeight) - Go to Features โ Game Center
Create the following leaderboards:
| Leaderboard ID | Name | Score Format | Sort Order |
|---|---|---|---|
com.dappermagna.twentyfortyeight.highscores.3x3 |
Classic 3ร3 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.4x4 |
Classic 4ร4 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.5x5 |
Classic 5ร5 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.6x6 |
Classic 6ร6 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.7x7 |
Classic 7ร7 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.8x8 |
Classic 8ร8 High Scores | Integer | High to Low |
| Leaderboard ID | Name | Score Format | Sort Order |
|---|---|---|---|
com.dappermagna.twentyfortyeight.highscores.modern.3x3 |
Modern 3ร3 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.modern.4x4 |
Modern 4ร4 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.modern.5x5 |
Modern 5ร5 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.modern.6x6 |
Modern 6ร6 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.modern.7x7 |
Modern 7ร7 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.modern.8x8 |
Modern 8ร8 High Scores | Integer | High to Low |
| Leaderboard ID | Name | Score Format | Sort Order |
|---|---|---|---|
com.dappermagna.twentyfortyeight.highscores.walltastrophy.3x3 |
Walltastrophy 3ร3 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.walltastrophy.4x4 |
Walltastrophy 4ร4 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.walltastrophy.5x5 |
Walltastrophy 5ร5 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.walltastrophy.6x6 |
Walltastrophy 6ร6 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.walltastrophy.7x7 |
Walltastrophy 7ร7 High Scores | Integer | High to Low |
com.dappermagna.twentyfortyeight.highscores.walltastrophy.8x8 |
Walltastrophy 8ร8 High Scores | Integer | High to Low |
Adversarial mode is scored using normal 2048 merge scoring, but the objective is inverted: lower score is better. Configure these leaderboards with Low to High sort order.
| Leaderboard ID | Name | Score Format | Sort Order |
|---|---|---|---|
com.dappermagna.twentyfortyeight.highscores.adversarial.3x3 |
Adversarial 3ร3 Low Scores | Integer | Low to High |
com.dappermagna.twentyfortyeight.highscores.adversarial.4x4 |
Adversarial 4ร4 Low Scores | Integer | Low to High |
com.dappermagna.twentyfortyeight.highscores.adversarial.5x5 |
Adversarial 5ร5 Low Scores | Integer | Low to High |
com.dappermagna.twentyfortyeight.highscores.adversarial.6x6 |
Adversarial 6ร6 Low Scores | Integer | Low to High |
com.dappermagna.twentyfortyeight.highscores.adversarial.7x7 |
Adversarial 7ร7 Low Scores | Integer | Low to High |
com.dappermagna.twentyfortyeight.highscores.adversarial.8x8 |
Adversarial 8ร8 Low Scores | Integer | Low to High |
Note: Only the default win tile (2048) is supported for leaderboard submissions. Custom win tiles do not submit scores to leaderboards.
Create the following achievements (all should be 100% completion):
| Achievement ID | Name | Description |
|---|---|---|
com.dappermagna.twentyfortyeight.tile128 |
Tile 128 | Create a 128 tile |
com.dappermagna.twentyfortyeight.tile256 |
Tile 256 | Create a 256 tile |
com.dappermagna.twentyfortyeight.tile512 |
Tile 512 | Create a 512 tile |
com.dappermagna.twentyfortyeight.tile1024 |
Tile 1024 | Create a 1024 tile |
com.dappermagna.twentyfortyeight.tile2048 |
Tile 2048 | Create a 2048 tile |
com.dappermagna.twentyfortyeight.tile4096 |
Tile 4096 | Create a 4096 tile |
com.dappermagna.twentyfortyeight.firstwin |
First Win | Reach 2048 for the first time |
com.dappermagna.twentyfortyeight.score10000 |
Score 10,000 | Reach a score of 10,000 |
com.dappermagna.twentyfortyeight.score25000 |
Score 25,000 | Reach a score of 25,000 |
com.dappermagna.twentyfortyeight.score50000 |
Score 50,000 | Reach a score of 50,000 |
com.dappermagna.twentyfortyeight.score100000 |
Score 100,000 | Reach a score of 100,000 |
- Game Center requires a real iOS device for testing (not the simulator)
- Sign in to Game Center on your device
- The app will automatically authenticate when launched
- Leaderboard and Achievement buttons will appear when Game Center is available
The project is organized into three main components:
A fully-testable, UI-independent game engine that implements the classic 2048 rules:
- Game2048Engine: move logic, merge rules, win/game-over detection
- GameState: immutable state representation for undo/redo
- GameConfig: configurable board size + win conditions
- GameMode: ruleset variants (Modern, Classic, Walltastrophy, Adversarial)
- IRandomSource: abstraction for deterministic testing
- GameStateDto: JSON-friendly serialization for persistence
- MoveAnalyzer / HeuristicMoveAdvisor: platform-agnostic move analysis and coaching
Cross-platform UI built with .NET MAUI using MVVM pattern:
- GameViewModel: observable game state, commands, persistence
- TileViewModel: tile representation
- MainPage: responsive board with gesture and keyboard input
Comprehensive test suite using MSTest covering:
- Move/merge correctness for all directions
- Spawn behavior with deterministic RNG
- Win and game-over detection
- Undo/redo and serialization
- Move analysis / coach heuristics
- Ruleset identifiers (board size + mode)
- slnx format: New XML-based solution file format for .NET 10
- Central Package Management (CPM): package versions in
Directory.Packages.props - Consolidated props: common build properties in
Directory.Build.props - src/
- TwentyFortyEight.Core
- TwentyFortyEight.Maui
- TwentyFortyEight.ViewModels
- test/
- TwentyFortyEight.Core.Tests
- TwentyFortyEight.ViewModels.Tests
- .NET 10
- .NET MAUI
- MSTest
- CommunityToolkit.Mvvm
- Objective: Combine tiles to create a tile with the value 2048
- Movement: Swipe or use arrow keys to move all tiles in that direction
- Merging: Adjacent tiles with the same value merge into one (value doubles)
- Scoring: Score increases by the value of each merged tile
- New Tiles: After each move, a new tile appears in a random empty spot (value depends on game mode)
- Winning: Reach the 2048 tile (game can continue after winning)
- Game Over: No more valid moves available
Adversarial mode flips the role: you place tiles to try to block the AI.
- Input: Tap an empty cell to spawn a random
2or4, then the AI automatically makes one move. - Win: The AI has no legal moves (board is locked).
- Loss: The AI creates the win tile (2048 by default).
- Scoring: Standard 2048 merge scoring (non-negative), but lower final score is better.
GitHub Actions builds and tests the solution on pushes and pull requests.
See LICENSE for details.