arbitrary-numbers 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -64
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,7 +10,15 @@
|
|
|
10
10
|
[](package.json)
|
|
11
11
|
</div>
|
|
12
12
|
|
|
13
|
-
`arbitrary-numbers`
|
|
13
|
+
`arbitrary-numbers` fills a specific gap: JavaScript's `Number` type silently loses precision above `Number.MAX_SAFE_INTEGER`, and `BigInt` can't represent decimals, so working with extremely large or small magnitudes often requires manual scaling into very large integers.
|
|
14
|
+
|
|
15
|
+
Numbers are stored as a normalized `coefficient × 10^exponent` pair. That makes arithmetic across wildly different scales fast and predictable — exactly what idle games and simulations need when values span from `1` to `10^300` in the same loop.
|
|
16
|
+
|
|
17
|
+
- **Immutable by default** — every operation returns a new instance, no surprise mutations
|
|
18
|
+
- **Fused operations** (`mulAdd`, `subMul`, ...) — reduce allocations in hot loops
|
|
19
|
+
- **Formula pipelines** — define an expression once, apply it to any number of values
|
|
20
|
+
- **Pluggable display** — swap between scientific, unit (K/M/B/T), and letter notation without touching game logic
|
|
21
|
+
- **Zero dependencies** — nothing to audit, nothing to break
|
|
14
22
|
|
|
15
23
|
## Install
|
|
16
24
|
|
|
@@ -25,46 +33,77 @@ Requires TypeScript `"strict": true`.
|
|
|
25
33
|
```typescript
|
|
26
34
|
import { an, chain, formula, unitNotation } from "arbitrary-numbers";
|
|
27
35
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
let gold = an(5, 6); // 5,000,000
|
|
36
|
+
// JavaScript range limits
|
|
37
|
+
const jsHuge = Number("1e500"); // Infinity
|
|
38
|
+
const jsTiny = Number("1e-500"); // 0
|
|
32
39
|
|
|
33
|
-
//
|
|
34
|
-
const
|
|
35
|
-
|
|
40
|
+
// Arbitrary range in both directions
|
|
41
|
+
const huge = an(1, 500);
|
|
42
|
+
const tiny = an(1, -500);
|
|
43
|
+
|
|
44
|
+
// One-off pipeline with chain(): (6.2e15 - 8.5e13) * 0.75
|
|
45
|
+
const damage = chain(an(6.2, 15))
|
|
46
|
+
.subMul(an(8.5, 13), an(7.5, -1))
|
|
36
47
|
.floor()
|
|
37
48
|
.done();
|
|
38
49
|
|
|
39
|
-
|
|
50
|
+
// Reusable per-tick formula: gold = (gold * 1.08) + 2_500_000
|
|
51
|
+
const tick = formula("tick").mulAdd(an(1.08), an(2.5, 6));
|
|
40
52
|
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
let gold = an(7.5, 12);
|
|
54
|
+
for (let i = 0; i < 3; i += 1) {
|
|
55
|
+
gold = tick.apply(gold);
|
|
56
|
+
}
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
console.log("=== Range limits (JS vs arbitrary-numbers) ===");
|
|
59
|
+
console.log(`JS Number('1e500') -> ${jsHuge}`);
|
|
60
|
+
console.log(`AN an(1, 500) -> ${huge.toString()}`);
|
|
61
|
+
console.log(`JS Number('1e-500') -> ${jsTiny}`);
|
|
62
|
+
console.log(`AN an(1, -500) -> ${tiny.toString()}`);
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
console.log("");
|
|
65
|
+
console.log("=== Game math helpers ===");
|
|
66
|
+
console.log(`Damage (chain + fused subMul) -> ${damage.toString(unitNotation)}`);
|
|
67
|
+
console.log(`Gold after 3 ticks (formula) -> ${gold.toString(unitNotation)}`);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Example output when running this in a repository checkout (for example with `npx tsx examples/quickstart.ts`):
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
=== Range limits (JS vs arbitrary-numbers) ===
|
|
74
|
+
JS Number('1e500') -> Infinity
|
|
75
|
+
AN an(1, 500) -> 1.00e+500
|
|
76
|
+
JS Number('1e-500') -> 0
|
|
77
|
+
AN an(1, -500) -> 1.00e-500
|
|
78
|
+
|
|
79
|
+
=== Game math helpers ===
|
|
80
|
+
Damage (chain + fused subMul) -> 4.59 Qa
|
|
81
|
+
Gold after 3 ticks (formula) -> 9.45 T
|
|
53
82
|
```
|
|
54
83
|
|
|
55
84
|
## Table of contents
|
|
56
85
|
|
|
86
|
+
- [Install](#install)
|
|
87
|
+
- [Quick start](#quick-start)
|
|
88
|
+
- [Table of contents](#table-of-contents)
|
|
57
89
|
- [Creating numbers](#creating-numbers)
|
|
58
90
|
- [Arithmetic](#arithmetic)
|
|
59
91
|
- [Fused operations](#fused-operations)
|
|
60
|
-
- [Fluent builder - chain()](#fluent-builder---chain)
|
|
61
|
-
- [Reusable formulas - formula()](#reusable-formulas---formula)
|
|
92
|
+
- [Fluent builder - `chain()`](#fluent-builder---chain)
|
|
93
|
+
- [Reusable formulas - `formula()`](#reusable-formulas---formula)
|
|
94
|
+
- [chain() vs formula()](#chain-vs-formula)
|
|
62
95
|
- [Comparison and predicates](#comparison-and-predicates)
|
|
63
96
|
- [Rounding and math](#rounding-and-math)
|
|
64
97
|
- [Display and formatting](#display-and-formatting)
|
|
98
|
+
- [scientificNotation (default)](#scientificnotation-default)
|
|
99
|
+
- [unitNotation - K, M, B, T...](#unitnotation---k-m-b-t)
|
|
100
|
+
- [AlphabetNotation - a, b, c... aa, ab...](#alphabetnotation---a-b-c-aa-ab)
|
|
65
101
|
- [Precision control](#precision-control)
|
|
66
102
|
- [Errors](#errors)
|
|
67
103
|
- [Utilities](#utilities)
|
|
104
|
+
- [ArbitraryNumberOps - mixed `number | ArbitraryNumber` input](#arbitrarynumberops---mixed-number--arbitrarynumber-input)
|
|
105
|
+
- [ArbitraryNumberGuard - type guards](#arbitrarynumberguard---type-guards)
|
|
106
|
+
- [ArbitraryNumberHelpers - game and simulation patterns](#arbitrarynumberhelpers---game-and-simulation-patterns)
|
|
68
107
|
- [Writing a custom plugin](#writing-a-custom-plugin)
|
|
69
108
|
- [Idle game example](#idle-game-example)
|
|
70
109
|
- [Performance](#performance)
|
|
@@ -423,67 +462,117 @@ an(3.2, 6).toString(new TierNotation({ separator: " " })) // "3.20 M"
|
|
|
423
462
|
|
|
424
463
|
## Idle game example
|
|
425
464
|
|
|
426
|
-
A self-contained simulation showing
|
|
465
|
+
A self-contained simulation showing hyper-growth, fused ops, helpers, and where plain JS `number` overflows while `ArbitraryNumber` keeps working.
|
|
427
466
|
|
|
428
467
|
```typescript
|
|
429
468
|
import {
|
|
430
|
-
|
|
431
|
-
|
|
469
|
+
an, chain,
|
|
470
|
+
UnitNotation,
|
|
471
|
+
CLASSIC_UNITS,
|
|
472
|
+
letterNotation,
|
|
473
|
+
ArbitraryNumberHelpers as helpers,
|
|
432
474
|
} from "arbitrary-numbers";
|
|
475
|
+
import type { ArbitraryNumber } from "arbitrary-numbers";
|
|
433
476
|
|
|
434
|
-
let gold
|
|
435
|
-
let
|
|
477
|
+
let gold = an(5, 6); // 5,000,000
|
|
478
|
+
let gps = an(2, 5); // 200,000 per tick
|
|
479
|
+
let reactorCost = an(1, 9);
|
|
480
|
+
let reactors = 0;
|
|
436
481
|
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
] as const;
|
|
482
|
+
const display = new UnitNotation({
|
|
483
|
+
units: CLASSIC_UNITS,
|
|
484
|
+
fallback: letterNotation,
|
|
485
|
+
});
|
|
442
486
|
|
|
443
|
-
function
|
|
444
|
-
|
|
487
|
+
function fmt(value: ArbitraryNumber, decimals = 2): string {
|
|
488
|
+
return value.toString(display, decimals);
|
|
445
489
|
}
|
|
446
490
|
|
|
447
|
-
function
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
gold
|
|
451
|
-
|
|
452
|
-
console.log(` bought ${u.label} GPS: ${goldPerSec.toString(unitNotation)}`);
|
|
453
|
-
}
|
|
491
|
+
function snapshot(tick: number): void {
|
|
492
|
+
console.log(
|
|
493
|
+
`[t=${String(tick).padStart(4)}] SNAPSHOT `
|
|
494
|
+
+ `gold=${fmt(gold, 2).padStart(12)} gps=${fmt(gps, 2).padStart(12)}`,
|
|
495
|
+
);
|
|
454
496
|
}
|
|
455
497
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
498
|
+
console.log("=== Hyper-growth idle loop (720 ticks) ===");
|
|
499
|
+
console.log(`start gold=${fmt(gold)} gps=${fmt(gps)} reactorCost=${fmt(reactorCost)}`);
|
|
500
|
+
|
|
501
|
+
for (let t = 1; t <= 720; t += 1) {
|
|
502
|
+
// Core growth: gold = (gold * 1.12) + gps
|
|
503
|
+
gold = gold.mulAdd(an(1.12), gps);
|
|
504
|
+
|
|
505
|
+
if (t % 60 === 0 && helpers.meetsOrExceeds(gold, reactorCost)) {
|
|
506
|
+
const before = gps;
|
|
507
|
+
gold = gold.sub(reactorCost);
|
|
508
|
+
gps = chain(gps).mul(an(1, 25)).floor().done();
|
|
509
|
+
reactorCost = reactorCost.mul(an(8));
|
|
510
|
+
reactors += 1;
|
|
511
|
+
|
|
512
|
+
console.log(
|
|
513
|
+
`[t=${String(t).padStart(4)}] REACTOR #${String(reactors).padStart(2)} `
|
|
514
|
+
+ `gps ${fmt(before)} -> ${fmt(gps)} `
|
|
515
|
+
+ `nextCost=${fmt(reactorCost)}`,
|
|
516
|
+
);
|
|
517
|
+
}
|
|
463
518
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
console.log(`[t=${String(t).padStart(
|
|
519
|
+
if (t === 240 || t === 480) {
|
|
520
|
+
const before = gps;
|
|
521
|
+
gps = chain(gps)
|
|
522
|
+
.mul(an(1, 4))
|
|
523
|
+
.add(an(7.5, 6))
|
|
524
|
+
.floor()
|
|
525
|
+
.done();
|
|
526
|
+
console.log(`[t=${String(t).padStart(4)}] PRESTIGE gps ${fmt(before)} -> ${fmt(gps)}`);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (t % 120 === 0) {
|
|
530
|
+
snapshot(t);
|
|
472
531
|
}
|
|
473
532
|
}
|
|
533
|
+
|
|
534
|
+
console.log("\n=== Final scale check ===");
|
|
535
|
+
console.log(`reactors bought: ${reactors}`);
|
|
536
|
+
console.log(`final gold (unit+letter): ${fmt(gold)}`);
|
|
537
|
+
console.log(`final gps (unit+letter): ${fmt(gps)}`);
|
|
538
|
+
console.log(`final gold as JS Number: ${gold.toNumber()}`);
|
|
539
|
+
console.log(`final gps as JS Number : ${gps.toNumber()}`);
|
|
540
|
+
console.log("If JS shows Infinity while unit+letter output stays finite, the library is doing its job.");
|
|
474
541
|
```
|
|
475
542
|
|
|
476
543
|
Output:
|
|
477
544
|
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
[t=
|
|
484
|
-
[t=
|
|
485
|
-
[t=
|
|
486
|
-
[t=
|
|
545
|
+
```text
|
|
546
|
+
=== Hyper-growth idle loop (720 ticks) ===
|
|
547
|
+
start gold=5.00 M gps=200.00 K reactorCost=1.00 B
|
|
548
|
+
[t= 60] REACTOR # 1 gps 200.00 K -> 2.00 No nextCost=8.00 B
|
|
549
|
+
[t= 120] REACTOR # 2 gps 2.00 No -> 20.00 SpDc nextCost=64.00 B
|
|
550
|
+
[t= 120] SNAPSHOT gold= 14.94 Dc gps= 20.00 SpDc
|
|
551
|
+
[t= 180] REACTOR # 3 gps 20.00 SpDc -> 200.00 QiVg nextCost=512.00 B
|
|
552
|
+
[t= 240] REACTOR # 4 gps 200.00 QiVg -> 2.00 ai nextCost=4.10 T
|
|
553
|
+
[t= 240] PRESTIGE gps 2.00 ai -> 20.00 aj
|
|
554
|
+
[t= 240] SNAPSHOT gold= 1.49 SpVg gps= 20.00 aj
|
|
555
|
+
[t= 300] REACTOR # 5 gps 20.00 aj -> 200.00 ar nextCost=32.77 T
|
|
556
|
+
[t= 360] REACTOR # 6 gps 200.00 ar -> 2.00 ba nextCost=262.14 T
|
|
557
|
+
[t= 360] SNAPSHOT gold= 1.49 at gps= 2.00 ba
|
|
558
|
+
[t= 420] REACTOR # 7 gps 2.00 ba -> 20.00 bi nextCost=2.10 Qa
|
|
559
|
+
[t= 480] REACTOR # 8 gps 20.00 bi -> 200.00 bq nextCost=16.78 Qa
|
|
560
|
+
[t= 480] PRESTIGE gps 200.00 bq -> 2.00 bs
|
|
561
|
+
[t= 480] SNAPSHOT gold= 149.43 bj gps= 2.00 bs
|
|
562
|
+
[t= 540] REACTOR # 9 gps 2.00 bs -> 20.00 ca nextCost=134.22 Qa
|
|
563
|
+
[t= 600] REACTOR #10 gps 20.00 ca -> 200.00 ci nextCost=1.07 Qi
|
|
564
|
+
[t= 600] SNAPSHOT gold= 149.43 cb gps= 200.00 ci
|
|
565
|
+
[t= 660] REACTOR #11 gps 200.00 ci -> 2.00 cr nextCost=8.59 Qi
|
|
566
|
+
[t= 720] REACTOR #12 gps 2.00 cr -> 20.00 cz nextCost=68.72 Qi
|
|
567
|
+
[t= 720] SNAPSHOT gold= 14.94 cs gps= 20.00 cz
|
|
568
|
+
|
|
569
|
+
=== Final scale check ===
|
|
570
|
+
reactors bought: 12
|
|
571
|
+
final gold (unit+letter): 14.94 cs
|
|
572
|
+
final gps (unit+letter): 20.00 cz
|
|
573
|
+
final gold as JS Number: 1.494328222485101e+292
|
|
574
|
+
final gps as JS Number : Infinity
|
|
575
|
+
If JS shows Infinity while unit+letter output stays finite, the library is doing its job.
|
|
487
576
|
```
|
|
488
577
|
|
|
489
578
|
## Performance
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arbitrary-numbers",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Arbitrary-magnitude arithmetic for idle games and simulations, with fused operations, formula pipelines, and pluggable number formatting.",
|
|
5
5
|
"author": "Chris",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|