Skip to content

Repository files navigation

Flag Geo — Guess the Country

A flag-and-geography quiz. You're shown a country's flag and have to find that country on a world map. Single-player runs fully offline in the browser (Vite + React) — the map, the flag SVGs and the country names are all bundled at build time, so there are no runtime network requests. An optional Online mode lets you race other players live against a self-hosted dedicated server.

Gameplay

  • A flag is shown; click the matching country on the interactive world map.
  • Rounds are timed, and your accuracy and best/average times are tracked per session.
  • Every round is logged to a persistent History screen.
  • Indistinguishable flags are treated fairly: guessing Monaco when shown Indonesia counts as correct — see src/game/flagTwins.ts.

Modes

  • Play — endless free practice.
  • Challenge — a finite, scored run; answer in 3s for full points, decaying the longer you take.
  • Online — multiplayer rooms on a shared server (see below).
  • History / Settings.

Difficulty & options

Configured on the Settings screen and persisted to localStorage:

  • Continents — restrict the pool to one or more regions.
  • Country size — small (< 50,000 km²), medium, or large (> 1,000,000 km²).
  • Confirm mode — click-to-confirm, or select then press space.
  • Country labels, sound on/off, volume, answer time limit.
  • Language — English or Russian.

Online multiplayer

The Online tab connects to a dedicated server that you or a friend self-host. In a room, everyone races the same server-chosen flag sequence simultaneously; the server is authoritative for the sequence, round timing, answer correctness and scoring, so results can't be forged.

Flow: open Online → enter the server URL (and a password if the server requires one) → pick a nickname and a flag avatar → create or join a room by code → the host starts the match → race, with a live scoreboard, then final standings. Match results feed an all-time leaderboard stored on the server.

The server lives in server/ and is a small Rust (axum + tokio) binary with bundled SQLite. See server/README.md for run, Docker and reverse-proxy (subpath wss) instructions. Quick start:

cd server
cp .env.example .env      # set JWT_SECRET, optional SERVER_PASSWORD, CORS_ORIGINS
cargo run                 # listens on :8080

Then in the game's Online tab, enter http://localhost:8080.

Tech stack

  • React 18 + Zustand for state (separate stores for game loop, settings, history, UI, and online).
  • d3-geo + topojson-client for the map projection and rendering, using the world-atlas TopoJSON.
  • flag-icons for flag SVGs; world-countries for ISO codes, areas, regions and localized names.
  • Vite + TypeScript for the web app; Connect / gRPC-Web (@connectrpc/connect-web) is the online transport.
  • Rust (tonic, tonic-web, tokio, rusqlite) for the optional online server. The wire protocol is a single protobuf contract in proto/, compiled to Rust (prost/tonic) and TypeScript (buf), so client and server types can't drift.

Project layout

proto/                  The protobuf wire contract (source of truth for both sides)
scripts/                Build-time codegen (data, server data, protocol)
src/
  components/           Map, flag, prompt, controls, stats, feedback
  screens/              Game, Challenge, Online, History, Settings
  store/                Zustand stores (game, settings, history, ui, online)
  game/                 Mode registry, country pool, flag twins, sound, stats
  online/               Connect clients + event stream, generated protocol types, online UI
  online/gen/           Generated TypeScript protocol types (buf)
  data/                 Generated country metadata + loader
  i18n/                 Localized country names + UI strings (en, ru)
  map/                  World TopoJSON loading
  assets/               Bundled world TopoJSON
server/                 Dedicated multiplayer server (Rust)
  src/game/             Country table + flag twins, generated from the client data
  src/grpc/             tonic services + protobuf<->domain conversion
  src/protocol.rs       Internal domain types for the room actor

The game loop (src/store/gameStore.ts) is renderer- and mode-agnostic. Online play reuses the same map/board: the online store feeds round state through gameStore, and confirm() submits to the server instead of scoring locally.

Getting started

Requires Node.js. The online server additionally needs the Rust toolchain.

npm install
npm run gen-data   # generate bundled country/map/locale data (one-time, or after dep bumps)
npm run dev        # start the Vite dev server (http://localhost:5173)

Scripts

Command Description
npm run gen-data Regenerate bundled data into src/data, src/i18n/locales, src/assets.
npm run gen-server-data Regenerate the server's country/flag-twin tables from the client data.
npm run gen-protocol Compile proto/ into TypeScript types under src/online/gen/ (buf).
npm run dev Vite dev server with hot reload.
npm run build Type-check and produce a production web build in dist/.
npm run preview Serve the production build locally.
npm run typecheck Type-check without emitting.
npm test Run the unit tests (vitest).
npm run lint Lint with ESLint (typescript-eslint + react-hooks).

For the server, see server/README.md (cargo run, cargo test, Docker).

Data generation

npm run gen-data runs at build time from local npm packages and produces, all checked into the repo so the app is self-contained:

  • src/assets/countries-110m.json — world map TopoJSON (from world-atlas).
  • src/data/countries.json — per-country metadata (ISO codes, area, region).
  • src/i18n/locales/{en,ru}.json — country names keyed by numeric ISO code.

Keys are normalized numeric ISO 3166-1 codes (no leading zeros) so the metadata, locale names, and map ids always line up. To add a language, emit another locale file in gen-data.mjs and register it in src/i18n/index.ts.

The server's copy of the country data (server/src/game/) is regenerated from these files with npm run gen-server-data, and the online protocol types (src/online/gen/) from the proto/ contract with npm run gen-protocol.

About

An offline flag & geography guessing game — React + Tauri, playable in the browser or as a desktop app

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages