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+ }
0 commit comments