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 <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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user