mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
tetris high score
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
#include <buzzer.hpp>
|
#include <buzzer.hpp>
|
||||||
#include <disp_tools.hpp>
|
#include <disp_tools.hpp>
|
||||||
#include <display.hpp>
|
#include <display.hpp>
|
||||||
|
#include <nvs.h>
|
||||||
#include <power_helper.hpp>
|
#include <power_helper.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -20,10 +21,12 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
#include "esp_random.h"
|
#include "esp_random.h"
|
||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
|
|
||||||
@@ -223,8 +226,8 @@ public:
|
|||||||
oy = (fb.height() - bh) / 2; // centered vertically with margin
|
oy = (fb.height() - bh) / 2; // centered vertically with margin
|
||||||
}
|
}
|
||||||
|
|
||||||
void render(const Board& b, int px, int py, int prot, int pidx, int score, int level, int lines, int nextIdx,
|
void render(const Board& b, int px, int py, int prot, int pidx, int score, int highScore, int level, int lines,
|
||||||
bool ghost, bool paused, bool gameOver, int fps, int avgFrameMs10) {
|
int nextIdx, bool ghost, bool paused, bool gameOver, int fps, int avgFrameMs10) {
|
||||||
auto& ps = PerfStats::get();
|
auto& ps = PerfStats::get();
|
||||||
auto tus = []() { return esp_timer_get_time(); };
|
auto tus = []() { return esp_timer_get_time(); };
|
||||||
uint64_t t0 = tus(); // frame start (no full clear; buffer already zeroed by async post-blit)
|
uint64_t t0 = tus(); // frame start (no full clear; buffer already zeroed by async post-blit)
|
||||||
@@ -257,6 +260,10 @@ public:
|
|||||||
drawLabel(hudX, yHUD, "SCORE");
|
drawLabel(hudX, yHUD, "SCORE");
|
||||||
yHUD += hudFontHeight() + kHudLabelGap;
|
yHUD += hudFontHeight() + kHudLabelGap;
|
||||||
drawNumber(hudX, yHUD, score);
|
drawNumber(hudX, yHUD, score);
|
||||||
|
yHUD += hudFontHeight() + kHudLabelGap;
|
||||||
|
drawLabel(hudX, yHUD, "BEST");
|
||||||
|
yHUD += hudFontHeight() + kHudLabelGap;
|
||||||
|
drawNumber(hudX, yHUD, highScore);
|
||||||
yHUD += hudFontHeight() + kHudBlockGap;
|
yHUD += hudFontHeight() + kHudBlockGap;
|
||||||
drawLabel(hudX, yHUD, "LEVEL");
|
drawLabel(hudX, yHUD, "LEVEL");
|
||||||
yHUD += hudFontHeight() + kHudLabelGap;
|
yHUD += hudFontHeight() + kHudLabelGap;
|
||||||
@@ -681,16 +688,18 @@ private:
|
|||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Game
|
// Game
|
||||||
struct ScoreState {
|
struct ScoreState {
|
||||||
int level = 0;
|
int level = 0;
|
||||||
int score = 0;
|
int score = 0;
|
||||||
int lines = 0;
|
int highScore = 0;
|
||||||
int dropMs = cfg::DropMsStart;
|
int lines = 0;
|
||||||
|
int dropMs = cfg::DropMsStart;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Game {
|
class Game {
|
||||||
public:
|
public:
|
||||||
explicit Game(AppContext& ctx) :
|
explicit Game(AppContext& ctx) :
|
||||||
appContext(ctx), fb(ctx.framebuffer), input(ctx.input), clock(ctx.clock), renderer(ctx.framebuffer) {
|
appContext(ctx), fb(ctx.framebuffer), input(ctx.input), clock(ctx.clock), renderer(ctx.framebuffer) {
|
||||||
|
loadHighScore();
|
||||||
nextPiece = bag.next();
|
nextPiece = bag.next();
|
||||||
spawn();
|
spawn();
|
||||||
lastFall = clock.millis();
|
lastFall = clock.millis();
|
||||||
@@ -970,6 +979,32 @@ private:
|
|||||||
else if (!wantSlow && ph.is_slow())
|
else if (!wantSlow && ph.is_slow())
|
||||||
ph.set_slow(false);
|
ph.set_slow(false);
|
||||||
}
|
}
|
||||||
|
void loadHighScore() {
|
||||||
|
nvs_handle_t h;
|
||||||
|
if (nvs_open("tetris", NVS_READONLY, &h) == ESP_OK) {
|
||||||
|
uint32_t stored = 0;
|
||||||
|
if (nvs_get_u32(h, "best", &stored) == ESP_OK) {
|
||||||
|
uint32_t capped = std::min<uint32_t>(stored, static_cast<uint32_t>(std::numeric_limits<int>::max()));
|
||||||
|
score.highScore = static_cast<int>(capped);
|
||||||
|
}
|
||||||
|
nvs_close(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void persistHighScore() {
|
||||||
|
nvs_handle_t h;
|
||||||
|
if (nvs_open("tetris", NVS_READWRITE, &h) == ESP_OK) {
|
||||||
|
uint32_t value = score.highScore > 0 ? static_cast<uint32_t>(score.highScore) : 0;
|
||||||
|
nvs_set_u32(h, "best", value);
|
||||||
|
nvs_commit(h);
|
||||||
|
nvs_close(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void updateHighScoreIfNeeded() {
|
||||||
|
if (score.score > score.highScore) {
|
||||||
|
score.highScore = score.score;
|
||||||
|
persistHighScore();
|
||||||
|
}
|
||||||
|
}
|
||||||
void paintHUD() {
|
void paintHUD() {
|
||||||
// Complete previous frame transfer & implicit clear so dma_buf pixel area is zeroed before we draw
|
// Complete previous frame transfer & implicit clear so dma_buf pixel area is zeroed before we draw
|
||||||
SMD::async_draw_wait();
|
SMD::async_draw_wait();
|
||||||
@@ -989,8 +1024,8 @@ private:
|
|||||||
frameAccumMs = 0;
|
frameAccumMs = 0;
|
||||||
lastStatTime = rStart;
|
lastStatTime = rStart;
|
||||||
}
|
}
|
||||||
renderer.render(board, px, py, rot, current, score.score, score.level, score.lines, nextPiece, true, paused,
|
renderer.render(board, px, py, rot, current, score.score, score.highScore, score.level, score.lines, nextPiece,
|
||||||
!running, statFps, statAvgFrameMs10);
|
true, paused, !running, statFps, statAvgFrameMs10);
|
||||||
uint64_t rEndUs = esp_timer_get_time();
|
uint64_t rEndUs = esp_timer_get_time();
|
||||||
{
|
{
|
||||||
auto& ps = PerfStats::get();
|
auto& ps = PerfStats::get();
|
||||||
@@ -1008,8 +1043,11 @@ private:
|
|||||||
dirty = false;
|
dirty = false;
|
||||||
}
|
}
|
||||||
void restart() {
|
void restart() {
|
||||||
|
updateHighScoreIfNeeded();
|
||||||
board.clear();
|
board.clear();
|
||||||
score = ScoreState{};
|
int best = score.highScore;
|
||||||
|
score = ScoreState{};
|
||||||
|
score.highScore = best;
|
||||||
bag.reset(); // new randomized sequence without reconstructing (avoids potential random_device issues)
|
bag.reset(); // new randomized sequence without reconstructing (avoids potential random_device issues)
|
||||||
nextPiece = bag.next();
|
nextPiece = bag.next();
|
||||||
running = true;
|
running = true;
|
||||||
@@ -1036,6 +1074,7 @@ private:
|
|||||||
gameOverTime = clock.millis();
|
gameOverTime = clock.millis();
|
||||||
gameOverPrevPressed = true; // require a release after delay
|
gameOverPrevPressed = true; // require a release after delay
|
||||||
// slow mode applied centrally next step
|
// slow mode applied centrally next step
|
||||||
|
updateHighScoreIfNeeded();
|
||||||
Buzzer::get().beepGameOver();
|
Buzzer::get().beepGameOver();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1122,6 +1161,7 @@ private:
|
|||||||
gameOverTime = clock.millis();
|
gameOverTime = clock.millis();
|
||||||
gameOverPrevPressed = true;
|
gameOverPrevPressed = true;
|
||||||
// slow mode applied centrally next step
|
// slow mode applied centrally next step
|
||||||
|
updateHighScoreIfNeeded();
|
||||||
Buzzer::get().beepGameOver();
|
Buzzer::get().beepGameOver();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1130,6 +1170,7 @@ private:
|
|||||||
static const int pts[5] = {0, 100, 300, 500, 800};
|
static const int pts[5] = {0, 100, 300, 500, 800};
|
||||||
score.lines += c;
|
score.lines += c;
|
||||||
score.score += pts[c] * (score.level + 1);
|
score.score += pts[c] * (score.level + 1);
|
||||||
|
updateHighScoreIfNeeded();
|
||||||
int nl = score.lines / cfg::LevelStepClr;
|
int nl = score.lines / cfg::LevelStepClr;
|
||||||
if (nl != score.level) {
|
if (nl != score.level) {
|
||||||
score.level = nl;
|
score.level = nl;
|
||||||
@@ -1177,9 +1218,9 @@ private:
|
|||||||
|
|
||||||
void runStep() {
|
void runStep() {
|
||||||
game.step();
|
game.step();
|
||||||
const auto now = context.clock.millis();
|
const auto now = context.clock.millis();
|
||||||
const auto plan = game.recommendedSleepMs(now);
|
const auto plan = game.recommendedSleepMs(now);
|
||||||
uint32_t delay = plan.normal_ms != 0 ? plan.normal_ms : plan.slow_ms;
|
uint32_t delay = plan.normal_ms != 0 ? plan.normal_ms : plan.slow_ms;
|
||||||
scheduleNextTick(delay);
|
scheduleNextTick(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user