Skip to content

Commit f47d40a

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 f47d40a

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

src/displayapp/screens/Dice.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ 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 = (static_cast<uint32_t>(xTaskGetTickCount()) << 32) ^ (uint64_t) &rng;
47+
rng.inc = (static_cast<uint32_t>(motionController.X() << 32) ^ (static_cast<uint32_t>(motionController.Y() << 16)) ^ static_cast<uint32_t>(motionController.Z());
48+
uint8_t discard = pcg32_random_r(&rng);
49+
for (uint32_t i = 0; i < discard; i++)
50+
pcg32_random_r(&rng);
5151

5252
lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20,
5353
LV_COLOR_WHITE,
@@ -157,12 +157,10 @@ void Dice::Refresh() {
157157
void Dice::Roll() {
158158
uint8_t resultIndividual;
159159
uint16_t resultTotal = 0;
160-
std::uniform_int_distribution<> distrib(1, dCounter.GetValue());
161-
162160
lv_label_set_text(resultIndividualLabel, "");
163161

164162
if (nCounter.GetValue() == 1) {
165-
resultTotal = distrib(gen);
163+
resultTotal = bounded_rand(rng, dCounter.GetValue()) + 1;
166164
if (dCounter.GetValue() == 2) {
167165
switch (resultTotal) {
168166
case 1:
@@ -175,7 +173,7 @@ void Dice::Roll() {
175173
}
176174
} else {
177175
for (uint8_t i = 0; i < nCounter.GetValue(); i++) {
178-
resultIndividual = distrib(gen);
176+
resultIndividual = bounded_rand(rng, dCounter.GetValue()) + 1;
179177
resultTotal += resultIndividual;
180178
lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, std::to_string(resultIndividual).c_str());
181179
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)