From f11a2ae707756d15fe960fc46a3a7112ddbf268b Mon Sep 17 00:00:00 2001 From: shobowale Date: Sun, 19 Jan 2025 19:59:14 +0100 Subject: [PATCH 1/6] Merged Adder from implemantation for Infinitime 1.14 --- src/CMakeLists.txt | 1 + src/displayapp/DisplayApp.cpp | 1 + src/displayapp/UserApps.h | 1 + src/displayapp/apps/Apps.h.in | 1 + src/displayapp/apps/CMakeLists.txt | 1 + src/displayapp/screens/Adder.cpp | 311 +++++++++++++++++++++++++++++ src/displayapp/screens/Adder.h | 106 ++++++++++ 7 files changed, 422 insertions(+) create mode 100644 src/displayapp/screens/Adder.cpp create mode 100644 src/displayapp/screens/Adder.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e2b69b8b02..759c05a2c3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -384,6 +384,7 @@ list(APPEND SOURCE_FILES displayapp/screens/ApplicationList.cpp displayapp/screens/Notifications.cpp displayapp/screens/Twos.cpp + displayapp/screens/Adder.cpp displayapp/screens/HeartRate.cpp displayapp/screens/FlashLight.cpp displayapp/screens/List.cpp diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 6671ac9e51..5a15eca4f8 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -23,6 +23,7 @@ #include "displayapp/screens/SystemInfo.h" #include "displayapp/screens/Tile.h" #include "displayapp/screens/Twos.h" +#include "displayapp/screens/Adder.h" #include "displayapp/screens/FlashLight.h" #include "displayapp/screens/BatteryInfo.h" #include "displayapp/screens/Steps.h" diff --git a/src/displayapp/UserApps.h b/src/displayapp/UserApps.h index 67bbfa7d41..2e158e8bf5 100644 --- a/src/displayapp/UserApps.h +++ b/src/displayapp/UserApps.h @@ -6,6 +6,7 @@ #include "displayapp/screens/Dice.h" #include "displayapp/screens/Timer.h" #include "displayapp/screens/Twos.h" +#include "displayapp/screens/Adder.h" #include "displayapp/screens/Tile.h" #include "displayapp/screens/ApplicationList.h" #include "displayapp/screens/WatchFaceDigital.h" diff --git a/src/displayapp/apps/Apps.h.in b/src/displayapp/apps/Apps.h.in index 2104a267c0..03baf42c14 100644 --- a/src/displayapp/apps/Apps.h.in +++ b/src/displayapp/apps/Apps.h.in @@ -21,6 +21,7 @@ namespace Pinetime { Paint, Paddle, Twos, + Adder, HeartRate, Navigation, StopWatch, diff --git a/src/displayapp/apps/CMakeLists.txt b/src/displayapp/apps/CMakeLists.txt index d78587609e..70fe4a85cd 100644 --- a/src/displayapp/apps/CMakeLists.txt +++ b/src/displayapp/apps/CMakeLists.txt @@ -10,6 +10,7 @@ else () set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Paint") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Paddle") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Twos") + set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Adder") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Dice") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Metronome") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation") diff --git a/src/displayapp/screens/Adder.cpp b/src/displayapp/screens/Adder.cpp new file mode 100644 index 0000000000..084f9518b0 --- /dev/null +++ b/src/displayapp/screens/Adder.cpp @@ -0,0 +1,311 @@ +#define LV_MONOSERT +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/Adder.h" +#include //randr +#include "task.h" + +using namespace Pinetime::Applications::Screens; + +Adder::Adder(Pinetime::Components::LittleVgl& lvgl, Controllers::FS& fs) : lvgl {lvgl}, filesystem {fs} { + + AppReady = false; + + LoadGame(); + + + TileBufferSize = TileSize * TileSize; + TileBuffer = new lv_color_t[TileBufferSize]; + + DisplayHeight = LV_VER_RES; + DisplayWidth = LV_HOR_RES; + + FieldHeight = DisplayHeight / TileSize - 2; + FieldWidth = DisplayWidth / TileSize - 1; + FieldOffsetHorizontal = (DisplayWidth - FieldWidth * TileSize) / 2; + FieldOffsetVertical = (DisplayHeight - FieldHeight * TileSize) / 2 + (TileSize + 0.5) / 2; + + FieldSize = FieldWidth * FieldHeight; + + Field = new AdderField[FieldSize]; + + InitBody(); + + for (unsigned int ti = 0; ti < TileBufferSize; ti++) + TileBuffer[ti] = LV_COLOR_WHITE; + + createLevel(); + + taskRefresh = lv_task_create(RefreshTaskCallback, AdderDelayInterval, LV_TASK_PRIO_MID, this); +} + +Adder::~Adder() { + delete[] Field; + delete[] TileBuffer; + lv_task_del(taskRefresh); + lv_obj_clean(lv_scr_act()); +} + +void Adder::LoadGame(){ + lfs_file f; + + + if (filesystem.FileOpen(&f, GameSavePath, LFS_O_RDONLY) == LFS_ERR_OK) { + filesystem.FileRead(&f, reinterpret_cast(&Data), sizeof(AdderSave)); + filesystem.FileClose(&f); + if(Data.Version!=AdderVersion){ + Data= AdderSave(); + }else + HighScore = std::max(Data.HighScore,HighScore); + + } + else{ + Data=AdderSave(); + filesystem.DirCreate("games"); + filesystem.DirCreate("games/adder"); + SaveGame(); + } + Data.Version=AdderVersion; + +} + +void Adder::SaveGame(){ + lfs_file f; + + if (filesystem.FileOpen(&f, GameSavePath, LFS_O_WRONLY | LFS_O_CREAT) != LFS_ERR_OK) + return; + + filesystem.FileWrite(&f, reinterpret_cast(&Data), sizeof(AdderSave)); + filesystem.FileClose(&f); +} + +void Adder::InitBody() { + AdderBody.clear(); + unsigned int start_position = (FieldHeight / 2) * FieldWidth + FieldWidth / 2 + 2; + unsigned int body[] = {start_position, start_position - 1}; + AdderBody.assign(body, body + 2); +} + +void Adder::createLevel() { + for (unsigned int i = 0; i < FieldSize; i++) { + unsigned int x = i % FieldWidth; + unsigned int y = i / FieldWidth; + if (y == 0 || y == FieldHeight - 1 || x == 0 || x == FieldWidth - 1) + Field[i] = SOLID; + else + Field[i] = BLANK; + } +} + +void Adder::GameOver() { + unsigned int Digit[] = {7, 0, 5, 3}; + + unsigned int Offset = FieldOffsetHorizontal > FieldOffsetVertical ? FieldOffsetHorizontal : FieldOffsetVertical; + for (unsigned int r = 3 * Offset; r < DisplayWidth - 4 * Offset; r += 16) { + for (unsigned int i = 0; i < 4; i++) { + for (unsigned int j = 0; j < 64; j++) + DigitBuffer[63 - j] = + (DigitFont[Digit[i]][j / 8] & 1 << j % 8) ? LV_COLOR_WHITE : LV_COLOR_BLACK; // Bitmagic to map the font to an image array + + lv_area_t area; + area.x1 = r + 8 * i; + area.y1 = r; + area.x2 = area.x1 + 7; + area.y2 = area.y1 + 7; + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); + lvgl.FlushDisplay(&area, DigitBuffer); + } + } + createLevel(); + AdderDirection = 1; + InitBody(); + AppReady = false; + + if(HighScore > Data.HighScore) + SaveGame(); +} + +bool Adder::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + switch (event) { + case TouchEvents::SwipeLeft: + AdderDirection = -1; + break; + case TouchEvents::SwipeUp: + AdderDirection = -FieldWidth; + break; + case TouchEvents::SwipeDown: + AdderDirection = +FieldWidth; + break; + case TouchEvents::SwipeRight: + AdderDirection = 1; + break; + case TouchEvents::LongTap: + FullReDraw(); + break; + default: + break; + } + if (prevAdderDirection == -AdderDirection) + AdderDirection = -AdderDirection; + + if (AdderDirection != prevAdderDirection) + prevAdderDirection = AdderDirection; + return true; +} + +MoveConsequence Adder::checkMove() { + if (AdderBody.front() + AdderDirection < FieldSize) { + if (Field[AdderBody.front() + AdderDirection] == BLANK) + return MOVE; + if (Field[AdderBody.front() + AdderDirection] == FOOD) + return EAT; + } + + return DEATH; +} + +void Adder::updateScore(unsigned int Score) { + + unsigned int Digit[] = {0, Score % 10, (Score % 100 - Score % 10) / 10, (Score - Score % 100) / 100}; + + // Print Score + for (unsigned int i = 0; i < 4; i++) { + for (unsigned int j = 0; j < 64; j++) + DigitBuffer[j] = (DigitFont[Digit[i]][j / 8] & 1 << j % 8) ? LV_COLOR_WHITE : LV_COLOR_BLACK; + // Bitmagic to map the font to an image array + + lv_area_t area; + area.x1 = DisplayWidth - 16 - 8 * i; + area.y1 = 4; + area.x2 = area.x1 + 7; + area.y2 = area.y1 + 7; + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); + lvgl.FlushDisplay(&area, DigitBuffer); + vTaskDelay(1); // Hack to give give the system time to refresh + } + + // Check if HighScore changed + unsigned int HScore = (HighScore > Score) ? HighScore : Score; + unsigned int HS_Digit[] = {0, HScore % 10, (HScore % 100 - HScore % 10) / 10, (HScore - HScore % 100) / 100}; + // Print Highscore + for (unsigned int i = 0; i < 4; i++) { + for (unsigned int j = 0; j < 64; j++) + DigitBuffer[j] = (DigitFont[HS_Digit[i]][j / 8] & 1 << j % 8) ? LV_COLOR_WHITE : LV_COLOR_BLACK; + // Bitmagic to map the font to an image array + + lv_area_t area; + area.x1 = 40 - 8 * i; + area.y1 = 4; + area.x2 = area.x1 + 7; + area.y2 = area.y1 + 7; + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); + lvgl.FlushDisplay(&area, DigitBuffer); + vTaskDelay(1); // Hack to give give the system time to refresh + } + HighScore = HScore; +} + +void Adder::createFood() { + Blanks.clear(); + + for (unsigned int i = 0; i < FieldSize; ++i) + if (Field[i] == BLANK) + Blanks.push_back(i); + + unsigned int pos = rand() % Blanks.size(); + + Field[Blanks[pos]] = FOOD; + updateSingleTile(Blanks[pos] % FieldWidth, Blanks[pos] / FieldWidth, LV_COLOR_GREEN); +} + +void Adder::updatePosition() { + + Field[AdderBody.front()] = BODY; + Field[AdderBody.back()] = BLANK; + + switch (checkMove()) { + + case DEATH: + GameOver(); + break; + + case EAT: + AdderBody.push_front(AdderBody.front() + AdderDirection); + createFood(); + updateScore(AdderBody.size() - 2); + break; + + case MOVE: + AdderBody.pop_back(); + AdderBody.push_front(AdderBody.front() + AdderDirection); + break; + } +} + +void Adder::FullReDraw() { + lv_color_t selectColor = LV_COLOR_BLACK; + + for (unsigned int x = 0; x < FieldWidth; x++) { + for (unsigned int y = 0; y < FieldHeight; y++) { + + switch (Field[y * FieldWidth + x]) { + case BODY: + selectColor = LV_COLOR_YELLOW; + break; + case SOLID: + selectColor = LV_COLOR_WHITE; + break; + case FOOD: + selectColor = LV_COLOR_GREEN; + break; + default: + selectColor = LV_COLOR_BLACK; + break; + } + for (unsigned int ti = 0; ti < TileBufferSize; ti++) + TileBuffer[ti] = selectColor; + + lv_area_t area; + + area.x1 = x * TileSize + FieldOffsetHorizontal; + area.y1 = y * TileSize + FieldOffsetVertical; + area.x2 = area.x1 + TileSize - 1; + area.y2 = area.y1 + TileSize - 1; + lvgl.FlushDisplay(&area, TileBuffer); + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); + vTaskDelay(1); // Hack to give give the system time to refresh + } + } +} + +void Adder::Refresh() { + updateDisplay(); +} + +void Adder::updateSingleTile(unsigned int FieldPosX, unsigned int FieldPosY, lv_color_t Color) { + for (unsigned int ti = 0; ti < TileBufferSize; ti++) + TileBuffer[ti] = Color; + + lv_area_t area; + area.x1 = FieldPosX * TileSize + FieldOffsetHorizontal; + area.y1 = FieldPosY * TileSize + FieldOffsetVertical; + area.x2 = area.x1 + TileSize - 1; + area.y2 = area.y1 + TileSize - 1; + lvgl.FlushDisplay(&area, TileBuffer); + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); + vTaskDelay(1); // Hack to give give the system time to refresh +} + +void Adder::updateDisplay() { + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); + updatePosition(); + if (!AppReady) { + FullReDraw(); + createFood(); + updateSingleTile(AdderBody.back() % FieldWidth, AdderBody.back() / FieldWidth, LV_COLOR_BLACK); + updateScore(0); + AppReady = true; + } else { + updateSingleTile(AdderBody.front() % FieldWidth, AdderBody.front() / FieldWidth, LV_COLOR_YELLOW); + updateSingleTile(AdderBody.back() % FieldWidth, AdderBody.back() / FieldWidth, LV_COLOR_BLACK); + } +} diff --git a/src/displayapp/screens/Adder.h b/src/displayapp/screens/Adder.h new file mode 100644 index 0000000000..0c9b16dc05 --- /dev/null +++ b/src/displayapp/screens/Adder.h @@ -0,0 +1,106 @@ +#pragma once + +#include "displayapp/apps/Apps.h" +#include "displayapp/Controllers.h" +#include "displayapp/screens/Screen.h" +#include "components/fs/FS.h" +#include +#include +#include + +namespace Pinetime { + namespace Applications { + namespace Screens { + + const unsigned int AdderVersion = 1; + + struct AdderSave { + unsigned int Level; + unsigned int HighScore; + unsigned int Version; + + AdderSave() : Level(0), HighScore(0),Version(AdderVersion){}; + }; + + enum AdderField { UNDEFINED, BLANK, SOLID, PORTAL, BODY, FOOD }; + + enum MoveConsequence { DEATH = 0, EAT, MOVE }; + + class Adder : public Screen { + + public: + Adder(Pinetime::Components::LittleVgl& lvgl, Pinetime::Controllers::FS& fs); + ~Adder() override; + void Refresh() override; + bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override; + ; + + private: // Private members and methods + + const char* GameSavePath = "/games/adder/adder.sav"; + + Controllers::FS& filesystem; + AdderSave Data; + const unsigned int TileSize = 9; + const unsigned int AdderDelayInterval = 200; + bool AppReady; + unsigned int DisplayHeight; + unsigned int DisplayWidth; + + unsigned int FieldWidth; + unsigned int FieldHeight; + unsigned int FieldSize; + unsigned int FieldOffsetHorizontal; + unsigned int FieldOffsetVertical; + + AdderField* Field; + std::list AdderBody; + std::vector Blanks; + + int prevAdderDirection = 0; + int AdderDirection = 1; + char lastKey = 0; + + unsigned int TileBufferSize; + lv_color_t* TileBuffer; + lv_color_t DigitBuffer[64]; + Pinetime::Components::LittleVgl& lvgl; + lv_task_t* taskRefresh; + + unsigned int HighScore = 2; + void createFood(); + void InitBody(); + + MoveConsequence checkMove(); + void createLevel(); + void FullReDraw(); + void updateDisplay(); + void updateSingleTile(unsigned int FieldPosX, unsigned int FieldPosY, lv_color_t Color); + void updatePosition(); + void updateScore(unsigned int Score); + void GameOver(); + void LoadGame(); + void SaveGame(); + + const char DigitFont[10][8] = {{0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) + {0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) + {0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) + {0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) + {0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) + {0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) + {0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) + {0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) + {0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) + {0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}}; // U+0039 (9) + }; + } + template <> + struct AppTraits { + static constexpr Apps app = Apps::Adder; + static constexpr const char* icon = "S"; + static Screens::Screen* Create(AppControllers& controllers) { + return new Screens::Adder(controllers.lvgl, controllers.filesystem); + }; + }; + } +} \ No newline at end of file From 96d19ce572fd2d074a1e112a05fc4d632af635d3 Mon Sep 17 00:00:00 2001 From: shobowale Date: Sun, 19 Jan 2025 21:18:18 +0100 Subject: [PATCH 2/6] Refactored Code --- src/displayapp/screens/Adder.cpp | 464 ++++++++++++++++--------------- src/displayapp/screens/Adder.h | 150 +++++----- 2 files changed, 328 insertions(+), 286 deletions(-) diff --git a/src/displayapp/screens/Adder.cpp b/src/displayapp/screens/Adder.cpp index 084f9518b0..e3944c4176 100644 --- a/src/displayapp/screens/Adder.cpp +++ b/src/displayapp/screens/Adder.cpp @@ -1,311 +1,337 @@ -#define LV_MONOSERT #include "displayapp/DisplayApp.h" #include "displayapp/screens/Adder.h" -#include //randr -#include "task.h" +#include // For std::rand +#include // For std::max using namespace Pinetime::Applications::Screens; -Adder::Adder(Pinetime::Components::LittleVgl& lvgl, Controllers::FS& fs) : lvgl {lvgl}, filesystem {fs} { +Adder::Adder(Pinetime::Components::LittleVgl& lvgl, Controllers::FS& fs) + : lvgl(lvgl), filesystem(fs) { + InitializeGame(); +} - AppReady = false; +Adder::~Adder() { + Cleanup(); +} +void Adder::InitializeGame() { LoadGame(); - - - TileBufferSize = TileSize * TileSize; - TileBuffer = new lv_color_t[TileBufferSize]; - DisplayHeight = LV_VER_RES; - DisplayWidth = LV_HOR_RES; + tileBuffer = new lv_color_t[TileSize * TileSize]; + std::fill(tileBuffer, tileBuffer + TileSize * TileSize, LV_COLOR_WHITE); - FieldHeight = DisplayHeight / TileSize - 2; - FieldWidth = DisplayWidth / TileSize - 1; - FieldOffsetHorizontal = (DisplayWidth - FieldWidth * TileSize) / 2; - FieldOffsetVertical = (DisplayHeight - FieldHeight * TileSize) / 2 + (TileSize + 0.5) / 2; + displayHeight = LV_VER_RES; + displayWidth = LV_HOR_RES; - FieldSize = FieldWidth * FieldHeight; + fieldHeight = displayHeight / TileSize - 2; + fieldWidth = displayWidth / TileSize - 1; + fieldOffsetHorizontal = (displayWidth - fieldWidth * TileSize) / 2; + fieldOffsetVertical = (displayHeight - fieldHeight * TileSize) / 2 + (TileSize + 0.5) / 2; - Field = new AdderField[FieldSize]; + fieldSize = fieldWidth * fieldHeight; + field = new AdderField[fieldSize]; - InitBody(); + InitializeBody(); + CreateLevel(); - for (unsigned int ti = 0; ti < TileBufferSize; ti++) - TileBuffer[ti] = LV_COLOR_WHITE; + refreshTask = lv_task_create([](lv_task_t* task) { + auto* adder = static_cast(task->user_data); + adder->Refresh(); + }, + AdderDelayInterval, LV_TASK_PRIO_MID, this); - createLevel(); - - taskRefresh = lv_task_create(RefreshTaskCallback, AdderDelayInterval, LV_TASK_PRIO_MID, this); + appReady = false; + vTaskDelay(5); + UpdateScore(0); } -Adder::~Adder() { - delete[] Field; - delete[] TileBuffer; - lv_task_del(taskRefresh); +void Adder::Cleanup() { + delete[] field; + delete[] tileBuffer; + if (refreshTask) { + lv_task_del(refreshTask); + } lv_obj_clean(lv_scr_act()); } -void Adder::LoadGame(){ - lfs_file f; +void Adder::LoadGame() { + lfs_file file; + if (filesystem.FileOpen(&file, GameSavePath, LFS_O_RDONLY) == LFS_ERR_OK) { + filesystem.FileRead(&file, reinterpret_cast(&data), sizeof(AdderSave)); + filesystem.FileClose(&file); - if (filesystem.FileOpen(&f, GameSavePath, LFS_O_RDONLY) == LFS_ERR_OK) { - filesystem.FileRead(&f, reinterpret_cast(&Data), sizeof(AdderSave)); - filesystem.FileClose(&f); - if(Data.Version!=AdderVersion){ - Data= AdderSave(); - }else - HighScore = std::max(Data.HighScore,HighScore); - - } - else{ - Data=AdderSave(); - filesystem.DirCreate("games"); - filesystem.DirCreate("games/adder"); + if (data.Version != AdderVersion) { + data = AdderSave(); + } else { + highScore = std::max(data.HighScore, highScore); + } + } else { + data = AdderSave(); + filesystem.DirCreate("/games"); + filesystem.DirCreate("/games/adder"); SaveGame(); } - Data.Version=AdderVersion; +} +void Adder::SaveGame() { + lfs_file file; + + if (filesystem.FileOpen(&file, GameSavePath, LFS_O_WRONLY | LFS_O_CREAT) == LFS_ERR_OK) { + filesystem.FileWrite(&file, reinterpret_cast(&data), sizeof(AdderSave)); + filesystem.FileClose(&file); + } } -void Adder::SaveGame(){ - lfs_file f; - - if (filesystem.FileOpen(&f, GameSavePath, LFS_O_WRONLY | LFS_O_CREAT) != LFS_ERR_OK) - return; +void Adder::ResetGame() { + GameOver(); + appReady = false; + highScore = std::max(highScore, static_cast(adderBody.size() - 2)); - filesystem.FileWrite(&f, reinterpret_cast(&Data), sizeof(AdderSave)); - filesystem.FileClose(&f); + SaveGame(); + + CreateLevel(); + InitializeBody(); + UpdateScore(0); + FullRedraw(); } -void Adder::InitBody() { - AdderBody.clear(); - unsigned int start_position = (FieldHeight / 2) * FieldWidth + FieldWidth / 2 + 2; - unsigned int body[] = {start_position, start_position - 1}; - AdderBody.assign(body, body + 2); +void Adder::InitializeBody() { + adderBody.clear(); + + unsigned int startPosition = (fieldHeight / 2) * fieldWidth + fieldWidth / 2 + 2; + adderBody = {startPosition, startPosition - 1}; + + currentDirection = 1; // Start moving to the right + prevDirection = currentDirection; } -void Adder::createLevel() { - for (unsigned int i = 0; i < FieldSize; i++) { - unsigned int x = i % FieldWidth; - unsigned int y = i / FieldWidth; - if (y == 0 || y == FieldHeight - 1 || x == 0 || x == FieldWidth - 1) - Field[i] = SOLID; - else - Field[i] = BLANK; +void Adder::CreateLevel() { + for (unsigned int i = 0; i < fieldSize; ++i) { + unsigned int x = i % fieldWidth; + unsigned int y = i / fieldWidth; + if (y == 0 || y == fieldHeight - 1 || x == 0 || x == fieldWidth - 1) { + field[i] = AdderField::SOLID; + } else { + field[i] = AdderField::BLANK; + } } } -void Adder::GameOver() { - unsigned int Digit[] = {7, 0, 5, 3}; - - unsigned int Offset = FieldOffsetHorizontal > FieldOffsetVertical ? FieldOffsetHorizontal : FieldOffsetVertical; - for (unsigned int r = 3 * Offset; r < DisplayWidth - 4 * Offset; r += 16) { - for (unsigned int i = 0; i < 4; i++) { - for (unsigned int j = 0; j < 64; j++) - DigitBuffer[63 - j] = - (DigitFont[Digit[i]][j / 8] & 1 << j % 8) ? LV_COLOR_WHITE : LV_COLOR_BLACK; // Bitmagic to map the font to an image array - - lv_area_t area; - area.x1 = r + 8 * i; - area.y1 = r; - area.x2 = area.x1 + 7; - area.y2 = area.y1 + 7; - lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); - lvgl.FlushDisplay(&area, DigitBuffer); +void Adder::CreateFood() { + blanks.clear(); + for (unsigned int i = 0; i < fieldSize; ++i) { + if (field[i] == AdderField::BLANK) { + blanks.push_back(i); } } - createLevel(); - AdderDirection = 1; - InitBody(); - AppReady = false; - if(HighScore > Data.HighScore) - SaveGame(); + if (!blanks.empty()) { + unsigned int randomIndex = std::rand() % blanks.size(); + field[blanks[randomIndex]] = AdderField::FOOD; + UpdateSingleTile(blanks[randomIndex] % fieldWidth, blanks[randomIndex] / fieldWidth, LV_COLOR_GREEN); + } } bool Adder::OnTouchEvent(Pinetime::Applications::TouchEvents event) { switch (event) { case TouchEvents::SwipeLeft: - AdderDirection = -1; + currentDirection = -1; break; case TouchEvents::SwipeUp: - AdderDirection = -FieldWidth; + currentDirection = -fieldWidth; break; case TouchEvents::SwipeDown: - AdderDirection = +FieldWidth; + currentDirection = fieldWidth; break; case TouchEvents::SwipeRight: - AdderDirection = 1; + currentDirection = 1; break; case TouchEvents::LongTap: - FullReDraw(); + FullRedraw(); // Adjusted to method spelled as "FullRedraw" break; default: break; } - if (prevAdderDirection == -AdderDirection) - AdderDirection = -AdderDirection; - if (AdderDirection != prevAdderDirection) - prevAdderDirection = AdderDirection; - return true; -} + // Prevent the adder from directly reversing direction + if (prevDirection == -currentDirection) { + currentDirection = -currentDirection; + } -MoveConsequence Adder::checkMove() { - if (AdderBody.front() + AdderDirection < FieldSize) { - if (Field[AdderBody.front() + AdderDirection] == BLANK) - return MOVE; - if (Field[AdderBody.front() + AdderDirection] == FOOD) - return EAT; + // Update previous direction if it differs + if (currentDirection != prevDirection) { + prevDirection = currentDirection; } - return DEATH; + return true; // Return true to indicate the touch event was handled } -void Adder::updateScore(unsigned int Score) { - - unsigned int Digit[] = {0, Score % 10, (Score % 100 - Score % 10) / 10, (Score - Score % 100) / 100}; - // Print Score - for (unsigned int i = 0; i < 4; i++) { - for (unsigned int j = 0; j < 64; j++) - DigitBuffer[j] = (DigitFont[Digit[i]][j / 8] & 1 << j % 8) ? LV_COLOR_WHITE : LV_COLOR_BLACK; - // Bitmagic to map the font to an image array +void Adder::UpdatePosition() { + unsigned int newHead = adderBody.front() + currentDirection; + Adder::MoveConsequence result = CheckMove(); // Fully qualify MoveConsequence - lv_area_t area; - area.x1 = DisplayWidth - 16 - 8 * i; - area.y1 = 4; - area.x2 = area.x1 + 7; - area.y2 = area.y1 + 7; - lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); - lvgl.FlushDisplay(&area, DigitBuffer); - vTaskDelay(1); // Hack to give give the system time to refresh - } + switch (result) { + case Adder::MoveConsequence::DEATH: // Fully qualify + ResetGame(); + return; - // Check if HighScore changed - unsigned int HScore = (HighScore > Score) ? HighScore : Score; - unsigned int HS_Digit[] = {0, HScore % 10, (HScore % 100 - HScore % 10) / 10, (HScore - HScore % 100) / 100}; - // Print Highscore - for (unsigned int i = 0; i < 4; i++) { - for (unsigned int j = 0; j < 64; j++) - DigitBuffer[j] = (DigitFont[HS_Digit[i]][j / 8] & 1 << j % 8) ? LV_COLOR_WHITE : LV_COLOR_BLACK; - // Bitmagic to map the font to an image array + case Adder::MoveConsequence::EAT: // Fully qualify + adderBody.push_front(newHead); + CreateFood(); + UpdateScore(adderBody.size() - 2); + break; - lv_area_t area; - area.x1 = 40 - 8 * i; - area.y1 = 4; - area.x2 = area.x1 + 7; - area.y2 = area.y1 + 7; - lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); - lvgl.FlushDisplay(&area, DigitBuffer); - vTaskDelay(1); // Hack to give give the system time to refresh + case Adder::MoveConsequence::MOVE: // Fully qualify + adderBody.pop_back(); + adderBody.push_front(newHead); + break; } - HighScore = HScore; -} - -void Adder::createFood() { - Blanks.clear(); - - for (unsigned int i = 0; i < FieldSize; ++i) - if (Field[i] == BLANK) - Blanks.push_back(i); - unsigned int pos = rand() % Blanks.size(); - - Field[Blanks[pos]] = FOOD; - updateSingleTile(Blanks[pos] % FieldWidth, Blanks[pos] / FieldWidth, LV_COLOR_GREEN); + field[adderBody.front()] = AdderField::BODY; + field[adderBody.back()] = AdderField::BLANK; } -void Adder::updatePosition() { - - Field[AdderBody.front()] = BODY; - Field[AdderBody.back()] = BLANK; - - switch (checkMove()) { - - case DEATH: - GameOver(); - break; - - case EAT: - AdderBody.push_front(AdderBody.front() + AdderDirection); - createFood(); - updateScore(AdderBody.size() - 2); - break; +Adder::MoveConsequence Adder::CheckMove() const { + unsigned int newHead = adderBody.front() + currentDirection; + if (newHead >= fieldSize) { + return Adder::MoveConsequence::DEATH; // Fully qualify + } - case MOVE: - AdderBody.pop_back(); - AdderBody.push_front(AdderBody.front() + AdderDirection); - break; + switch (field[newHead]) { + case AdderField::BLANK: + return Adder::MoveConsequence::MOVE; // Fully qualify + case AdderField::FOOD: + return Adder::MoveConsequence::EAT; // Fully qualify + default: + return Adder::MoveConsequence::DEATH; // Fully qualify } } -void Adder::FullReDraw() { - lv_color_t selectColor = LV_COLOR_BLACK; - - for (unsigned int x = 0; x < FieldWidth; x++) { - for (unsigned int y = 0; y < FieldHeight; y++) { +void Adder::Refresh() { + if (!appReady) { + FullRedraw(); + CreateFood(); + appReady = true; + } else { + UpdatePosition(); + UpdateSingleTile(adderBody.front() % fieldWidth, adderBody.front() / fieldWidth, LV_COLOR_YELLOW); + UpdateSingleTile(adderBody.back() % fieldWidth, adderBody.back() / fieldWidth, LV_COLOR_BLACK); + } +} - switch (Field[y * FieldWidth + x]) { - case BODY: - selectColor = LV_COLOR_YELLOW; +void Adder::FullRedraw() { + for (unsigned int x = 0; x < fieldWidth; ++x) { + for (unsigned int y = 0; y < fieldHeight; ++y) { + lv_color_t color; + switch (field[y * fieldWidth + x]) { + case AdderField::BODY: + color = LV_COLOR_YELLOW; break; - case SOLID: - selectColor = LV_COLOR_WHITE; + case AdderField::SOLID: + color = LV_COLOR_WHITE; break; - case FOOD: - selectColor = LV_COLOR_GREEN; + case AdderField::FOOD: + color = LV_COLOR_GREEN; break; default: - selectColor = LV_COLOR_BLACK; + color = LV_COLOR_BLACK; break; } - for (unsigned int ti = 0; ti < TileBufferSize; ti++) - TileBuffer[ti] = selectColor; + UpdateSingleTile(x, y, color); + } + } +} + +void Adder::UpdateSingleTile(unsigned int x, unsigned int y, lv_color_t color) { + std::fill(tileBuffer, tileBuffer + TileSize * TileSize, color); + lv_area_t area { + .x1 = static_cast(x * TileSize + fieldOffsetHorizontal), + .y1 = static_cast(y * TileSize + fieldOffsetVertical), + .x2 = static_cast(x * TileSize + fieldOffsetHorizontal + TileSize - 1), + .y2 = static_cast(y * TileSize + fieldOffsetVertical + TileSize - 1) + }; + + lvgl.FlushDisplay(&area, tileBuffer); +} + +void Adder::GameOver() { + unsigned int digits[] = { 7, 0, 5, 3 }; // Digits forming the "GAME OVER" display + + // Determine offset based on field dimensions + unsigned int offset = fieldOffsetHorizontal > fieldOffsetVertical ? fieldOffsetHorizontal : fieldOffsetVertical; + + // Render "GAME OVER" animation + for (unsigned int r = 3 * offset; r < displayWidth - 4 * offset; r += 16) { + for (unsigned int i = 0; i < 4; i++) { + for (unsigned int j = 0; j < 64; j++) { + // Map font bits into the display buffer + digitBuffer[63 - j] = (DigitFont[digits[i]][j / 8] & (1 << (j % 8))) ? LV_COLOR_WHITE : LV_COLOR_BLACK; + } lv_area_t area; + area.x1 = r + 8 * i; + area.y1 = r; + area.x2 = area.x1 + 7; + area.y2 = area.y1 + 7; - area.x1 = x * TileSize + FieldOffsetHorizontal; - area.y1 = y * TileSize + FieldOffsetVertical; - area.x2 = area.x1 + TileSize - 1; - area.y2 = area.y1 + TileSize - 1; - lvgl.FlushDisplay(&area, TileBuffer); lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); - vTaskDelay(1); // Hack to give give the system time to refresh + lvgl.FlushDisplay(&area, digitBuffer); } } } -void Adder::Refresh() { - updateDisplay(); -} +void Adder::UpdateScore(unsigned int score) { + // Extract individual digits of the score + unsigned int digits[] = { 0, score % 10, (score % 100 - score % 10) / 10, (score - score % 100) / 100 }; -void Adder::updateSingleTile(unsigned int FieldPosX, unsigned int FieldPosY, lv_color_t Color) { - for (unsigned int ti = 0; ti < TileBufferSize; ti++) - TileBuffer[ti] = Color; - - lv_area_t area; - area.x1 = FieldPosX * TileSize + FieldOffsetHorizontal; - area.y1 = FieldPosY * TileSize + FieldOffsetVertical; - area.x2 = area.x1 + TileSize - 1; - area.y2 = area.y1 + TileSize - 1; - lvgl.FlushDisplay(&area, TileBuffer); - lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); - vTaskDelay(1); // Hack to give give the system time to refresh -} + // Render the score + for (unsigned int i = 0; i < 4; i++) { + for (unsigned int j = 0; j < 64; j++) { + // Map font bits into the display buffer (using bit manipulation) + digitBuffer[j] = (DigitFont[digits[i]][j / 8] & (1 << (j % 8))) ? LV_COLOR_WHITE : LV_COLOR_BLACK; + } -void Adder::updateDisplay() { - lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); - updatePosition(); - if (!AppReady) { - FullReDraw(); - createFood(); - updateSingleTile(AdderBody.back() % FieldWidth, AdderBody.back() / FieldWidth, LV_COLOR_BLACK); - updateScore(0); - AppReady = true; - } else { - updateSingleTile(AdderBody.front() % FieldWidth, AdderBody.front() / FieldWidth, LV_COLOR_YELLOW); - updateSingleTile(AdderBody.back() % FieldWidth, AdderBody.back() / FieldWidth, LV_COLOR_BLACK); + lv_area_t area; + area.x1 = displayWidth - 16 - 8 * i; // Adjust X to display digits + area.y1 = 4; // Y-offset for Score + area.x2 = area.x1 + 7; + area.y2 = area.y1 + 7; + + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); + lvgl.FlushDisplay(&area, digitBuffer); + vTaskDelay(1); // Small delay to allow display refresh } + + // Update the high score if necessary + unsigned int highScoreToWrite = (highScore > score) ? highScore : score; + unsigned int highScoreDigits[] = { + 0, + highScoreToWrite % 10, + (highScoreToWrite % 100 - highScoreToWrite % 10) / 10, + (highScoreToWrite - highScoreToWrite % 100) / 100 + }; + + // Render the high score + for (unsigned int i = 0; i < 4; i++) { + for (unsigned int j = 0; j < 64; j++) { + // Map font bits into the display buffer + digitBuffer[j] = (DigitFont[highScoreDigits[i]][j / 8] & (1 << (j % 8))) ? LV_COLOR_WHITE : LV_COLOR_BLACK; + } + + lv_area_t area; + area.x1 = 40 - 8 * i; // Adjust X to display digits + area.y1 = 4; // Y-offset for High Score + area.x2 = area.x1 + 7; + area.y2 = area.y1 + 7; + + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); + lvgl.FlushDisplay(&area, digitBuffer); + vTaskDelay(1); // Small delay to allow display refresh + } + + // Save the high score if it has changed + highScore = highScoreToWrite; } + diff --git a/src/displayapp/screens/Adder.h b/src/displayapp/screens/Adder.h index 0c9b16dc05..19da3d1a12 100644 --- a/src/displayapp/screens/Adder.h +++ b/src/displayapp/screens/Adder.h @@ -12,95 +12,111 @@ namespace Pinetime { namespace Applications { namespace Screens { - const unsigned int AdderVersion = 1; + // Save file version + constexpr unsigned int AdderVersion = 1; struct AdderSave { - unsigned int Level; - unsigned int HighScore; - unsigned int Version; - - AdderSave() : Level(0), HighScore(0),Version(AdderVersion){}; + unsigned int Level{0}; + unsigned int HighScore{0}; + unsigned int Version{AdderVersion}; }; - enum AdderField { UNDEFINED, BLANK, SOLID, PORTAL, BODY, FOOD }; + enum class AdderField { UNDEFINED, BLANK, SOLID, BODY, FOOD }; - enum MoveConsequence { DEATH = 0, EAT, MOVE }; class Adder : public Screen { public: Adder(Pinetime::Components::LittleVgl& lvgl, Pinetime::Controllers::FS& fs); ~Adder() override; + + + enum class MoveConsequence { DEATH, EAT, MOVE }; + + // Overridden functions void Refresh() override; bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override; - ; - private: // Private members and methods - - const char* GameSavePath = "/games/adder/adder.sav"; + private: + static constexpr const char* GameSavePath = "/games/adder/adder.sav"; + static constexpr unsigned int TileSize = 9; + static constexpr unsigned int AdderDelayInterval = 200; Controllers::FS& filesystem; - AdderSave Data; - const unsigned int TileSize = 9; - const unsigned int AdderDelayInterval = 200; - bool AppReady; - unsigned int DisplayHeight; - unsigned int DisplayWidth; - - unsigned int FieldWidth; - unsigned int FieldHeight; - unsigned int FieldSize; - unsigned int FieldOffsetHorizontal; - unsigned int FieldOffsetVertical; - - AdderField* Field; - std::list AdderBody; - std::vector Blanks; - - int prevAdderDirection = 0; - int AdderDirection = 1; - char lastKey = 0; - - unsigned int TileBufferSize; - lv_color_t* TileBuffer; - lv_color_t DigitBuffer[64]; Pinetime::Components::LittleVgl& lvgl; - lv_task_t* taskRefresh; - - unsigned int HighScore = 2; - void createFood(); - void InitBody(); - - MoveConsequence checkMove(); - void createLevel(); - void FullReDraw(); - void updateDisplay(); - void updateSingleTile(unsigned int FieldPosX, unsigned int FieldPosY, lv_color_t Color); - void updatePosition(); - void updateScore(unsigned int Score); - void GameOver(); + + AdderSave data; // Game save data + AdderField* field{nullptr}; + + lv_task_t* refreshTask{nullptr}; + lv_color_t* tileBuffer{nullptr}; + lv_color_t digitBuffer[64]; + + unsigned int displayHeight{0}; + unsigned int displayWidth{0}; + unsigned int fieldWidth{0}; + unsigned int fieldHeight{0}; + unsigned int fieldSize{0}; + unsigned int highScore{2}; + + unsigned int fieldOffsetHorizontal{0}; + unsigned int fieldOffsetVertical{0}; + + std::list adderBody; + std::vector blanks; + + int prevDirection{0}; + int currentDirection{1}; + + bool appReady{false}; + + // Methods + void InitializeGame(); void LoadGame(); void SaveGame(); - - const char DigitFont[10][8] = {{0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) - {0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) - {0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) - {0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) - {0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) - {0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) - {0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) - {0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) - {0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) - {0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}}; // U+0039 (9) + void ResetGame(); + + void InitializeBody(); + void CreateFood(); + void CreateLevel(); + + void UpdatePosition(); + void FullRedraw(); + void UpdateSingleTile(unsigned int fieldX, unsigned int fieldY, lv_color_t color); + void UpdateScore(unsigned int score); + void HandleGameOver(); + + MoveConsequence CheckMove() const; + + void Cleanup(); // Proper clean-up of allocated resources + + void updateScore(unsigned int score); // Updates the score and high score + void GameOver(); // Handles game-over logic + + static constexpr const char DigitFont[10][8] = { // Font for digits 0-9 + {0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // 0 + {0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // 1 + {0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // 2 + {0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // 3 + {0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // 4 + {0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // 5 + {0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // 6 + {0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // 7 + {0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // 8 + {0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00} // 9 + }; }; - } - template <> - struct AppTraits { + } // namespace Screens + + // Application Traits + template <> + struct AppTraits { static constexpr Apps app = Apps::Adder; static constexpr const char* icon = "S"; static Screens::Screen* Create(AppControllers& controllers) { return new Screens::Adder(controllers.lvgl, controllers.filesystem); - }; + } }; - } -} \ No newline at end of file + + } // namespace Applications +} // namespace Pinetime From 94b04957f7effdd0d725042efca44dceda6a9da3 Mon Sep 17 00:00:00 2001 From: shobowale Date: Tue, 21 Jan 2025 22:17:11 +0100 Subject: [PATCH 3/6] Fixed Timing Issues --- .vscode/settings.json | 3 ++- src/displayapp/screens/Adder.cpp | 18 ++++++++++++------ src/displayapp/screens/Adder.h | 5 +++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a7b04eea3c..aa7768f142 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -65,6 +65,7 @@ "stdexcept": "cpp", "streambuf": "cpp", "cinttypes": "cpp", - "typeinfo": "cpp" + "typeinfo": "cpp", + "list": "cpp" } } diff --git a/src/displayapp/screens/Adder.cpp b/src/displayapp/screens/Adder.cpp index e3944c4176..2b75a81480 100644 --- a/src/displayapp/screens/Adder.cpp +++ b/src/displayapp/screens/Adder.cpp @@ -41,8 +41,7 @@ void Adder::InitializeGame() { AdderDelayInterval, LV_TASK_PRIO_MID, this); appReady = false; - vTaskDelay(5); - UpdateScore(0); + vTaskDelay(20); } void Adder::Cleanup() { @@ -87,7 +86,7 @@ void Adder::ResetGame() { GameOver(); appReady = false; highScore = std::max(highScore, static_cast(adderBody.size() - 2)); - + data.HighScore=highScore; SaveGame(); CreateLevel(); @@ -213,11 +212,16 @@ void Adder::Refresh() { if (!appReady) { FullRedraw(); CreateFood(); + vTaskDelay(1); //Required to let the OS draw the tile completely + UpdateScore(0); + vTaskDelay(1); //Required to let the OS draw the tile completely appReady = true; } else { UpdatePosition(); UpdateSingleTile(adderBody.front() % fieldWidth, adderBody.front() / fieldWidth, LV_COLOR_YELLOW); + vTaskDelay(1); //Required to let the OS draw the tile completely UpdateSingleTile(adderBody.back() % fieldWidth, adderBody.back() / fieldWidth, LV_COLOR_BLACK); + vTaskDelay(1); //Required to let the OS draw the tile completely } } @@ -240,6 +244,7 @@ void Adder::FullRedraw() { break; } UpdateSingleTile(x, y, color); + vTaskDelay(1); //Required to let the OS draw the tile completely } } } @@ -267,7 +272,7 @@ void Adder::GameOver() { for (unsigned int i = 0; i < 4; i++) { for (unsigned int j = 0; j < 64; j++) { // Map font bits into the display buffer - digitBuffer[63 - j] = (DigitFont[digits[i]][j / 8] & (1 << (j % 8))) ? LV_COLOR_WHITE : LV_COLOR_BLACK; + digitBuffer[63 - j] = (DigitFont[digits[i]][j / 8] & 1 << j % 8) ? LV_COLOR_WHITE : LV_COLOR_BLACK; // Bitmagic to rotate the Digits to look like Letters } lv_area_t area; @@ -278,6 +283,7 @@ void Adder::GameOver() { lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); lvgl.FlushDisplay(&area, digitBuffer); + vTaskDelay(1); //Required to let the OS draw the tile completely } } } @@ -301,7 +307,7 @@ void Adder::UpdateScore(unsigned int score) { lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); lvgl.FlushDisplay(&area, digitBuffer); - vTaskDelay(1); // Small delay to allow display refresh + vTaskDelay(20); // Small delay to allow display refresh } // Update the high score if necessary @@ -328,7 +334,7 @@ void Adder::UpdateScore(unsigned int score) { lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); lvgl.FlushDisplay(&area, digitBuffer); - vTaskDelay(1); // Small delay to allow display refresh + vTaskDelay(20); // Small delay to allow display refresh } // Save the high score if it has changed diff --git a/src/displayapp/screens/Adder.h b/src/displayapp/screens/Adder.h index 19da3d1a12..7676aee37f 100644 --- a/src/displayapp/screens/Adder.h +++ b/src/displayapp/screens/Adder.h @@ -3,8 +3,8 @@ #include "displayapp/apps/Apps.h" #include "displayapp/Controllers.h" #include "displayapp/screens/Screen.h" -#include "components/fs/FS.h" #include +#include "components/fs/FS.h" #include #include @@ -42,8 +42,9 @@ namespace Pinetime { static constexpr unsigned int TileSize = 9; static constexpr unsigned int AdderDelayInterval = 200; - Controllers::FS& filesystem; + Pinetime::Components::LittleVgl& lvgl; + Controllers::FS& filesystem; AdderSave data; // Game save data AdderField* field{nullptr}; From b3964fdcb1a0d094031adeed05bfb9b4a373dcc7 Mon Sep 17 00:00:00 2001 From: shobowale Date: Thu, 23 Jan 2025 21:40:42 +0100 Subject: [PATCH 4/6] Applied formating patches and removed unecessary comment --- src/displayapp/screens/Adder.cpp | 93 ++++++++++++++++---------------- src/displayapp/screens/Adder.h | 80 +++++++++++++-------------- 2 files changed, 82 insertions(+), 91 deletions(-) diff --git a/src/displayapp/screens/Adder.cpp b/src/displayapp/screens/Adder.cpp index 2b75a81480..5bd8b7ce7c 100644 --- a/src/displayapp/screens/Adder.cpp +++ b/src/displayapp/screens/Adder.cpp @@ -1,12 +1,11 @@ #include "displayapp/DisplayApp.h" #include "displayapp/screens/Adder.h" -#include // For std::rand -#include // For std::max +#include // For std::rand +#include // For std::max using namespace Pinetime::Applications::Screens; -Adder::Adder(Pinetime::Components::LittleVgl& lvgl, Controllers::FS& fs) - : lvgl(lvgl), filesystem(fs) { +Adder::Adder(Pinetime::Components::LittleVgl& lvgl, Controllers::FS& fs) : lvgl(lvgl), filesystem(fs) { InitializeGame(); } @@ -34,14 +33,17 @@ void Adder::InitializeGame() { InitializeBody(); CreateLevel(); - refreshTask = lv_task_create([](lv_task_t* task) { + refreshTask = lv_task_create( + [](lv_task_t* task) { auto* adder = static_cast(task->user_data); adder->Refresh(); }, - AdderDelayInterval, LV_TASK_PRIO_MID, this); + AdderDelayInterval, + LV_TASK_PRIO_MID, + this); appReady = false; - vTaskDelay(20); + vTaskDelay(20); } void Adder::Cleanup() { @@ -86,7 +88,7 @@ void Adder::ResetGame() { GameOver(); appReady = false; highScore = std::max(highScore, static_cast(adderBody.size() - 2)); - data.HighScore=highScore; + data.HighScore = highScore; SaveGame(); CreateLevel(); @@ -101,7 +103,7 @@ void Adder::InitializeBody() { unsigned int startPosition = (fieldHeight / 2) * fieldWidth + fieldWidth / 2 + 2; adderBody = {startPosition, startPosition - 1}; - currentDirection = 1; // Start moving to the right + currentDirection = 1; // Start moving to the right prevDirection = currentDirection; } @@ -147,8 +149,7 @@ bool Adder::OnTouchEvent(Pinetime::Applications::TouchEvents event) { currentDirection = 1; break; case TouchEvents::LongTap: - FullRedraw(); // Adjusted to method spelled as "FullRedraw" - break; + FullRedraw(); default: break; } @@ -163,26 +164,25 @@ bool Adder::OnTouchEvent(Pinetime::Applications::TouchEvents event) { prevDirection = currentDirection; } - return true; // Return true to indicate the touch event was handled + return true; // Return true to indicate the touch event was handled } - void Adder::UpdatePosition() { unsigned int newHead = adderBody.front() + currentDirection; - Adder::MoveConsequence result = CheckMove(); // Fully qualify MoveConsequence + Adder::MoveConsequence result = CheckMove(); switch (result) { - case Adder::MoveConsequence::DEATH: // Fully qualify + case Adder::MoveConsequence::DEATH: ResetGame(); return; - case Adder::MoveConsequence::EAT: // Fully qualify + case Adder::MoveConsequence::EAT: adderBody.push_front(newHead); CreateFood(); UpdateScore(adderBody.size() - 2); break; - case Adder::MoveConsequence::MOVE: // Fully qualify + case Adder::MoveConsequence::MOVE: adderBody.pop_back(); adderBody.push_front(newHead); break; @@ -195,16 +195,16 @@ void Adder::UpdatePosition() { Adder::MoveConsequence Adder::CheckMove() const { unsigned int newHead = adderBody.front() + currentDirection; if (newHead >= fieldSize) { - return Adder::MoveConsequence::DEATH; // Fully qualify + return Adder::MoveConsequence::DEATH; } switch (field[newHead]) { case AdderField::BLANK: - return Adder::MoveConsequence::MOVE; // Fully qualify + return Adder::MoveConsequence::MOVE; case AdderField::FOOD: - return Adder::MoveConsequence::EAT; // Fully qualify + return Adder::MoveConsequence::EAT; default: - return Adder::MoveConsequence::DEATH; // Fully qualify + return Adder::MoveConsequence::DEATH; } } @@ -212,16 +212,16 @@ void Adder::Refresh() { if (!appReady) { FullRedraw(); CreateFood(); - vTaskDelay(1); //Required to let the OS draw the tile completely + vTaskDelay(1); // Required to let the OS draw the tile completely UpdateScore(0); - vTaskDelay(1); //Required to let the OS draw the tile completely + vTaskDelay(1); // Required to let the OS draw the tile completely appReady = true; } else { UpdatePosition(); UpdateSingleTile(adderBody.front() % fieldWidth, adderBody.front() / fieldWidth, LV_COLOR_YELLOW); - vTaskDelay(1); //Required to let the OS draw the tile completely + vTaskDelay(1); // Required to let the OS draw the tile completely UpdateSingleTile(adderBody.back() % fieldWidth, adderBody.back() / fieldWidth, LV_COLOR_BLACK); - vTaskDelay(1); //Required to let the OS draw the tile completely + vTaskDelay(1); // Required to let the OS draw the tile completely } } @@ -244,25 +244,23 @@ void Adder::FullRedraw() { break; } UpdateSingleTile(x, y, color); - vTaskDelay(1); //Required to let the OS draw the tile completely + vTaskDelay(1); // Required to let the OS draw the tile completely } } } void Adder::UpdateSingleTile(unsigned int x, unsigned int y, lv_color_t color) { std::fill(tileBuffer, tileBuffer + TileSize * TileSize, color); - lv_area_t area { - .x1 = static_cast(x * TileSize + fieldOffsetHorizontal), - .y1 = static_cast(y * TileSize + fieldOffsetVertical), - .x2 = static_cast(x * TileSize + fieldOffsetHorizontal + TileSize - 1), - .y2 = static_cast(y * TileSize + fieldOffsetVertical + TileSize - 1) - }; + lv_area_t area {.x1 = static_cast(x * TileSize + fieldOffsetHorizontal), + .y1 = static_cast(y * TileSize + fieldOffsetVertical), + .x2 = static_cast(x * TileSize + fieldOffsetHorizontal + TileSize - 1), + .y2 = static_cast(y * TileSize + fieldOffsetVertical + TileSize - 1)}; lvgl.FlushDisplay(&area, tileBuffer); } void Adder::GameOver() { - unsigned int digits[] = { 7, 0, 5, 3 }; // Digits forming the "GAME OVER" display + unsigned int digits[] = {7, 0, 5, 3}; // Digits forming the "GAME OVER" display // Determine offset based on field dimensions unsigned int offset = fieldOffsetHorizontal > fieldOffsetVertical ? fieldOffsetHorizontal : fieldOffsetVertical; @@ -272,7 +270,9 @@ void Adder::GameOver() { for (unsigned int i = 0; i < 4; i++) { for (unsigned int j = 0; j < 64; j++) { // Map font bits into the display buffer - digitBuffer[63 - j] = (DigitFont[digits[i]][j / 8] & 1 << j % 8) ? LV_COLOR_WHITE : LV_COLOR_BLACK; // Bitmagic to rotate the Digits to look like Letters + digitBuffer[63 - j] = (DigitFont[digits[i]][j / 8] & 1 << j % 8) + ? LV_COLOR_WHITE + : LV_COLOR_BLACK; // Bitmagic to rotate the Digits to look like Letters } lv_area_t area; @@ -283,14 +283,14 @@ void Adder::GameOver() { lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); lvgl.FlushDisplay(&area, digitBuffer); - vTaskDelay(1); //Required to let the OS draw the tile completely + vTaskDelay(1); // Required to let the OS draw the tile completely } } } void Adder::UpdateScore(unsigned int score) { // Extract individual digits of the score - unsigned int digits[] = { 0, score % 10, (score % 100 - score % 10) / 10, (score - score % 100) / 100 }; + unsigned int digits[] = {0, score % 10, (score % 100 - score % 10) / 10, (score - score % 100) / 100}; // Render the score for (unsigned int i = 0; i < 4; i++) { @@ -300,24 +300,22 @@ void Adder::UpdateScore(unsigned int score) { } lv_area_t area; - area.x1 = displayWidth - 16 - 8 * i; // Adjust X to display digits + area.x1 = displayWidth - 16 - 8 * i; // Adjust X to display digits area.y1 = 4; // Y-offset for Score area.x2 = area.x1 + 7; area.y2 = area.y1 + 7; lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); lvgl.FlushDisplay(&area, digitBuffer); - vTaskDelay(20); // Small delay to allow display refresh + vTaskDelay(20); // Small delay to allow display refresh } // Update the high score if necessary unsigned int highScoreToWrite = (highScore > score) ? highScore : score; - unsigned int highScoreDigits[] = { - 0, - highScoreToWrite % 10, - (highScoreToWrite % 100 - highScoreToWrite % 10) / 10, - (highScoreToWrite - highScoreToWrite % 100) / 100 - }; + unsigned int highScoreDigits[] = {0, + highScoreToWrite % 10, + (highScoreToWrite % 100 - highScoreToWrite % 10) / 10, + (highScoreToWrite - highScoreToWrite % 100) / 100}; // Render the high score for (unsigned int i = 0; i < 4; i++) { @@ -327,17 +325,16 @@ void Adder::UpdateScore(unsigned int score) { } lv_area_t area; - area.x1 = 40 - 8 * i; // Adjust X to display digits - area.y1 = 4; // Y-offset for High Score + area.x1 = 40 - 8 * i; // Adjust X to display digits + area.y1 = 4; // Y-offset for High Score area.x2 = area.x1 + 7; area.y2 = area.y1 + 7; lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); lvgl.FlushDisplay(&area, digitBuffer); - vTaskDelay(20); // Small delay to allow display refresh + vTaskDelay(20); // Small delay to allow display refresh } // Save the high score if it has changed highScore = highScoreToWrite; } - diff --git a/src/displayapp/screens/Adder.h b/src/displayapp/screens/Adder.h index 7676aee37f..81d4d3c585 100644 --- a/src/displayapp/screens/Adder.h +++ b/src/displayapp/screens/Adder.h @@ -16,21 +16,19 @@ namespace Pinetime { constexpr unsigned int AdderVersion = 1; struct AdderSave { - unsigned int Level{0}; - unsigned int HighScore{0}; - unsigned int Version{AdderVersion}; + unsigned int Level {0}; + unsigned int HighScore {0}; + unsigned int Version {AdderVersion}; }; enum class AdderField { UNDEFINED, BLANK, SOLID, BODY, FOOD }; - class Adder : public Screen { public: Adder(Pinetime::Components::LittleVgl& lvgl, Pinetime::Controllers::FS& fs); ~Adder() override; - enum class MoveConsequence { DEATH, EAT, MOVE }; // Overridden functions @@ -42,34 +40,33 @@ namespace Pinetime { static constexpr unsigned int TileSize = 9; static constexpr unsigned int AdderDelayInterval = 200; - Pinetime::Components::LittleVgl& lvgl; Controllers::FS& filesystem; AdderSave data; // Game save data - AdderField* field{nullptr}; + AdderField* field {nullptr}; - lv_task_t* refreshTask{nullptr}; - lv_color_t* tileBuffer{nullptr}; + lv_task_t* refreshTask {nullptr}; + lv_color_t* tileBuffer {nullptr}; lv_color_t digitBuffer[64]; - unsigned int displayHeight{0}; - unsigned int displayWidth{0}; - unsigned int fieldWidth{0}; - unsigned int fieldHeight{0}; - unsigned int fieldSize{0}; - unsigned int highScore{2}; + unsigned int displayHeight {0}; + unsigned int displayWidth {0}; + unsigned int fieldWidth {0}; + unsigned int fieldHeight {0}; + unsigned int fieldSize {0}; + unsigned int highScore {2}; - unsigned int fieldOffsetHorizontal{0}; - unsigned int fieldOffsetVertical{0}; + unsigned int fieldOffsetHorizontal {0}; + unsigned int fieldOffsetVertical {0}; std::list adderBody; std::vector blanks; - int prevDirection{0}; - int currentDirection{1}; + int prevDirection {0}; + int currentDirection {1}; - bool appReady{false}; + bool appReady {false}; // Methods void InitializeGame(); @@ -80,44 +77,41 @@ namespace Pinetime { void InitializeBody(); void CreateFood(); void CreateLevel(); - + void UpdatePosition(); void FullRedraw(); void UpdateSingleTile(unsigned int fieldX, unsigned int fieldY, lv_color_t color); void UpdateScore(unsigned int score); - void HandleGameOver(); + void GameOver(); MoveConsequence CheckMove() const; - - void Cleanup(); // Proper clean-up of allocated resources - - void updateScore(unsigned int score); // Updates the score and high score - void GameOver(); // Handles game-over logic - - static constexpr const char DigitFont[10][8] = { // Font for digits 0-9 - {0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // 0 - {0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // 1 - {0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // 2 - {0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // 3 - {0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // 4 - {0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // 5 - {0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // 6 - {0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // 7 - {0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // 8 - {0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00} // 9 + + static constexpr const char DigitFont[10][8] = { + // Font for digits 0-9 + {0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // 0 + {0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // 1 + {0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // 2 + {0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // 3 + {0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // 4 + {0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // 5 + {0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // 6 + {0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // 7 + {0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // 8 + {0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00} // 9 }; }; - } // namespace Screens + } // namespace Screens // Application Traits template <> struct AppTraits { static constexpr Apps app = Apps::Adder; static constexpr const char* icon = "S"; + static Screens::Screen* Create(AppControllers& controllers) { return new Screens::Adder(controllers.lvgl, controllers.filesystem); } }; - - } // namespace Applications -} // namespace Pinetime + + } // namespace Applications +} // namespace Pinetime From 834d34d2d9268f8b5ac6ee7e41a9d9e77047ddfd Mon Sep 17 00:00:00 2001 From: shobowale Date: Thu, 23 Jan 2025 21:56:02 +0100 Subject: [PATCH 5/6] Renamed/Fixed CleanUp() in header file --- src/displayapp/screens/Adder.cpp | 4 ++-- src/displayapp/screens/Adder.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/displayapp/screens/Adder.cpp b/src/displayapp/screens/Adder.cpp index 5bd8b7ce7c..9f633e587b 100644 --- a/src/displayapp/screens/Adder.cpp +++ b/src/displayapp/screens/Adder.cpp @@ -10,7 +10,7 @@ Adder::Adder(Pinetime::Components::LittleVgl& lvgl, Controllers::FS& fs) : lvgl( } Adder::~Adder() { - Cleanup(); + CleanUp(); } void Adder::InitializeGame() { @@ -46,7 +46,7 @@ void Adder::InitializeGame() { vTaskDelay(20); } -void Adder::Cleanup() { +void Adder::CleanUp() { delete[] field; delete[] tileBuffer; if (refreshTask) { diff --git a/src/displayapp/screens/Adder.h b/src/displayapp/screens/Adder.h index 81d4d3c585..50fca28649 100644 --- a/src/displayapp/screens/Adder.h +++ b/src/displayapp/screens/Adder.h @@ -77,12 +77,13 @@ namespace Pinetime { void InitializeBody(); void CreateFood(); void CreateLevel(); - + void UpdatePosition(); void FullRedraw(); void UpdateSingleTile(unsigned int fieldX, unsigned int fieldY, lv_color_t color); void UpdateScore(unsigned int score); void GameOver(); + void CleanUp(); MoveConsequence CheckMove() const; From c16b1e6432bae750d894a19a652f1cf7ef7c143a Mon Sep 17 00:00:00 2001 From: shobowale Date: Fri, 24 Jan 2025 11:10:55 +0100 Subject: [PATCH 6/6] Formating --- src/displayapp/screens/Adder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/displayapp/screens/Adder.h b/src/displayapp/screens/Adder.h index 50fca28649..d96c6953f7 100644 --- a/src/displayapp/screens/Adder.h +++ b/src/displayapp/screens/Adder.h @@ -86,7 +86,7 @@ namespace Pinetime { void CleanUp(); MoveConsequence CheckMove() const; - + static constexpr const char DigitFont[10][8] = { // Font for digits 0-9 {0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // 0