{"id":50964757,"url":"https://github.com/kauri-off/flag-geo-game","last_synced_at":"2026-06-18T18:32:40.686Z","repository":{"id":364134794,"uuid":"1266570332","full_name":"kauri-off/flag-geo-game","owner":"kauri-off","description":"An offline flag \u0026 geography guessing game — React + Tauri, playable in the browser or as a desktop app","archived":false,"fork":false,"pushed_at":"2026-06-11T19:47:08.000Z","size":2009,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-11T20:16:31.391Z","etag":null,"topics":["educational-game","flags","game","geography-game","offline","quiz-game","react","tauri","typescript","vite","world-map"],"latest_commit_sha":null,"homepage":"https://kauri-off.github.io/flag-geo-game/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kauri-off.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-11T18:39:53.000Z","updated_at":"2026-06-11T19:47:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kauri-off/flag-geo-game","commit_stats":null,"previous_names":["kauri-off/flag-geo-game"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/kauri-off/flag-geo-game","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kauri-off%2Fflag-geo-game","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kauri-off%2Fflag-geo-game/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kauri-off%2Fflag-geo-game/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kauri-off%2Fflag-geo-game/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kauri-off","download_url":"https://codeload.github.com/kauri-off/flag-geo-game/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kauri-off%2Fflag-geo-game/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34503508,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["educational-game","flags","game","geography-game","offline","quiz-game","react","tauri","typescript","vite","world-map"],"created_at":"2026-06-18T18:32:38.640Z","updated_at":"2026-06-18T18:32:40.681Z","avatar_url":"https://github.com/kauri-off.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flag Geo — Guess the Country\n\nA flag-and-geography quiz. You're shown a country's flag and have to find that\ncountry on a world map. Single-player runs **fully offline** in the browser\n(Vite + React) — the map, the flag SVGs and the country names are all bundled at\nbuild time, so there are no runtime network requests. An optional **Online**\nmode lets you race other players live against a self-hosted dedicated server.\n\n## Gameplay\n\n- A flag is shown; click the matching country on the interactive world map.\n- Rounds are timed, and your accuracy and best/average times are tracked per\n  session.\n- Every round is logged to a persistent **History** screen.\n- Indistinguishable flags are treated fairly: guessing Monaco when shown\n  Indonesia counts as correct — see `src/game/flagTwins.ts`.\n\n### Modes\n\n- **Play** — endless free practice.\n- **Challenge** — a finite, scored run; answer in 3s for full points, decaying\n  the longer you take.\n- **Online** — multiplayer rooms on a shared server (see below).\n- **History** / **Settings**.\n\n### Difficulty \u0026 options\n\nConfigured on the **Settings** screen and persisted to `localStorage`:\n\n- **Continents** — restrict the pool to one or more regions.\n- **Country size** — small (`\u003c 50,000 km²`), medium, or large (`\u003e 1,000,000 km²`).\n- **Confirm mode** — click-to-confirm, or select then press space.\n- **Country labels**, sound on/off, volume, answer time limit.\n- **Language** — English or Russian.\n\n## Online multiplayer\n\nThe **Online** tab connects to a dedicated server that you or a friend\nself-host. In a room, everyone races the **same server-chosen flag sequence**\nsimultaneously; the server is authoritative for the sequence, round timing,\nanswer correctness and scoring, so results can't be forged.\n\nFlow: open **Online** → enter the server URL (and a password if the server\nrequires one) → pick a nickname and a **flag avatar** → create or join a room by\ncode → the host starts the match → race, with a live scoreboard, then final\nstandings. Match results feed an all-time **leaderboard** stored on the server.\n\nThe server lives in [`server/`](server/) and is a small Rust (axum + tokio)\nbinary with bundled SQLite. See [`server/README.md`](server/README.md) for run,\nDocker and reverse-proxy (subpath `wss`) instructions. Quick start:\n\n```sh\ncd server\ncp .env.example .env      # set JWT_SECRET, optional SERVER_PASSWORD, CORS_ORIGINS\ncargo run                 # listens on :8080\n```\n\nThen in the game's Online tab, enter `http://localhost:8080`.\n\n## Tech stack\n\n- **React 18** + **Zustand** for state (separate stores for game loop, settings,\n  history, UI, and online).\n- **d3-geo** + **topojson-client** for the map projection and rendering, using\n  the `world-atlas` TopoJSON.\n- **flag-icons** for flag SVGs; **world-countries** for ISO codes, areas,\n  regions and localized names.\n- **Vite** + **TypeScript** for the web app; **Connect / gRPC-Web**\n  (`@connectrpc/connect-web`) is the online transport.\n- **Rust** (tonic, tonic-web, tokio, rusqlite) for the optional online server.\n  The wire protocol is a single **protobuf** contract in `proto/`, compiled to\n  Rust (prost/tonic) and TypeScript (buf), so client and server types can't drift.\n\n## Project layout\n\n```\nproto/                  The protobuf wire contract (source of truth for both sides)\nscripts/                Build-time codegen (data, server data, protocol)\nsrc/\n  components/           Map, flag, prompt, controls, stats, feedback\n  screens/              Game, Challenge, Online, History, Settings\n  store/                Zustand stores (game, settings, history, ui, online)\n  game/                 Mode registry, country pool, flag twins, sound, stats\n  online/               Connect clients + event stream, generated protocol types, online UI\n  online/gen/           Generated TypeScript protocol types (buf)\n  data/                 Generated country metadata + loader\n  i18n/                 Localized country names + UI strings (en, ru)\n  map/                  World TopoJSON loading\n  assets/               Bundled world TopoJSON\nserver/                 Dedicated multiplayer server (Rust)\n  src/game/             Country table + flag twins, generated from the client data\n  src/grpc/             tonic services + protobuf\u003c-\u003edomain conversion\n  src/protocol.rs       Internal domain types for the room actor\n```\n\nThe game loop (`src/store/gameStore.ts`) is renderer- and mode-agnostic. Online\nplay reuses the same map/board: the online store feeds round state through\n`gameStore`, and `confirm()` submits to the server instead of scoring locally.\n\n## Getting started\n\nRequires Node.js. The online server additionally needs the\n[Rust toolchain](https://www.rust-lang.org/tools/install).\n\n```sh\nnpm install\nnpm run gen-data   # generate bundled country/map/locale data (one-time, or after dep bumps)\nnpm run dev        # start the Vite dev server (http://localhost:5173)\n```\n\n### Scripts\n\n| Command                   | Description                                                                |\n| ------------------------- | -------------------------------------------------------------------------- |\n| `npm run gen-data`        | Regenerate bundled data into `src/data`, `src/i18n/locales`, `src/assets`. |\n| `npm run gen-server-data` | Regenerate the server's country/flag-twin tables from the client data.     |\n| `npm run gen-protocol`    | Compile `proto/` into TypeScript types under `src/online/gen/` (buf).       |\n| `npm run dev`             | Vite dev server with hot reload.                                           |\n| `npm run build`           | Type-check and produce a production web build in `dist/`.                  |\n| `npm run preview`         | Serve the production build locally.                                        |\n| `npm run typecheck`       | Type-check without emitting.                                               |\n\nFor the server, see [`server/README.md`](server/README.md) (`cargo run`,\n`cargo test`, Docker).\n\n## Data generation\n\n`npm run gen-data` runs at build time from local npm packages and produces, all\nchecked into the repo so the app is self-contained:\n\n- `src/assets/countries-110m.json` — world map TopoJSON (from `world-atlas`).\n- `src/data/countries.json` — per-country metadata (ISO codes, area, region).\n- `src/i18n/locales/{en,ru}.json` — country names keyed by numeric ISO code.\n\nKeys are normalized numeric ISO 3166-1 codes (no leading zeros) so the metadata,\nlocale names, and map ids always line up. To add a language, emit another locale\nfile in `gen-data.mjs` and register it in `src/i18n/index.ts`.\n\nThe server's copy of the country data (`server/src/game/`) is regenerated from\nthese files with `npm run gen-server-data`, and the online protocol types\n(`src/online/gen/`) from the `proto/` contract with `npm run gen-protocol`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkauri-off%2Fflag-geo-game","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkauri-off%2Fflag-geo-game","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkauri-off%2Fflag-geo-game/lists"}