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
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: nyanBOX CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Cache PlatformIO
uses: actions/cache@v3
with:
path: |
~/.platformio/.cache
~/.platformio/packages
~/.platformio/platforms
key: ${{ runner.os }}-pio-${{ hashFiles('**/platformio.ini') }}
restore-keys: |
${{ runner.os }}-pio-

- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install platformio

- name: Build ESP32 firmware
working-directory: "VScode Platformio"
run: pio run -e esp32dev
continue-on-error: true

- name: Run native tests
working-directory: "VScode Platformio"
run: pio test -e native
continue-on-error: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* ____________________________
This software is licensed under the MIT License:
https://github.com/jbohack/nyanBOX
________________________________________ */

#include <level_calculations.h>

int calculateXPRequiredForLevel(int level) {
if (level <= 1) return 0;
return (level * level) + 10;
}

int calculateCurrentLevelFromXP(int currentXP) {
if (currentXP < 0) return 1;

int level = 1;
while (level < 99 && currentXP >= calculateXPRequiredForLevel(level + 1)) {
level++;
}
return level;
}

const char* calculateRankName(int level) {
if (level <= 5) return "N00b";
else if (level <= 15) return "Skid";
else if (level <= 25) return "Wannabe";
else if (level <= 40) return "L33t";
else if (level <= 55) return "Hacker";
else if (level <= 70) return "Uber Hacker";
else if (level <= 85) return "Elite";
else if (level <= 95) return "Godlike";
else return "Legend";
}

int calculateNewXP(int currentXP, int amount) {
if (currentXP + amount > 65535) {
return 65535;
} else {
return currentXP + amount;
}
}
41 changes: 41 additions & 0 deletions VScode Platformio/lib/level_calculations/src/level_calculations.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* ____________________________
This software is licensed under the MIT License:
https://github.com/jbohack/nyanBOX
________________________________________ */

#ifndef LEVEL_CALCULATIONS_H
#define LEVEL_CALCULATIONS_H

// Pure functions for level system calculations
// These functions have no side effects and are easily testable

/**
* Calculate XP required to reach a specific level
* @param level The target level (1-99)
* @return XP required for that level
*/
int calculateXPRequiredForLevel(int level);

/**
* Calculate current level based on total XP
* @param currentXP Total experience points
* @return Current level (1-99)
*/
int calculateCurrentLevelFromXP(int currentXP);

/**
* Get rank name for a given level
* @param level Player level
* @return Rank name string
*/
const char* calculateRankName(int level);

/**
* Add XP to the current XP
* @param currentXP Current experience points
* @param amount Amount of XP to add
* @return New total XP
*/
int calculateNewXP(int currentXP, int amount);

#endif
10 changes: 9 additions & 1 deletion VScode Platformio/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,12 @@ lib_deps =
adafruit/Adafruit NeoPixel@^1.12.3
bblanchon/ArduinoJson@^7.4.2
board_build.partitions = min_spiffs.csv
monitor_speed = 115200
monitor_speed = 115200

[env:native]
platform = native
build_flags = -std=c++11 -I lib/level_calculations
test_framework = unity
lib_deps =
throwtheswitch/Unity@^2.5.2
build_src_filter = -<*>
41 changes: 8 additions & 33 deletions VScode Platformio/src/level_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <EEPROM.h>
#include <U8g2lib.h>
#include <level_calculations.h>
#include "../include/level_system.h"
#include "../include/sleep_manager.h"
#include "../include/pindefs.h"
Expand All @@ -21,11 +22,6 @@ static bool buttonCenterPressed = false;

void saveLevelData();

int getXPRequiredForLevel(int level) {
if (level <= 1) return 0;
return (level * level) + 10;
}

void loadLevelData() {
uint8_t magic = EEPROM.read(EEPROM_ADDRESS_LEVEL_MAGIC);
if (magic != LEVEL_MAGIC_NUMBER) {
Expand Down Expand Up @@ -58,57 +54,36 @@ void levelSystemSetup() {
}

void addXP(int amount) {
if (currentXP + amount > 65535) {
currentXP = 65535;
} else {
currentXP += amount;
}

currentXP = calculateNewXP(currentXP, amount);
saveLevelData();
}

int getCurrentLevel() {
int level = 1;
while (level < 99 && currentXP >= getXPRequiredForLevel(level + 1)) {
level++;
}
return level;
return calculateCurrentLevelFromXP(currentXP);
}

int getCurrentXP() {
return currentXP;
}

int getXPForNextLevel() {
int currentLevel = getCurrentLevel();
int currentLevel = calculateCurrentLevelFromXP(currentXP);
if (currentLevel >= 99) return 0;
return getXPRequiredForLevel(currentLevel + 1);
}

const char* getRankName(int level) {
if (level <= 5) return "N00b";
else if (level <= 15) return "Skid";
else if (level <= 25) return "Wannabe";
else if (level <= 40) return "L33t";
else if (level <= 55) return "Hacker";
else if (level <= 70) return "Uber Hacker";
else if (level <= 85) return "Elite";
else if (level <= 95) return "Godlike";
else return "Legend";
return calculateXPRequiredForLevel(currentLevel + 1);
}

void displayLevelScreen() {
u8g2.clearBuffer();

int currentLevel = getCurrentLevel();
int currentLevel = calculateCurrentLevelFromXP(currentXP);

u8g2.setFont(u8g2_font_helvB14_tr);
char levelStr[8];
sprintf(levelStr, "Level %d", currentLevel);
int levelWidth = u8g2.getUTF8Width(levelStr);
u8g2.drawStr((128 - levelWidth) / 2, 18, levelStr);

const char* rankName = getRankName(currentLevel);
const char* rankName = calculateRankName(currentLevel);
u8g2.setFont(u8g2_font_helvR08_tr);
int rankWidth = u8g2.getUTF8Width(rankName);
u8g2.drawStr((128 - rankWidth) / 2, 32, rankName);
Expand All @@ -131,7 +106,7 @@ void displayLevelScreen() {
int fillWidth = 0;
if (currentLevel < 99) {
int nextLevelXP = getXPForNextLevel();
int currentLevelXP = getXPRequiredForLevel(currentLevel);
int currentLevelXP = calculateXPRequiredForLevel(currentLevel);
if (nextLevelXP > currentLevelXP) {
int progress = map(currentXP - currentLevelXP, 0, nextLevelXP - currentLevelXP, 0, 100);
fillWidth = map(progress, 0, 100, 0, barWidth - 2);
Expand Down
55 changes: 55 additions & 0 deletions VScode Platformio/test/test_level_system.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <unity.h>
#include <level_calculations.h>

void test_xp_required_for_level_calculation() {
TEST_ASSERT_EQUAL(0, calculateXPRequiredForLevel(-1));
TEST_ASSERT_EQUAL(0, calculateXPRequiredForLevel(0));
TEST_ASSERT_EQUAL(0, calculateXPRequiredForLevel(1));
TEST_ASSERT_EQUAL(14, calculateXPRequiredForLevel(2));
TEST_ASSERT_EQUAL(19, calculateXPRequiredForLevel(3));
TEST_ASSERT_EQUAL(26, calculateXPRequiredForLevel(4));
TEST_ASSERT_EQUAL(99*99 + 10, calculateXPRequiredForLevel(99));
}

void test_current_level_calculation() {
TEST_ASSERT_EQUAL(1, calculateCurrentLevelFromXP(-1));
TEST_ASSERT_EQUAL(1, calculateCurrentLevelFromXP(0));
TEST_ASSERT_EQUAL(1, calculateCurrentLevelFromXP(1));
TEST_ASSERT_EQUAL(1, calculateCurrentLevelFromXP(13));
TEST_ASSERT_EQUAL(2, calculateCurrentLevelFromXP(14));
TEST_ASSERT_EQUAL(2, calculateCurrentLevelFromXP(18));
TEST_ASSERT_EQUAL(3, calculateCurrentLevelFromXP(19));
}

void test_rank_name_calculation() {
TEST_ASSERT_EQUAL_STRING("N00b", calculateRankName(1));
TEST_ASSERT_EQUAL_STRING("N00b", calculateRankName(5));
TEST_ASSERT_EQUAL_STRING("Skid", calculateRankName(6));
TEST_ASSERT_EQUAL_STRING("Skid", calculateRankName(15));
TEST_ASSERT_EQUAL_STRING("Wannabe", calculateRankName(16));
TEST_ASSERT_EQUAL_STRING("Wannabe", calculateRankName(25));
TEST_ASSERT_EQUAL_STRING("L33t", calculateRankName(40));
TEST_ASSERT_EQUAL_STRING("Hacker", calculateRankName(55));
TEST_ASSERT_EQUAL_STRING("Uber Hacker", calculateRankName(70));
TEST_ASSERT_EQUAL_STRING("Elite", calculateRankName(85));
TEST_ASSERT_EQUAL_STRING("Godlike", calculateRankName(95));
TEST_ASSERT_EQUAL_STRING("Legend", calculateRankName(99));
}

void test_increase_xp() {
TEST_ASSERT_EQUAL(110, calculateNewXP(100, 10));
TEST_ASSERT_EQUAL(100, calculateNewXP(0, 100));
TEST_ASSERT_EQUAL(65535, calculateNewXP(65534, 1));
TEST_ASSERT_EQUAL(65535, calculateNewXP(65535, 10));
}

int main(int argc, char **argv) {
UNITY_BEGIN();

RUN_TEST(test_xp_required_for_level_calculation);
RUN_TEST(test_current_level_calculation);
RUN_TEST(test_rank_name_calculation);
RUN_TEST(test_increase_xp);

return UNITY_END();
}