Skip to content

Instantly share code, notes, and snippets.

@ashnur
Forked from fta2012/ReplicatedRandomChrome.js
Created September 25, 2015 10:12
Show Gist options
  • Save ashnur/4827292c701ac899fea2 to your computer and use it in GitHub Desktop.
Save ashnur/4827292c701ac899fea2 to your computer and use it in GitHub Desktop.
Demonstrates how to replicate a stream of Math.random() given two past observations for **Chrome**. Code was only tried on Chrome 43 (07/2015). Answers the question from this issue: https://github.com/fta2012/ReplicatedRandom/issues/2.
var rngstate;
function MathRandom() {
// Our own implementation of Math.random().
// Source code was copied from https://code.google.com/p/chromium/codesearch#chromium/src/v8/src/math.js&q=MathRandom&sq=package:chromium&type=cs&l=130
// You need to solve for the rngstate first before this can be used.
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
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();
var observation = Math.random();
console.assert(observation == predicted);
console.log(observation, predicted);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment