Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "src/libs/arduinoFFT"]
path = src/libs/arduinoFFT
url = https://github.com/kosme/arduinoFFT.git
[submodule "src/pcg-cpp"]
path = src/pcg-cpp
url = https://github.com/imneme/pcg-cpp
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.10)

project(pinetime-app C CXX ASM)


# define some variables just for this example to determine file locations
set(NRF_PROJECT_NAME pinetime-app)
set(NRF_BOARD pca10040)
Expand Down Expand Up @@ -473,6 +472,8 @@ list(APPEND SOURCE_FILES
components/stopwatch/StopWatchController.cpp
components/alarm/AlarmController.cpp
components/fs/FS.cpp
components/rng/PCG.cpp

drivers/Cst816s.cpp
FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c
Expand Down Expand Up @@ -662,6 +663,7 @@ set(INCLUDE_FILES
components/timer/Timer.h
components/stopwatch/StopWatchController.h
components/alarm/AlarmController.h
components/rng/PCG.h
drivers/Cst816s.h
FreeRTOS/portmacro.h
FreeRTOS/portmacro_cmsis.h
Expand Down
105 changes: 105 additions & 0 deletions src/components/rng/PCG.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "components/rng/PCG.h"

using namespace Pinetime::Controllers;

RNG::RNG(result_type value) {
// pcg32_srandom_r(&rng, 0x853c49e6748fea9bULL, value);
rng.state = 0U;
rng.inc = (value << 1u) | 1u;
(*this)(); // pcg32_random_r(rng);
rng.state += 0x853c49e6748fea9bULL;
(*this)(); // pcg32_random_r(rng);
};

RNG& RNG::operator=(const pcg32_random_t& other) {
rng.state = other.state;
rng.inc = other.inc | 1;
return *this;
};

template <class SeedSeq>
RNG::RNG(SeedSeq& seq) {
seed(seq);
};

/*
RNG::RNG(const RNG& other) {
RNG tmp = other;
RNG::result_type* ptr = (RNG::result_type*) this;
tmp.generate(ptr, ptr + (sizeof(RNG) / sizeof(RNG::result_type)));
};
*/
template <typename SeedSeq>
void RNG::seed(SeedSeq& seq) {
RNG::result_type* ptr = (RNG::result_type*) this;
seq.generate(ptr, ptr + (sizeof(RNG) / sizeof(RNG::result_type)));
rng.inc |= 1;
};

void RNG::seed(RNG& other) {
RNG::result_type* ptr = (RNG::result_type*) this;
other.generate(ptr, ptr + (sizeof(RNG) / sizeof(RNG::result_type)));
rng.inc |= 1;
};

RNG::result_type RNG::operator()() {
// return pcg32_random_r(&rng);
uint64_t oldstate = rng.state;
rng.state = oldstate * 6364136223846793005ULL + rng.inc;
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
uint32_t rot = oldstate >> 59u;
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
};

RNG::result_type RNG::operator()(RNG::result_type bound) {
// return pcg32_boundedrand_r(&rng, bounds);
// To avoid bias, we need to make the range of the RNG a multiple of
// bound, which we do by dropping output less than a threshold.
// A naive scheme to calculate the threshold would be to do
//
// uint32_t threshold = 0x100000000ull % bound;
//
// but 64-bit div/mod is slower than 32-bit div/mod (especially on
// 32-bit platforms). In essence, we do
//
// uint32_t threshold = (0x100000000ull-bound) % bound;
//
// because this version will calculate the same modulus, but the LHS
// value is less than 2^32.

uint32_t threshold = -bound % bound;

// Uniformity guarantees that this loop will terminate. In practice, it
// should usually terminate quickly; on average (assuming all bounds are
// equally likely), 82.25% of the time, we can expect it to require just
// one iteration. In the worst case, someone passes a bound of 2^31 + 1
// (i.e., 2147483649), which invalidates almost 50% of the range. In
// practice, bounds are typically small and only a tiny amount of the range
// is eliminated.
for (;;) {
uint32_t r = (*this)(); // pcg32_random_r(rng);
if (r >= threshold)
return r % bound;
}
};

// std::seed_seq interface
template <typename It>
void RNG::generate(It start, It end) {
for (; start != end; ++start)
*start = (*this)();
};

std::size_t RNG::size() const noexcept {
return sizeof(rng) / sizeof(RNG::result_type);
};

template <class OutputIt>
void RNG::param(OutputIt dest) const {
std::size_t i = 0;
const std::size_t n = size();
RNG::result_type* ptr = (RNG::result_type*) this;
for (; i < n; ++i, ++dest, ++ptr) {
*dest = ptr;
}
};
81 changes: 81 additions & 0 deletions src/components/rng/PCG.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#pragma once
#include <cstdint>
#include <FreeRTOS.h>
#include <timers.h>
#include "components/motion/MotionController.h"

namespace Pinetime {
namespace Controllers {
/*
* PCG Random Number Generation for C.
*
* Copyright 2014 Melissa O'Neill <oneill@pcg-random.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For additional information about the PCG random number generation scheme,
* including its license and other licensing options, visit
*
* http://www.pcg-random.org
*/

struct pcg_state_setseq_64 { // Internals are *Private*.
uint64_t state = 0x853c49e6748fea9bULL; // RNG state. All values are possible.
uint64_t inc = 0xda3e39cb94b95bdbULL; // Controls which RNG sequence (stream) is
// selected. Must *always* be odd.
// pcg_state_setseq_64() = default;
// pcg_state_setseq_64(uint64_t s, uint64_t i);
};
typedef struct pcg_state_setseq_64 pcg32_random_t;

struct RNG {
pcg32_random_t rng = {};
// <random> interface
using result_type = uint32_t;

static constexpr result_type min() {
return result_type {};
};

static constexpr result_type max() {
return ~result_type {0UL};
};

// Constructors
// RNG();
RNG() = default;
explicit RNG(result_type value);

template <class SeedSeq>
explicit RNG(SeedSeq& seq);

RNG& operator=(const pcg32_random_t& other);

template <typename SeedSeq>
void seed(SeedSeq& seq);

void seed(RNG& other);

result_type operator()();
result_type operator()(result_type bound);
// std::seed_seq interface
template <typename It>
void generate(It start, It end);

std::size_t size() const noexcept;

template <class OutputIt>
void param(OutputIt dest) const;
};
}
}
2 changes: 2 additions & 0 deletions src/displayapp/Controllers.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Pinetime {
class Timer;
class MusicService;
class NavigationService;
struct RNG;
}

namespace System {
Expand Down Expand Up @@ -53,6 +54,7 @@ namespace Pinetime {
Pinetime::Components::LittleVgl& lvgl;
Pinetime::Controllers::MusicService* musicService;
Pinetime::Controllers::NavigationService* navigationService;
Pinetime::Controllers::RNG* prngController;
};
}
}
1 change: 1 addition & 0 deletions src/displayapp/DisplayApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ void DisplayApp::PushMessageToSystemTask(Pinetime::System::Messages message) {
void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
this->controllers.systemTask = systemTask;
this->controllers.prngController = &(systemTask->prngController);
}

void DisplayApp::Register(Pinetime::Controllers::SimpleWeatherService* weatherService) {
Expand Down
18 changes: 6 additions & 12 deletions src/displayapp/screens/Dice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,10 @@ namespace {

Dice::Dice(Controllers::MotionController& motionController,
Controllers::MotorController& motorController,
Controllers::Settings& settingsController)
Controllers::Settings& settingsController,
Controllers::RNG& prngController)
: motorController {motorController}, motionController {motionController}, settingsController {settingsController} {
std::seed_seq sseq {static_cast<uint32_t>(xTaskGetTickCount()),
static_cast<uint32_t>(motionController.X()),
static_cast<uint32_t>(motionController.Y()),
static_cast<uint32_t>(motionController.Z())};
gen.seed(sseq);
rng.seed(prngController);

lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20,
LV_COLOR_WHITE,
Expand Down Expand Up @@ -79,8 +76,7 @@ Dice::Dice(Controllers::MotionController& motionController,
lv_obj_align(dCounter.GetObject(), dCounterLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
dCounter.SetValue(6);

std::uniform_int_distribution<> distrib(0, resultColors.size() - 1);
currentColorIndex = distrib(gen);
currentColorIndex = rng(resultColors.size());

resultTotalLabel = MakeLabel(&jetbrains_mono_42,
resultColors[currentColorIndex],
Expand Down Expand Up @@ -157,12 +153,10 @@ void Dice::Refresh() {
void Dice::Roll() {
uint8_t resultIndividual;
uint16_t resultTotal = 0;
std::uniform_int_distribution<> distrib(1, dCounter.GetValue());

lv_label_set_text(resultIndividualLabel, "");

if (nCounter.GetValue() == 1) {
resultTotal = distrib(gen);
resultTotal = rng(dCounter.GetValue()) + 1;
if (dCounter.GetValue() == 2) {
switch (resultTotal) {
case 1:
Expand All @@ -175,7 +169,7 @@ void Dice::Roll() {
}
} else {
for (uint8_t i = 0; i < nCounter.GetValue(); i++) {
resultIndividual = distrib(gen);
resultIndividual = rng(dCounter.GetValue()) + 1;
resultTotal += resultIndividual;
lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, std::to_string(resultIndividual).c_str());
if (i < (nCounter.GetValue() - 1)) {
Expand Down
13 changes: 9 additions & 4 deletions src/displayapp/screens/Dice.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
#include "displayapp/screens/Screen.h"
#include "displayapp/widgets/Counter.h"
#include "displayapp/Controllers.h"
#include "components/rng/PCG.h"
#include "Symbols.h"

#include <array>
#include <random>
#include <cstdint>

namespace Pinetime {
namespace Applications {
Expand All @@ -16,7 +17,8 @@ namespace Pinetime {
public:
Dice(Controllers::MotionController& motionController,
Controllers::MotorController& motorController,
Controllers::Settings& settingsController);
Controllers::Settings& settingsController,
Controllers::RNG& prngController);
~Dice() override;
void Roll();
void Refresh() override;
Expand All @@ -29,7 +31,7 @@ namespace Pinetime {
lv_task_t* refreshTask;
bool enableShakeForDice = false;

std::mt19937 gen;
Controllers::RNG rng;

std::array<lv_color_t, 3> resultColors = {LV_COLOR_YELLOW, LV_COLOR_MAGENTA, LV_COLOR_AQUA};
uint8_t currentColorIndex;
Expand All @@ -54,7 +56,10 @@ namespace Pinetime {
static constexpr const char* icon = Screens::Symbols::dice;

static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::Dice(controllers.motionController, controllers.motorController, controllers.settingsController);
return new Screens::Dice(controllers.motionController,
controllers.motorController,
controllers.settingsController,
*controllers.prngController);
};

static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
Expand Down
7 changes: 4 additions & 3 deletions src/displayapp/screens/Twos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

using namespace Pinetime::Applications::Screens;

Twos::Twos() {
Twos::Twos(Pinetime::Controllers::RNG& prngController) {
rng.seed(prngController); // Pinetime::Controllers::RNG {prngController(), prngController()}; // = prngController.Seed();

struct colorPair {
lv_color_t bg;
Expand Down Expand Up @@ -86,9 +87,9 @@ bool Twos::placeNewTile() {
return false; // game lost
}

int random = rand() % nEmpty;
int random = rng(nEmpty);

if ((rand() % 100) < 90) {
if (rng(100) < 90) {
grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 2;
} else {
grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 4;
Expand Down
9 changes: 6 additions & 3 deletions src/displayapp/screens/Twos.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "displayapp/screens/Screen.h"
#include "displayapp/Controllers.h"

#include "displayapp/screens/Dice.h"

namespace Pinetime {
namespace Applications {
struct TwosTile {
Expand All @@ -14,7 +16,7 @@ namespace Pinetime {
namespace Screens {
class Twos : public Screen {
public:
Twos();
Twos(Controllers::RNG& prngController);
~Twos() override;

bool OnTouchEvent(TouchEvents event) override;
Expand All @@ -29,6 +31,7 @@ namespace Pinetime {
static constexpr int nRows = 4;
static constexpr int nCells = nCols * nRows;
TwosTile grid[nRows][nCols];
Controllers::RNG rng;
unsigned int score = 0;
void updateGridDisplay();
bool tryMerge(int newRow, int newCol, int oldRow, int oldCol);
Expand All @@ -42,8 +45,8 @@ namespace Pinetime {
static constexpr Apps app = Apps::Twos;
static constexpr const char* icon = "2";

static Screens::Screen* Create(AppControllers& /*controllers*/) {
return new Screens::Twos();
static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::Twos(*controllers.prngController);
};

static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
Expand Down
Loading
Loading