random.test.ts 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import { describe, expect, it } from "vitest";
  2. import { randomSeed, seedRandom, type RandomSource } from "../src/index.js";
  3. describe("seedRandom", () => {
  4. it("same seed produces the identical next() sequence", () => {
  5. const a = seedRandom(42);
  6. const b = seedRandom(42);
  7. const seqA = Array.from({ length: 100 }, () => a.next());
  8. const seqB = Array.from({ length: 100 }, () => b.next());
  9. expect(seqB).toEqual(seqA);
  10. });
  11. it("next() draws are within [0, 1)", () => {
  12. const rng = seedRandom(7);
  13. for (let i = 0; i < 1000; i++) {
  14. const draw = rng.next();
  15. expect(draw).toBeGreaterThanOrEqual(0);
  16. expect(draw).toBeLessThan(1);
  17. }
  18. });
  19. it("different seeds diverge", () => {
  20. const a = seedRandom(1);
  21. const b = seedRandom(2);
  22. expect(a.next()).not.toBe(b.next());
  23. });
  24. it("nextInt returns integers in [0, max) for positive max", () => {
  25. const rng = seedRandom(99);
  26. for (let i = 0; i < 500; i++) {
  27. const value = rng.nextInt(10);
  28. expect(Number.isInteger(value)).toBe(true);
  29. expect(value).toBeGreaterThanOrEqual(0);
  30. expect(value).toBeLessThan(10);
  31. }
  32. });
  33. it("nextInt rejects non-positive max", () => {
  34. const rng = seedRandom(1);
  35. expect(() => rng.nextInt(0)).toThrow(/max must be a positive number/);
  36. expect(() => rng.nextInt(-3)).toThrow(/max must be a positive number/);
  37. expect(() => rng.nextInt(Number.NaN)).toThrow(/max must be a positive number/);
  38. });
  39. it("setSeed resets the sequence to the new seed's stream", () => {
  40. const rng = seedRandom(1);
  41. rng.next();
  42. rng.next();
  43. const fresh = seedRandom(1);
  44. expect(rng.state().calls).toBe(2);
  45. rng.setSeed(1);
  46. expect(rng.state()).toEqual({ seed: 1, calls: 0 });
  47. expect(rng.next()).toBe(fresh.next());
  48. expect(rng.next()).toBe(fresh.next());
  49. });
  50. it("setSeed normalizes to uint32 and resets calls", () => {
  51. const rng = seedRandom(0);
  52. rng.next();
  53. rng.setSeed(4294967297);
  54. expect(rng.state().calls).toBe(0);
  55. expect(rng.state().seed).toBe(1);
  56. });
  57. it("state() reports seed and calls; calls counts draws", () => {
  58. const rng: RandomSource = seedRandom(123);
  59. expect(rng.state()).toEqual({ seed: 123, calls: 0 });
  60. rng.next();
  61. rng.nextInt(5);
  62. expect(rng.state()).toEqual({ seed: 123, calls: 2 });
  63. });
  64. });
  65. describe("randomSeed", () => {
  66. it("returns a finite uint32 number", () => {
  67. const seed = randomSeed();
  68. expect(typeof seed).toBe("number");
  69. expect(Number.isFinite(seed)).toBe(true);
  70. expect(seed).toBeGreaterThanOrEqual(0);
  71. expect(seed).toBeLessThanOrEqual(0xffffffff);
  72. });
  73. it("produces a usable seed for seedRandom", () => {
  74. const rng = seedRandom(randomSeed());
  75. expect(rng.next()).toBeGreaterThanOrEqual(0);
  76. expect(rng.next()).toBeLessThan(1);
  77. });
  78. });