adds 2048 clone game
styles table reads touch events allows moving tiles allows merging tiles improves tile movement allows merging tiles adds score display implements color edit comments adjust game logic disallows double merges
This commit is contained in:
parent
b4fb8897ac
commit
12617ed1bf
|
@ -259,6 +259,7 @@ set(LVGL_SRC
|
||||||
libs/lvgl/src/lv_objx/lv_cont.c
|
libs/lvgl/src/lv_objx/lv_cont.c
|
||||||
libs/lvgl/src/lv_objx/lv_label.h
|
libs/lvgl/src/lv_objx/lv_label.h
|
||||||
libs/lvgl/src/lv_objx/lv_label.c
|
libs/lvgl/src/lv_objx/lv_label.c
|
||||||
|
libs/lvgl/src/lv_objx/lv_table.c
|
||||||
libs/lvgl/src/lv_themes/lv_theme.c
|
libs/lvgl/src/lv_themes/lv_theme.c
|
||||||
libs/lvgl/src/lv_themes/lv_theme.h
|
libs/lvgl/src/lv_themes/lv_theme.h
|
||||||
libs/lvgl/src/lv_themes/lv_theme_night.h
|
libs/lvgl/src/lv_themes/lv_theme_night.h
|
||||||
|
@ -347,6 +348,7 @@ list(APPEND SOURCE_FILES
|
||||||
displayapp/screens/FirmwareValidation.cpp
|
displayapp/screens/FirmwareValidation.cpp
|
||||||
displayapp/screens/ApplicationList.cpp
|
displayapp/screens/ApplicationList.cpp
|
||||||
displayapp/screens/Notifications.cpp
|
displayapp/screens/Notifications.cpp
|
||||||
|
displayapp/screens/Twos.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
drivers/St7789.cpp
|
drivers/St7789.cpp
|
||||||
drivers/SpiNorFlash.cpp
|
drivers/SpiNorFlash.cpp
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
enum class Apps {None, Launcher, Clock, SysInfo, Meter, Gauge, Brightness, Music, FirmwareValidation, Paint, Paddle, Notifications};
|
enum class Apps {None, Launcher, Clock, SysInfo, Meter, Gauge, Brightness, Music, FirmwareValidation, Paint, Paddle, Notifications, Twos};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "displayapp/screens/Notifications.h"
|
#include "displayapp/screens/Notifications.h"
|
||||||
#include "displayapp/screens/SystemInfo.h"
|
#include "displayapp/screens/SystemInfo.h"
|
||||||
#include "displayapp/screens/Tile.h"
|
#include "displayapp/screens/Tile.h"
|
||||||
|
#include "displayapp/screens/Twos.h"
|
||||||
#include "drivers/Cst816s.h"
|
#include "drivers/Cst816s.h"
|
||||||
#include "drivers/St7789.h"
|
#include "drivers/St7789.h"
|
||||||
#include "drivers/Watchdog.h"
|
#include "drivers/Watchdog.h"
|
||||||
|
@ -202,6 +203,7 @@ void DisplayApp::RunningState() {
|
||||||
// case Apps::Test: currentScreen.reset(new Screens::Message(this)); break;
|
// case Apps::Test: currentScreen.reset(new Screens::Message(this)); break;
|
||||||
case Apps::SysInfo: currentScreen.reset(new Screens::SystemInfo(this, dateTimeController, batteryController, brightnessController, bleController, watchdog)); break;
|
case Apps::SysInfo: currentScreen.reset(new Screens::SystemInfo(this, dateTimeController, batteryController, brightnessController, bleController, watchdog)); break;
|
||||||
case Apps::Meter: currentScreen.reset(new Screens::Meter(this)); break;
|
case Apps::Meter: currentScreen.reset(new Screens::Meter(this)); break;
|
||||||
|
case Apps::Twos: currentScreen.reset(new Screens::Twos(this)); break;
|
||||||
case Apps::Gauge: currentScreen.reset(new Screens::Gauge(this)); break;
|
case Apps::Gauge: currentScreen.reset(new Screens::Gauge(this)); break;
|
||||||
case Apps::Paint: currentScreen.reset(new Screens::InfiniPaint(this, lvgl)); break;
|
case Apps::Paint: currentScreen.reset(new Screens::InfiniPaint(this, lvgl)); break;
|
||||||
case Apps::Paddle: currentScreen.reset(new Screens::Paddle(this, lvgl)); break;
|
case Apps::Paddle: currentScreen.reset(new Screens::Paddle(this, lvgl)); break;
|
||||||
|
|
|
@ -61,7 +61,7 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
|
||||||
{Symbols::paintbrush, Apps::Paint},
|
{Symbols::paintbrush, Apps::Paint},
|
||||||
{Symbols::info, Apps::Notifications},
|
{Symbols::info, Apps::Notifications},
|
||||||
{Symbols::paddle, Apps::Paddle},
|
{Symbols::paddle, Apps::Paddle},
|
||||||
{Symbols::none, Apps::None}
|
{"2", Apps::Twos}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
271
src/displayapp/screens/Twos.cpp
Normal file
271
src/displayapp/screens/Twos.cpp
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
#include "Twos.h"
|
||||||
|
#include <lvgl/lvgl.h>
|
||||||
|
#include <string>
|
||||||
|
#include <charconv>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
|
extern lv_font_t jetbrains_mono_bold_20;
|
||||||
|
|
||||||
|
Twos::Twos(Pinetime::Applications::DisplayApp *app) : Screen(app) {
|
||||||
|
|
||||||
|
// create styles to apply to different valued tiles
|
||||||
|
static lv_style_t style_cell1;
|
||||||
|
lv_style_copy(&style_cell1, &lv_style_plain);
|
||||||
|
style_cell1.body.border.width = 1;
|
||||||
|
style_cell1.text.font = &jetbrains_mono_bold_20;
|
||||||
|
style_cell1.body.padding.top = 16;
|
||||||
|
style_cell1.body.padding.bottom = 16;
|
||||||
|
style_cell1.body.main_color = LV_COLOR_MAKE(214, 197, 165);
|
||||||
|
style_cell1.body.grad_color = LV_COLOR_MAKE(214, 197, 165);
|
||||||
|
style_cell1.text.color = LV_COLOR_BLACK;
|
||||||
|
|
||||||
|
static lv_style_t style_cell2;
|
||||||
|
lv_style_copy(&style_cell2, &style_cell1);
|
||||||
|
style_cell2.body.main_color = LV_COLOR_MAKE(209, 146, 92);
|
||||||
|
style_cell2.body.grad_color = LV_COLOR_MAKE(209, 146, 92);
|
||||||
|
style_cell2.text.color = LV_COLOR_WHITE;
|
||||||
|
|
||||||
|
static lv_style_t style_cell3;
|
||||||
|
lv_style_copy(&style_cell3, &style_cell2);
|
||||||
|
style_cell3.body.main_color = LV_COLOR_MAKE(246, 94, 59);
|
||||||
|
style_cell3.body.grad_color = LV_COLOR_MAKE(246, 94, 59);
|
||||||
|
|
||||||
|
static lv_style_t style_cell4;
|
||||||
|
lv_style_copy(&style_cell4, &style_cell3);
|
||||||
|
style_cell4.body.main_color = LV_COLOR_MAKE(212, 170, 28);
|
||||||
|
style_cell4.body.grad_color = LV_COLOR_MAKE(212, 170, 28);
|
||||||
|
|
||||||
|
// format grid display
|
||||||
|
gridDisplay = lv_table_create(lv_scr_act(), nullptr);
|
||||||
|
lv_table_set_style(gridDisplay, LV_TABLE_STYLE_CELL1, &style_cell1);
|
||||||
|
lv_table_set_style(gridDisplay, LV_TABLE_STYLE_CELL2, &style_cell2);
|
||||||
|
lv_table_set_style(gridDisplay, LV_TABLE_STYLE_CELL3, &style_cell3);
|
||||||
|
lv_table_set_style(gridDisplay, LV_TABLE_STYLE_CELL4, &style_cell4);
|
||||||
|
lv_table_set_col_cnt(gridDisplay, 4);
|
||||||
|
lv_table_set_row_cnt(gridDisplay, 4);
|
||||||
|
lv_table_set_col_width(gridDisplay, 0, LV_HOR_RES/4);
|
||||||
|
lv_table_set_col_width(gridDisplay, 1, LV_HOR_RES/4);
|
||||||
|
lv_table_set_col_width(gridDisplay, 2, LV_HOR_RES/4);
|
||||||
|
lv_table_set_col_width(gridDisplay, 3, LV_HOR_RES/4);
|
||||||
|
lv_obj_align(gridDisplay, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
|
||||||
|
|
||||||
|
// initialize grid
|
||||||
|
for(int row = 0; row < 4; row++) {
|
||||||
|
for(int col = 0; col < 4; col++) {
|
||||||
|
grid[row][col].value = 0;
|
||||||
|
lv_table_set_cell_type(gridDisplay, row, col, 2);
|
||||||
|
lv_table_set_cell_align(gridDisplay, row, col, LV_LABEL_ALIGN_CENTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
placeNewTile();
|
||||||
|
placeNewTile();
|
||||||
|
|
||||||
|
// format score text
|
||||||
|
scoreText = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_width(scoreText, LV_HOR_RES);
|
||||||
|
lv_label_set_align(scoreText, LV_ALIGN_IN_LEFT_MID);
|
||||||
|
lv_obj_align(scoreText, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||||
|
lv_label_set_text(scoreText, ("Score: " + std::to_string(score)).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
Twos::~Twos() {
|
||||||
|
lv_obj_clean(lv_scr_act());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Twos::Refresh() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Twos::OnButtonPushed() {
|
||||||
|
running = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Twos::placeNewTile() {
|
||||||
|
std::vector< std::pair <int,int> > availableCells;
|
||||||
|
for(int row = 0; row < 4; row++) {
|
||||||
|
for(int col = 0; col < 4; col++) {
|
||||||
|
if(!grid[row][col].value) {
|
||||||
|
availableCells.push_back(std::make_pair(row, col));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableCells.size() == 0) {
|
||||||
|
return false; // game lost
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = availableCells.cbegin();
|
||||||
|
int random = rand() % availableCells.size();
|
||||||
|
std::advance(it, random);
|
||||||
|
std::pair <int,int> newCell = *it;
|
||||||
|
|
||||||
|
if ((rand() % 100) < 90) grid[newCell.first][newCell.second].value = 2;
|
||||||
|
else grid[newCell.first][newCell.second].value = 4;
|
||||||
|
updateGridDisplay(grid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Twos::tryMerge(Tile grid[][4], int &newRow, int &newCol, int oldRow, int oldCol) {
|
||||||
|
if((grid[newRow][newCol].value == grid[oldRow][oldCol].value)) {
|
||||||
|
if((newCol != oldCol) || (newRow != oldRow)) {
|
||||||
|
if(!grid[newRow][newCol].merged) {
|
||||||
|
unsigned int newVal = grid[oldRow][oldCol].value *= 2;
|
||||||
|
grid[newRow][newCol].value = newVal;
|
||||||
|
score += newVal;
|
||||||
|
lv_label_set_text(scoreText, ("Score: " + std::to_string(score)).c_str());
|
||||||
|
grid[oldRow][oldCol].value = 0;
|
||||||
|
grid[newRow][newCol].merged = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Twos::tryMove(Tile grid[][4], int newRow, int newCol, int oldRow, int oldCol) {
|
||||||
|
if(((newCol >= 0) && (newCol != oldCol)) || ((newRow >= 0) && (newRow != oldRow))) {
|
||||||
|
grid[newRow][newCol].value = grid[oldRow][oldCol].value;
|
||||||
|
grid[oldRow][oldCol].value = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||||
|
bool validMove;
|
||||||
|
validMove = false;
|
||||||
|
for(int row = 0; row < 4; row++) {
|
||||||
|
for(int col = 0; col < 4; col++) {
|
||||||
|
grid[row][col].merged = false; // reinitialize merge state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch(event) {
|
||||||
|
case TouchEvents::SwipeLeft:
|
||||||
|
for(int col = 1; col < 4; col++) { // ignore tiles already on far left
|
||||||
|
for(int row = 0; row < 4; row++) {
|
||||||
|
if(grid[row][col].value) {
|
||||||
|
int newCol = -1;
|
||||||
|
for(int potentialNewCol = col - 1; potentialNewCol >= 0; potentialNewCol--) {
|
||||||
|
if(!grid[row][potentialNewCol].value) {
|
||||||
|
newCol = potentialNewCol;
|
||||||
|
}
|
||||||
|
else { // blocked by another tile
|
||||||
|
if(tryMerge(grid, row, potentialNewCol, row, col)) validMove = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(tryMove(grid, row, newCol, row, col)) validMove = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validMove) {
|
||||||
|
placeNewTile();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case TouchEvents::SwipeRight:
|
||||||
|
for(int col = 2; col >= 0; col--) { // ignore tiles already on far right
|
||||||
|
for(int row = 0; row < 4; row++) {
|
||||||
|
if(grid[row][col].value) {
|
||||||
|
int newCol = -1;
|
||||||
|
for(int potentialNewCol = col + 1; potentialNewCol < 4; potentialNewCol++) {
|
||||||
|
if(!grid[row][potentialNewCol].value) {
|
||||||
|
newCol = potentialNewCol;
|
||||||
|
}
|
||||||
|
else { // blocked by another tile
|
||||||
|
if(tryMerge(grid, row, potentialNewCol, row, col)) validMove = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(tryMove(grid, row, newCol, row, col)) validMove = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validMove) {
|
||||||
|
placeNewTile();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case TouchEvents::SwipeUp:
|
||||||
|
for(int row = 1; row < 4; row++) { // ignore tiles already on top
|
||||||
|
for(int col = 0; col < 4; col++) {
|
||||||
|
if(grid[row][col].value) {
|
||||||
|
int newRow = -1;
|
||||||
|
for(int potentialNewRow = row - 1; potentialNewRow >= 0; potentialNewRow--) {
|
||||||
|
if(!grid[potentialNewRow][col].value) {
|
||||||
|
newRow = potentialNewRow;
|
||||||
|
}
|
||||||
|
else { // blocked by another tile
|
||||||
|
if(tryMerge(grid, potentialNewRow, col, row, col)) validMove = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(tryMove(grid, newRow, col, row, col)) validMove = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validMove) {
|
||||||
|
placeNewTile();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case TouchEvents::SwipeDown:
|
||||||
|
for(int row = 2; row >=0; row--) { // ignore tiles already on bottom
|
||||||
|
for(int col = 0; col < 4; col++) {
|
||||||
|
if(grid[row][col].value) {
|
||||||
|
int newRow = -1;
|
||||||
|
for(int potentialNewRow = row + 1; potentialNewRow < 4; potentialNewRow++) {
|
||||||
|
if(!grid[potentialNewRow][col].value) {
|
||||||
|
newRow = potentialNewRow;
|
||||||
|
}
|
||||||
|
else { // blocked by another tile
|
||||||
|
if(tryMerge(grid, potentialNewRow, col, row, col)) validMove = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(tryMove(grid, newRow, col, row, col)) validMove = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validMove) {
|
||||||
|
placeNewTile();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Twos::updateGridDisplay(Tile grid[][4]) {
|
||||||
|
for(int row = 0; row < 4; row++) {
|
||||||
|
for(int col = 0; col < 4; col++) {
|
||||||
|
if (grid[row][col].value) {
|
||||||
|
lv_table_set_cell_value(gridDisplay, row, col, (std::to_string(grid[row][col].value)).c_str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lv_table_set_cell_value(gridDisplay, row, col, "");
|
||||||
|
}
|
||||||
|
switch (grid[row][col].value) {
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
lv_table_set_cell_type(gridDisplay, row, col, 1);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
case 16:
|
||||||
|
lv_table_set_cell_type(gridDisplay, row, col, 2);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
case 64:
|
||||||
|
lv_table_set_cell_type(gridDisplay, row, col, 3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
lv_table_set_cell_type(gridDisplay, row, col, 4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
src/displayapp/screens/Twos.h
Normal file
34
src/displayapp/screens/Twos.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lvgl/src/lv_core/lv_obj.h>
|
||||||
|
#include "Screen.h"
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Applications {
|
||||||
|
struct Tile {
|
||||||
|
bool merged = false;
|
||||||
|
unsigned int value = 0;
|
||||||
|
};
|
||||||
|
namespace Screens {
|
||||||
|
class Twos : public Screen {
|
||||||
|
public:
|
||||||
|
Twos(DisplayApp* app);
|
||||||
|
~Twos() override;
|
||||||
|
bool Refresh() override;
|
||||||
|
bool OnButtonPushed() override;
|
||||||
|
bool OnTouchEvent(TouchEvents event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool running = true;
|
||||||
|
lv_obj_t *scoreText;
|
||||||
|
lv_obj_t *gridDisplay;
|
||||||
|
Tile grid[4][4];
|
||||||
|
unsigned int score = 0;
|
||||||
|
void updateGridDisplay(Tile grid[][4]);
|
||||||
|
bool tryMerge(Tile grid[][4], int &newRow, int &newCol, int oldRow, int oldCol);
|
||||||
|
bool tryMove(Tile grid[][4], int newRow, int newCol, int oldRow, int oldCol);
|
||||||
|
bool placeNewTile();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue