Skip to content

Commit b801061

Browse files
committed
Use Melissa E. O'Neill's pcg for rng and Lemire's unbiased bounded rand
Mersenne Twister's state size is huge (kb's of state), instead we'll swap with a pcg. The pcg's state is 128 bits and has a equidistributed 2^64 period (plenty for a quick rng). To replicate std::distribution we use lemire's unbiased bounded random method.
1 parent 7128fc0 commit b801061

File tree

2 files changed

+43
-12
lines changed

2 files changed

+43
-12
lines changed

src/displayapp/screens/Dice.cpp

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ Dice::Dice(Controllers::MotionController& motionController,
4343
Controllers::MotorController& motorController,
4444
Controllers::Settings& settingsController)
4545
: motorController {motorController}, motionController {motionController}, settingsController {settingsController} {
46-
std::seed_seq sseq {static_cast<uint32_t>(xTaskGetTickCount()),
47-
static_cast<uint32_t>(motionController.X()),
48-
static_cast<uint32_t>(motionController.Y()),
49-
static_cast<uint32_t>(motionController.Z())};
50-
gen.seed(sseq);
46+
rng.state =
47+
(static_cast<uint64_t>(xTaskGetTickCount()) << 32) ^ (static_cast<uint64_t>(motionController.NbSteps()) << 16) ^ (uint64_t) &rng;
48+
rng.inc = (static_cast<uint64_t>(motionController.X()) << 32) ^ (static_cast<uint64_t>(motionController.Y()) << 16) ^
49+
static_cast<uint64_t>(motionController.Z());
50+
uint8_t discard = pcg32_random_r(&rng);
51+
for (uint32_t i = 0; i < discard; i++)
52+
pcg32_random_r(&rng);
5153

5254
lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20,
5355
LV_COLOR_WHITE,
@@ -79,8 +81,7 @@ Dice::Dice(Controllers::MotionController& motionController,
7981
lv_obj_align(dCounter.GetObject(), dCounterLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
8082
dCounter.SetValue(6);
8183

82-
std::uniform_int_distribution<> distrib(0, resultColors.size() - 1);
83-
currentColorIndex = distrib(gen);
84+
currentColorIndex = bounded_rand(rng, resultColors.size());
8485

8586
resultTotalLabel = MakeLabel(&jetbrains_mono_42,
8687
resultColors[currentColorIndex],
@@ -157,12 +158,10 @@ void Dice::Refresh() {
157158
void Dice::Roll() {
158159
uint8_t resultIndividual;
159160
uint16_t resultTotal = 0;
160-
std::uniform_int_distribution<> distrib(1, dCounter.GetValue());
161-
162161
lv_label_set_text(resultIndividualLabel, "");
163162

164163
if (nCounter.GetValue() == 1) {
165-
resultTotal = distrib(gen);
164+
resultTotal = bounded_rand(rng, dCounter.GetValue()) + 1;
166165
if (dCounter.GetValue() == 2) {
167166
switch (resultTotal) {
168167
case 1:
@@ -175,7 +174,7 @@ void Dice::Roll() {
175174
}
176175
} else {
177176
for (uint8_t i = 0; i < nCounter.GetValue(); i++) {
178-
resultIndividual = distrib(gen);
177+
resultIndividual = bounded_rand(rng, dCounter.GetValue()) + 1;
179178
resultTotal += resultIndividual;
180179
lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, std::to_string(resultIndividual).c_str());
181180
if (i < (nCounter.GetValue() - 1)) {

src/displayapp/screens/Dice.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,38 @@
1212
namespace Pinetime {
1313
namespace Applications {
1414
namespace Screens {
15+
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
16+
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
17+
// Website: https://www.pcg-random.org/download.html
18+
// See: https://www.apache.org/licenses/GPL-compatibility.html
19+
typedef struct {
20+
uint64_t state;
21+
uint64_t inc;
22+
} pcg32_random_t;
23+
24+
uint32_t pcg32_random_r(pcg32_random_t* rng) {
25+
uint64_t oldstate = rng->state;
26+
// Advance internal state
27+
rng->state = oldstate * 6364136223846793005ULL + (rng->inc | 1);
28+
// Calculate output function (XSH RR), uses old state for max ILP
29+
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
30+
uint32_t rot = oldstate >> 59u;
31+
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
32+
}
33+
34+
// Lemire's Method (slight rewrite) [0, range)
35+
uint32_t bounded_rand(pcg32_random_t& rng, uint32_t range) {
36+
uint64_t m;
37+
uint32_t t = (-range) % range;
38+
uint32_t l;
39+
do {
40+
uint32_t x = pcg32_random_r(&rng);
41+
m = uint64_t(x) * uint64_t(range);
42+
l = uint32_t(m);
43+
} while (l < t);
44+
return m >> 32;
45+
}
46+
1547
class Dice : public Screen {
1648
public:
1749
Dice(Controllers::MotionController& motionController,
@@ -29,7 +61,7 @@ namespace Pinetime {
2961
lv_task_t* refreshTask;
3062
bool enableShakeForDice = false;
3163

32-
std::mt19937 gen;
64+
pcg32_random_t rng;
3365

3466
std::array<lv_color_t, 3> resultColors = {LV_COLOR_YELLOW, LV_COLOR_MAGENTA, LV_COLOR_AQUA};
3567
uint8_t currentColorIndex;

0 commit comments

Comments
 (0)