arbitrary-numbers 1.0.1 → 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.
Files changed (2) hide show
  1. package/README.md +132 -61
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -33,31 +33,52 @@ Requires TypeScript `"strict": true`.
33
33
  ```typescript
34
34
  import { an, chain, formula, unitNotation } from "arbitrary-numbers";
35
35
 
36
- // Exact at any scale, no overflow or silent precision loss
37
- const base = an(1, 9); // 1,000,000,000
38
- const armor = an(2, 6); // 2,000,000
39
- 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
40
39
 
41
- // Damage formula: (base - armor) * 0.75
42
- const damage = chain(base)
43
- .subMul(armor, an(7.5, -1))
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))
44
47
  .floor()
45
48
  .done();
46
49
 
47
- damage.toString(unitNotation) // "748.50 M"
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));
48
52
 
49
- // Per-tick update: fused op, one allocation instead of two
50
- gold = gold.mulAdd(an(1.05), an(1, 4)); // (gold * 1.05) + 10,000
53
+ let gold = an(7.5, 12);
54
+ for (let i = 0; i < 3; i += 1) {
55
+ gold = tick.apply(gold);
56
+ }
51
57
 
52
- // Reusable formula pipeline applied to multiple values
53
- const applyArmor = formula("Armor").subMul(armor, an(7.5, -1)).floor();
54
- const physDamage = applyArmor.apply(base);
55
- const magDamage = applyArmor.apply(an(8, 8));
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()}`);
56
63
 
57
- // Formatting adapts at every scale automatically
58
- an(1.5, 3).toString(unitNotation) // "1.50 K"
59
- an(1.5, 6).toString(unitNotation) // "1.50 M"
60
- an(1.5, 9).toString(unitNotation) // "1.50 B"
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
61
82
  ```
62
83
 
63
84
  ## Table of contents
@@ -441,67 +462,117 @@ an(3.2, 6).toString(new TierNotation({ separator: " " })) // "3.20 M"
441
462
 
442
463
  ## Idle game example
443
464
 
444
- A self-contained simulation showing `an()`, fused ops, `chain()`, helpers, and `unitNotation` working together.
465
+ A self-contained simulation showing hyper-growth, fused ops, helpers, and where plain JS `number` overflows while `ArbitraryNumber` keeps working.
445
466
 
446
467
  ```typescript
447
468
  import {
448
- ArbitraryNumber, an, chain,
449
- unitNotation, ArbitraryNumberHelpers as helpers,
469
+ an, chain,
470
+ UnitNotation,
471
+ CLASSIC_UNITS,
472
+ letterNotation,
473
+ ArbitraryNumberHelpers as helpers,
450
474
  } from "arbitrary-numbers";
475
+ import type { ArbitraryNumber } from "arbitrary-numbers";
451
476
 
452
- let gold = ArbitraryNumber.Zero;
453
- let goldPerSec = an(1);
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;
454
481
 
455
- const UPGRADES = [
456
- { label: "Copper Pick ", cost: an(50), mult: an(5) },
457
- { label: "Iron Mine ", cost: an(1, 3), mult: an(20) },
458
- { label: "Gold Refinery", cost: an(5, 6), mult: an(1, 4) },
459
- ] as const;
482
+ const display = new UnitNotation({
483
+ units: CLASSIC_UNITS,
484
+ fallback: letterNotation,
485
+ });
460
486
 
461
- function tick(): void {
462
- gold = gold.add(goldPerSec);
487
+ function fmt(value: ArbitraryNumber, decimals = 2): string {
488
+ return value.toString(display, decimals);
463
489
  }
464
490
 
465
- function tryBuyAll(): void {
466
- for (const u of UPGRADES) {
467
- if (!helpers.meetsOrExceeds(gold, u.cost)) continue;
468
- gold = gold.sub(u.cost);
469
- goldPerSec = goldPerSec.mul(u.mult);
470
- console.log(` bought ${u.label} GPS: ${goldPerSec.toString(unitNotation)}`);
471
- }
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
+ );
472
496
  }
473
497
 
474
- function prestige(multiplier: ArbitraryNumber): void {
475
- goldPerSec = chain(goldPerSec)
476
- .mulAdd(multiplier, an(1)) // (gps * mult) + 1, fused
477
- .floor()
478
- .done();
479
- console.log(` prestige! new GPS: ${goldPerSec.toString(unitNotation)}`);
480
- }
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
+ }
481
518
 
482
- for (let t = 1; t <= 1_000_000; t++) {
483
- tick();
484
- if (t % 10 === 0) tryBuyAll();
485
- if (t === 51_000) prestige(an(1.5));
486
- if (t % 250_000 === 0) {
487
- const g = gold.toString(unitNotation, 3);
488
- const gps = goldPerSec.toString(unitNotation, 3);
489
- console.log(`[t=${String(t).padStart(9)}] gold: ${g.padStart(12)} gps: ${gps}`);
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);
490
531
  }
491
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.");
492
541
  ```
493
542
 
494
543
  Output:
495
544
 
496
- ```
497
- bought Copper Pick GPS: 5.00
498
- bought Iron Mine GPS: 100.00
499
- bought Gold Refinery GPS: 1.00 M
500
- prestige! new GPS: 1.50 M
501
- [t= 250000] gold: 199.750 B gps: 1.500 M
502
- [t= 500000] gold: 574.750 B gps: 1.500 M
503
- [t= 750000] gold: 949.750 B gps: 1.500 M
504
- [t= 1000000] gold: 1.325 T gps: 1.500 M
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.
505
576
  ```
506
577
 
507
578
  ## Performance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arbitrary-numbers",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
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",