Skip to content

fix(trades): normalize the rate against deuterium like the other resources - #39

Merged
rolljee merged 1 commit into
masterfrom
fix/parse-rate-normalizes-deuterium
Aug 15, 2026
Merged

fix(trades): normalize the rate against deuterium like the other resources#39
rolljee merged 1 commit into
masterfrom
fix/parse-rate-normalizes-deuterium

Conversation

@rolljee

@rolljee rolljee commented Aug 15, 2026

Copy link
Copy Markdown
Owner

The bug

parseRate(rate, type) is documented as normalizing a rate against a reference resource. The metal and crystal branches do that — they divide the other two terms by the term being sold. The deut branch reads them raw:

} else if (type === 'deut') {
  res = { rateMetal: metal, rateCrystal: crystal, rateDeut: 1 };
}

A rate is a ratio, so 4:3:2 states exactly what 2:1.5:1 states. After this function it did not:

rate sellMetal(1000) sellDeut(1000)
2:1.5:1 { deut: 200, crystal: 300 } { metal: 1200, crystal: 600 }
4:3:2 (before) { deut: 200, crystal: 300 } { metal: 2400, crystal: 1200 }
4:3:2 (after) { deut: 200, crystal: 300 } { metal: 1200, crystal: 600 }

Selling deuterium paid out scaled by the deuterium term. It only ever looked right because rates are conventionally written against 1 deuterium, which makes the division a no-op — every rate in the README, in the presets of ogame-ui and in !ogc on the Discord bot is of that form.

Found from the trade calculator in ogame-ui, whose custom-rate form lets you type any three numbers.

The fix

Divide by deut, like the other two branches divide by their own term. Two lines.

Compatibility

No change for any rate whose deuterium term is 1, which is every rate in the docs and every preset shipped by the callers. Only rates written against another scale move — and those were wrong.

Tests

  • parseRate('4:3:2', 'deut') normalizes to { rateMetal: 2, rateCrystal: 1.5, rateDeut: 1 }.
  • 4:3:2, 10:7.5:5 and 2:1.5:1 parse identically for all three reference resources.
  • sellDeut(100000, 50, 50, '4:3:2') returns the same as at 2:1.5:1.

Lint, types and the 318 tests pass locally.

Noted, not fixed here

parseRate still accepts terms it cannot use: a 0 term divides by zero and comes back as Infinity, an empty one ('2::1') reads as 0, and a negative one yields negative resources — hasNaN catches none of the three. That is a separate change with a wider blast radius (it turns cases that return a number into throws), so it is left out of this patch. ogame-ui validates its input before calling.

🤖 Generated with Claude Code

…urces

`parseRate(rate, 'deut')` read the metal and crystal terms raw, where the
metal and crystal branches divide by the term being sold. A rate is a
ratio, so `4:3:2` is the same rate as `2:1.5:1` — but it came back scaled
by the deuterium term, and `sellDeut` paid twice as much for the very
same deuterium.

It only ever looked right because rates are conventionally written
against 1 deuterium, which makes the division a no-op. The README already
described the function as normalizing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rolljee
rolljee merged commit 0830328 into master Aug 15, 2026
3 checks passed
@rolljee
rolljee deleted the fix/parse-rate-normalizes-deuterium branch August 15, 2026 05:42
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 4.0.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant