import InfoBox from '../components/InfoBox.astro';
import PullQuote from '../components/PullQuote.astro';

When fruit tier scoring rules were originally introduced during [five-new-combos-and-fruit-tiers](/posts/five-new-combos-and-fruit-tiers#decide-the-system-before-adding-the-fruit), point values were stored in the engine's `EDIBLE_POINTS` map. However, the game's help screen and edible tooltips described these values using hardcoded string literals inside localized dictionary files. When scoring values were balanced, the UI help text remained fixed to stale numbers, creating quiet discrepancies between what the help page promised and what the engine actually awarded during gameplay.

## Extracting dynamic fruit facts

To eliminate drift between engine constants and UI presentation, static point descriptors were removed from dictionary files. A new `fruitFacts.ts` module was introduced in the help route to compute fruit values dynamically directly from `EDIBLE_POINTS`.

```typescript
export function getFruitFact(type: EdibleType, locale: Locale): string {
  const points = EDIBLE_POINTS[type];
  const template = strings.fruitPointTemplate[locale];
  return template.replace('{points}', points.toString());
}
```

Instead of storing full sentences per fruit, dictionary files now supply a single parameterized format string. Replacing hardcoded values with dynamic interpolation ensures that any future adjustment to `EDIBLE_POINTS` updates all help copy across both languages automatically.

<InfoBox variant="note" title="Engine contract boundary">
UI help components query `EDIBLE_POINTS` as a read-only constant, keeping scoring calculation logic completely within the engine while letting presentation layers format output for display.
</InfoBox>

## Simplifying the dictionary schema

Removing duplicate point numbers reduced translation dictionary overhead across all supported languages. The dictionary schema dropped multiple per-fruit point descriptors in favor of a shared template string.

<PullQuote attribution="Domain single source of truth">
Deriving presentation text from engine constants prevents documentation drift without duplicating scoring formulas across translation dictionaries.
</PullQuote>

This reduction simplified string maintenance while ensuring that adding a new edible tier requires updating only the engine config and icon asset.

## Locking derived copy with tests

To prevent regressions where dynamic formatting might fail or return unpopulated placeholders, a new test suite in `fruitFacts.test.ts` validates output for every registered edible tier.

```typescript
it('formats point values correctly for every edible type', () => {
  for (const type of EDIBLE_TYPES) {
    const fact = getFruitFact(type, 'en');
    expect(fact).toContain(EDIBLE_POINTS[type].toString());
    expect(fact).not.toContain('{points}');
  }
});
```

With `fruitFacts.ts` in place, all help components and edible tooltips render derived point values, ensuring complete consistency across the game's documentation and scoring engine.