Here I present a portable pseudo random number generator based on MD5 Hashes.
I am using an interesting way to create the objects very lightweight and encapsulate all of the functionality in a simple to use module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | -- "GameRand.lua" -- -- This is an implementation of a md5 based -- pseudo random number generator which creates the same -- sequence of values for implementations on different -- platforms (Hans Raaf) -- copy what we need as local local floor=math.floor local byte=string.byte local sub=string.sub local random=math.random local tostring=tostring local assert=assert local crypto = require('crypto') local digest=crypto.digest local md5=crypto.md5 -- make it into a module module(...) -- All functions are local and used only inside the objects I create local randInt = function(self,min,max) assert(self.pos > 0) -- is 0 if neither seed nor randomize was called assert(max-min < 256 and max-min > 0) -- only possible values if self.pos>16 then self.digest=digest(md5,self.digest,true) self.pos=1 end local x=floor(byte(sub(self.digest,self.pos,self.pos)) *(max-min+1)/256)+min self.pos=self.pos+1 self.my_step=self.my_step+1 return x end local seed = function(self,s) self.my_step=0 self.my_seed=s self.digest=digest(md5,self.my_seed,true) self.pos=1 end local randomize = function(self) self:seed(tostring(random())) end local step = function(self,step) assert(self.pos>0) -- is 0 if neither seed nor randomize was called -- fast forward to a position local i -- shortcut for full 16 steps for i=1, floor(step/16) do self.digest=digest(md5,self.digest,true) self.pos=1 self.my_step=self.my_step+16 end -- set the offsets to the right position self.pos=self.pos+step%16 self.my_step=self.my_step+step%16 end return function() -- create a new Object :) return { my_step=0, my_seed=nil, digest=nil, pos=0, randInt=randInt, seed=seed, randomize=randomize, step=step } end |
Here some code which demonstrates its basic usage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | local GameRand = require "GameRand" -- create a GameRand object gr1=GameRand() -- seed with "test" gr1:seed('test') print('Show 20 Random Values of gr1 ') for x=1, 20 do print(x..': '..gr1:randInt(0,9)) end -- now use a second GameRand object and "catch up" gr2=GameRand() print('\nStart gr2 at Step 18') gr2:seed('test') -- seed with same seed as gr1 gr2:step(17) -- step 17 steps.. for x=18, 20 do print(x..': '..gr2:randInt(0,9)) end -- now have both running besides each other print('\nPrint gr1 and gr2 from step 21') for x=21, 33 do print(x..': '..gr1:randInt(0,9)..' - '..gr2:randInt(0,9)) end -- dump the tables (uses my other example code for 'print_r()' print_r(gr1) print_r(gr2) |