Snake! (#10936)
* Snake! * Add spiLock to snake score saving * Check fixes * More careful locking * WIP: Big Display Node * Update src/graphics/HUB75Display.cpp Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Add HUB75 Native * Add Tetris game module with core logic and UI integration - Implement TetrisGame class for game logic, including piece movement, rotation, and line clearing. - Create TetrisModule class to manage game state, input handling, and rendering on OLED display. - Introduce high score tracking with persistence and optional mesh broadcasting. - Define UI states for title, playing, paused, game over, and high scores. - Implement input handling for game controls and state transitions. - Add rendering functions for the game board, high scores, and title screen. * feat(snake): add snake graphics and update display logic in SnakeModule * Prompt for Initials for Tetris, too * refactor games module * Games refactor * hub75 native double buffer * Games tuning * Make joystick repeat events on held button * Add clouds and colors * Fix breakout and more color * difficulty tuning * trunk * refactor game announcements, etc * Scale chirpy gravity as game speed increases * Portduino, check for hub75 display before reading in hub75 options * Final(?) games tuning * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Properly ignore input when games screen not shown * Fail gracefully when HUB75 is selected but not supported. --------- Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Ixitxachitl <kramerfm@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
GitHub
Thomas Göttgens
coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Ixitxachitl
Copilot Autofix powered by AI
parent
d3d02af817
commit
8d1fbbf55f
@@ -0,0 +1,100 @@
|
||||
#include "TestUtil.h"
|
||||
#include "modules/games/ChirpyRunner.h"
|
||||
#include <unity.h>
|
||||
|
||||
// Pure-logic tests for ChirpyRunnerGame: initial state, jump lifts Chirpy off the ground,
|
||||
// obstacles spawn and a collision ends the run, and a dead game is inert. No device globals.
|
||||
|
||||
static const uint32_t kSeed = 0xC0FFEEu;
|
||||
|
||||
void test_reset_initialState()
|
||||
{
|
||||
ChirpyRunnerGame game;
|
||||
game.reset(kSeed);
|
||||
TEST_ASSERT_TRUE(game.isPlaying());
|
||||
TEST_ASSERT_EQUAL_UINT32(0, game.score());
|
||||
TEST_ASSERT_TRUE(game.onGround());
|
||||
TEST_ASSERT_EQUAL_INT16(ChirpyRunnerGame::GROUND_Y - ChirpyRunnerGame::CHIRPY_H, game.chirpyY());
|
||||
}
|
||||
|
||||
void test_jump_liftsChirpy()
|
||||
{
|
||||
ChirpyRunnerGame game;
|
||||
game.reset(kSeed);
|
||||
const int16_t groundY = game.chirpyY();
|
||||
game.jump();
|
||||
game.step();
|
||||
TEST_ASSERT_FALSE(game.onGround());
|
||||
TEST_ASSERT_TRUE(game.chirpyY() < groundY); // rose above the ground rest position
|
||||
}
|
||||
|
||||
void test_jump_ignoredWhileAirborne()
|
||||
{
|
||||
ChirpyRunnerGame game;
|
||||
game.reset(kSeed);
|
||||
game.jump();
|
||||
game.step();
|
||||
const int16_t yAfterFirst = game.chirpyY();
|
||||
// A second jump mid-air must not re-launch: after another step Chirpy keeps descending toward
|
||||
// the ground under gravity rather than shooting back up.
|
||||
game.jump();
|
||||
game.step();
|
||||
// Not asserting exact physics, only that we're still airborne and moving as one arc, not reset.
|
||||
TEST_ASSERT_FALSE(game.onGround());
|
||||
(void)yAfterFirst;
|
||||
}
|
||||
|
||||
void test_obstacleSpawnsAndCollisionEndsGame()
|
||||
{
|
||||
ChirpyRunnerGame game;
|
||||
game.reset(kSeed);
|
||||
// One step spawns the first obstacle.
|
||||
game.step();
|
||||
bool any = false;
|
||||
for (uint8_t i = 0; i < ChirpyRunnerGame::obstacleSlots(); i++)
|
||||
any = any || game.obstacleActive(i);
|
||||
TEST_ASSERT_TRUE(any);
|
||||
|
||||
// Never jumping, an obstacle must reach grounded Chirpy and end the run.
|
||||
int steps = 0;
|
||||
while (game.isPlaying() && steps < 2000) {
|
||||
game.step();
|
||||
steps++;
|
||||
}
|
||||
TEST_ASSERT_FALSE(game.isPlaying());
|
||||
}
|
||||
|
||||
void test_deadGame_stepIsNoOp()
|
||||
{
|
||||
ChirpyRunnerGame game;
|
||||
game.reset(kSeed);
|
||||
int steps = 0;
|
||||
while (game.isPlaying() && steps < 2000) {
|
||||
game.step();
|
||||
steps++;
|
||||
}
|
||||
TEST_ASSERT_FALSE(game.isPlaying());
|
||||
const uint32_t scoreBefore = game.score();
|
||||
TEST_ASSERT_FALSE(game.step()); // stays dead, no further change
|
||||
TEST_ASSERT_EQUAL_UINT32(scoreBefore, game.score());
|
||||
}
|
||||
|
||||
void setUp(void) {}
|
||||
|
||||
void tearDown(void) {}
|
||||
|
||||
extern "C" {
|
||||
void setup()
|
||||
{
|
||||
initializeTestEnvironment();
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_reset_initialState);
|
||||
RUN_TEST(test_jump_liftsChirpy);
|
||||
RUN_TEST(test_jump_ignoredWhileAirborne);
|
||||
RUN_TEST(test_obstacleSpawnsAndCollisionEndsGame);
|
||||
RUN_TEST(test_deadGame_stepIsNoOp);
|
||||
exit(UNITY_END());
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
}
|
||||
Reference in New Issue
Block a user