mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 15:17:48 +01:00
snake app
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
#include "cardboy/apps/gameboy_app.hpp"
|
#include "cardboy/apps/gameboy_app.hpp"
|
||||||
#include "cardboy/apps/menu_app.hpp"
|
#include "cardboy/apps/menu_app.hpp"
|
||||||
#include "cardboy/apps/settings_app.hpp"
|
#include "cardboy/apps/settings_app.hpp"
|
||||||
|
#include "cardboy/apps/snake_app.hpp"
|
||||||
#include "cardboy/apps/tetris_app.hpp"
|
#include "cardboy/apps/tetris_app.hpp"
|
||||||
#include "cardboy/backend/esp_backend.hpp"
|
#include "cardboy/backend/esp_backend.hpp"
|
||||||
#include "cardboy/sdk/app_system.hpp"
|
#include "cardboy/sdk/app_system.hpp"
|
||||||
@@ -234,6 +235,7 @@ extern "C" void app_main() {
|
|||||||
system.registerApp(apps::createMenuAppFactory());
|
system.registerApp(apps::createMenuAppFactory());
|
||||||
system.registerApp(apps::createSettingsAppFactory());
|
system.registerApp(apps::createSettingsAppFactory());
|
||||||
system.registerApp(apps::createClockAppFactory());
|
system.registerApp(apps::createClockAppFactory());
|
||||||
|
system.registerApp(apps::createSnakeAppFactory());
|
||||||
system.registerApp(apps::createTetrisAppFactory());
|
system.registerApp(apps::createTetrisAppFactory());
|
||||||
system.registerApp(apps::createGameboyAppFactory());
|
system.registerApp(apps::createGameboyAppFactory());
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,5 @@ add_subdirectory(menu)
|
|||||||
add_subdirectory(clock)
|
add_subdirectory(clock)
|
||||||
add_subdirectory(settings)
|
add_subdirectory(settings)
|
||||||
add_subdirectory(gameboy)
|
add_subdirectory(gameboy)
|
||||||
|
add_subdirectory(snake)
|
||||||
add_subdirectory(tetris)
|
add_subdirectory(tetris)
|
||||||
|
|||||||
9
Firmware/sdk/apps/snake/CMakeLists.txt
Normal file
9
Firmware/sdk/apps/snake/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
target_sources(cardboy_apps
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/snake_app.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(cardboy_apps
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
11
Firmware/sdk/apps/snake/include/cardboy/apps/snake_app.hpp
Normal file
11
Firmware/sdk/apps/snake/include/cardboy/apps/snake_app.hpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cardboy/sdk/app_framework.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace apps {
|
||||||
|
|
||||||
|
std::unique_ptr<cardboy::sdk::IAppFactory> createSnakeAppFactory();
|
||||||
|
|
||||||
|
} // namespace apps
|
||||||
432
Firmware/sdk/apps/snake/src/snake_app.cpp
Normal file
432
Firmware/sdk/apps/snake/src/snake_app.cpp
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
#include "cardboy/apps/snake_app.hpp"
|
||||||
|
|
||||||
|
#include "cardboy/apps/menu_app.hpp"
|
||||||
|
#include "cardboy/gfx/font16x8.hpp"
|
||||||
|
#include "cardboy/sdk/app_framework.hpp"
|
||||||
|
#include "cardboy/sdk/app_system.hpp"
|
||||||
|
#include "cardboy/sdk/display_spec.hpp"
|
||||||
|
#include "cardboy/sdk/input_state.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <deque>
|
||||||
|
#include <random>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace apps {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using cardboy::sdk::AppButtonEvent;
|
||||||
|
using cardboy::sdk::AppContext;
|
||||||
|
using cardboy::sdk::AppEvent;
|
||||||
|
using cardboy::sdk::AppEventType;
|
||||||
|
using cardboy::sdk::AppTimerHandle;
|
||||||
|
using cardboy::sdk::InputState;
|
||||||
|
|
||||||
|
constexpr char kSnakeAppName[] = "Snake";
|
||||||
|
|
||||||
|
constexpr int kBoardWidth = 32;
|
||||||
|
constexpr int kBoardHeight = 20;
|
||||||
|
constexpr int kCellSize = 10;
|
||||||
|
constexpr int kInitialSnakeLength = 5;
|
||||||
|
constexpr int kScorePerFood = 10;
|
||||||
|
constexpr int kMinMoveIntervalMs = 80;
|
||||||
|
constexpr int kBaseMoveIntervalMs = 220;
|
||||||
|
constexpr int kIntervalSpeedupPerSegment = 4;
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
|
||||||
|
bool operator==(const Point& other) const { return x == other.x && y == other.y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Direction { Up, Down, Left, Right };
|
||||||
|
|
||||||
|
[[nodiscard]] std::uint32_t randomSeed(AppContext& ctx) {
|
||||||
|
if (auto* rnd = ctx.random())
|
||||||
|
return rnd->nextUint32();
|
||||||
|
static std::random_device rd;
|
||||||
|
return rd();
|
||||||
|
}
|
||||||
|
|
||||||
|
class SnakeGame {
|
||||||
|
public:
|
||||||
|
explicit SnakeGame(AppContext& ctx) : context(ctx), framebuffer(ctx.framebuffer) {
|
||||||
|
rng.seed(randomSeed(context));
|
||||||
|
loadHighScore();
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onStart() {
|
||||||
|
scheduleMoveTimer();
|
||||||
|
dirty = true;
|
||||||
|
renderIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onStop() { cancelMoveTimer(); }
|
||||||
|
|
||||||
|
void handleEvent(const AppEvent& event) {
|
||||||
|
switch (event.type) {
|
||||||
|
case AppEventType::Button:
|
||||||
|
handleButtons(event.button);
|
||||||
|
break;
|
||||||
|
case AppEventType::Timer:
|
||||||
|
handleTimer(event.timer.handle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
renderIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AppContext& context;
|
||||||
|
typename AppContext::Framebuffer& framebuffer;
|
||||||
|
|
||||||
|
std::deque<Point> snake;
|
||||||
|
Point food{};
|
||||||
|
Direction direction = Direction::Right;
|
||||||
|
Direction queuedDirection = Direction::Right;
|
||||||
|
bool paused = false;
|
||||||
|
bool gameOver = false;
|
||||||
|
bool dirty = false;
|
||||||
|
int score = 0;
|
||||||
|
int highScore = 0;
|
||||||
|
AppTimerHandle moveTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
|
std::mt19937 rng;
|
||||||
|
|
||||||
|
void handleButtons(const AppButtonEvent& evt) {
|
||||||
|
const auto& cur = evt.current;
|
||||||
|
const auto& prev = evt.previous;
|
||||||
|
if (cur.b && !prev.b) {
|
||||||
|
context.requestAppSwitchByName(kMenuAppName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur.select && !prev.select) {
|
||||||
|
reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur.start && !prev.start) {
|
||||||
|
if (gameOver)
|
||||||
|
reset();
|
||||||
|
else {
|
||||||
|
paused = !paused;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameOver)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (cur.up && !prev.up)
|
||||||
|
queueDirection(Direction::Up);
|
||||||
|
else if (cur.down && !prev.down)
|
||||||
|
queueDirection(Direction::Down);
|
||||||
|
else if (cur.left && !prev.left)
|
||||||
|
queueDirection(Direction::Left);
|
||||||
|
else if (cur.right && !prev.right)
|
||||||
|
queueDirection(Direction::Right);
|
||||||
|
|
||||||
|
if (cur.a && !prev.a && !paused)
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleTimer(AppTimerHandle handle) {
|
||||||
|
if (handle == moveTimer && !paused && !gameOver)
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
cancelMoveTimer();
|
||||||
|
|
||||||
|
snake.clear();
|
||||||
|
const int centerX = kBoardWidth / 2;
|
||||||
|
const int centerY = kBoardHeight / 2;
|
||||||
|
for (int i = 0; i < kInitialSnakeLength; ++i)
|
||||||
|
snake.push_back(Point{centerX - i, centerY});
|
||||||
|
|
||||||
|
direction = Direction::Right;
|
||||||
|
queuedDirection = Direction::Right;
|
||||||
|
paused = false;
|
||||||
|
gameOver = false;
|
||||||
|
score = 0;
|
||||||
|
dirty = true;
|
||||||
|
|
||||||
|
if (!spawnFood())
|
||||||
|
onGameOver();
|
||||||
|
|
||||||
|
scheduleMoveTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void advance() {
|
||||||
|
direction = queuedDirection;
|
||||||
|
Point nextHead = snake.front();
|
||||||
|
switch (direction) {
|
||||||
|
case Direction::Up:
|
||||||
|
--nextHead.y;
|
||||||
|
break;
|
||||||
|
case Direction::Down:
|
||||||
|
++nextHead.y;
|
||||||
|
break;
|
||||||
|
case Direction::Left:
|
||||||
|
--nextHead.x;
|
||||||
|
break;
|
||||||
|
case Direction::Right:
|
||||||
|
++nextHead.x;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCollision(nextHead)) {
|
||||||
|
onGameOver();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snake.push_front(nextHead);
|
||||||
|
if (nextHead == food) {
|
||||||
|
score += kScorePerFood;
|
||||||
|
updateHighScore();
|
||||||
|
if (!spawnFood()) {
|
||||||
|
onGameOver();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scheduleMoveTimer();
|
||||||
|
if (auto* buzzer = context.buzzer())
|
||||||
|
buzzer->beepMove();
|
||||||
|
} else {
|
||||||
|
snake.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isCollision(const Point& nextHead) const {
|
||||||
|
if (nextHead.x < 0 || nextHead.x >= kBoardWidth || nextHead.y < 0 || nextHead.y >= kBoardHeight)
|
||||||
|
return true;
|
||||||
|
return std::find(snake.begin(), snake.end(), nextHead) != snake.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onGameOver() {
|
||||||
|
if (gameOver)
|
||||||
|
return;
|
||||||
|
gameOver = true;
|
||||||
|
cancelMoveTimer();
|
||||||
|
dirty = true;
|
||||||
|
if (auto* buzzer = context.buzzer())
|
||||||
|
buzzer->beepGameOver();
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueDirection(Direction next) {
|
||||||
|
if (isOpposite(direction, next) || isOpposite(queuedDirection, next))
|
||||||
|
return;
|
||||||
|
queuedDirection = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static bool isOpposite(Direction a, Direction b) {
|
||||||
|
if ((a == Direction::Up && b == Direction::Down) || (a == Direction::Down && b == Direction::Up))
|
||||||
|
return true;
|
||||||
|
if ((a == Direction::Left && b == Direction::Right) || (a == Direction::Right && b == Direction::Left))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool spawnFood() {
|
||||||
|
std::vector<Point> freeCells;
|
||||||
|
freeCells.reserve(kBoardWidth * kBoardHeight - static_cast<int>(snake.size()));
|
||||||
|
for (int y = 0; y < kBoardHeight; ++y) {
|
||||||
|
for (int x = 0; x < kBoardWidth; ++x) {
|
||||||
|
Point p{x, y};
|
||||||
|
if (std::find(snake.begin(), snake.end(), p) == snake.end())
|
||||||
|
freeCells.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (freeCells.empty())
|
||||||
|
return false;
|
||||||
|
std::uniform_int_distribution<std::size_t> dist(0, freeCells.size() - 1);
|
||||||
|
food = freeCells[dist(rng)];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scheduleMoveTimer() {
|
||||||
|
cancelMoveTimer();
|
||||||
|
const std::uint32_t interval = currentInterval();
|
||||||
|
moveTimer = context.scheduleRepeatingTimer(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancelMoveTimer() {
|
||||||
|
if (moveTimer != cardboy::sdk::kInvalidAppTimer) {
|
||||||
|
context.cancelTimer(moveTimer);
|
||||||
|
moveTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::uint32_t currentInterval() const {
|
||||||
|
int interval = kBaseMoveIntervalMs - static_cast<int>(snake.size()) * kIntervalSpeedupPerSegment;
|
||||||
|
if (interval < kMinMoveIntervalMs)
|
||||||
|
interval = kMinMoveIntervalMs;
|
||||||
|
return static_cast<std::uint32_t>(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateHighScore() {
|
||||||
|
if (score <= highScore)
|
||||||
|
return;
|
||||||
|
highScore = score;
|
||||||
|
if (auto* storage = context.storage())
|
||||||
|
storage->writeUint32("snake", "best", static_cast<std::uint32_t>(highScore));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadHighScore() {
|
||||||
|
if (auto* storage = context.storage()) {
|
||||||
|
std::uint32_t stored = 0;
|
||||||
|
if (storage->readUint32("snake", "best", stored))
|
||||||
|
highScore = static_cast<int>(stored);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderIfNeeded() {
|
||||||
|
if (!dirty)
|
||||||
|
return;
|
||||||
|
dirty = false;
|
||||||
|
|
||||||
|
framebuffer.frameReady();
|
||||||
|
framebuffer.clear(false);
|
||||||
|
|
||||||
|
drawBoard();
|
||||||
|
drawFood();
|
||||||
|
drawSnake();
|
||||||
|
drawHud();
|
||||||
|
|
||||||
|
framebuffer.sendFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int boardOriginX() const {
|
||||||
|
return (cardboy::sdk::kDisplayWidth - kBoardWidth * kCellSize) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int boardOriginY() const {
|
||||||
|
const int centered = (cardboy::sdk::kDisplayHeight - kBoardHeight * kCellSize) / 2;
|
||||||
|
return std::max(24, centered);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawBoard() {
|
||||||
|
const int originX = boardOriginX();
|
||||||
|
const int originY = boardOriginY();
|
||||||
|
const int width = kBoardWidth * kCellSize;
|
||||||
|
const int height = kBoardHeight * kCellSize;
|
||||||
|
|
||||||
|
const int x0 = originX;
|
||||||
|
const int y0 = originY;
|
||||||
|
const int x1 = originX + width - 1;
|
||||||
|
const int y1 = originY + height - 1;
|
||||||
|
for (int x = x0; x <= x1; ++x) {
|
||||||
|
framebuffer.drawPixel(x, y0, true);
|
||||||
|
framebuffer.drawPixel(x, y1, true);
|
||||||
|
}
|
||||||
|
for (int y = y0; y <= y1; ++y) {
|
||||||
|
framebuffer.drawPixel(x0, y, true);
|
||||||
|
framebuffer.drawPixel(x1, y, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawSnake() {
|
||||||
|
if (snake.empty())
|
||||||
|
return;
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (const auto& segment: snake) {
|
||||||
|
drawSnakeSegment(segment, index == 0);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawSnakeSegment(const Point& segment, bool head) {
|
||||||
|
const int originX = boardOriginX() + segment.x * kCellSize;
|
||||||
|
const int originY = boardOriginY() + segment.y * kCellSize;
|
||||||
|
for (int dy = 0; dy < kCellSize; ++dy) {
|
||||||
|
for (int dx = 0; dx < kCellSize; ++dx) {
|
||||||
|
const bool border = dx == 0 || dy == 0 || dx == kCellSize - 1 || dy == kCellSize - 1;
|
||||||
|
bool fill = ((dx + dy) & 0x1) == 0;
|
||||||
|
if (head)
|
||||||
|
fill = true;
|
||||||
|
const bool on = border || fill;
|
||||||
|
framebuffer.drawPixel(originX + dx, originY + dy, on);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawFood() {
|
||||||
|
const int cx = boardOriginX() + food.x * kCellSize + kCellSize / 2;
|
||||||
|
const int cy = boardOriginY() + food.y * kCellSize + kCellSize / 2;
|
||||||
|
const int r = std::max(2, kCellSize / 2 - 1);
|
||||||
|
for (int dy = -r; dy <= r; ++dy) {
|
||||||
|
for (int dx = -r; dx <= r; ++dx) {
|
||||||
|
if (std::abs(dx) + std::abs(dy) <= r)
|
||||||
|
framebuffer.drawPixel(cx + dx, cy + dy, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawHud() {
|
||||||
|
const int margin = 12;
|
||||||
|
const int textY = 8;
|
||||||
|
const std::string scoreStr = "SCORE " + std::to_string(score);
|
||||||
|
const std::string bestStr = "BEST " + std::to_string(highScore);
|
||||||
|
font16x8::drawText(framebuffer, margin, textY, scoreStr, 1, true, 1);
|
||||||
|
const int bestX = cardboy::sdk::kDisplayWidth - font16x8::measureText(bestStr, 1, 1) - margin;
|
||||||
|
font16x8::drawText(framebuffer, bestX, textY, bestStr, 1, true, 1);
|
||||||
|
|
||||||
|
const int footerY = cardboy::sdk::kDisplayHeight - 24;
|
||||||
|
const std::string menuStr = "B MENU";
|
||||||
|
const std::string selectStr = "SELECT RESET";
|
||||||
|
const std::string startStr = "START PAUSE";
|
||||||
|
const int selectX = (cardboy::sdk::kDisplayWidth - font16x8::measureText(selectStr, 1, 1)) / 2;
|
||||||
|
const int startX = cardboy::sdk::kDisplayWidth - font16x8::measureText(startStr, 1, 1) - margin;
|
||||||
|
font16x8::drawText(framebuffer, margin, footerY, menuStr, 1, true, 1);
|
||||||
|
font16x8::drawText(framebuffer, selectX, footerY, selectStr, 1, true, 1);
|
||||||
|
font16x8::drawText(framebuffer, startX, footerY, startStr, 1, true, 1);
|
||||||
|
|
||||||
|
if (paused && !gameOver)
|
||||||
|
drawBanner("PAUSED");
|
||||||
|
else if (gameOver)
|
||||||
|
drawBanner("GAME OVER");
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawBanner(std::string_view text) {
|
||||||
|
const int w = font16x8::measureText(text, 2, 1);
|
||||||
|
const int h = font16x8::kGlyphHeight * 2;
|
||||||
|
const int x = (cardboy::sdk::kDisplayWidth - w) / 2;
|
||||||
|
const int y = boardOriginY() + kBoardHeight * kCellSize / 2 - h / 2;
|
||||||
|
for (int yy = -4; yy < h + 4; ++yy)
|
||||||
|
for (int xx = -6; xx < w + 6; ++xx)
|
||||||
|
framebuffer.drawPixel(x + xx, y + yy, yy == -4 || yy == h + 3 || xx == -6 || xx == w + 5);
|
||||||
|
font16x8::drawText(framebuffer, x, y, text, 2, true, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SnakeApp final : public cardboy::sdk::IApp {
|
||||||
|
public:
|
||||||
|
explicit SnakeApp(AppContext& ctx) : game(ctx) {}
|
||||||
|
|
||||||
|
void onStart() override { game.onStart(); }
|
||||||
|
void onStop() override { game.onStop(); }
|
||||||
|
void handleEvent(const AppEvent& event) override { game.handleEvent(event); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SnakeGame game;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SnakeFactory final : public cardboy::sdk::IAppFactory {
|
||||||
|
public:
|
||||||
|
const char* name() const override { return kSnakeAppName; }
|
||||||
|
std::unique_ptr<cardboy::sdk::IApp> create(AppContext& context) override {
|
||||||
|
return std::make_unique<SnakeApp>(context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::unique_ptr<cardboy::sdk::IAppFactory> createSnakeAppFactory() { return std::make_unique<SnakeFactory>(); }
|
||||||
|
|
||||||
|
} // namespace apps
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "cardboy/apps/gameboy_app.hpp"
|
#include "cardboy/apps/gameboy_app.hpp"
|
||||||
#include "cardboy/apps/menu_app.hpp"
|
#include "cardboy/apps/menu_app.hpp"
|
||||||
#include "cardboy/apps/settings_app.hpp"
|
#include "cardboy/apps/settings_app.hpp"
|
||||||
|
#include "cardboy/apps/snake_app.hpp"
|
||||||
#include "cardboy/apps/tetris_app.hpp"
|
#include "cardboy/apps/tetris_app.hpp"
|
||||||
#include "cardboy/backend/desktop_backend.hpp"
|
#include "cardboy/backend/desktop_backend.hpp"
|
||||||
#include "cardboy/sdk/app_system.hpp"
|
#include "cardboy/sdk/app_system.hpp"
|
||||||
@@ -29,6 +30,7 @@ int main() {
|
|||||||
system.registerApp(apps::createSettingsAppFactory());
|
system.registerApp(apps::createSettingsAppFactory());
|
||||||
system.registerApp(apps::createClockAppFactory());
|
system.registerApp(apps::createClockAppFactory());
|
||||||
system.registerApp(apps::createGameboyAppFactory());
|
system.registerApp(apps::createGameboyAppFactory());
|
||||||
|
system.registerApp(apps::createSnakeAppFactory());
|
||||||
system.registerApp(apps::createTetrisAppFactory());
|
system.registerApp(apps::createTetrisAppFactory());
|
||||||
|
|
||||||
system.run();
|
system.run();
|
||||||
|
|||||||
Reference in New Issue
Block a user