Skip to content

Commit 033a09d

Browse files
authored
Merge pull request #154 from jedmijares/twos-game
Adds 2048 clone game
2 parents e0082f0 + 12617ed commit 033a09d

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)