Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fe29bff

Browse files
authored
Adds cxxrandom unit test and fixes interface problems (#2099)
* Adds cxxrandom unit test and fixes interface problems * Tightens braces * Adds detection code for Shuffle's seqID/engID * Adds usage examples for cxxrandom * Gives cxxrandom objects id ranges, sort of * Updates changelog * Updates changelog.txt * Increases id space for cxxrandom * Fixes bool distribution error message and improves check * Adds comment explaining the seeded RNG tests for cxxrandom * Fixes type problem for 32bit builds * Reduces loop count a few magnitudes * Fixes a mistake in test.cxxrandom_seed
1 parent 9643246 commit fe29bff

5 files changed

Lines changed: 202 additions & 114 deletions

File tree

docs/Lua API.rst

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4401,7 +4401,7 @@ Native functions (exported to Lua)
44014401

44024402
adds a number to the sequence
44034403

4404-
- ``ShuffleSequence(rngID, seqID)``
4404+
- ``ShuffleSequence(seqID, rngID)``
44054405

44064406
shuffles the number sequence
44074407

@@ -4464,7 +4464,7 @@ Lua plugin classes
44644464
``bool_distribution``
44654465
~~~~~~~~~~~~~~~~~~~~~
44664466

4467-
- ``init(min, max)``: constructor
4467+
- ``init(chance)``: constructor
44684468
- ``next(id)``: returns next boolean in the distribution
44694469

44704470
- ``id``: engine ID to pass to native function
@@ -4477,6 +4477,41 @@ Lua plugin classes
44774477
- ``shuffle()``: shuffles the sequence of numbers
44784478
- ``next()``: returns next number in the sequence
44794479

4480+
Usage
4481+
-----
4482+
4483+
The basic idea is you create a number distribution which you generate random numbers along. The C++ relies
4484+
on engines keeping state information to determine the next number along the distribution.
4485+
You're welcome to try and (ab)use this knowledge for your RNG purposes.
4486+
4487+
Example::
4488+
4489+
local rng = require('plugins.cxxrandom')
4490+
local norm_dist = rng.normal_distribution(6820,116) // avg, stddev
4491+
local engID = rng.MakeNewEngine(0)
4492+
-- somewhat reminiscent of the C++ syntax
4493+
print(norm_dist:next(engID))
4494+
4495+
-- a bit more streamlined
4496+
local cleanup = true --delete engine on cleanup
4497+
local number_generator = rng.crng:new(engID, cleanup, norm_dist)
4498+
print(number_generator:next())
4499+
4500+
-- simplified
4501+
print(rng.rollNormal(engID,6820,116))
4502+
4503+
The number sequences are much simpler. They're intended for where you need to randomly generate an index, perhaps in a loop for an array. You technically don't need an engine to use it, if you don't mind never shuffling.
4504+
4505+
Example::
4506+
4507+
local rng = require('plugins.cxxrandom')
4508+
local g = rng.crng:new(rng.MakeNewEngine(0), true, rng.num_sequence:new(0,table_size))
4509+
g:shuffle()
4510+
for _ = 1, table_size do
4511+
func(array[g:next()])
4512+
end
4513+
4514+
44804515
dig-now
44814516
=======
44824517

docs/changelog.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
4242
- `tweak` partial-items: displays percentages on partially-consumed items such as hospital cloth
4343

4444
## Fixes
45+
- `cxxrandom`: fixed exception when calling ``bool_distribution``
46+
- `cxxrandom`: fixed id order for ShuffleSequence, but adds code to detect which parameter is which so each id is used correctly. 16000 limit before things get weird (previous was 16 bits)
4547
- `autofarm` removed restriction on only planting 'discovered' plants
4648
- `luasocket` (and others): return correct status code when closing socket connections
4749

@@ -64,6 +66,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
6466
- Include recently-added tweaks in example dfhack.init file, clean up dreamfort onMapLoad.init file
6567

6668
## Documentation
69+
- `cxxrandom`: added usage examples
6770
- Add more examples to the plugin skeleton files so they are more informative for a newbie
6871
- Lua API.rst added: ``isHidden(unit)``, ``isFortControlled(unit)``, ``getOuterContainerRef(unit)``, ``getOuterContainerRef(item)``
6972
- Update download link and installation instructions for Visual C++ 2015 build tools on Windows

plugins/cxxrandom.cpp

Lines changed: 83 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -37,94 +37,82 @@ DFHACK_PLUGIN("cxxrandom");
3737
#define PLUGIN_VERSION 2.0
3838
color_ostream *cout = nullptr;
3939

40-
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
41-
{
40+
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands) {
4241
cout = &out;
4342
return CR_OK;
4443
}
4544

46-
DFhackCExport command_result plugin_shutdown (color_ostream &out)
47-
{
45+
DFhackCExport command_result plugin_shutdown (color_ostream &out) {
4846
return CR_OK;
4947
}
5048

51-
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
52-
{
49+
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) {
5350
return CR_OK;
5451
}
5552

53+
#define EK_ID_BASE (1ll << 40)
5654

5755
class EnginesKeeper
5856
{
5957
private:
60-
EnginesKeeper() {}
61-
std::unordered_map<uint16_t, std::mt19937_64> m_engines;
62-
uint16_t counter = 0;
58+
EnginesKeeper() = default;
59+
std::unordered_map<uint64_t, std::mt19937_64> m_engines;
60+
uint64_t id_counter = EK_ID_BASE;
6361
public:
64-
static EnginesKeeper& Instance()
65-
{
62+
static EnginesKeeper& Instance() {
6663
static EnginesKeeper instance;
6764
return instance;
6865
}
69-
uint16_t NewEngine( uint64_t seed )
70-
{
66+
uint64_t NewEngine( uint64_t seed ) {
67+
auto id = ++id_counter;
68+
CHECK_INVALID_ARGUMENT(m_engines.count(id) == 0);
7169
std::mt19937_64 engine( seed != 0 ? seed : std::chrono::system_clock::now().time_since_epoch().count() );
72-
m_engines[++counter] = engine;
73-
return counter;
70+
m_engines[id] = engine;
71+
return id;
7472
}
75-
void DestroyEngine( uint16_t id )
76-
{
73+
void DestroyEngine( uint64_t id ) {
7774
m_engines.erase( id );
7875
}
79-
void NewSeed( uint16_t id, uint64_t seed )
80-
{
76+
void NewSeed( uint64_t id, uint64_t seed ) {
8177
CHECK_INVALID_ARGUMENT( m_engines.find( id ) != m_engines.end() );
8278
m_engines[id].seed( seed != 0 ? seed : std::chrono::system_clock::now().time_since_epoch().count() );
8379
}
84-
std::mt19937_64& RNG( uint16_t id )
85-
{
80+
std::mt19937_64& RNG( uint64_t id ) {
8681
CHECK_INVALID_ARGUMENT( m_engines.find( id ) != m_engines.end() );
8782
return m_engines[id];
8883
}
8984
};
9085

9186

92-
uint16_t GenerateEngine( uint64_t seed )
93-
{
87+
uint64_t GenerateEngine( uint64_t seed ) {
9488
return EnginesKeeper::Instance().NewEngine( seed );
9589
}
9690

97-
void DestroyEngine( uint16_t id )
98-
{
91+
void DestroyEngine( uint64_t id ) {
9992
EnginesKeeper::Instance().DestroyEngine( id );
10093
}
10194

102-
void NewSeed( uint16_t id, uint64_t seed )
103-
{
95+
void NewSeed( uint64_t id, uint64_t seed ) {
10496
EnginesKeeper::Instance().NewSeed( id, seed );
10597
}
10698

10799

108-
int rollInt(uint16_t id, int min, int max)
109-
{
100+
int rollInt(uint64_t id, int min, int max) {
110101
std::uniform_int_distribution<int> ND(min, max);
111102
return ND(EnginesKeeper::Instance().RNG(id));
112103
}
113104

114-
double rollDouble(uint16_t id, double min, double max)
115-
{
105+
double rollDouble(uint64_t id, double min, double max) {
116106
std::uniform_real_distribution<double> ND(min, max);
117107
return ND(EnginesKeeper::Instance().RNG(id));
118108
}
119109

120-
double rollNormal(uint16_t id, double mean, double stddev)
121-
{
110+
double rollNormal(uint64_t id, double mean, double stddev) {
122111
std::normal_distribution<double> ND(mean, stddev);
123112
return ND(EnginesKeeper::Instance().RNG(id));
124113
}
125114

126-
bool rollBool(uint16_t id, float p)
127-
{
115+
bool rollBool(uint64_t id, float p) {
128116
std::bernoulli_distribution ND(p);
129117
return ND(EnginesKeeper::Instance().RNG(id));
130118
}
@@ -137,118 +125,104 @@ class NumberSequence
137125
std::vector<int64_t> m_numbers;
138126
public:
139127
NumberSequence(){}
140-
NumberSequence( int64_t start, int64_t end )
141-
{
142-
for( int64_t i = start; i <= end; ++i )
143-
{
128+
NumberSequence( int64_t start, int64_t end ) {
129+
for( int64_t i = start; i <= end; ++i ) {
144130
m_numbers.push_back( i );
145131
}
146132
}
147133
void Add( int64_t num ) { m_numbers.push_back( num ); }
148-
void Reset() { m_numbers.clear(); }
149-
int64_t Next()
150-
{
151-
if(m_position >= m_numbers.size())
152-
{
134+
void Reset() { m_numbers.clear(); }
135+
int64_t Next() {
136+
if(m_position >= m_numbers.size()) {
153137
m_position = 0;
154138
}
155139
return m_numbers[m_position++];
156140
}
157-
void Shuffle( uint16_t id )
158-
{
159-
std::shuffle( std::begin( m_numbers ), std::end( m_numbers ), EnginesKeeper::Instance().RNG( id ) );
141+
void Shuffle( uint64_t engID ) {
142+
std::shuffle( std::begin( m_numbers ), std::end( m_numbers ), EnginesKeeper::Instance().RNG(engID));
160143
}
161-
void Print()
162-
{
163-
for( auto v : m_numbers )
164-
{
144+
void Print() {
145+
for( auto v : m_numbers ) {
165146
cout->print( "%" PRId64 " ", v );
166147
}
167148
}
168149
};
169150

151+
#define SK_ID_BASE 0
152+
170153
class SequenceKeeper
171154
{
172155
private:
173-
SequenceKeeper() {}
174-
std::unordered_map<uint16_t, NumberSequence> m_sequences;
175-
uint16_t counter = 0;
156+
SequenceKeeper() = default;
157+
std::unordered_map<uint64_t, NumberSequence> m_sequences;
158+
uint64_t id_counter = SK_ID_BASE;
176159
public:
177-
static SequenceKeeper& Instance()
178-
{
160+
static SequenceKeeper& Instance() {
179161
static SequenceKeeper instance;
180162
return instance;
181163
}
182-
uint16_t MakeNumSequence( int64_t start, int64_t end )
183-
{
184-
m_sequences[++counter] = NumberSequence( start, end );
185-
return counter;
186-
}
187-
uint16_t MakeNumSequence()
188-
{
189-
m_sequences[++counter] = NumberSequence();
190-
return counter;
191-
}
192-
void DestroySequence( uint16_t id )
193-
{
194-
m_sequences.erase( id );
195-
}
196-
void AddToSequence( uint16_t id, int64_t num )
197-
{
198-
CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() );
199-
m_sequences[id].Add( num );
200-
}
201-
void Shuffle( uint16_t id, uint16_t rng_id )
202-
{
203-
CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() );
204-
m_sequences[id].Shuffle( rng_id );
205-
}
206-
int64_t NextInSequence( uint16_t id )
207-
{
208-
CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() );
209-
return m_sequences[id].Next();
210-
}
211-
void PrintSequence( uint16_t id )
212-
{
213-
CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() );
214-
auto seq = m_sequences[id];
164+
uint64_t MakeNumSequence( int64_t start, int64_t end ) {
165+
auto id = ++id_counter;
166+
CHECK_INVALID_ARGUMENT(m_sequences.count(id) == 0);
167+
m_sequences[id] = NumberSequence(start, end);
168+
return id;
169+
}
170+
uint64_t MakeNumSequence() {
171+
auto id = ++id_counter;
172+
CHECK_INVALID_ARGUMENT(m_sequences.count(id) == 0);
173+
m_sequences[id] = NumberSequence();
174+
return id;
175+
}
176+
void DestroySequence( uint64_t seqID ) {
177+
m_sequences.erase(seqID);
178+
}
179+
void AddToSequence(uint64_t seqID, int64_t num ) {
180+
CHECK_INVALID_ARGUMENT(m_sequences.find(seqID) != m_sequences.end());
181+
m_sequences[seqID].Add(num);
182+
}
183+
void Shuffle(uint64_t seqID, uint64_t engID ) {
184+
uint64_t sid = seqID >= SK_ID_BASE ? seqID : engID;
185+
uint64_t eid = engID >= EK_ID_BASE ? engID : seqID;
186+
CHECK_INVALID_ARGUMENT(m_sequences.find(sid) != m_sequences.end());
187+
m_sequences[sid].Shuffle(eid);
188+
}
189+
int64_t NextInSequence( uint64_t seqID ) {
190+
CHECK_INVALID_ARGUMENT(m_sequences.find(seqID) != m_sequences.end());
191+
return m_sequences[seqID].Next();
192+
}
193+
void PrintSequence( uint64_t seqID ) {
194+
CHECK_INVALID_ARGUMENT(m_sequences.find(seqID) != m_sequences.end());
195+
auto seq = m_sequences[seqID];
215196
seq.Print();
216197
}
217198
};
218199

219200

220-
uint16_t MakeNumSequence( int64_t start, int64_t end )
221-
{
222-
if( start == end )
223-
{
201+
uint64_t MakeNumSequence( int64_t start, int64_t end ) {
202+
if (start == end) {
224203
return SequenceKeeper::Instance().MakeNumSequence();
225204
}
226-
return SequenceKeeper::Instance().MakeNumSequence( start, end );
205+
return SequenceKeeper::Instance().MakeNumSequence(start, end);
227206
}
228207

229-
void DestroyNumSequence( uint16_t id )
230-
{
231-
SequenceKeeper::Instance().DestroySequence( id );
208+
void DestroyNumSequence( uint64_t seqID ) {
209+
SequenceKeeper::Instance().DestroySequence(seqID);
232210
}
233211

234-
void AddToSequence( uint16_t id, int64_t num )
235-
{
236-
SequenceKeeper::Instance().AddToSequence( id, num );
212+
void AddToSequence(uint64_t seqID, int64_t num ) {
213+
SequenceKeeper::Instance().AddToSequence(seqID, num);
237214
}
238215

239-
void ShuffleSequence( uint16_t rngID, uint16_t id )
240-
{
241-
SequenceKeeper::Instance().Shuffle( id, rngID );
216+
void ShuffleSequence(uint64_t seqID, uint64_t engID ) {
217+
SequenceKeeper::Instance().Shuffle(seqID, engID);
242218
}
243219

244-
int64_t NextInSequence( uint16_t id )
245-
{
246-
return SequenceKeeper::Instance().NextInSequence( id );
220+
int64_t NextInSequence( uint64_t seqID ) {
221+
return SequenceKeeper::Instance().NextInSequence(seqID);
247222
}
248223

249-
void DebugSequence( uint16_t id )
250-
{
251-
SequenceKeeper::Instance().PrintSequence( id );
224+
void DebugSequence( uint64_t seqID ) {
225+
SequenceKeeper::Instance().PrintSequence(seqID);
252226
}
253227

254228

plugins/lua/cxxrandom.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ bool_distribution = {}
151151
function bool_distribution:new(chance)
152152
local o = {}
153153
self.__index = self
154-
if type(min) ~= 'number' or type(max) ~= 'number' then
155-
error("Invalid arguments in bool_distribution construction. min and max must be numbers.")
154+
if type(chance) ~= 'number' or chance < 0 or chance > 1 then
155+
error("Invalid arguments in bool_distribution construction. chance must be a number between 0.0 and 1.0 (both included).")
156156
end
157157
o.p = chance
158158
setmetatable(o,self)
@@ -208,7 +208,7 @@ function num_sequence:shuffle()
208208
if self.rngID == 'nil' then
209209
error("Add num_sequence object to crng as distribution, before attempting to shuffle.")
210210
end
211-
ShuffleSequence(self.rngID, self.seqID)
211+
ShuffleSequence(self.seqID, self.rngID)
212212
end
213213

214214
return _ENV

0 commit comments

Comments
 (0)