├── .gitignore ├── LICENSE ├── README.md ├── alea.js ├── index.d.ts ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (C) 2010 by Johannes Baagøe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alea 2 | 3 | A simple copy-and-paste implementation of Johannes Baagøe's Alea PRNG 4 | 5 | Mostly packaged so I can easily include it in my projeccts. Nothing more 6 | 7 | JavaScript's Math.random() is fast, but has problems. First, it isn't seedable, second, its randomness leaves a bit to be desired. [Johannes Baagøe](http://baagoe.org/) has done some great work in trying to find a more modern PRNG algorithm that performs well on JavaScript, and Alea seems to be the one that has come out ahead ([benchmarks](http://jsperf.com/prng-comparison)). 8 | 9 | ## Installation 10 | 11 | npm install alea 12 | 13 | ## Usage 14 | 15 | var Alea = require('alea') 16 | 17 | var prng = new Alea() // add an optional seed param 18 | 19 | var nextRandnum = prng() // just call the return value of Alea 20 | 21 | ## Additions 22 | 23 | Also adds the ability to sync up two Alea PRNGs via the importState and exportState methods. 24 | 25 | var prng1 = new Alea(200) 26 | 27 | prng1() 28 | prng1() 29 | 30 | // after generating a few random numbers, we will initialize a new PRNG 31 | 32 | var prng2 = Alea.importState(prng1.exportState()) 33 | 34 | // this should echo true, true, true 35 | console.log(prng2() == prng1()) 36 | console.log(prng2() == prng1()) 37 | console.log(prng2() == prng1()) 38 | 39 | The theory behind this is that while a server is running a simulation (for example, a game) and clients connect to the server, each client will run its own simulation without having to depend 100% on the server for every update of the simulation state. By importing the current generator state from the server, a client can join in at any time and have an accurate simulation fully in sync with the server. 40 | 41 | ## Acknowledgements 42 | 43 | Everything in this module was made by Johannes Baagøe. I just wanted this in npm. 44 | Read more on his [homepage](http://baagoe.org/). 45 | -------------------------------------------------------------------------------- /alea.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof exports === 'object') { 3 | module.exports = factory(); 4 | } else if (typeof define === 'function' && define.amd) { 5 | define(factory); 6 | } else { 7 | root.Alea = factory(); 8 | } 9 | }(this, function () { 10 | 11 | 'use strict'; 12 | 13 | // From http://baagoe.com/en/RandomMusings/javascript/ 14 | 15 | // importState to sync generator states 16 | Alea.importState = function(i){ 17 | var random = new Alea(); 18 | random.importState(i); 19 | return random; 20 | }; 21 | 22 | return Alea; 23 | 24 | function Alea() { 25 | return (function(args) { 26 | // Johannes Baagøe , 2010 27 | var s0 = 0; 28 | var s1 = 0; 29 | var s2 = 0; 30 | var c = 1; 31 | 32 | if (args.length == 0) { 33 | args = [+new Date]; 34 | } 35 | var mash = Mash(); 36 | s0 = mash(' '); 37 | s1 = mash(' '); 38 | s2 = mash(' '); 39 | 40 | for (var i = 0; i < args.length; i++) { 41 | s0 -= mash(args[i]); 42 | if (s0 < 0) { 43 | s0 += 1; 44 | } 45 | s1 -= mash(args[i]); 46 | if (s1 < 0) { 47 | s1 += 1; 48 | } 49 | s2 -= mash(args[i]); 50 | if (s2 < 0) { 51 | s2 += 1; 52 | } 53 | } 54 | mash = null; 55 | 56 | var random = function() { 57 | var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32 58 | s0 = s1; 59 | s1 = s2; 60 | return s2 = t - (c = t | 0); 61 | }; 62 | random.next = random; 63 | random.uint32 = function() { 64 | return random() * 0x100000000; // 2^32 65 | }; 66 | random.fract53 = function() { 67 | return random() + 68 | (random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53 69 | }; 70 | random.version = 'Alea 0.9'; 71 | random.args = args; 72 | 73 | // my own additions to sync state between two generators 74 | random.exportState = function(){ 75 | return [s0, s1, s2, c]; 76 | }; 77 | random.importState = function(i){ 78 | s0 = +i[0] || 0; 79 | s1 = +i[1] || 0; 80 | s2 = +i[2] || 0; 81 | c = +i[3] || 0; 82 | }; 83 | 84 | return random; 85 | 86 | } (Array.prototype.slice.call(arguments))); 87 | } 88 | 89 | function Mash() { 90 | var n = 0xefc8249d; 91 | 92 | var mash = function(data) { 93 | data = data.toString(); 94 | for (var i = 0; i < data.length; i++) { 95 | n += data.charCodeAt(i); 96 | var h = 0.02519603282416938 * n; 97 | n = h >>> 0; 98 | h -= n; 99 | h *= n; 100 | n = h >>> 0; 101 | h -= n; 102 | n += h * 0x100000000; // 2^32 103 | } 104 | return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 105 | }; 106 | 107 | mash.version = 'Mash 0.9'; 108 | return mash; 109 | } 110 | })); 111 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "alea" { 2 | function Alea(...args: any[]): { 3 | (): number; 4 | next(): number; 5 | uint32(): number; 6 | fract53(): number; 7 | version: string; 8 | args: any[]; 9 | exportState(): [number, number, number, number] 10 | importState(state: [number, number, number, number]): void; 11 | } 12 | 13 | export default Alea; 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alea", 3 | "version": "1.0.1", 4 | "description": "Implementation of the Alea PRNG by Johannes Baagøe", 5 | "main": "alea.js", 6 | "scripts": { 7 | "test": "node test" 8 | }, 9 | "keywords": ["badass", "pseudo", "random", "number","generator"], 10 | "devDependencies":{ 11 | "tape": "~0.1.1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/coverslide/node-alea" 16 | }, 17 | "author": { 18 | "name": "Richard Hoffman", 19 | "email": "coverslide@gmail.com", 20 | "url": "http://coverslide.me" 21 | }, 22 | "testling": { 23 | "files": "test.js", 24 | "browsers": { 25 | "ie": [ 6, 7, 8, 9 ], 26 | "ff": [ 3.6, 4, 17, "nightly" ], 27 | "chrome": [ 22, 23, "canary" ], 28 | "opera": [ 10, 11, 12, "next" ], 29 | "safari": [ 5.1 ] 30 | } 31 | }, 32 | "license": "MIT" 33 | } 34 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var Alea = require('./alea') 3 | 4 | 'use strict' 5 | 6 | test("make sure two seeded values are the same", function(t){ 7 | 8 | var prng1 = Alea(1) 9 | var prng2 = Alea(3) 10 | var prng3 = Alea(1) 11 | 12 | var a = prng1() 13 | var b = prng2() 14 | var c = prng3() 15 | 16 | t.equal(a, c, 'return values of the same seed') 17 | t.notEqual(a, b, 'return values of different seed') 18 | 19 | // test return values directly 20 | t.equal(prng1(), prng3(), 'same seed called again') 21 | 22 | t.notEqual(prng1(), prng2(), 'different seed again') 23 | t.notEqual(prng1(), prng3(), 'prng1 called more times than prng3') 24 | t.notEqual(prng2(), prng3(), 'prng3 called again') 25 | 26 | t.equal(prng1(), prng3(), 'call counts equal again') 27 | 28 | //not sure why explicit end() is needed here 29 | t.end() 30 | }) 31 | 32 | test("Known values test", function(t){ 33 | 34 | var prng1 = Alea(12345) 35 | 36 | //predefined numbers 37 | var values = [ 38 | 0.27138191112317145, 39 | 0.19615925149992108, 40 | 0.6810678059700876 41 | ] 42 | 43 | t.equal(prng1(), values[0], 'check value 1') 44 | t.equal(prng1(), values[1], 'check value 2') 45 | t.equal(prng1(), values[2], 'check value 3') 46 | 47 | t.end() 48 | }) 49 | 50 | test("Uint32 test", function(t){ 51 | 52 | var prng1 = Alea(12345) 53 | 54 | //predefined numbers 55 | var values = [ 56 | 1165576433, 57 | 842497570, 58 | 2925163953 59 | ] 60 | 61 | t.equal(prng1.uint32(), values[0], 'check value 1') 62 | t.equal(prng1.uint32(), values[1], 'check value 2') 63 | t.equal(prng1.uint32(), values[2], 'check value 3') 64 | 65 | t.end() 66 | }) 67 | 68 | test("Fract53 test", function(t){ 69 | 70 | var prng1 = Alea(12345) 71 | 72 | //predefined numbers 73 | var values = [ 74 | 0.27138191116884325, 75 | 0.6810678062004586, 76 | 0.3407802057882554 77 | ] 78 | 79 | t.equal(prng1.fract53(), values[0], 'check value 1') 80 | t.equal(prng1.fract53(), values[1], 'check value 2') 81 | t.equal(prng1.fract53(), values[2], 'check value 3') 82 | 83 | t.end() 84 | }) 85 | 86 | test("Import with Alea.importState()", function(t){ 87 | 88 | var prng1 = Alea(200) 89 | 90 | // generate a few numbers 91 | prng1() 92 | prng1() 93 | prng1() 94 | 95 | var e = prng1.exportState() 96 | 97 | var prng4 = Alea.importState(e) 98 | 99 | t.equal(prng1(), prng4(), 'synced prngs, call 1') 100 | t.equal(prng1(), prng4(), 'synced prngs, call 2') 101 | t.equal(prng1(), prng4(), 'synced prngs, call 3') 102 | 103 | t.end() 104 | }) 105 | 106 | test("Resync two differring prngs with prng.importState()", function(t){ 107 | var prng1 = Alea(200000) 108 | var prng2 = Alea(9000) 109 | 110 | // generate a few numbers 111 | 112 | t.notEqual(prng1(), prng2(), 'just generating randomness, call 1') 113 | t.notEqual(prng1(), prng2(), 'just generating randomness, call 2') 114 | t.notEqual(prng1(), prng2(), 'just generating randomness, call 3') 115 | 116 | // sync prng2 to prng1 117 | prng2.importState(prng1.exportState()) 118 | 119 | t.equal(prng1(), prng2(), 'imported prng, call 1') 120 | t.equal(prng1(), prng2(), 'imported prng, call 2') 121 | t.equal(prng1(), prng2(), 'imported prng, call 3') 122 | 123 | // let's test they still sync up if called non-sequentially 124 | 125 | prng1() 126 | prng1() 127 | 128 | var a1 = prng1() 129 | var b1 = prng1() 130 | var c1 = prng1() 131 | 132 | prng2() 133 | prng2() 134 | 135 | var a2 = prng2() 136 | var b2 = prng2() 137 | var c2 = prng2() 138 | 139 | t.equal(a1, a2, 'return values should sync based on number of calls, call 1') 140 | t.equal(b1, b2, 'return values should sync based on number of calls, call 2') 141 | t.equal(c1, c2, 'return values should sync based on number of calls, call 3') 142 | 143 | t.end() 144 | }) 145 | --------------------------------------------------------------------------------