The challenge
As a solo founder, I needed to ship simultaneously on desktop and mobile while maintaining the agility to iterate on game balance, add content rapidly, and gather player insights without sacrificing privacy.
- Solo development constraints: Limited time meant choosing between platforms or finding a way to support both without duplicating work.
- Content at scale: A roguelike needs hundreds of cards with varied mechanics, but writing custom code for each would create an unsustainable bottleneck.
- Player trust: Modern gamers expect privacy-first analytics, not invasive data collection—especially from indie developers.
Architecture approach
Multi-shell monorepo pattern
Hexagonal architecture with platform-agnostic game engine and thin platform adapters:
apps/mobile, apps/desktop (shells: routing/bundling only)
↓
packages/game-state (shared zustand store)
↓
packages/screens (screen compositions)
↓
packages/ui (UI primitives)
↓
packages/game-core (game engine: pure TS, zero platform deps)Key architectural decisions
- Platform-agnostic core: Game engine has zero React, React Native, or platform dependencies — pure TypeScript business logic.
- Dependency inversion: Core engine has no knowledge of platforms that consume it; dependencies point inward only.
- Shared everything: Screens, UI components, state management, and game logic shared across all platforms.
- Deterministic gameplay: Seeded RNG enables testing, debugging, and future replay systems.
Key outcomes
Shipped two platforms as one person
100% code sharing meant feature parity by default—no duplicate work, no platform-specific bugs.
Eliminated content bottlenecks
Standard effects system handles 95% of cards declaratively; Content Forge lets non-engineers create game-ready assets with AI assistance.
Built player trust
Opt-in telemetry with hashed IDs, automatic retention limits, and instant opt-out earned beta community confidence.
Game engine design
Turn structure
An 11-phase canonical turn eliminates race conditions and timing ambiguity:
- Start of turn → passive effects trigger
- Spin the slot machine → draw symbols
- Mana symbols → add to pools
- Spells → execute effect chains
- Player creatures → attack opponent
- Enemy creatures → attack player
- End of combat → cleanup triggers
Determinism for testing
Seeded RNG (never Math.random()) means every test can reproduce exact gameplay sequences—critical for debugging card interactions and validating balance changes.
Core systems
Meta-progression loops
- Card unlocks: Win battles and complete achievements to expand your permanent collection across all runs.
- Ascension perks: Earn points from victories to purchase permanent modifiers (max 5 active)—build synergies between runs.
- In-run economy: Spend 10 mana for tactical choices like artifacts, HP refills, deck removal, or rarity upgrades.
Effects system at scale
Standard effects (dealDamage, apply, mana, modify, spawn, cleanse, draw) handle 95% of the 350+ cards declaratively:
- Every effect supports conditions (mana/HP/turn thresholds) and repeat counts
- Targeting helpers (adjacent, surrounding, all creatures) prevent manual bugs
- Custom effects registry exists only for truly unique mechanics
Privacy-first telemetry
Designed an analytics system that earns player trust: opt-in only, hashed IDs, automatic data deletion, and instant opt-out. Built with Terraform for infrastructure as code.
What gets tracked
- Sessions: platform, OS, duration (no device fingerprinting)
- Run outcomes: victory/defeat with final deck composition and artifacts
- Player choices: which cards/artifacts offered vs. selected
- Errors: crash reports (always sent if telemetry enabled)
Client-side safeguards
- SHA-256 hashed profile IDs—never plain text
- Event batching with 45-second flush + exponential backoff retry
- 24-hour local discard policy for undelivered events
Content Forge: AI-assisted creation
Built a Python desktop app that lets non-engineers create wizards, cards, and artifacts with AI-generated pixel art—then export game-ready JSON + sprites in one click.
Workflow
- Describe the card/wizard/artifact in natural language
- AI generates pixel art + suggested effects
- Refine mechanics using the effects palette
- Export validates against Zod schemas
- Drop JSON + sprites into game content directory—auto-loaded on next launch
Development practices
Testing philosophy
"Game-true" unit tests model real gameplay sequences with seeded RNG:
- Card contracts: verify rules text matches actual behavior
- Effects battery: comprehensive coverage for standard + custom effects
- Phase-aware timing: assert effects fire in correct turn phases
- Reproducible runs: seeded tests catch non-deterministic bugs
Monorepo structure
- pnpm workspaces for dependency management
- Shared TypeScript config across packages
- Vitest for unified testing
By the numbers
Lessons learned
What worked
- Ports-and-adapters from day one: Testing game logic without launching a mobile emulator or desktop window saved hundreds of hours.
- Documentation as a first-class artifact: Writing rules/architecture docs enabled effective AI pair programming and async collaboration.
- Opt-in telemetry with no compromises: SHA-256 hashing, 90-day auto-delete, and instant opt-out built beta community trust from day one.
- Tooling investment pays dividends: Content Forge turned content creation from a week-long engineering task into a minutes-long design task.
What I'd do differently
- Event-driven from the start: Refactoring from phase-based to event-driven mid-development was painful; should have started there.
- GUI-first for Content Forge: Started as a JSON editor; building the visual UI from day one would have accelerated non-engineer adoption.
- Replay system earlier: Deterministic RNG enables replays for free; wish I'd built this for debugging and content creation sooner.
- Smaller, composable effects: Some custom effects are monolithic; more aggressive composition would improve extensibility.
Let's connect
I'm available to discuss the architecture, technical decisions, and product thinking behind Slotomancer. Happy to dive deep into cross-platform architecture, game engine design, or infrastructure as code.