From 23b2d5eba5996bf1392c3ce36d183b1cb6fc07a8 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 22 Dec 2017 22:41:45 -0800 Subject: [PATCH 1/2] Merge branch cxxrandom Implements helper functions for random number generation. Implemented using C++11 library. Exported Lua Functions: - seedRNG(seed) - rollInt(min, max) - rollDouble(min, max) - rollNormal(mean, std_deviation) - rollBool(chance_for_true) - resetIndexRolls(string, array_length) --String identifies the instance of SimpleNumDistribution to reset - rollIndex(string, array_length) --String identifies the instance of SimpleNumDistribution to use --(Shuffles a vector of indices, Next() increments through then reshuffles when end() is reached) On branch cxxrandom-rel Changes to be committed: modified: plugins/CMakeLists.txt new file: plugins/cxxrandom.cpp new file: plugins/lua/cxxrandom.lua ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Squashed commit of the following: commit 3a7ef70d45f3e0c2fe367141dd0349dddaaff60d Merge: fd9f1982 7aa0608c Author: Josh Cooper Date: Fri Dec 22 22:17:27 2017 -0800 Merge remote-tracking branch 'origin/temp' into cxxrandom commit 7aa0608cb85dcf82686193db7a6e9d1318f5f2a5 Author: Josh Cooper Date: Thu Dec 21 21:43:35 2017 -0800 Revises cxxrandom plugin New functions: - seedRNG(ushort seed) --lua exported - GetDistribContainer() --internal singleton - RNG() --internal singleton Summary: - Removed class CXXRNG --Refactored functions that used CXXRNG Changes to be committed: modified: plugins/cxxrandom.cpp commit b42979818a01c1121eace7b1ac14676f5ad5d8b2 Author: Josh Cooper Date: Wed Dec 20 13:21:49 2017 -0800 Fixes plugin_init() Misread the lines indicated by lethosor to be excluded, had broken the plugin in the process. Changes to be committed: modified: plugins/cxxrandom.cpp commit 753a00a14d9e6519d299638e014abf30509940af Author: Josh Cooper Date: Wed Dec 20 12:36:17 2017 -0800 Cleans up cxxrandom.cpp DFHack contributions specifies: -spaces instead of tabs, so tabs were converted. -C++ headers before dfhack stuff, so it was done *Also added author name, creation date, and last updated date. Changes to be committed: modified: plugins/cxxrandom.cpp commit 498ebe4b8fdccc01ac1f169269f3093c830a8a10 Author: Josh Cooper Date: Tue Dec 19 22:51:58 2017 -0800 Updates cxxrandom, fixes instance leak deleted header moved definition to cpp file #lethosor fixed singleton instance, no longer a pointer commented out dfhack commands, now only init/shutdown and exported lua functions modified: cxxrandom.cpp deleted: cxxrandom.h commit 821044bef2a0201d0d74192e445c7b29766b42a1 Author: Josh Cooper Date: Sun Dec 17 04:01:11 2017 -0800 Fixes RollIndex and Renames RollNormal Renamed the Normal Distribution RNG function to fit the standard. Now named RollNormal(m,s) Fixed some wonky white space in the lua macro export block. Fixed a stupid mistake with the RollIndex output. (it was outputting 0's) Updated usage details. Changes to be committed: modified: plugins/cxxrandom.cpp commit 1536f43d137b6bc55d55759b43bdccf6ff429b33 Author: Josh Cooper Date: Fri Dec 15 08:50:08 2017 -0800 Fixes/Improves cxxrandom Modified return types Corrected index distribution code commit 8629c7e1509522cb0cc4b649914b90d033cb4763 Author: Josh Cooper Date: Thu Dec 14 19:02:29 2017 -0800 Implements SimpleNumDistribution Exported additional functions to lua. Functions allow the generation of random 0-N index values. Generation promises all unique values [0,N] will be returned once each when generation is run N times. On branch cxxrandom Changes to be committed: modified: plugins/cxxrandom.cpp modified: plugins/cxxrandom.h commit f035f3d20415790542cf83e5e696261661d911f3 Author: Josh Cooper Date: Wed Dec 13 23:55:39 2017 -0800 Implements cxxrandom cxxrandom was implemented using a singleton. This singleton provides an interface for generating uniform numbers, or numbers in a normal distribution, and also booleans(given the probability for the true outcome) The singleton interface is wrapped in functions which are exposed for lua usage. Integrated into plugins/CMakeLists.txt On branch dev Changes to be committed: modified: CMakeLists.txt new file: cxxrandom.cpp new file: cxxrandom.h new file: lua/cxxrandom.lua --- plugins/CMakeLists.txt | 1 + plugins/cxxrandom.cpp | 190 ++++++++++++++++++++++++++++++++++++++ plugins/lua/cxxrandom.lua | 3 + 3 files changed, 194 insertions(+) create mode 100644 plugins/cxxrandom.cpp create mode 100644 plugins/lua/cxxrandom.lua diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e85e0aaf4..2a40c5282 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -105,6 +105,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(confirm confirm.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(createitem createitem.cpp) DFHACK_PLUGIN(cursecheck cursecheck.cpp) + DFHACK_PLUGIN(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(deramp deramp.cpp) DFHACK_PLUGIN(dig dig.cpp) DFHACK_PLUGIN(digFlood digFlood.cpp) diff --git a/plugins/cxxrandom.cpp b/plugins/cxxrandom.cpp new file mode 100644 index 000000000..23b10931e --- /dev/null +++ b/plugins/cxxrandom.cpp @@ -0,0 +1,190 @@ +/* Plugin for exporting C++11 random number functionality +*Exports functions for random number generation +*Functions: +- seedRNG(seed) +- rollInt(min, max) +- rollDouble(min, max) +- rollNormal(mean, std_deviation) +- rollBool(chance_for_true) +- resetIndexRolls(string, array_length) --String identifies the instance of SimpleNumDistribution to reset +- rollIndex(string, array_length) --String identifies the instance of SimpleNumDistribution to use + --(Shuffles a vector of indices, Next() increments through then reshuffles when end() is reached) +Author: Josh Cooper +Created: Dec. 13 2017 +Updated: Dec. 21 2017 +*/ + + +#include +#include +#include +#include +#include + +#include "Core.h" +#include "DataFuncs.h" +#include +#include +#include + + +using namespace DFHack; +DFHACK_PLUGIN("cxxrandom"); +#define PLUGIN_VERSION 1.0 + + +//command_result cxxrandom (color_ostream &out, std::vector & parameters); +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) +{ + /* + commands.push_back(PluginCommand( + "cxxrandom", "C++xx Random Numbers", cxxrandom, false, + " This plugin cannot be used on the commandline.\n" + " Import into a lua script to have access to exported RNG functions:\n" + " local rng = require('plugins.cxxrandom')\n\n" + " Exported Functions:\n" + " rng.ResetIndexRolls(string_ref, total_indices)\n" + " rng.RollIndex(string_ref, total_indices)\n" + " rng.RollInt(min, max)\n" + " rng.RollDouble(min, max)\n" + " rng.RollNormal(mean, std_deviation)\n" + " rng.RollBool(chance_of_true)\n" + " rng.BlastDistributions()\n\n" + ));*/ + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown (color_ostream &out) +{ + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + return CR_OK; +} + + +std::default_random_engine& RNG() +{ + static std::default_random_engine instance(std::chrono::system_clock::now().time_since_epoch().count()); + return instance; +} + +void seedRNG(unsigned short seed) +{ + RNG() = std::default_random_engine(seed); +} + + + +class SimpleNumDistribution +{ +private: + unsigned short m_position = 0; + std::vector m_distribution; + +public: + SimpleNumDistribution(unsigned short N) + { + m_position = 0; + m_distribution.reserve(N); + for(int i = 1; i <= N; ++i) + { + m_distribution.push_back(i); + } + Reset(); + } + + void Reset() + { + std::shuffle(std::begin(m_distribution), std::end(m_distribution), RNG()); + } + + unsigned short Length() const { return m_distribution.size(); } + + unsigned short Next() + { + if(m_position >= m_distribution.size()) + { + m_position = 0; + Reset(); + } + return m_distribution[m_position++]; + } +}; + + + +typedef std::unordered_map DistributionContainer; +DistributionContainer& GetDistribContainer() +{ + static DistributionContainer instance; + return instance; +} + +void resetIndexRolls(std::string ref, unsigned short N) +{ + DistributionContainer& ND_index = GetDistribContainer(); + auto iter = ND_index.find(ref); + if(iter == ND_index.end() || iter->second.Length() != N ) + { + if(iter != ND_index.end()) + ND_index.erase(iter); + + iter = ND_index.emplace(ref, SimpleNumDistribution(N)).first; + } + iter->second.Reset(); +} + +int rollIndex(std::string ref, unsigned short N) +{ + DistributionContainer& ND_index = GetDistribContainer(); + auto iter = GetDistribContainer().find(ref); + if(iter == ND_index.end() || iter->second.Length() != N ) + { + if(iter != ND_index.end()) + ND_index.erase(iter); + + iter = ND_index.emplace(ref, SimpleNumDistribution(N)).first; + } + return iter->second.Next(); +} + + +int rollInt(int min, int max) +{ + std::uniform_int_distribution ND(min, max); + return ND(RNG()); +} + +double rollDouble(double min, double max) +{ + std::uniform_real_distribution ND(min, max); + return ND(RNG()); +} + +double rollNormal(double mean, double stddev) +{ + std::normal_distribution ND(mean, stddev); + return ND(RNG()); +} + +bool rollBool(float p) +{ + std::bernoulli_distribution ND(p); + return ND(RNG()); +} + + +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(resetIndexRolls), + DFHACK_LUA_FUNCTION(rollIndex), + DFHACK_LUA_FUNCTION(seedRNG), + DFHACK_LUA_FUNCTION(rollInt), + DFHACK_LUA_FUNCTION(rollDouble), + DFHACK_LUA_FUNCTION(rollNormal), + DFHACK_LUA_FUNCTION(rollBool), + DFHACK_LUA_END +}; + diff --git a/plugins/lua/cxxrandom.lua b/plugins/lua/cxxrandom.lua new file mode 100644 index 000000000..7f38ce5ce --- /dev/null +++ b/plugins/lua/cxxrandom.lua @@ -0,0 +1,3 @@ +local _ENV = mkmodule('plugins.cxxrandom') + +return _ENV \ No newline at end of file From bc32d15beaf2498b70dcf55478825b707e208f8b Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 29 Apr 2018 16:23:57 -0700 Subject: [PATCH 2/2] [Release] cxxrandom v2.0 Major Revision update v2.0 =-=-=-=-= Native functions(exported to lua): -GenerateEngine: returns engine id (args: seed) -DestroyEngine: destroys corresponding engine (args: rngID) -NewSeed re-seeds engine (args: rngID, seed) -rollInt generates random integer (args: rngID, min, max) -rollDouble generates random double (args: rngID, min, max) -rollNormal generates random normal[gaus.] (args: rngID, avg, stddev) -rollBool generates random boolean (args: rngID, chance) -MakeNumSequence returns sequence id (args: start, end) -AddToSequence adds a number to the sequence (args: seqID, num) -ShuffleSequence shuffles the number sequence (args: rngID, seqID) -NextInSequence returns the next number in seq.(args: seqID) Lua plugin functions: -MakeNewEngine returns engine id (args: seed) Lua plugin classes: -crng methods: -init(id, df, dist) :: constructor id - Reference ID of engine to use in RNGenerations df (optional) - bool indicating whether to destroy the Engine when the crng object is garbage collected dist (optional) - lua number distribution to use -__gc() :: destructor -changeSeed(seed) :: alters engine's seed value -setNumDistrib(distrib) :: set's the number distribution crng object should use distrib - number distribution object to use in RNGenerations -next() :: returns the next number in the distribution -shuffle() :: effectively shuffles the number distribution -normal_distribution methods: -init(avg, stddev) :: constructor -next(id) :: returns next number in the distribution id - engine ID to pass to native function -real_distribution methods: -init(min, max) :: constructor -next(id) :: returns next number in the distribution id - engine ID to pass to native function -int_distribution methods: -init(min, max) :: constructor -next(id) :: returns next number in the distribution id - engine ID to pass to native function -bool_distribution methods: -init(min, max) :: constructor -next(id) :: returns next boolean in the distribution id - engine ID to pass to native function -num_sequence methods: -init(a, b) :: constructor -add(num) :: adds num to the end of the number sequence -shuffle() :: shuffles the sequence of numbers -next() :: returns next number in the sequence Adds missing function exports. Fixes numerous problems I won't go into --- plugins/cxxrandom.cpp | 267 +++++++++++++++++++++++++++----------- plugins/lua/cxxrandom.lua | 211 ++++++++++++++++++++++++++++++ 2 files changed, 405 insertions(+), 73 deletions(-) diff --git a/plugins/cxxrandom.cpp b/plugins/cxxrandom.cpp index 23b10931e..517d460ea 100644 --- a/plugins/cxxrandom.cpp +++ b/plugins/cxxrandom.cpp @@ -20,22 +20,33 @@ Updated: Dec. 21 2017 #include #include #include +#include +#include +#include +#include "Error.h" #include "Core.h" #include "DataFuncs.h" #include #include #include - +/* +typedef unsigned short uint16_t; +typedef unsigned long long uint64_t; +typedef long long int64_t; +using uint16_t = unsigned short; +using uint64_t = unsigned long long; +using int64_t = long long;*/ using namespace DFHack; DFHACK_PLUGIN("cxxrandom"); -#define PLUGIN_VERSION 1.0 - +#define PLUGIN_VERSION 2.0 +color_ostream *cout = nullptr; //command_result cxxrandom (color_ostream &out, std::vector & parameters); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { + cout = &out; /* commands.push_back(PluginCommand( "cxxrandom", "C++xx Random Numbers", cxxrandom, false, @@ -64,127 +75,237 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan return CR_OK; } +#pragma region "EnginesKeeper Stuff" + +class EnginesKeeper +{ +private: + EnginesKeeper() {} + std::unordered_map m_engines; + uint16_t counter = 0; +public: + static EnginesKeeper& Instance() + { + static EnginesKeeper instance; + return instance; + } + uint16_t NewEngine( uint64_t seed ) + { + std::mt19937_64 engine( seed != 0 ? seed : std::chrono::system_clock::now().time_since_epoch().count() ); + m_engines[++counter] = engine; + return counter; + } + void DestroyEngine( uint16_t id ) + { + m_engines.erase( id ); + } + void NewSeed( uint16_t id, uint64_t seed ) + { + CHECK_INVALID_ARGUMENT( m_engines.find( id ) != m_engines.end() ); + m_engines[id].seed( seed != 0 ? seed : std::chrono::system_clock::now().time_since_epoch().count() ); + } + std::mt19937_64& RNG( uint16_t id ) + { + CHECK_INVALID_ARGUMENT( m_engines.find( id ) != m_engines.end() ); + return m_engines[id]; + } +}; + +#pragma endregion + +#pragma region "EngineKeeper Wrappers" + +uint16_t GenerateEngine( uint64_t seed ) +{ + return EnginesKeeper::Instance().NewEngine( seed ); +} + +void DestroyEngine( uint16_t id ) +{ + EnginesKeeper::Instance().DestroyEngine( id ); +} -std::default_random_engine& RNG() +void NewSeed( uint16_t id, uint64_t seed ) { - static std::default_random_engine instance(std::chrono::system_clock::now().time_since_epoch().count()); - return instance; + EnginesKeeper::Instance().NewSeed( id, seed ); } -void seedRNG(unsigned short seed) +#pragma endregion + +#pragma region "std Distribution Rollers" + +int rollInt(uint16_t id, int min, int max) +{ + std::uniform_int_distribution ND(min, max); + return ND(EnginesKeeper::Instance().RNG(id)); +} + +double rollDouble(uint16_t id, double min, double max) { - RNG() = std::default_random_engine(seed); + std::uniform_real_distribution ND(min, max); + return ND(EnginesKeeper::Instance().RNG(id)); +} + +double rollNormal(uint16_t id, double mean, double stddev) +{ + std::normal_distribution ND(mean, stddev); + return ND(EnginesKeeper::Instance().RNG(id)); +} + +bool rollBool(uint16_t id, float p) +{ + std::bernoulli_distribution ND(p); + return ND(EnginesKeeper::Instance().RNG(id)); } +#pragma endregion +#pragma region "Number Sequence Stuff" -class SimpleNumDistribution +class NumberSequence { private: unsigned short m_position = 0; - std::vector m_distribution; - + std::vector m_numbers; public: - SimpleNumDistribution(unsigned short N) + NumberSequence(){} + NumberSequence( int64_t start, int64_t end ) { - m_position = 0; - m_distribution.reserve(N); - for(int i = 1; i <= N; ++i) + for( int64_t i = start; i <= end; ++i ) { - m_distribution.push_back(i); + m_numbers.push_back( i ); } - Reset(); } - - void Reset() + void Add( int64_t num ) { m_numbers.push_back( num ); } + void Reset() { m_numbers.clear(); } + int64_t Next() { - std::shuffle(std::begin(m_distribution), std::end(m_distribution), RNG()); + if(m_position >= m_numbers.size()) + { + m_position = 0; + } + return m_numbers[m_position++]; } - - unsigned short Length() const { return m_distribution.size(); } - - unsigned short Next() + void Shuffle( uint16_t id ) + { + std::shuffle( std::begin( m_numbers ), std::end( m_numbers ), EnginesKeeper::Instance().RNG( id ) ); + } + void Print() { - if(m_position >= m_distribution.size()) + char buffer1[256] = {0}; + char buffer2[256] = {0}; + for( auto v : m_numbers ) { - m_position = 0; - Reset(); + sprintf( buffer2, "%s%d", buffer1, v ); + sprintf( buffer1, "%s ", buffer2 ); } - return m_distribution[m_position++]; + cout->print( buffer1 ); } }; +class SequenceKeeper +{ +private: + SequenceKeeper() {} + std::unordered_map m_sequences; + uint16_t counter = 0; +public: + static SequenceKeeper& Instance() + { + static SequenceKeeper instance; + return instance; + } + uint16_t MakeNumSequence( int64_t start, int64_t end ) + { + m_sequences[++counter] = NumberSequence( start, end ); + return counter; + } + uint16_t MakeNumSequence() + { + m_sequences[++counter] = NumberSequence(); + return counter; + } + void DestroySequence( uint16_t id ) + { + m_sequences.erase( id ); + } + void AddToSequence( uint16_t id, int64_t num ) + { + CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() ); + m_sequences[id].Add( num ); + } + void Shuffle( uint16_t id, uint16_t rng_id ) + { + CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() ); + m_sequences[id].Shuffle( rng_id ); + } + int64_t NextInSequence( uint16_t id ) + { + CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() ); + return m_sequences[id].Next(); + } + void PrintSequence( uint16_t id ) + { + CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() ); + auto seq = m_sequences[id]; + seq.Print(); + } +}; +#pragma endregion -typedef std::unordered_map DistributionContainer; -DistributionContainer& GetDistribContainer() -{ - static DistributionContainer instance; - return instance; -} +#pragma region "Sequence Wrappers" -void resetIndexRolls(std::string ref, unsigned short N) +uint16_t MakeNumSequence( int64_t start, int64_t end ) { - DistributionContainer& ND_index = GetDistribContainer(); - auto iter = ND_index.find(ref); - if(iter == ND_index.end() || iter->second.Length() != N ) + if( start == end ) { - if(iter != ND_index.end()) - ND_index.erase(iter); - - iter = ND_index.emplace(ref, SimpleNumDistribution(N)).first; + return SequenceKeeper::Instance().MakeNumSequence(); } - iter->second.Reset(); + return SequenceKeeper::Instance().MakeNumSequence( start, end ); } -int rollIndex(std::string ref, unsigned short N) +void DestroyNumSequence( uint16_t id ) { - DistributionContainer& ND_index = GetDistribContainer(); - auto iter = GetDistribContainer().find(ref); - if(iter == ND_index.end() || iter->second.Length() != N ) - { - if(iter != ND_index.end()) - ND_index.erase(iter); - - iter = ND_index.emplace(ref, SimpleNumDistribution(N)).first; - } - return iter->second.Next(); + SequenceKeeper::Instance().DestroySequence( id ); } - -int rollInt(int min, int max) +void AddToSequence( uint16_t id, int64_t num ) { - std::uniform_int_distribution ND(min, max); - return ND(RNG()); + SequenceKeeper::Instance().AddToSequence( id, num ); } - -double rollDouble(double min, double max) + +void ShuffleSequence( uint16_t rngID, uint16_t id ) { - std::uniform_real_distribution ND(min, max); - return ND(RNG()); + SequenceKeeper::Instance().Shuffle( id, rngID ); } - -double rollNormal(double mean, double stddev) + +int64_t NextInSequence( uint16_t id ) { - std::normal_distribution ND(mean, stddev); - return ND(RNG()); + return SequenceKeeper::Instance().NextInSequence( id ); } - -bool rollBool(float p) + +void DebugSequence( uint16_t id ) { - std::bernoulli_distribution ND(p); - return ND(RNG()); + SequenceKeeper::Instance().PrintSequence( id ); } +#pragma endregion DFHACK_PLUGIN_LUA_FUNCTIONS { - DFHACK_LUA_FUNCTION(resetIndexRolls), - DFHACK_LUA_FUNCTION(rollIndex), - DFHACK_LUA_FUNCTION(seedRNG), + DFHACK_LUA_FUNCTION(GenerateEngine), + DFHACK_LUA_FUNCTION(DestroyEngine), + DFHACK_LUA_FUNCTION(NewSeed), DFHACK_LUA_FUNCTION(rollInt), DFHACK_LUA_FUNCTION(rollDouble), DFHACK_LUA_FUNCTION(rollNormal), DFHACK_LUA_FUNCTION(rollBool), + DFHACK_LUA_FUNCTION(MakeNumSequence), + DFHACK_LUA_FUNCTION(DestroyNumSequence), + DFHACK_LUA_FUNCTION(AddToSequence), + DFHACK_LUA_FUNCTION(ShuffleSequence), + DFHACK_LUA_FUNCTION(NextInSequence), + DFHACK_LUA_FUNCTION(DebugSequence), DFHACK_LUA_END }; diff --git a/plugins/lua/cxxrandom.lua b/plugins/lua/cxxrandom.lua index 7f38ce5ce..1521c4c9e 100644 --- a/plugins/lua/cxxrandom.lua +++ b/plugins/lua/cxxrandom.lua @@ -1,3 +1,214 @@ local _ENV = mkmodule('plugins.cxxrandom') +function MakeNewEngine(seed) + if type(seed) == 'number' then + if seed == 0 then + print(":WARNING: Seeds equal to 0 are used if no seed is provided. This indicates to cxxrandom.plug.dll that the engine needs to be seeded with the current time.\nRecommendation: use a non-zero value for your seed, or don't provide a seed to use the time since epoch(1969~).") + end + return GenerateEngine(seed) + elseif type(seed) == 'nil' then + return GenerateEngine(0) + else + error("Argument `seed` must be a number, or nil.") + end +end + +--Class: crng +------------- +crng = {} +function crng:new(engineID, destroyEngineOnDestruction, distrib) + local o = {} + self.__index = self + local idtype = type(engineID) + local flagtype = type(destroyEngineOnDestruction) + + if idtype == 'number' then + o.rngID = engineID + elseif idtype == 'nil' then + o.rngID = GenerateEngine(0) + else + error("Invalid argument type (engineID): " .. tostring(engineID)) + end + + if flagtype ~= 'nil' and flagtype == 'boolean' then + o.destroyid = destroyEngineOnDestruction + elseif flagtype == 'nil' then + o.destroyid = true + else + error("Invalid arugment type (destroyEngineOnDestruction): " .. tostring(destroyEngineOnDestruction)) + end + + if type(distrib) ~= 'nil' then + if type(distrib) == 'table' and type(distrib.next) == 'function' then + o.distrib = distrib + o.distrib.rngID = o.rngID + else + error("Invalid distribution used as an argument. Cannot set this as the number distribution.") + end + end + setmetatable(o,self) + return o +end +--crng destructor - we may need to destroy the engine, the user may be doing it manually though +function crng:__gc() + if self.destroyid then + DestroyEngine(self.rngID) + end +end +function crng:changeSeed(seed) + if type(seed) == 'number' then + if seed == 0 then + print(":WARNING: Seeds equal to 0 are used if no seed is provided. This indicates to cxxrandom.plug.dll that the engine needs to be seeded with the current time.\nRecommendation: use a non-zero value for your seed, or don't provide a seed to use the time since epoch(1969~).") + end + return NewSeed(self.rngID, seed) + elseif type(seed) == 'nil' then + return NewSeed(self.rngID, 0) + else + error("Argument `seed` must be a number, or nil.") + end +end +function crng:setNumDistrib(distrib) + if type(distrib) == 'table' and type(distrib.next) == 'function' then + self.distrib = distrib + self.distrib.rngID = self.rngID + else + error("Invalid distribution used as an argument. Cannot set this as the number distribution.") + end +end +function crng:next() + if type(self.distrib) == 'table' and type(self.distrib.next) == 'function' then + return self.distrib:next(self.rngID) + else + error("crng object does not have a valid number distribution set") + end +end +function crng:shuffle() + if type(self.distrib) == 'table' and type(self.distrib.shuffle) == 'function' then + self.distrib:shuffle(self.rngID) + else + print(":WARNING: No self.distrib.shuffle not found.") + changeSeed(0) + end +end + +--Class: normal_distribution +---------------------------- +normal_distribution = {} +function normal_distribution:new(avg, stddev) + local o = {} + self.__index = self + if type(avg) ~= 'number' or type(stddev) ~= 'number' then + error("Invalid arguments in normal_distribution construction. Average and standard deviation must be numbers.") + end + o.average = avg + o.std_deviation = stddev + setmetatable(o,self) + return o +end +function normal_distribution:next(id) + return rollNormal(id, self.average, self.std_deviation) +end + +--Class: real_distribution +---------------------------- +real_distribution = {} +function real_distribution:new(min, max) + local o = {} + self.__index = self + if type(min) ~= 'number' or type(max) ~= 'number' then + error("Invalid arguments in real_distribution construction. min and max must be numbers.") + end + o.min = min + o.max = max + setmetatable(o,self) + return o +end +function real_distribution:next(id) + return rollDouble(id, self.min, self.max) +end + +--Class: int_distribution +---------------------------- +int_distribution = {} +function int_distribution:new(min, max) + local o = {} + self.__index = self + if type(min) ~= 'number' or type(max) ~= 'number' then + error("Invalid arguments in int_distribution construction. min and max must be numbers.") + end + o.min = min + o.max = max + setmetatable(o,self) + return o +end +function int_distribution:next(id) + return rollInt(id, self.min, self.max) +end + +--Class: bool_distribution +---------------------------- +bool_distribution = {} +function bool_distribution:new(chance) + local o = {} + self.__index = self + if type(min) ~= 'number' or type(max) ~= 'number' then + error("Invalid arguments in bool_distribution construction. min and max must be numbers.") + end + o.p = chance + setmetatable(o,self) + return o +end +function bool_distribution:next(id) + return rollBool(id, self.p) +end + +--Class: num_sequence +---------------------------- +num_sequence = {} +function num_sequence:new(a,b) + local o = {} + self.__index = self + local btype = type(b) + local atype = type(a) + if atype == 'number' and btype == 'number' then + if a == b then + print(":WARNING: You've provided two equal arguments to initialize your sequence with. This is the mechanism used to indicate to cxxrandom.plug.dll that an empty sequence is desired.\nRecommendation: provide no arguments if you wish for an empty sequence.") + end + o.seqID = MakeNumSequence(a, b) + elseif atype == 'table' then + o.seqID = MakeNumSequence(0,0) + for _,v in pairs(a) do + if type(v) ~= 'number' then + error("num_sequence can only be initialized using numbers. " .. tostring(v) .. " is not a number.") + end + AddToSequence(o.seqID, v) + end + elseif atype == "nil" and btype == "nil" then + o.seqID = MakeNumSequence(0,0) + else + error("Invalid arguments - a: " .. tostring(a) .. " and b: " .. tostring(b)) + end + print("seqID:"..o.seqID) + setmetatable(o,self) + return o +end +function num_sequence:__gc() + DestroyNumSequence(self.seqID) +end +function num_sequence:add(x) + if type(x) ~= 'number' then + error("Cannot add non-number to num_sequence.") + end + AddToSequence(self.seqID, x) +end +function num_sequence:next() + return NextInSequence(self.seqID) +end +function num_sequence:shuffle() + if self.rngID == 'nil' then + error("Add num_sequence object to crng as distribution, before attempting to shuffle.") + end + ShuffleSequence(self.rngID, self.seqID) +end + return _ENV \ No newline at end of file