diff --git a/.gitmodules b/.gitmodules index a6b4d5fd6f..785e59164e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4a354df64..be36611718 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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) @@ -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 @@ -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 @@ -682,6 +684,7 @@ set(INCLUDE_FILES buttonhandler/ButtonHandler.h touchhandler/TouchHandler.h utility/Math.h + pcg-cpp/include/pcg_random.hpp ) include_directories( diff --git a/src/components/rng/PCG.cpp b/src/components/rng/PCG.cpp new file mode 100644 index 0000000000..30b32c3f0d --- /dev/null +++ b/src/components/rng/PCG.cpp @@ -0,0 +1,22 @@ +#include "components/rng/PCG.h" + +Pinetime::Controllers::RNG::State Pinetime::Controllers::RNG::Seed(Pinetime::Controllers::RNG::rng_uint s, + Pinetime::Controllers::RNG::rng_uint i) { + return rng = State(s, i); +} + +Pinetime::Controllers::RNG::State Pinetime::Controllers::RNG::Seed() { + using namespace Pinetime::Controllers; + Pinetime::Controllers::RNG::State new_rng((uint64_t) Generate() << 32 ^ (uint64_t) Generate(), + (uint64_t) Generate() << 32 ^ (uint64_t) Generate()); + return new_rng; +} + +Pinetime::Controllers::RNG::rng_out Pinetime::Controllers::RNG::Generate() { + return rng(); +}; + +// See pcg-cpp/sample/codebook.cpp +Pinetime::Controllers::RNG::rng_out Pinetime::Controllers::RNG::GenerateBounded(Pinetime::Controllers::RNG::rng_out range) { + return rng(range); +}; \ No newline at end of file diff --git a/src/components/rng/PCG.h b/src/components/rng/PCG.h new file mode 100644 index 0000000000..40b431c94e --- /dev/null +++ b/src/components/rng/PCG.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include "components/motion/MotionController.h" +#include "pcg-cpp/include/pcg_random.hpp" + +namespace Pinetime { + namespace Controllers { + struct RNG { + + /* + struct pcg_random_t { + rng_uint state = {}; + rng_uint inc = {}; + }; + */ + using State = pcg32; + using rng_uint = State::state_type;// uint32_t; + using rng_out = State::result_type;// uint16_t; + + State rng = {}; + + State Seed(rng_uint s, rng_uint i); + // Generate another RNG struct with data generated via this one + State Seed(); + // Produces an unsigned result within the full range of the data type + rng_out Generate(); + // Produces an unsigned result within [0, range) + rng_out GenerateBounded(rng_out range); + + RNG() : rng() {}; + + RNG& operator=(const State& pcg_state) { + rng = pcg_state; + return *this; + }; + + RNG(State pcg_state) { + rng = pcg_state; + }; + + ~RNG() = default; + }; + } +} \ No newline at end of file diff --git a/src/displayapp/Controllers.h b/src/displayapp/Controllers.h index 223c7c699e..0a375f51c7 100644 --- a/src/displayapp/Controllers.h +++ b/src/displayapp/Controllers.h @@ -26,6 +26,7 @@ namespace Pinetime { class Timer; class MusicService; class NavigationService; + class RNG; } namespace System { @@ -53,6 +54,7 @@ namespace Pinetime { Pinetime::Components::LittleVgl& lvgl; Pinetime::Controllers::MusicService* musicService; Pinetime::Controllers::NavigationService* navigationService; + Pinetime::Controllers::RNG* prngController; }; } } diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 35330fb7f7..1065347843 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -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) { diff --git a/src/displayapp/screens/Dice.cpp b/src/displayapp/screens/Dice.cpp index 302c5f3fb2..270f15f486 100644 --- a/src/displayapp/screens/Dice.cpp +++ b/src/displayapp/screens/Dice.cpp @@ -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(xTaskGetTickCount()), - static_cast(motionController.X()), - static_cast(motionController.Y()), - static_cast(motionController.Z())}; - gen.seed(sseq); + rng = prngController.Seed(); lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20, LV_COLOR_WHITE, @@ -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.GenerateBounded(resultColors.size()); resultTotalLabel = MakeLabel(&jetbrains_mono_42, resultColors[currentColorIndex], @@ -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.GenerateBounded(dCounter.GetValue()) + 1; if (dCounter.GetValue() == 2) { switch (resultTotal) { case 1: @@ -175,7 +169,7 @@ void Dice::Roll() { } } else { for (uint8_t i = 0; i < nCounter.GetValue(); i++) { - resultIndividual = distrib(gen); + resultIndividual = rng.GenerateBounded(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)) { diff --git a/src/displayapp/screens/Dice.h b/src/displayapp/screens/Dice.h index d12848d3cf..4f5a287413 100644 --- a/src/displayapp/screens/Dice.h +++ b/src/displayapp/screens/Dice.h @@ -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 -#include +#include namespace Pinetime { namespace Applications { @@ -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; @@ -29,7 +31,7 @@ namespace Pinetime { lv_task_t* refreshTask; bool enableShakeForDice = false; - std::mt19937 gen; + Controllers::RNG rng; std::array resultColors = {LV_COLOR_YELLOW, LV_COLOR_MAGENTA, LV_COLOR_AQUA}; uint8_t currentColorIndex; @@ -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*/) { diff --git a/src/displayapp/screens/Twos.cpp b/src/displayapp/screens/Twos.cpp index 6f2eff40c4..6cd7a96d88 100644 --- a/src/displayapp/screens/Twos.cpp +++ b/src/displayapp/screens/Twos.cpp @@ -5,7 +5,8 @@ using namespace Pinetime::Applications::Screens; -Twos::Twos() { +Twos::Twos(Pinetime::Controllers::RNG& prngController) { + rng = prngController.Seed(); struct colorPair { lv_color_t bg; @@ -86,9 +87,9 @@ bool Twos::placeNewTile() { return false; // game lost } - int random = rand() % nEmpty; + int random = rng.GenerateBounded(nEmpty); - if ((rand() % 100) < 90) { + if (rng.GenerateBounded(100) < 90) { grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 2; } else { grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 4; diff --git a/src/displayapp/screens/Twos.h b/src/displayapp/screens/Twos.h index 9ebd7f2e44..886b82e559 100644 --- a/src/displayapp/screens/Twos.h +++ b/src/displayapp/screens/Twos.h @@ -4,6 +4,8 @@ #include "displayapp/screens/Screen.h" #include "displayapp/Controllers.h" +#include "displayapp/screens/Dice.h" + namespace Pinetime { namespace Applications { struct TwosTile { @@ -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; @@ -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); @@ -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*/) { diff --git a/src/main.cpp b/src/main.cpp index d0ab3e4887..ce087e725e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,6 +37,7 @@ #include "components/heartrate/HeartRateController.h" #include "components/stopwatch/StopWatchController.h" #include "components/fs/FS.h" +#include "components/rng/PCG.h" #include "drivers/Spi.h" #include "drivers/SpiMaster.h" #include "drivers/SpiNorFlash.h" @@ -361,8 +362,14 @@ int main() { } systemTask.Start(); - nimble_port_init(); + // ble_ll functions should probably be called after ble_ll_init which is called from nimble_port_init + Pinetime::Controllers::RNG::State prngBleInit; + ble_ll_rand_data_get((uint8_t*) &prngBleInit, sizeof(prngBleInit)); + // TODO: Seed with lifetime stats + *((uint32_t*) &prngBleInit) ^= xTaskGetTickCount(); + prngBleInit(); + systemTask.prngController.rng = prngBleInit; vTaskStartScheduler(); diff --git a/src/pcg-cpp b/src/pcg-cpp new file mode 160000 index 0000000000..428802d1a5 --- /dev/null +++ b/src/pcg-cpp @@ -0,0 +1 @@ +Subproject commit 428802d1a5634f96bcd0705fab379ff0113bcf13 diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 56bf9273e1..9b3b9504a9 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -82,7 +82,8 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, spiNorFlash, heartRateController, motionController, - fs) { + fs), + prngController {} { } void SystemTask::Start() { diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 606ddd3492..b3b2ab2709 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -18,6 +18,7 @@ #include "components/stopwatch/StopWatchController.h" #include "components/alarm/AlarmController.h" #include "components/fs/FS.h" +#include "components/rng/PCG.h" #include "touchhandler/TouchHandler.h" #include "buttonhandler/ButtonHandler.h" #include "buttonhandler/ButtonActions.h" @@ -128,6 +129,10 @@ namespace Pinetime { Pinetime::Controllers::ButtonHandler& buttonHandler; Pinetime::Controllers::NimbleController nimbleController; + public: + Pinetime::Controllers::RNG prngController; + + private: static void Process(void* instance); void Work(); bool isBleDiscoveryTimerRunning = false;