> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/igorek05m/daily-geogame/llms.txt
> Use this file to discover all available pages before exploring further.

# Game mechanics

> Technical details about how Daily GeoGame works under the hood

Understand the systems and calculations that power Daily GeoGame.

## Guess validation

### Country matching

When you submit a guess, the game:

1. Normalizes your input (converts to lowercase, trims whitespace)
2. Searches the country database for an exact name match
3. Validates against 195+ countries from the RestCountries API
4. Rejects the guess if the country name is invalid or already guessed

The country database includes both common names ("United States") and official names. The game uses the common name for matching.

### Duplicate prevention

The game tracks all your guesses in the session and prevents duplicate entries. If you try to guess the same country twice, you'll see an "Already guessed!" alert.

## Distance calculation

### Haversine formula

Daily GeoGame uses the Haversine formula to calculate the great-circle distance between two points on a sphere (Earth). This provides accurate distances between countries based on their geographic centers.

```typescript theme={null}
function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
  const R = 6371; // Earth's radius in km
  const dLat = deg2rad(lat2 - lat1);
  const dLon = deg2rad(lon2 - lon1);
  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
    Math.sin(dLon / 2) * Math.sin(dLon / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  return Math.round(R * c);
}
```

See `app/lib/geoUtils.ts:5` for the implementation.

<Note>
  Distances are calculated from the latitude/longitude coordinates of each country's geographic center, not political capitals.
</Note>

## Bearing and direction

### Direction calculation

The bearing angle determines which direction to travel from your guess to reach the target country. The game calculates this using trigonometric functions on the latitude/longitude coordinates.

```typescript theme={null}
function getBearingAngle(lat1, lon1, lat2, lon2) {
  const startLat = deg2rad(lat1);
  const startLng = deg2rad(lon1);
  const destLat = deg2rad(lat2);
  const destLng = deg2rad(lon2);

  const y = Math.sin(destLng - startLng) * Math.cos(destLat);
  const x = Math.cos(startLat) * Math.sin(destLat) - 
            Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
  let brng = Math.atan2(y, x);
  return (brng * 180 / Math.PI + 360) % 360; // Convert to degrees
}
```

See `app/lib/geoUtils.ts:33` for the implementation.

### Visual representation

The bearing angle rotates an arrow icon (↑) using CSS transforms. An angle of 0° points north, 90° points east, 180° points south, and 270° points west.

## Neighbor highlighting

### Border detection

The game identifies neighboring countries using ISO 3166-1 alpha-3 country codes. When you make a guess, the system:

1. Retrieves the target country's `borders` array (e.g., `["DEU", "CZE", "SVK"]` for Poland)
2. Checks if your guessed country's alpha-3 code is in that array
3. Highlights the guess with a yellow pin icon if it's a neighbor
4. Updates the map to visually show border countries

See `app/api/progress/route.ts:44-48` for the connection logic.

### Geographic hierarchy

If your guess isn't a neighbor, the game checks broader geographic connections:

* **Subregion match**: Same subregion (e.g., "Central Europe")
* **Region match**: Same continent (e.g., "Europe")
* **No connection**: Different region entirely

This provides progressively weaker clues as you get farther from the target.

## Progressive hint system

### Hint generation

Hints are generated when the daily game is created (at midnight UTC). The system:

1. Fetches data from the CIA World Factbook using the country's FIPS code
2. Organizes facts into themed packages (Geography, Economy, People, etc.)
3. Extracts specific data points (climate, terrain, GDP, population, etc.)
4. Stores up to 6 hint packages in the database

See `app/api/daily/route.ts:114` for hint generation.

### Hint unlocking

The unlock mechanism is controlled server-side to prevent cheating:

```typescript theme={null}
const guessesCount = progress?.guesses?.length || 0;
const allowedHintsCount = guessesCount + 1;
const isGameOver = guessesCount >= 6 || isWon;

const hints = hintPackages.map((pkg, index) => {
  if (isGameOver || index < allowedHintsCount) {
    return pkg; // Reveal hint
  }
  return {
    title: pkg.title,
    hints: pkg.hints.map(h => ({ 
      label: h.label,
      value: "???" // Hide value
    }))
  };
});
```

See `app/api/daily/route.ts:139-156` for the unlock implementation.

<Tip>
  Hints are obfuscated with "???" until unlocked. The client never receives locked hint values, preventing inspection via browser dev tools.
</Tip>

## Session persistence

### Anonymous session tracking

Daily GeoGame uses session cookies to save your progress without requiring login:

1. On your first visit, the server generates a UUID session ID
2. The ID is stored in a secure, HTTP-only cookie (`geo_session`)
3. Your guesses, wins, and stats are linked to this session ID in MongoDB
4. The cookie persists for 10 years (`maxAge: 60 * 60 * 24 * 365 * 10`)

See `app/api/progress/route.ts:18-22` for session creation.

### Progress saving

After each guess, the game saves your progress via POST to `/api/progress`:

```typescript theme={null}
await collection.updateOne(
  { sessionId, date },
  { $set: { guesses, won: isWon, lastPlayed: new Date() } },
  { upsert: true }
);
```

This allows you to refresh the page or return later without losing your game state.

<Note>
  Progress is stored per date, so you can play previous days' games and each maintains separate progress.
</Note>

## Daily reset

### Country selection

Every day at midnight UTC, a new country is selected:

1. The game randomly picks from the country database
2. Fetches detailed data from RestCountries API
3. Retrieves CIA World Factbook data using the country's FIPS code
4. Generates hint packages
5. Stores the game in MongoDB with the date as the key

See `app/api/daily/route.ts:71-137` for the daily game creation logic.

### Date-based routing

The game uses YYYY-MM-DD date strings to identify each daily puzzle:

* Default: Today's date (`new Date().toISOString().split('T')[0]`)
* Archive: Access previous games via `?date=2024-03-15` query parameter
* Validation: Dates must be between the game start date and today

This allows players to go back and play missed days.

## Stats tracking

The game tracks both personal and global statistics:

### Personal stats

* **Games played**: Total number of days you've played
* **Wins**: Number of correct guesses
* **Win rate**: Percentage of games won

Personal stats are tied to your session ID.

### Global stats

* **Total players**: Number of unique sessions that played each day
* **Total winners**: Players who guessed correctly
* **Global win rate**: Percentage of all players who won
* **Guess distribution**: Histogram showing which guess number players won on

Global stats are calculated server-side from all sessions for each date.

<Tip>
  Stats are updated immediately after you win or use all 6 guesses, so you can see how you compare to other players.
</Tip>
