Bug: Guardians with empty names get random names on every tick instead of being locked at loop start #161

Closed
opened 2026-05-27 10:29:58 +02:00 by Anexim · 2 comments
Owner

Bug Description

Guardians defined with name: '' (floors 90-140 compound/exotic guardians) get a new random name generated every time getGuardianForFloor() is called, because generateGuardianName() uses Math.random(). This means the guardian's name changes every tick/refresh. Guardians should have their names locked in at loop start and remain stable.

Additionally, the procedural combo guardians (floor 150+) also use Math.random() for name generation in generateComboGuardianName(), which has the same issue — the name is regenerated on every lookup.

Root Cause

In src/lib/game/data/guardian-encounters.ts:

  1. Lines 37-43generateGuardianName() uses Math.random() to pick a prefix and title
  2. Lines 45-52generateComboGuardianName() also uses Math.random()
  3. Lines 130-136getGuardianForFloor() calls generateGuardianName() for static guardians with empty names on every invocation
  4. Lines 117-123getExtendedGuardian() calls generateComboGuardianName() on every invocation

Since getGuardianForFloor() is called during gameplay (tick processing, UI rendering), the names keep changing.

Fix Required

Option A: Pre-generate names at loop start (recommended)

  1. Add a function initializeGuardianNames() that runs once at loop start (called from initGame or prestige reset)
  2. Loop through all BASE_GUARDIANS entries with name: '' and assign a deterministic name using a seeded random or just pick the first prefix
  3. For combo guardians (150+), pre-generate names for the first N combo floors (e.g., 150-240) at loop start
  4. Store the generated names so subsequent calls to getGuardianForFloor() return the same name

Option B: Deterministic names based on floor

Instead of Math.random(), derive the name from the floor number:

// Use floor number to index into prefix/title arrays deterministically
const prefixIdx = floor % prefixes.length;
const titleIdx = Math.floor(floor / 10) % GUARDIAN_TITLES.length;

Option C: Pre-populate names in guardian-data.ts

Simply fill in the name field for floors 90, 100, 110, 120, 130, 140 in guardian-data.ts with fixed names, and remove the if (!g.name) fallback in getGuardianForFloor().

Affected Files

  • src/lib/game/data/guardian-encounters.tsgenerateGuardianName() (lines 37-43), generateComboGuardianName() (lines 45-52), getGuardianForFloor() (lines 128-137), getExtendedGuardian() (lines 115-124)
  • src/lib/game/data/guardian-data.ts — empty name fields on floors 90, 100, 110, 120, 130, 140
  • src/lib/game/stores/gameStore.ts — where initGame() is defined (to call name initialization)
## Bug Description Guardians defined with `name: ''` (floors 90-140 compound/exotic guardians) get a new random name generated every time `getGuardianForFloor()` is called, because `generateGuardianName()` uses `Math.random()`. This means the guardian's name changes every tick/refresh. Guardians should have their names locked in at loop start and remain stable. Additionally, the procedural combo guardians (floor 150+) also use `Math.random()` for name generation in `generateComboGuardianName()`, which has the same issue — the name is regenerated on every lookup. ## Root Cause In `src/lib/game/data/guardian-encounters.ts`: 1. **Lines 37-43** — `generateGuardianName()` uses `Math.random()` to pick a prefix and title 2. **Lines 45-52** — `generateComboGuardianName()` also uses `Math.random()` 3. **Lines 130-136** — `getGuardianForFloor()` calls `generateGuardianName()` for static guardians with empty names on every invocation 4. **Lines 117-123** — `getExtendedGuardian()` calls `generateComboGuardianName()` on every invocation Since `getGuardianForFloor()` is called during gameplay (tick processing, UI rendering), the names keep changing. ## Fix Required ### Option A: Pre-generate names at loop start (recommended) 1. Add a function `initializeGuardianNames()` that runs once at loop start (called from `initGame` or prestige reset) 2. Loop through all `BASE_GUARDIANS` entries with `name: ''` and assign a deterministic name using a seeded random or just pick the first prefix 3. For combo guardians (150+), pre-generate names for the first N combo floors (e.g., 150-240) at loop start 4. Store the generated names so subsequent calls to `getGuardianForFloor()` return the same name ### Option B: Deterministic names based on floor Instead of `Math.random()`, derive the name from the floor number: ```typescript // Use floor number to index into prefix/title arrays deterministically const prefixIdx = floor % prefixes.length; const titleIdx = Math.floor(floor / 10) % GUARDIAN_TITLES.length; ``` ### Option C: Pre-populate names in guardian-data.ts Simply fill in the `name` field for floors 90, 100, 110, 120, 130, 140 in `guardian-data.ts` with fixed names, and remove the `if (!g.name)` fallback in `getGuardianForFloor()`. ## Affected Files - `src/lib/game/data/guardian-encounters.ts` — `generateGuardianName()` (lines 37-43), `generateComboGuardianName()` (lines 45-52), `getGuardianForFloor()` (lines 128-137), `getExtendedGuardian()` (lines 115-124) - `src/lib/game/data/guardian-data.ts` — empty `name` fields on floors 90, 100, 110, 120, 130, 140 - `src/lib/game/stores/gameStore.ts` — where `initGame()` is defined (to call name initialization)
Anexim added the ai:todo label 2026-05-27 10:29:58 +02:00
n8n-gitea was assigned by Anexim 2026-05-27 10:29:58 +02:00
Author
Owner

Starting work on #161 — Guardian names change every tick due to Math.random() in name generation. Will implement Option B (deterministic names based on floor number) for all procedural guardians.

Starting work on #161 — Guardian names change every tick due to Math.random() in name generation. Will implement Option B (deterministic names based on floor number) for all procedural guardians.
Author
Owner

Fixed #161 — Guardian names are now deterministic per floor.

Changes:

  • src/lib/game/data/guardian-encounters.ts — Replaced Math.random() with floor-based deterministic indexing in generateGuardianName() and generateComboGuardianName(). Both functions now accept a floor parameter that is used to pick prefix/title from the arrays via modulo arithmetic.
  • src/lib/game/__tests__/guardian-names.test.ts — 17 regression tests verifying name determinism across all guardian types (compound, exotic, combo) and edge cases.

Root cause: Math.random() was called on every getGuardianForFloor() / getExtendedGuardian() invocation, producing different names each tick/refresh.

Fix: Floor number is now used as the seed for prefix/title selection, making names stable regardless of how many times the function is called.

All 902 tests pass.

Fixed #161 — Guardian names are now deterministic per floor. **Changes:** - `src/lib/game/data/guardian-encounters.ts` — Replaced `Math.random()` with floor-based deterministic indexing in `generateGuardianName()` and `generateComboGuardianName()`. Both functions now accept a `floor` parameter that is used to pick prefix/title from the arrays via modulo arithmetic. - `src/lib/game/__tests__/guardian-names.test.ts` — 17 regression tests verifying name determinism across all guardian types (compound, exotic, combo) and edge cases. **Root cause:** `Math.random()` was called on every `getGuardianForFloor()` / `getExtendedGuardian()` invocation, producing different names each tick/refresh. **Fix:** Floor number is now used as the seed for prefix/title selection, making names stable regardless of how many times the function is called. All 902 tests pass.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Anexim/Mana-Loop#161