Provably-fair game-outcome engine in C++17 with Android JNI bindings. The engine and its bridge ship today; the consumer surface (game UIs, wallet, on-chain payments) is the planned product, tracked in the Roadmap below.
| Component | State |
|---|---|
ProvablyFairCore engine (coinflip, roulette, crash, mines, blackjack deck) |
implemented (cpp_engine/src/ProvablyFairCore.cpp) |
| Self-contained HMAC-SHA256 (no OpenSSL) | implemented (cpp_engine/include/Sha256.h, FIPS 180-4 + RFC 2104) |
| Desktop test harness | implemented (cpp_engine/src/main.cpp) |
Android NDK build to libcasino-engine.so (arm64-v8a, armeabi-v7a, x86, x86_64) |
implemented |
JNI bridge + Kotlin NativeGameEngine facade |
implemented (cpp_engine/src/native-lib.cpp, app/.../engine/NativeGameEngine.kt) |
MainActivity JNI smoke test |
implemented |
| Roulette / Blackjack / Crash / Mines game screens | not implemented yet |
| Wallet, USDC deposits/withdrawals on Polygon | not implemented yet |
| Firebase Auth / Firestore / Cloud Functions | not implemented yet |
| Gemini-powered in-app assistant | not implemented yet |
| NewsAPI feed, Room caching | not implemented yet |
| Biometric-gated key storage | not implemented yet |
What you can run today: the desktop test binary exercises every game algorithm and the input-validation paths; the Android app installs and the JNI smoke test confirms the native bridge round-trips strings through the engine.
ProvablyFairCore is a pure C++ class that derives deterministic, verifiable game outcomes from a (serverSeed, clientSeed, nonce) triple using HMAC-SHA256. Each public method returns "result|hash" so the caller can both consume the result and persist the hash for later third-party verification.
- Rejection sampling for unbiased outcomes. Roulette (
[0, 37)), Mines and Blackjack (Fisher-Yates with shrinking ranges) all reject hash chunks that would introduce modulo bias and rehash when the current hash is exhausted. - Exhaustion is treated as an error, not silently biased. If rejection sampling fails to find a valid chunk within the safety bound (an astronomically unlikely event that indicates a broken entropy path), the engine throws instead of falling back to a fixed value.
- No external crypto dependency.
Sha256.his a header-only, FIPS 180-4 conformant SHA-256 plus an RFC 2104 HMAC wrapper, so the engine builds identically on desktop CMake and the Android NDK with zero third-party libraries.
UI (Jetpack Compose) -- planned
downward via StateFlow
ViewModel + Repository layer -- planned
|
+-- Room (single source of truth) -- planned
+-- Retrofit / Firebase / Polygon RPC -- planned
Game outcomes
Kotlin NativeGameEngine ──JNI──► ProvablyFairCore (C++) -- IMPLEMENTED
├── HMAC-SHA256 (Sha256.h)
├── Coinflip
├── Roulette (rejection sampling)
├── Crash (1% house edge)
├── Mines (Fisher-Yates partial shuffle)
└── Blackjack (Fisher-Yates full shuffle)
The lower half of the diagram is what compiles and runs today. The upper half (UI, repository, persistence, network) is the planned product surface described in the Roadmap.
cmake -S cpp_engine -B cpp_engine/build -DCMAKE_BUILD_TYPE=Release
cmake --build cpp_engine/build --parallel
./cpp_engine/build/casino_testThe test binary prints 10 rounds for each game plus input-validation pass/fail. It is the same binary CI runs on every push.
./gradlew assembleDebugThis compiles libcasino-engine.so via the Android NDK for the four target ABIs (arm64-v8a, armeabi-v7a, x86, x86_64) and bundles it into the debug APK. The app launches into a JNI smoke screen that calls evaluateCoinflip and evaluateCrashPoint once and renders the results.
Prerequisites: Android Studio (or the equivalent SDK + NDK + CMake bundle) and JDK 17.
For each round the engine derives the outcome from HMAC-SHA256(serverSeed, clientSeed:nonce) and returns the full hash alongside the result. Once a round closes you can verify any past outcome independently:
- Take
serverSeed,clientSeed,noncefrom the round. - Compute
HMAC-SHA256(serverSeed, clientSeed + ":" + nonce)with any conformant implementation. - Apply the documented derivation for the game (e.g. for coinflip: parity of the first byte; for roulette: the first non-rejected 16-bit chunk modulo 37).
- Compare against the recorded result.
If the engine ever returns a different value than the verifier computes, the round is invalid.
- Commits: Conventional Commits (
feat,fix,refactor,docs,chore,ci,test, ...). - Branching: feature branches off
dev, merged via PR intodev, batched intomainper release. - PRs: CI must be green. See
.github/CONTRIBUTING.mdfor the full process. - Changelog: Keep a Changelog format in
CHANGELOG.md. Unreleased changes accumulate there until tagged. - Code of conduct: Contributor Covenant 2.1.
- C++ style: enforced via
.clang-formatincpp_engine/.
The eventual product target is a self-contained Android casino simulator backed by a real Polygon wallet, useful as a sandbox for testing game logic and crypto flows end-to-end without putting real money at risk (the operator controls both the house wallet and the player wallets they fund). The work below is ordered by intended implementation:
- Game UIs: Roulette, Blackjack, Crash and Mines screens that consume
NativeGameEngineand render results in Jetpack Compose. - App architecture: MVVM, Hilt for DI, Room as a single source of truth, Retrofit for any remote calls.
- Wallet: house-wallet generation/import via Android Keystore +
EncryptedSharedPreferences, gated byBiometricPrompt. - On-chain integration: USDC deposits/withdrawals on Polygon, Alchemy webhook for deposit detection, Firebase Cloud Function for withdrawal signing.
- Firebase: Auth (Google Sign-In), Firestore sync for users/rounds/bets, Cloud Messaging for deposit/withdrawal/security notifications.
- Auxiliary: Gemini-powered in-app assistant scoped to game rules, NewsAPI feed with offline-first caching.
Component-level progress is tracked in CHANGELOG.md ([Unreleased] block) and surfaced in the Status table above.
MIT. See LICENSE.