diff --git a/src/engine/PRNG.ts b/src/engine/PRNG.ts index 2fcf8733..3137f70f 100644 --- a/src/engine/PRNG.ts +++ b/src/engine/PRNG.ts @@ -1,16 +1,26 @@ -// Taken from https://gist.github.com/blixt/f17b47c62508be59987b +// Taken from https://github.com/bryc/code/blob/master/jshash/PRNGs.md // Ink uses a seedable PRNG of which there is none in native javascript. export class PRNG { private seed: number; constructor(seed: number) { - this.seed = seed % 2147483647; - if (this.seed <= 0) this.seed += 2147483646; + this.seed = seed; + } + + public nextSeed(): number { + let a = this.seed; + a |= 0; + a = (a + 0x6d2b79f5) | 0; + this.seed = a; + return a; } public next(): number { - return (this.seed = (this.seed * 16807) % 2147483647); + let a = this.seed; + let t = Math.imul(a ^ (a >>> 15), 1 | a); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return (t ^ (t >>> 14)) >>> 0; } public nextFloat(): number { - return (this.next() - 1) / 2147483646; + return this.next() / 4294967296; } } diff --git a/src/engine/Story.ts b/src/engine/Story.ts index b06d1419..f03d0420 100644 --- a/src/engine/Story.ts +++ b/src/engine/Story.ts @@ -1189,7 +1189,7 @@ export class Story extends InkObject { ". The maximum must be larger" ); - let resultSeed = this.state.storySeed + this.state.previousRandom; + let resultSeed = this.state.previousRandom; let random = new PRNG(resultSeed); let nextRandom = random.next(); @@ -1197,7 +1197,7 @@ export class Story extends InkObject { this.state.PushEvaluationStack(new IntValue(chosenValue)); // Next random number (rather than keeping the Random object around) - this.state.previousRandom = nextRandom; + this.state.previousRandom = random.nextSeed(); break; } @@ -1212,8 +1212,7 @@ export class Story extends InkObject { return throwNullException("minInt.value"); } - this.state.storySeed = seed.value; - this.state.previousRandom = 0; + this.state.previousRandom = seed.value; this.state.PushEvaluationStack(new Void()); break; @@ -1349,7 +1348,7 @@ export class Story extends InkObject { newList = new InkList(); } else { // Generate a random index for the element to take - let resultSeed = this.state.storySeed + this.state.previousRandom; + let resultSeed = this.state.previousRandom; let random = new PRNG(resultSeed); let nextRandom = random.next(); @@ -1377,7 +1376,7 @@ export class Story extends InkObject { newList = new InkList(randomItem.Key.originName, this); newList.Add(randomItem.Key, randomItem.Value); - this.state.previousRandom = nextRandom; + this.state.previousRandom = random.nextSeed(); } this.state.PushEvaluationStack(new ListValue(newList)); @@ -2092,7 +2091,9 @@ export class Story extends InkObject { } for (let i = 0; i <= iterationIndex; ++i) { - let chosen = random.next() % unpickedIndices.length; + let nextRandom = random.next(); + let chosen = nextRandom % unpickedIndices.length; + random.nextSeed(); let chosenIndex = unpickedIndices[chosen]; unpickedIndices.splice(chosen, 1); diff --git a/src/engine/StoryState.ts b/src/engine/StoryState.ts index 9a0588c1..35d7d07d 100644 --- a/src/engine/StoryState.ts +++ b/src/engine/StoryState.ts @@ -350,9 +350,9 @@ export class StoryState { this._turnIndices = new Map(); this.currentTurnIndex = -1; - let timeSeed = new Date().getTime(); - this.storySeed = new PRNG(timeSeed).next() % 100; - this.previousRandom = 0; + let timeSeed = Date.now(); + this.storySeed = new PRNG(timeSeed).nextSeed(); + this.previousRandom = this.storySeed; this._currentChoices = [];