Skip to content

epi13/ShinyWheel

Repository files navigation

ShinyWheel

ShinyWheel is a private, offline-capable shiny-hunting companion for Pokémon Scarlet and Violet. It turns the original one-file random-number prototype into a focused hunt workflow with fair randomization, transparent recommendations, persistent completion tracking, queues, favorites, progress, and accessible responsive screens.

ShinyWheel main hunt screen

Fan project. ShinyWheel is not affiliated with, endorsed by, or sponsored by Nintendo, Creatures Inc., GAME FREAK, The Pokémon Company, or PokeAPI.

Highlights

  • Smart Pick with visible positive and negative scoring factors, controlled randomness, recency penalties, favorites, regional variety, and resettable selection debt.
  • Pure Random using a cryptographically seeded shuffled bag and sampling without replacement.
  • One centralized eligibility pipeline: valid targets + filters − completed − temporarily excluded.
  • Completed hunts can only be restored from the dedicated Completed Hunts screen after confirmation.
  • Persistent queue, favorites, recent picks, temporary skips, filters, modes, themes, and completion timestamps.
  • Versioned, runtime-validated local storage with corruption recovery and migration from scarlet-shiny-caught.
  • Human-readable JSON export plus validated merge or intentional replace import.
  • Scarlet/Violet accents, light/dark appearance, reduced motion, responsive desktop rail and mobile bottom navigation.
  • Locally bundled 499-target Paldea + Kitakami dataset; the randomizer never depends on a live API.
  • Artwork loading/failure states, keyboard operation, semantic landmarks, dialogs, focus restoration, live announcements, and accessible progress summaries.
Mobile hunt Completed hunts Progress
Mobile Completed Progress

Architecture

React 19, strict TypeScript, and Vite form a static client-only application. Pure modules in src/domain own eligibility, randomization, scoring, and state transitions. src/storage owns schema validation, migration, import, and export. Feature screens and focused components consume those modules; no screen implements its own selection fallback.

State is reducer-driven and stored under shinywheel:state:v2. Core data lives in src/data/targets.json. PokeAPI's public sprite repository is a progressive artwork enhancement only; a failed request displays a resilient fallback and cannot stop selection or progress tracking.

Local development

Requires Node.js 22+ and npm 10+.

npm install
npm run dev

Vite serves the app at the printed local URL. The production build uses the /ShinyWheel/ base path for GitHub Pages.

Quality commands

npm run format
npm run format:check
npm run lint
npm run typecheck
npm run test
npm run build
npx playwright install chromium
npm run test:e2e
npm run check

The Vitest suite includes a 5,000-iteration stress assertion across pure random, smart recommendations, and session/queue generation. Playwright exercises completion, reload persistence, mode changes, deliberate restoration, favorites, queue behavior, keyboard dialogs, artwork failure, and mobile navigation.

Persistence, privacy, import, and export

Progress is stored in browser local storage and is not sent to ShinyWheel or an account service. Clearing browser site data removes it, so periodic export is recommended.

Exports are readable JSON with schemaVersion, exportedAt, and the versioned state: completed IDs/timestamps, favorites, queue, settings, filters, bag, selection debt, and history. Import parses JSON as inert data, validates the complete schema with Zod, removes unknown IDs, and supports:

  • Merge: preserves current completions and combines compatible progress.
  • Replace: replaces local state only after an explicit browser confirmation.

On first v2 load, numeric entries from the legacy scarlet-shiny-caught key migrate to canonical species-<national-id> IDs. Malformed legacy or current values safely fall back to defaults.

Data sources and limitations

Paldea and Kitakami regional membership, National Pokédex IDs, names, artwork IDs, and known special/shiny-lock notes were extracted from and audited against the repository's original 499-unique-target prototype. Regional overlap is represented as membership on one canonical target, preventing duplicate weighting.

Artwork is attributed to the PokeAPI sprites repository. Pokémon names, characters, and artwork are property of their respective rights holders.

This release intentionally does not claim unverified types, locations, biomes, encounter methods, spawn density, visibility, weather/time constraints, or complete form differentiation. Blueberry/Indigo Disk is not mixed into the supported pool because a complete, consistently verified dataset was not available in the prototype. See UPGRADE_REPORT.md for audit details.

Deployment

npm run build creates dist/. .github/workflows/pages.yml validates, builds, runs Chromium end-to-end tests, and deploys Pages only after changes reach main (or via manual dispatch). Enable GitHub Pages with GitHub Actions as the source. The hash-free single-route interface refreshes safely under /ShinyWheel/.

Contributing

Keep selection behavior inside src/domain; never add a fallback that bypasses eligibleTargets. New dataset fields must document confidence and sources. Before a pull request, run npm run check && npm run test:e2e and visually inspect desktop and mobile.

License: see LICENSE.

About

Scarlet Shiny Hunt Randomizer

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages