-
-
Save painkkiller/3050b1e37e451ed61b14e10052554526 to your computer and use it in GitHub Desktop.
EDIT: This code no longer works on chrome but still works for legacy versions of node. Updated code moved to https://github.com/fta2012/ReplicatedRandom/tree/master/node.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var rngstate; | |
function MathRandom() { | |
// Our own implementation of Math.random(). | |
// Source code was copied from https://github.com/v8/v8/blob/4.6.85/src/math.js#L131 | |
// You need to initialize rngstate with `solve` before this can be used. | |
// If using node.js, you have to s/18030/18273/g here and in `solve` since they implement it slightly differently: https://github.com/nodejs/node-v0.x-archive/blob/d13d7f74d794340ac5e126cfb4ce507fe0f803d5/deps/v8/src/math.js#L146 | |
console.assert(rngstate, "You need to set the global variable `rngstate` first. For example: `rngstate = solve(Math.random(), Math.random());`"); | |
if (!rngstate) return; | |
var r0 = (Math.imul(18030, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0; | |
rngstate[0] = r0; | |
var r1 = (Math.imul(36969, rngstate[1] & 0xFFFF) + (rngstate[1] >>> 16)) | 0; | |
rngstate[1] = r1; | |
var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0; | |
// Division by 0x100000000 through multiplication by reciprocal. | |
return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10; | |
} | |
function solve(first, second, gap) { | |
// Given two values of Math.random() and number of other calls to Math.random() in between, solve for the rngstate. | |
var first_x = first / 2.3283064365386962890625e-10; | |
var first_r0_lower = first_x >>> 16; | |
var first_r1_lower = first_x & 0xFFFF; | |
var second_x = second / 2.3283064365386962890625e-10; | |
var second_r0_lower = second_x >>> 16; | |
var second_r1_lower = second_x & 0xFFFF; | |
var first_r0_upper, first_r1_upper, second_r0, second_r1; | |
var found = false; | |
for (first_r0_upper = 0; first_r0_upper <= 0xFFFF; first_r0_upper++) { | |
second_r0 = (first_r0_upper << 16) | first_r0_lower; | |
for (var i = 0; i <= gap; i++) { | |
second_r0 = (Math.imul(18030, second_r0 & 0xFFFF) + (second_r0 >>> 16)) | 0; | |
} | |
if ((second_r0 & 0xFFFF) == second_r0_lower) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
return null; | |
} | |
found = false; | |
for (first_r1_upper = 0; first_r1_upper <= 0xFFFF; first_r1_upper++) { | |
second_r1 = (first_r1_upper << 16) | first_r1_lower; | |
for (var i = 0; i <= gap; i++) { | |
second_r1 = (Math.imul(36969, second_r1 & 0xFFFF) + (second_r1 >>> 16)) | 0; | |
} | |
if ((second_r1 & 0xFFFF) == second_r1_lower) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
return null; | |
} | |
return [second_r0, second_r1]; | |
} | |
// Example usage. Needs two prior known values (`observation1` and `observation2`) to pass to `solve` | |
var num_unknown_values = 0; // Change this to the number of calls to Math.random() between our two observations | |
var observation1 = Math.random(); | |
for (var i = 0; i < num_unknown_values; i++) { | |
Math.random(); | |
} | |
var observation2 = Math.random(); | |
rngstate = solve(observation1, observation2, num_unknown_values); | |
// Test | |
for (var i = 0; i < 10; i++) { | |
var predicted = MathRandom(); // Our own implementation of Math.random() with the solved rngstate from above | |
console.log('Predicted:', predicted); | |
var observation = Math.random(); | |
console.log('Actual:', observation); | |
console.assert(observation == predicted); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment