From 70c8d2c70536b82d83f8c6a2585e15b912d1177e Mon Sep 17 00:00:00 2001 From: Alexander Yakovlev Date: Sat, 10 Sep 2016 11:24:08 +0700 Subject: [PATCH] Fast PRNG instead of the slow Mersenne Twister --- src/random.coffee | 156 +++++++++------------------------------------- src/salet.coffee | 6 +- 2 files changed, 32 insertions(+), 130 deletions(-) diff --git a/src/random.coffee b/src/random.coffee index 753991d..d5591f9 100644 --- a/src/random.coffee +++ b/src/random.coffee @@ -1,149 +1,53 @@ ### -This implementation of the Mersenne Twister is a port of the JavaScript -version by Y. Okada. The JavaScript version was itself a port of a -C implementation, by Takuji Nishimura and Makoto Matsumoto. +Fast performance pseudo random number generator. -CoffeeScript port by: Jamis Buck -License: Public domain, baby. Knock yourself out. +Not the cryptographic-level randomness, but enough for most games. -The original CoffeeScript sources are always available on GitHub: -http://github.com/jamis/csmazes +Code from CoffeeScript Cookbook: +https://coffeescript-cookbook.github.io/chapters/math/generating-predictable-random-numbers ### class Random - N: 624 - M: 397 - MATRIX_A: 0x9908b0df - UPPER_MASK: 0x80000000 - LOWER_MASK: 0x7fffffff + constructor: (@seed) -> + @multiplier = 1664525 + @modulo = 4294967296 # 2**32-1; + @offset = 1013904223 + unless @seed? && 0 <= seed < @modulo + @seed = (new Date().valueOf() * new Date().getMilliseconds()) % @modulo - constructor: (seed) -> - @mt = new Array(@N) - @setSeed seed + # sets new seed value + seed: (seed) -> + @seed = seed - # makes the argument into an unsigned integer, if it is not already one - unsigned32: (n1) -> if n1 < 0 then (n1 ^ @UPPER_MASK) + @UPPER_MASK else n1 + getSeed: () -> + return @seed - # emulates underflow of subtracting two 32-bit unsigned integers. both arguments - # must be non-negative 32-bit integers. - subtraction32: (n1, n2) -> - if n1 < n2 - @unsigned32((0x100000000 - (n2 - n1)) % 0xffffffff) - else - n1 - n2 + # return a random integer 0 <= n < @modulo + randn: -> + # new_seed = (a * seed + c) % m + @seed = (@multiplier*@seed + @offset) % @modulo - # emulates overflow of adding two 32-bit integers. both arguments must be - # non-negative 32-bit integers. - addition32: (n1, n2) -> @unsigned32((n1 + n2) & 0xffffffff) + # return a random float 0 <= f < 1.0 + randf: -> + this.randn() / @modulo - # emulates overflow of multiplying two 32-bit integers. both arguments must - # be non-negative 32-bit integers. - multiplication32: (n1, n2) -> - sum = 0 - for i in [0...32] - if ((n1 >>> i) & 0x1) - sum = @addition32(sum, @unsigned32(n2 << i)) - sum + # return a random int 0 <= f < n + rand: (n) -> + Math.floor(this.randf() * n) - setSeed: (seed) -> - if !seed || typeof seed == "number" - @seedWithInteger seed - else - @seedWithArray seed - - defaultSeed: -> - currentDate = new Date() - currentDate.getMinutes() * 60000 + currentDate.getSeconds() * 1000 + currentDate.getMilliseconds() - - seedWithInteger: (seed) -> - @seed = seed ? @defaultSeed() - @mt[0] = @unsigned32(@seed & 0xffffffff) - @mti = 1 - - while @mti < @N - @mt[@mti] = @addition32( - @multiplication32(1812433253, @unsigned32(@mt[@mti-1] ^ (@mt[@mti-1] >>> 30))), - @mti) - @mti[@mti] = @unsigned32(@mt[@mti] & 0xffffffff) - @mti++ - - seedWithArray: (key) -> - @seedWithInteger 19650218 - - i = 1 - j = 0 - k = if @N > key.length then @N else key.length - - while k > 0 - _m = @multiplication32(@unsigned32(@mt[i-1] ^ (@mt[i-1] >>> 30)), 1664525) - @mt[i] = @addition32(@addition32(@unsigned32(@mt[i] ^ _m), key[j]), j) - @mt[i] = @unsigned32(@mt[i] & 0xffffffff) - - i++ - j++ - - if i >= @N - @mt[0] = @mt[@N-1] - i = 1 - - j = 0 if j >= key.length - k-- - - k = @N - 1 - while k > 0 - @mt[i] = @subtraction32( - @unsigned32(@mt[i] ^ @multiplication32(@unsigned32(@mt[i-1] ^ (@mt[i-1] >>> 30)), 1566083941)), i) - @mt[i] = @unsigned32(@mt[i] & 0xffffffff) - i++ - if i >= @N - @mt[0] = @mt[@N-1] - i = 1 - - @mt[0] = 0x80000000 - - nextInteger: (upper) -> - return 0 if (upper ? 1) < 1 - - mag01 = [0, @MATRIX_A] - - if @mti >= @N - kk = 0 - - while kk < @N - @M - y = @unsigned32((@mt[kk] & @UPPER_MASK) | (@mt[kk+1] & @LOWER_MASK)) - @mt[kk] = @unsigned32(@mt[kk+@M] ^ (y >>> 1) ^ mag01[y & 0x1]) - kk++ - - while kk < @N-1 - y = @unsigned32((@mt[kk] & @UPPER_MASK) | (@mt[kk+1] & @LOWER_MASK)) - @mt[kk] = @unsigned32(@mt[kk+@M-@N] ^ (y >>> 1) ^ mag01[y & 0x1]) - kk++ - - y = @unsigned32((@mt[@N-1] & @UPPER_MASK) | (@mt[0] & @LOWER_MASK)) - @mt[@N-1] = @unsigned32(@mt[@M-1] ^ (y >>> 1) ^ mag01[y & 0x1]) - @mti = 0 - - y = @mt[@mti++] - - y = @unsigned32(y ^ (y >>> 11)) - y = @unsigned32(y ^ ((y << 7) & 0x9d2c5680)) - y = @unsigned32(y ^ ((y << 15) & 0xefc60000)) - - @unsigned32(y ^ (y >>> 18)) % (upper ? 0x100000000) + # return a random int min <= f < max + randRange: (min, max) -> + min + this.rand(max-min) randomInt: (upper) -> - @nextInteger(upper) - - nextFloat: -> @nextInteger() / 0xffffffff - - nextBoolean: -> @nextInteger() % 2 == 0 + @rand(upper) # Returns the result of rolling *n* dice with *dx* sides, and adding *plus*. dice: (n, dx, plus) -> result = 0 for i in [0..n] console.log i - result += @nextInteger(1, dx) + result += 1 + @randRange(1,dx) if (plus) result += plus result diff --git a/src/salet.coffee b/src/salet.coffee index 4094e75..e5c8abc 100644 --- a/src/salet.coffee +++ b/src/salet.coffee @@ -417,10 +417,8 @@ class Salet console.error err else currentDate = new Date() - @progress.seed = currentDate.getMinutes() * 60000 - @progress.seed += currentDate.getSeconds() * 1000 - @progress.seed += currentDate.getMilliseconds() - @rnd = new Random(@progress.seed) + @rnd = new Random() + @progress.seed = @rnd.getSeed() @progress.sequence = [{link:@start, when:0}]