Merge branch 'develop' of https://github.com/DFHack/dfhack into remote_reader
# Conflicts: # docs/changelog.txtdevelop
						commit
						4b64d2dec6
					
				| @ -0,0 +1 @@ | ||||
| Subproject commit ddabf50f72cf369bf652a95c4d9fe31a1865a781 | ||||
| @ -1,2 +0,0 @@ | ||||
| PROJECT(jsoncpp) | ||||
| ADD_LIBRARY(jsoncpp STATIC jsoncpp.cpp) | ||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -1 +1 @@ | ||||
| Subproject commit c3025feb80c6f8e7441ea5dcf4f463a9cf89cbbd | ||||
| Subproject commit 372993c58851153320f3390119cdd2d97caef9db | ||||
| @ -0,0 +1,274 @@ | ||||
| /* 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 <random> | ||||
| #include <chrono> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
| #include <algorithm> | ||||
| #include <cstdint> | ||||
| #include <cinttypes> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "Error.h" | ||||
| #include "Core.h" | ||||
| #include "DataFuncs.h" | ||||
| #include <Console.h> | ||||
| #include <Export.h> | ||||
| #include <PluginManager.h> | ||||
| 
 | ||||
| using namespace DFHack; | ||||
| DFHACK_PLUGIN("cxxrandom"); | ||||
| #define PLUGIN_VERSION 2.0 | ||||
| color_ostream *cout = nullptr; | ||||
| 
 | ||||
| DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands) | ||||
| { | ||||
|     cout = &out; | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class EnginesKeeper | ||||
| { | ||||
| private: | ||||
|     EnginesKeeper() {} | ||||
|     std::unordered_map<uint16_t, std::mt19937_64> 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]; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| uint16_t GenerateEngine( uint64_t seed ) | ||||
| { | ||||
|     return EnginesKeeper::Instance().NewEngine( seed ); | ||||
| } | ||||
| 
 | ||||
| void DestroyEngine( uint16_t id ) | ||||
| { | ||||
|     EnginesKeeper::Instance().DestroyEngine( id ); | ||||
| } | ||||
| 
 | ||||
| void NewSeed( uint16_t id, uint64_t seed ) | ||||
| { | ||||
|     EnginesKeeper::Instance().NewSeed( id, seed ); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int      rollInt(uint16_t id, int min, int max) | ||||
| { | ||||
|     std::uniform_int_distribution<int> ND(min, max); | ||||
|     return ND(EnginesKeeper::Instance().RNG(id)); | ||||
| } | ||||
| 
 | ||||
| double   rollDouble(uint16_t id, double min, double max) | ||||
| { | ||||
|     std::uniform_real_distribution<double> ND(min, max); | ||||
|     return ND(EnginesKeeper::Instance().RNG(id)); | ||||
| } | ||||
| 
 | ||||
| double   rollNormal(uint16_t id, double mean, double stddev) | ||||
| { | ||||
|     std::normal_distribution<double> 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)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class NumberSequence | ||||
| { | ||||
| private: | ||||
|     unsigned short m_position = 0; | ||||
|     std::vector<int64_t> m_numbers; | ||||
| public: | ||||
|     NumberSequence(){} | ||||
|     NumberSequence( int64_t start, int64_t end ) | ||||
|     { | ||||
|         for( int64_t i = start; i <= end; ++i ) | ||||
|         { | ||||
|             m_numbers.push_back( i ); | ||||
|         } | ||||
|     } | ||||
|     void Add( int64_t num ) { m_numbers.push_back( num ); } | ||||
|     void Reset()            { m_numbers.clear(); } | ||||
|     int64_t Next() | ||||
|     { | ||||
|         if(m_position >= m_numbers.size()) | ||||
|         { | ||||
|             m_position = 0; | ||||
|         } | ||||
|         return m_numbers[m_position++]; | ||||
|     } | ||||
|     void Shuffle( uint16_t id ) | ||||
|     { | ||||
|         std::shuffle( std::begin( m_numbers ), std::end( m_numbers ), EnginesKeeper::Instance().RNG( id ) ); | ||||
|     } | ||||
|     void Print() | ||||
|     { | ||||
|         char buffer1[256] = {0}; | ||||
|         char buffer2[256] = {0}; | ||||
|         for( auto v : m_numbers ) | ||||
|         { | ||||
|             sprintf( buffer2, "%s%" PRId64, buffer1, v ); | ||||
|             sprintf( buffer1, "%s ", buffer2 ); | ||||
|         } | ||||
|         cout->print( "%s", buffer1 ); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class SequenceKeeper | ||||
| { | ||||
| private: | ||||
|     SequenceKeeper() {} | ||||
|     std::unordered_map<uint16_t, NumberSequence> 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(); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| uint16_t MakeNumSequence( int64_t start, int64_t end ) | ||||
| { | ||||
|     if( start == end ) | ||||
|     { | ||||
|         return SequenceKeeper::Instance().MakeNumSequence(); | ||||
|     } | ||||
|     return SequenceKeeper::Instance().MakeNumSequence( start, end ); | ||||
| } | ||||
| 
 | ||||
| void     DestroyNumSequence( uint16_t id ) | ||||
| { | ||||
|     SequenceKeeper::Instance().DestroySequence( id ); | ||||
| } | ||||
| 
 | ||||
| void     AddToSequence( uint16_t id, int64_t num ) | ||||
| { | ||||
|     SequenceKeeper::Instance().AddToSequence( id, num ); | ||||
| } | ||||
| 
 | ||||
| void     ShuffleSequence( uint16_t rngID, uint16_t id ) | ||||
| { | ||||
|     SequenceKeeper::Instance().Shuffle( id, rngID ); | ||||
| } | ||||
| 
 | ||||
| int64_t  NextInSequence( uint16_t id ) | ||||
| { | ||||
|     return SequenceKeeper::Instance().NextInSequence( id ); | ||||
| } | ||||
| 
 | ||||
| void DebugSequence( uint16_t id ) | ||||
| { | ||||
|     SequenceKeeper::Instance().PrintSequence( id ); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| DFHACK_PLUGIN_LUA_FUNCTIONS { | ||||
|     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 | ||||
| }; | ||||
| @ -0,0 +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 | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue