forked from InfiniTimeOrg/InfiniTime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New dice-rolling app: InfiniDice! (InfiniTimeOrg#1326)
Add new App `Dice.h` to randomly roll the dice(s). The number of dice can range from 1-9 (default 1), and the sides can range from d2-d99 (default d2). To have a haptic feedback we make Dice vibrate on roll. Regarding the use of C++ `<random>` library: There are known problems with `rand()` and `srand()` (see https://en.cppreference.com/w/cpp/numeric/random/rand) and the `<random>` library is preferred for this reason. The function used from `<random>` also avoids a very rare bias that would occur using `rand()` and modulo, when `RAND_MAX` is not a multiple of `d` and the initially generated number falls in the last "short" segment. This commit also updates the seed to derive entropy (via `seed_seq`) from a mix of the system tick count and the x,y,z components of the PineTime motion controller -- taking inspiration from and with credit to @w4tsn (InfiniTimeOrg#1199) Thanks for suggestions: * in Dice, when rolling 1d2, also show "HEADS" or "TAILS" -- suggestion by @medeyko * ui adjustments and result realignment -- suggestion by @Boteium --------- Co-authored-by: NeroBurner <[email protected]> Co-authored-by: Riku Isokoski <[email protected]> Co-authored-by: Paul Weiß <[email protected]> Co-authored-by: FintasticMan <[email protected]>
- Loading branch information
Showing
9 changed files
with
268 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ namespace Pinetime { | |
Metronome, | ||
Motion, | ||
Steps, | ||
Dice, | ||
PassKey, | ||
QuickSettings, | ||
Settings, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
#include "displayapp/screens/Dice.h" | ||
#include "displayapp/screens/Screen.h" | ||
#include "displayapp/screens/Symbols.h" | ||
#include "components/settings/Settings.h" | ||
#include "components/motor/MotorController.h" | ||
#include "components/motion/MotionController.h" | ||
|
||
using namespace Pinetime::Applications::Screens; | ||
|
||
namespace { | ||
lv_obj_t* MakeLabel(lv_font_t* font, | ||
lv_color_t color, | ||
lv_label_long_mode_t longMode, | ||
uint8_t width, | ||
lv_label_align_t labelAlignment, | ||
const char* text, | ||
lv_obj_t* reference, | ||
lv_align_t alignment, | ||
int8_t x, | ||
int8_t y) { | ||
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); | ||
lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font); | ||
lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color); | ||
lv_label_set_long_mode(label, longMode); | ||
if (width != 0) { | ||
lv_obj_set_width(label, width); | ||
} | ||
lv_label_set_align(label, labelAlignment); | ||
lv_label_set_text(label, text); | ||
lv_obj_align(label, reference, alignment, x, y); | ||
return label; | ||
} | ||
|
||
void btnRollEventHandler(lv_obj_t* obj, lv_event_t event) { | ||
auto* screen = static_cast<Dice*>(obj->user_data); | ||
if (event == LV_EVENT_CLICKED) { | ||
screen->Roll(); | ||
} | ||
} | ||
} | ||
|
||
Dice::Dice(Controllers::MotionController& motionController, | ||
Controllers::MotorController& motorController, | ||
Controllers::Settings& settingsController) | ||
: 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); | ||
|
||
lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20, | ||
LV_COLOR_WHITE, | ||
LV_LABEL_LONG_EXPAND, | ||
0, | ||
LV_LABEL_ALIGN_CENTER, | ||
"count", | ||
lv_scr_act(), | ||
LV_ALIGN_IN_TOP_LEFT, | ||
0, | ||
0); | ||
|
||
lv_obj_t* dCounterLabel = MakeLabel(&jetbrains_mono_bold_20, | ||
LV_COLOR_WHITE, | ||
LV_LABEL_LONG_EXPAND, | ||
0, | ||
LV_LABEL_ALIGN_CENTER, | ||
"sides", | ||
nCounterLabel, | ||
LV_ALIGN_OUT_RIGHT_MID, | ||
20, | ||
0); | ||
|
||
nCounter.Create(); | ||
lv_obj_align(nCounter.GetObject(), nCounterLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); | ||
nCounter.SetValue(1); | ||
|
||
dCounter.Create(); | ||
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); | ||
|
||
resultTotalLabel = MakeLabel(&jetbrains_mono_42, | ||
resultColors[currentColorIndex], | ||
LV_LABEL_LONG_BREAK, | ||
120, | ||
LV_LABEL_ALIGN_CENTER, | ||
"", | ||
lv_scr_act(), | ||
LV_ALIGN_IN_TOP_RIGHT, | ||
11, | ||
38); | ||
resultIndividualLabel = MakeLabel(&jetbrains_mono_bold_20, | ||
resultColors[currentColorIndex], | ||
LV_LABEL_LONG_BREAK, | ||
90, | ||
LV_LABEL_ALIGN_CENTER, | ||
"", | ||
resultTotalLabel, | ||
LV_ALIGN_OUT_BOTTOM_MID, | ||
0, | ||
10); | ||
|
||
Roll(); | ||
openingRoll = false; | ||
|
||
btnRoll = lv_btn_create(lv_scr_act(), nullptr); | ||
btnRoll->user_data = this; | ||
lv_obj_set_event_cb(btnRoll, btnRollEventHandler); | ||
lv_obj_set_size(btnRoll, 240, 50); | ||
lv_obj_align(btnRoll, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); | ||
|
||
btnRollLabel = MakeLabel(&jetbrains_mono_bold_20, | ||
LV_COLOR_WHITE, | ||
LV_LABEL_LONG_EXPAND, | ||
0, | ||
LV_LABEL_ALIGN_CENTER, | ||
Symbols::dice, | ||
btnRoll, | ||
LV_ALIGN_CENTER, | ||
0, | ||
0); | ||
|
||
// Spagetti code in motion controller: it only updates the shake speed when shake to wake is on... | ||
enableShakeForDice = !settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake); | ||
if (enableShakeForDice) { | ||
settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::Shake, true); | ||
} | ||
refreshTask = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); | ||
} | ||
|
||
Dice::~Dice() { | ||
// reset the shake to wake mode. | ||
if (enableShakeForDice) { | ||
settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::Shake, false); | ||
enableShakeForDice = false; | ||
} | ||
lv_task_del(refreshTask); | ||
lv_obj_clean(lv_scr_act()); | ||
} | ||
|
||
void Dice::Refresh() { | ||
// we only reset the hysteresis when at rest | ||
if (motionController.CurrentShakeSpeed() >= settingsController.GetShakeThreshold()) { | ||
if (currentRollHysteresis <= 0) { | ||
// this timestamp is used for the screen timeout | ||
lv_disp_get_next(NULL)->last_activity_time = lv_tick_get(); | ||
|
||
Roll(); | ||
} | ||
} else if (currentRollHysteresis > 0) | ||
--currentRollHysteresis; | ||
} | ||
|
||
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); | ||
if (dCounter.GetValue() == 2) { | ||
switch (resultTotal) { | ||
case 1: | ||
lv_label_set_text(resultIndividualLabel, "HEADS"); | ||
break; | ||
case 2: | ||
lv_label_set_text(resultIndividualLabel, "TAILS"); | ||
break; | ||
} | ||
} | ||
} else { | ||
for (uint8_t i = 0; i < nCounter.GetValue(); i++) { | ||
resultIndividual = distrib(gen); | ||
resultTotal += resultIndividual; | ||
lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, std::to_string(resultIndividual).c_str()); | ||
if (i < (nCounter.GetValue() - 1)) { | ||
lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, "+"); | ||
} | ||
} | ||
} | ||
|
||
lv_label_set_text_fmt(resultTotalLabel, "%d", resultTotal); | ||
if (openingRoll == false) { | ||
motorController.RunForDuration(30); | ||
NextColor(); | ||
currentRollHysteresis = rollHysteresis; | ||
} | ||
} | ||
|
||
void Dice::NextColor() { | ||
currentColorIndex = (currentColorIndex + 1) % resultColors.size(); | ||
lv_obj_set_style_local_text_color(resultTotalLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, resultColors[currentColorIndex]); | ||
lv_obj_set_style_local_text_color(resultIndividualLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, resultColors[currentColorIndex]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#pragma once | ||
|
||
#include "displayapp/apps/Apps.h" | ||
#include "displayapp/screens/Screen.h" | ||
#include "displayapp/widgets/Counter.h" | ||
#include "displayapp/Controllers.h" | ||
#include "Symbols.h" | ||
|
||
#include <array> | ||
#include <random> | ||
|
||
namespace Pinetime { | ||
namespace Applications { | ||
namespace Screens { | ||
class Dice : public Screen { | ||
public: | ||
Dice(Controllers::MotionController& motionController, | ||
Controllers::MotorController& motorController, | ||
Controllers::Settings& settingsController); | ||
~Dice() override; | ||
void Roll(); | ||
void Refresh() override; | ||
|
||
private: | ||
lv_obj_t* btnRoll; | ||
lv_obj_t* btnRollLabel; | ||
lv_obj_t* resultTotalLabel; | ||
lv_obj_t* resultIndividualLabel; | ||
lv_task_t* refreshTask; | ||
bool enableShakeForDice = false; | ||
|
||
std::mt19937 gen; | ||
|
||
std::array<lv_color_t, 3> resultColors = {LV_COLOR_YELLOW, LV_COLOR_MAGENTA, LV_COLOR_AQUA}; | ||
uint8_t currentColorIndex; | ||
void NextColor(); | ||
|
||
Widgets::Counter nCounter = Widgets::Counter(1, 9, jetbrains_mono_42); | ||
Widgets::Counter dCounter = Widgets::Counter(2, 99, jetbrains_mono_42); | ||
|
||
bool openingRoll = true; | ||
uint8_t currentRollHysteresis = 0; | ||
static constexpr uint8_t rollHysteresis = 10; | ||
|
||
Controllers::MotorController& motorController; | ||
Controllers::MotionController& motionController; | ||
Controllers::Settings& settingsController; | ||
}; | ||
} | ||
|
||
template <> | ||
struct AppTraits<Apps::Dice> { | ||
static constexpr Apps app = Apps::Dice; | ||
static constexpr const char* icon = Screens::Symbols::dice; | ||
|
||
static Screens::Screen* Create(AppControllers& controllers) { | ||
return new Screens::Dice(controllers.motionController, controllers.motorController, controllers.settingsController); | ||
}; | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters