Skip to content

Commit 3f0f369

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 3f0f369

File tree

2 files changed

+53
-13
lines changed

2 files changed

+53
-13
lines changed

src/displayapp/screens/Dice.cpp

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,31 @@
55
#include "components/motor/MotorController.h"
66
#include "components/motion/MotionController.h"
77

8+
namespace PCG {
9+
uint32_t pcg32_random_r(pcg32_random_t* rng) {
10+
uint64_t oldstate = rng->state;
11+
// Advance internal state
12+
rng->state = oldstate * 6364136223846793005ULL + (rng->inc | 1);
13+
// Calculate output function (XSH RR), uses old state for max ILP
14+
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
15+
uint32_t rot = oldstate >> 59u;
16+
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
17+
}
18+
19+
// Lemire's Method (slight rewrite) [0, range)
20+
uint32_t bounded_rand(pcg32_random_t& rng, uint32_t range) {
21+
uint64_t m;
22+
uint32_t t = (-range) % range;
23+
uint32_t l;
24+
do {
25+
uint32_t x = pcg32_random_r(&rng);
26+
m = uint64_t(x) * uint64_t(range);
27+
l = uint32_t(m);
28+
} while (l < t);
29+
return m >> 32;
30+
}
31+
};
32+
833
using namespace Pinetime::Applications::Screens;
934

1035
namespace {
@@ -43,11 +68,13 @@ Dice::Dice(Controllers::MotionController& motionController,
4368
Controllers::MotorController& motorController,
4469
Controllers::Settings& settingsController)
4570
: 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);
71+
rng.state =
72+
(static_cast<uint64_t>(xTaskGetTickCount()) << 32) ^ (static_cast<uint64_t>(motionController.NbSteps()) << 16) ^ (uint64_t) &rng;
73+
rng.inc = (static_cast<uint64_t>(motionController.X()) << 32) ^ (static_cast<uint64_t>(motionController.Y()) << 16) ^
74+
static_cast<uint64_t>(motionController.Z());
75+
uint8_t discard = PCG::pcg32_random_r(&rng);
76+
for (uint32_t i = 0; i < discard; i++)
77+
PCG::pcg32_random_r(&rng);
5178

5279
lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20,
5380
LV_COLOR_WHITE,
@@ -79,8 +106,7 @@ Dice::Dice(Controllers::MotionController& motionController,
79106
lv_obj_align(dCounter.GetObject(), dCounterLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
80107
dCounter.SetValue(6);
81108

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

85111
resultTotalLabel = MakeLabel(&jetbrains_mono_42,
86112
resultColors[currentColorIndex],
@@ -157,12 +183,10 @@ void Dice::Refresh() {
157183
void Dice::Roll() {
158184
uint8_t resultIndividual;
159185
uint16_t resultTotal = 0;
160-
std::uniform_int_distribution<> distrib(1, dCounter.GetValue());
161-
162186
lv_label_set_text(resultIndividualLabel, "");
163187

164188
if (nCounter.GetValue() == 1) {
165-
resultTotal = distrib(gen);
189+
resultTotal = PCG::bounded_rand(rng, dCounter.GetValue()) + 1;
166190
if (dCounter.GetValue() == 2) {
167191
switch (resultTotal) {
168192
case 1:
@@ -175,7 +199,7 @@ void Dice::Roll() {
175199
}
176200
} else {
177201
for (uint8_t i = 0; i < nCounter.GetValue(); i++) {
178-
resultIndividual = distrib(gen);
202+
resultIndividual = PCG::bounded_rand(rng, dCounter.GetValue()) + 1;
179203
resultTotal += resultIndividual;
180204
lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, std::to_string(resultIndividual).c_str());
181205
if (i < (nCounter.GetValue() - 1)) {

src/displayapp/screens/Dice.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,23 @@
77
#include "Symbols.h"
88

99
#include <array>
10-
#include <random>
10+
#include <cstdint>
11+
12+
namespace PCG {
13+
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
14+
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
15+
// Website: https://www.pcg-random.org/download.html
16+
// See: https://www.apache.org/licenses/GPL-compatibility.html
17+
typedef struct {
18+
uint64_t state;
19+
uint64_t inc;
20+
} pcg32_random_t;
21+
22+
uint32_t pcg32_random_r(pcg32_random_t* rng);
23+
24+
// Lemire's Method (slight rewrite) [0, range)
25+
uint32_t bounded_rand(pcg32_random_t& rng, uint32_t range);
26+
};
1127

1228
namespace Pinetime {
1329
namespace Applications {
@@ -29,7 +45,7 @@ namespace Pinetime {
2945
lv_task_t* refreshTask;
3046
bool enableShakeForDice = false;
3147

32-
std::mt19937 gen;
48+
PCG::pcg32_random_t rng;
3349

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

0 commit comments

Comments
 (0)