Skip to content

Commit 12617ed

Browse files
committed
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
1 parent b4fb889 commit 12617ed

6 files changed

Lines changed: 311 additions & 2 deletions

File tree

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ set(LVGL_SRC
259259
libs/lvgl/src/lv_objx/lv_cont.c
260260
libs/lvgl/src/lv_objx/lv_label.h
261261
libs/lvgl/src/lv_objx/lv_label.c
262+
libs/lvgl/src/lv_objx/lv_table.c
262263
libs/lvgl/src/lv_themes/lv_theme.c
263264
libs/lvgl/src/lv_themes/lv_theme.h
264265
libs/lvgl/src/lv_themes/lv_theme_night.h
@@ -347,6 +348,7 @@ list(APPEND SOURCE_FILES
347348
displayapp/screens/FirmwareValidation.cpp
348349
displayapp/screens/ApplicationList.cpp
349350
displayapp/screens/Notifications.cpp
351+
displayapp/screens/Twos.cpp
350352
main.cpp
351353
drivers/St7789.cpp
352354
drivers/SpiNorFlash.cpp

src/displayapp/Apps.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
namespace Pinetime {
44
namespace Applications {
5-
enum class Apps {None, Launcher, Clock, SysInfo, Meter, Gauge, Brightness, Music, FirmwareValidation, Paint, Paddle, Notifications};
5+
enum class Apps {None, Launcher, Clock, SysInfo, Meter, Gauge, Brightness, Music, FirmwareValidation, Paint, Paddle, Notifications, Twos};
66
}
77
}

src/displayapp/DisplayApp.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "displayapp/screens/Notifications.h"
1818
#include "displayapp/screens/SystemInfo.h"
1919
#include "displayapp/screens/Tile.h"
20+
#include "displayapp/screens/Twos.h"
2021
#include "drivers/Cst816s.h"
2122
#include "drivers/St7789.h"
2223
#include "drivers/Watchdog.h"
@@ -202,6 +203,7 @@ void DisplayApp::RunningState() {
202203
// case Apps::Test: currentScreen.reset(new Screens::Message(this)); break;
203204
case Apps::SysInfo: currentScreen.reset(new Screens::SystemInfo(this, dateTimeController, batteryController, brightnessController, bleController, watchdog)); break;
204205
case Apps::Meter: currentScreen.reset(new Screens::Meter(this)); break;
206+
case Apps::Twos: currentScreen.reset(new Screens::Twos(this)); break;
205207
case Apps::Gauge: currentScreen.reset(new Screens::Gauge(this)); break;
206208
case Apps::Paint: currentScreen.reset(new Screens::InfiniPaint(this, lvgl)); break;
207209
case Apps::Paddle: currentScreen.reset(new Screens::Paddle(this, lvgl)); break;

src/displayapp/screens/ApplicationList.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
6161
{Symbols::paintbrush, Apps::Paint},
6262
{Symbols::info, Apps::Notifications},
6363
{Symbols::paddle, Apps::Paddle},
64-
{Symbols::none, Apps::None}
64+
{"2", Apps::Twos}
6565
}
6666
};
6767

src/displayapp/screens/Twos.cpp

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
#include "Twos.h"
2+
#include <lvgl/lvgl.h>
3+
#include <string>
4+
#include <charconv>
5+
#include <array>
6+
#include <vector>
7+
#include <utility>
8+
9+
using namespace Pinetime::Applications::Screens;
10+
11+
extern lv_font_t jetbrains_mono_bold_20;
12+
13+
Twos::Twos(Pinetime::Applications::DisplayApp *app) : Screen(app) {
14+
15+
// create styles to apply to different valued tiles
16+
static lv_style_t style_cell1;
17+
lv_style_copy(&style_cell1, &lv_style_plain);
18+
style_cell1.body.border.width = 1;
19+
style_cell1.text.font = &jetbrains_mono_bold_20;
20+
style_cell1.body.padding.top = 16;
21+
style_cell1.body.padding.bottom = 16;
22+
style_cell1.body.main_color = LV_COLOR_MAKE(214, 197, 165);
23+
style_cell1.body.grad_color = LV_COLOR_MAKE(214, 197, 165);
24+
style_cell1.text.color = LV_COLOR_BLACK;
25+
26+
static lv_style_t style_cell2;
27+
lv_style_copy(&style_cell2, &style_cell1);
28+
style_cell2.body.main_color = LV_COLOR_MAKE(209, 146, 92);
29+
style_cell2.body.grad_color = LV_COLOR_MAKE(209, 146, 92);
30+
style_cell2.text.color = LV_COLOR_WHITE;
31+
32+
static lv_style_t style_cell3;
33+
lv_style_copy(&style_cell3, &style_cell2);
34+
style_cell3.body.main_color = LV_COLOR_MAKE(246, 94, 59);
35+
style_cell3.body.grad_color = LV_COLOR_MAKE(246, 94, 59);
36+
37+
static lv_style_t style_cell4;
38+
lv_style_copy(&style_cell4, &style_cell3);
39+
style_cell4.body.main_color = LV_COLOR_MAKE(212, 170, 28);
40+
style_cell4.body.grad_color = LV_COLOR_MAKE(212, 170, 28);
41+
42+
// format grid display
43+
gridDisplay = lv_table_create(lv_scr_act(), nullptr);
44+
lv_table_set_style(gridDisplay, LV_TABLE_STYLE_CELL1, &style_cell1);
45+
lv_table_set_style(gridDisplay, LV_TABLE_STYLE_CELL2, &style_cell2);
46+
lv_table_set_style(gridDisplay, LV_TABLE_STYLE_CELL3, &style_cell3);
47+
lv_table_set_style(gridDisplay, LV_TABLE_STYLE_CELL4, &style_cell4);
48+
lv_table_set_col_cnt(gridDisplay, 4);
49+
lv_table_set_row_cnt(gridDisplay, 4);
50+
lv_table_set_col_width(gridDisplay, 0, LV_HOR_RES/4);
51+
lv_table_set_col_width(gridDisplay, 1, LV_HOR_RES/4);
52+
lv_table_set_col_width(gridDisplay, 2, LV_HOR_RES/4);
53+
lv_table_set_col_width(gridDisplay, 3, LV_HOR_RES/4);
54+
lv_obj_align(gridDisplay, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
55+
56+
// initialize grid
57+
for(int row = 0; row < 4; row++) {
58+
for(int col = 0; col < 4; col++) {
59+
grid[row][col].value = 0;
60+
lv_table_set_cell_type(gridDisplay, row, col, 2);
61+
lv_table_set_cell_align(gridDisplay, row, col, LV_LABEL_ALIGN_CENTER);
62+
}
63+
}
64+
placeNewTile();
65+
placeNewTile();
66+
67+
// format score text
68+
scoreText = lv_label_create(lv_scr_act(), nullptr);
69+
lv_obj_set_width(scoreText, LV_HOR_RES);
70+
lv_label_set_align(scoreText, LV_ALIGN_IN_LEFT_MID);
71+
lv_obj_align(scoreText, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
72+
lv_label_set_text(scoreText, ("Score: " + std::to_string(score)).c_str());
73+
}
74+
75+
Twos::~Twos() {
76+
lv_obj_clean(lv_scr_act());
77+
}
78+
79+
bool Twos::Refresh() {
80+
return running;
81+
}
82+
83+
bool Twos::OnButtonPushed() {
84+
running = false;
85+
return true;
86+
}
87+
88+
bool Twos::placeNewTile() {
89+
std::vector< std::pair <int,int> > availableCells;
90+
for(int row = 0; row < 4; row++) {
91+
for(int col = 0; col < 4; col++) {
92+
if(!grid[row][col].value) {
93+
availableCells.push_back(std::make_pair(row, col));
94+
}
95+
}
96+
}
97+
98+
if (availableCells.size() == 0) {
99+
return false; // game lost
100+
}
101+
102+
auto it = availableCells.cbegin();
103+
int random = rand() % availableCells.size();
104+
std::advance(it, random);
105+
std::pair <int,int> newCell = *it;
106+
107+
if ((rand() % 100) < 90) grid[newCell.first][newCell.second].value = 2;
108+
else grid[newCell.first][newCell.second].value = 4;
109+
updateGridDisplay(grid);
110+
return true;
111+
}
112+
113+
bool Twos::tryMerge(Tile grid[][4], int &newRow, int &newCol, int oldRow, int oldCol) {
114+
if((grid[newRow][newCol].value == grid[oldRow][oldCol].value)) {
115+
if((newCol != oldCol) || (newRow != oldRow)) {
116+
if(!grid[newRow][newCol].merged) {
117+
unsigned int newVal = grid[oldRow][oldCol].value *= 2;
118+
grid[newRow][newCol].value = newVal;
119+
score += newVal;
120+
lv_label_set_text(scoreText, ("Score: " + std::to_string(score)).c_str());
121+
grid[oldRow][oldCol].value = 0;
122+
grid[newRow][newCol].merged = true;
123+
return true;
124+
}
125+
}
126+
}
127+
return false;
128+
}
129+
130+
bool Twos::tryMove(Tile grid[][4], int newRow, int newCol, int oldRow, int oldCol) {
131+
if(((newCol >= 0) && (newCol != oldCol)) || ((newRow >= 0) && (newRow != oldRow))) {
132+
grid[newRow][newCol].value = grid[oldRow][oldCol].value;
133+
grid[oldRow][oldCol].value = 0;
134+
return true;
135+
}
136+
return false;
137+
}
138+
139+
bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
140+
bool validMove;
141+
validMove = false;
142+
for(int row = 0; row < 4; row++) {
143+
for(int col = 0; col < 4; col++) {
144+
grid[row][col].merged = false; // reinitialize merge state
145+
}
146+
}
147+
switch(event) {
148+
case TouchEvents::SwipeLeft:
149+
for(int col = 1; col < 4; col++) { // ignore tiles already on far left
150+
for(int row = 0; row < 4; row++) {
151+
if(grid[row][col].value) {
152+
int newCol = -1;
153+
for(int potentialNewCol = col - 1; potentialNewCol >= 0; potentialNewCol--) {
154+
if(!grid[row][potentialNewCol].value) {
155+
newCol = potentialNewCol;
156+
}
157+
else { // blocked by another tile
158+
if(tryMerge(grid, row, potentialNewCol, row, col)) validMove = true;
159+
break;
160+
}
161+
}
162+
if(tryMove(grid, row, newCol, row, col)) validMove = true;
163+
}
164+
}
165+
}
166+
if (validMove) {
167+
placeNewTile();
168+
}
169+
return true;
170+
case TouchEvents::SwipeRight:
171+
for(int col = 2; col >= 0; col--) { // ignore tiles already on far right
172+
for(int row = 0; row < 4; row++) {
173+
if(grid[row][col].value) {
174+
int newCol = -1;
175+
for(int potentialNewCol = col + 1; potentialNewCol < 4; potentialNewCol++) {
176+
if(!grid[row][potentialNewCol].value) {
177+
newCol = potentialNewCol;
178+
}
179+
else { // blocked by another tile
180+
if(tryMerge(grid, row, potentialNewCol, row, col)) validMove = true;
181+
break;
182+
}
183+
}
184+
if(tryMove(grid, row, newCol, row, col)) validMove = true;
185+
}
186+
}
187+
}
188+
if (validMove) {
189+
placeNewTile();
190+
}
191+
return true;
192+
case TouchEvents::SwipeUp:
193+
for(int row = 1; row < 4; row++) { // ignore tiles already on top
194+
for(int col = 0; col < 4; col++) {
195+
if(grid[row][col].value) {
196+
int newRow = -1;
197+
for(int potentialNewRow = row - 1; potentialNewRow >= 0; potentialNewRow--) {
198+
if(!grid[potentialNewRow][col].value) {
199+
newRow = potentialNewRow;
200+
}
201+
else { // blocked by another tile
202+
if(tryMerge(grid, potentialNewRow, col, row, col)) validMove = true;
203+
break;
204+
}
205+
}
206+
if(tryMove(grid, newRow, col, row, col)) validMove = true;
207+
}
208+
}
209+
}
210+
if (validMove) {
211+
placeNewTile();
212+
}
213+
return true;
214+
case TouchEvents::SwipeDown:
215+
for(int row = 2; row >=0; row--) { // ignore tiles already on bottom
216+
for(int col = 0; col < 4; col++) {
217+
if(grid[row][col].value) {
218+
int newRow = -1;
219+
for(int potentialNewRow = row + 1; potentialNewRow < 4; potentialNewRow++) {
220+
if(!grid[potentialNewRow][col].value) {
221+
newRow = potentialNewRow;
222+
}
223+
else { // blocked by another tile
224+
if(tryMerge(grid, potentialNewRow, col, row, col)) validMove = true;
225+
break;
226+
}
227+
}
228+
if(tryMove(grid, newRow, col, row, col)) validMove = true;
229+
}
230+
}
231+
}
232+
if (validMove) {
233+
placeNewTile();
234+
}
235+
return true;
236+
default:
237+
return false;
238+
}
239+
return false;
240+
}
241+
242+
void Twos::updateGridDisplay(Tile grid[][4]) {
243+
for(int row = 0; row < 4; row++) {
244+
for(int col = 0; col < 4; col++) {
245+
if (grid[row][col].value) {
246+
lv_table_set_cell_value(gridDisplay, row, col, (std::to_string(grid[row][col].value)).c_str());
247+
}
248+
else {
249+
lv_table_set_cell_value(gridDisplay, row, col, "");
250+
}
251+
switch (grid[row][col].value) {
252+
case 0:
253+
case 2:
254+
case 4:
255+
lv_table_set_cell_type(gridDisplay, row, col, 1);
256+
break;
257+
case 8:
258+
case 16:
259+
lv_table_set_cell_type(gridDisplay, row, col, 2);
260+
break;
261+
case 32:
262+
case 64:
263+
lv_table_set_cell_type(gridDisplay, row, col, 3);
264+
break;
265+
default:
266+
lv_table_set_cell_type(gridDisplay, row, col, 4);
267+
break;
268+
}
269+
}
270+
}
271+
}

src/displayapp/screens/Twos.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
3+
#include <lvgl/src/lv_core/lv_obj.h>
4+
#include "Screen.h"
5+
6+
namespace Pinetime {
7+
namespace Applications {
8+
struct Tile {
9+
bool merged = false;
10+
unsigned int value = 0;
11+
};
12+
namespace Screens {
13+
class Twos : public Screen {
14+
public:
15+
Twos(DisplayApp* app);
16+
~Twos() override;
17+
bool Refresh() override;
18+
bool OnButtonPushed() override;
19+
bool OnTouchEvent(TouchEvents event) override;
20+
21+
private:
22+
bool running = true;
23+
lv_obj_t *scoreText;
24+
lv_obj_t *gridDisplay;
25+
Tile grid[4][4];
26+
unsigned int score = 0;
27+
void updateGridDisplay(Tile grid[][4]);
28+
bool tryMerge(Tile grid[][4], int &newRow, int &newCol, int oldRow, int oldCol);
29+
bool tryMove(Tile grid[][4], int newRow, int newCol, int oldRow, int oldCol);
30+
bool placeNewTile();
31+
};
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)