tetris high score

This commit is contained in:
2025-10-10 11:18:04 +02:00
parent 54d5f85538
commit 28411535bb

View File

@@ -13,6 +13,7 @@
#include <buzzer.hpp>
#include <disp_tools.hpp>
#include <display.hpp>
#include <nvs.h>
#include <power_helper.hpp>
#include <algorithm>
@@ -20,10 +21,12 @@
#include <cmath>
#include <cstdio>
#include <cstring>
#include <limits>
#include <random>
#include <string_view>
#include <vector>
#include <esp_err.h>
#include "esp_random.h"
#include "esp_timer.h"
@@ -223,8 +226,8 @@ public:
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,
bool ghost, bool paused, bool gameOver, int fps, int avgFrameMs10) {
void render(const Board& b, int px, int py, int prot, int pidx, int score, int highScore, int level, int lines,
int nextIdx, bool ghost, bool paused, bool gameOver, int fps, int avgFrameMs10) {
auto& ps = PerfStats::get();
auto tus = []() { return esp_timer_get_time(); };
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");
yHUD += hudFontHeight() + kHudLabelGap;
drawNumber(hudX, yHUD, score);
yHUD += hudFontHeight() + kHudLabelGap;
drawLabel(hudX, yHUD, "BEST");
yHUD += hudFontHeight() + kHudLabelGap;
drawNumber(hudX, yHUD, highScore);
yHUD += hudFontHeight() + kHudBlockGap;
drawLabel(hudX, yHUD, "LEVEL");
yHUD += hudFontHeight() + kHudLabelGap;
@@ -681,16 +688,18 @@ private:
// ─────────────────────────────────────────────────────────────────────────────
// Game
struct ScoreState {
int level = 0;
int score = 0;
int lines = 0;
int dropMs = cfg::DropMsStart;
int level = 0;
int score = 0;
int highScore = 0;
int lines = 0;
int dropMs = cfg::DropMsStart;
};
class Game {
public:
explicit Game(AppContext& ctx) :
appContext(ctx), fb(ctx.framebuffer), input(ctx.input), clock(ctx.clock), renderer(ctx.framebuffer) {
loadHighScore();
nextPiece = bag.next();
spawn();
lastFall = clock.millis();
@@ -970,6 +979,32 @@ private:
else if (!wantSlow && ph.is_slow())
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() {
// Complete previous frame transfer & implicit clear so dma_buf pixel area is zeroed before we draw
SMD::async_draw_wait();
@@ -989,8 +1024,8 @@ private:
frameAccumMs = 0;
lastStatTime = rStart;
}
renderer.render(board, px, py, rot, current, score.score, score.level, score.lines, nextPiece, true, paused,
!running, statFps, statAvgFrameMs10);
renderer.render(board, px, py, rot, current, score.score, score.highScore, score.level, score.lines, nextPiece,
true, paused, !running, statFps, statAvgFrameMs10);
uint64_t rEndUs = esp_timer_get_time();
{
auto& ps = PerfStats::get();
@@ -1008,8 +1043,11 @@ private:
dirty = false;
}
void restart() {
updateHighScoreIfNeeded();
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)
nextPiece = bag.next();
running = true;
@@ -1036,6 +1074,7 @@ private:
gameOverTime = clock.millis();
gameOverPrevPressed = true; // require a release after delay
// slow mode applied centrally next step
updateHighScoreIfNeeded();
Buzzer::get().beepGameOver();
}
}
@@ -1122,6 +1161,7 @@ private:
gameOverTime = clock.millis();
gameOverPrevPressed = true;
// slow mode applied centrally next step
updateHighScoreIfNeeded();
Buzzer::get().beepGameOver();
return;
}
@@ -1130,6 +1170,7 @@ private:
static const int pts[5] = {0, 100, 300, 500, 800};
score.lines += c;
score.score += pts[c] * (score.level + 1);
updateHighScoreIfNeeded();
int nl = score.lines / cfg::LevelStepClr;
if (nl != score.level) {
score.level = nl;
@@ -1177,9 +1218,9 @@ private:
void runStep() {
game.step();
const auto now = context.clock.millis();
const auto plan = game.recommendedSleepMs(now);
uint32_t delay = plan.normal_ms != 0 ? plan.normal_ms : plan.slow_ms;
const auto now = context.clock.millis();
const auto plan = game.recommendedSleepMs(now);
uint32_t delay = plan.normal_ms != 0 ? plan.normal_ms : plan.slow_ms;
scheduleNextTick(delay);
}