Twos code cleanup (#1220)

This commit is contained in:
Riku Isokoski 2022-07-06 11:29:23 +03:00 committed by GitHub
parent b9f5a0745d
commit 9b92861753
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 107 deletions

View file

@ -1,10 +1,7 @@
#include "displayapp/screens/Twos.h" #include "displayapp/screens/Twos.h"
#include <array>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <lvgl/lvgl.h> #include <lvgl/lvgl.h>
#include <utility>
#include <vector>
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
@ -21,33 +18,33 @@ Twos::Twos(Pinetime::Applications::DisplayApp* app) : Screen(app) {
lv_style_set_border_width(&style_cell1, LV_STATE_DEFAULT, 3); lv_style_set_border_width(&style_cell1, LV_STATE_DEFAULT, 3);
lv_style_set_bg_opa(&style_cell1, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_cell1, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_cell1, LV_STATE_DEFAULT, lv_color_hex(0xcdc0b4)); lv_style_set_bg_color(&style_cell1, LV_STATE_DEFAULT, lv_color_hex(0xcdc0b4));
lv_style_set_pad_top(&style_cell1, LV_STATE_DEFAULT, 25); lv_style_set_pad_top(&style_cell1, LV_STATE_DEFAULT, 29);
lv_style_set_text_color(&style_cell1, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_style_set_text_color(&style_cell1, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_style_set_border_color(&style_cell2, LV_STATE_DEFAULT, lv_color_hex(0xbbada0)); lv_style_set_border_color(&style_cell2, LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
lv_style_set_border_width(&style_cell2, LV_STATE_DEFAULT, 3); lv_style_set_border_width(&style_cell2, LV_STATE_DEFAULT, 3);
lv_style_set_bg_opa(&style_cell2, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_cell2, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_cell2, LV_STATE_DEFAULT, lv_color_hex(0xefdfc6)); lv_style_set_bg_color(&style_cell2, LV_STATE_DEFAULT, lv_color_hex(0xefdfc6));
lv_style_set_pad_top(&style_cell2, LV_STATE_DEFAULT, 25); lv_style_set_pad_top(&style_cell2, LV_STATE_DEFAULT, 29);
lv_style_set_text_color(&style_cell2, LV_STATE_DEFAULT, LV_COLOR_BLACK); lv_style_set_text_color(&style_cell2, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_style_set_border_color(&style_cell3, LV_STATE_DEFAULT, lv_color_hex(0xbbada0)); lv_style_set_border_color(&style_cell3, LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
lv_style_set_border_width(&style_cell3, LV_STATE_DEFAULT, 3); lv_style_set_border_width(&style_cell3, LV_STATE_DEFAULT, 3);
lv_style_set_bg_opa(&style_cell3, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_cell3, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_cell3, LV_STATE_DEFAULT, lv_color_hex(0xef9263)); lv_style_set_bg_color(&style_cell3, LV_STATE_DEFAULT, lv_color_hex(0xef9263));
lv_style_set_pad_top(&style_cell3, LV_STATE_DEFAULT, 25); lv_style_set_pad_top(&style_cell3, LV_STATE_DEFAULT, 29);
lv_style_set_border_color(&style_cell4, LV_STATE_DEFAULT, lv_color_hex(0xbbada0)); lv_style_set_border_color(&style_cell4, LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
lv_style_set_border_width(&style_cell4, LV_STATE_DEFAULT, 3); lv_style_set_border_width(&style_cell4, LV_STATE_DEFAULT, 3);
lv_style_set_bg_opa(&style_cell4, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_cell4, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_cell4, LV_STATE_DEFAULT, lv_color_hex(0xf76142)); lv_style_set_bg_color(&style_cell4, LV_STATE_DEFAULT, lv_color_hex(0xf76142));
lv_style_set_pad_top(&style_cell4, LV_STATE_DEFAULT, 25); lv_style_set_pad_top(&style_cell4, LV_STATE_DEFAULT, 29);
lv_style_set_border_color(&style_cell5, LV_STATE_DEFAULT, lv_color_hex(0xbbada0)); lv_style_set_border_color(&style_cell5, LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
lv_style_set_border_width(&style_cell5, LV_STATE_DEFAULT, 3); lv_style_set_border_width(&style_cell5, LV_STATE_DEFAULT, 3);
lv_style_set_bg_opa(&style_cell5, LV_STATE_DEFAULT, LV_OPA_COVER); lv_style_set_bg_opa(&style_cell5, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_bg_color(&style_cell5, LV_STATE_DEFAULT, lv_color_hex(0x007dc5)); lv_style_set_bg_color(&style_cell5, LV_STATE_DEFAULT, lv_color_hex(0x007dc5));
lv_style_set_pad_top(&style_cell5, LV_STATE_DEFAULT, 25); lv_style_set_pad_top(&style_cell5, LV_STATE_DEFAULT, 29);
// format grid display // format grid display
@ -57,24 +54,22 @@ Twos::Twos(Pinetime::Applications::DisplayApp* app) : Screen(app) {
lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL3, &style_cell3); lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL3, &style_cell3);
lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL4, &style_cell4); lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL4, &style_cell4);
lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL4 + 1, &style_cell5); lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL4 + 1, &style_cell5);
lv_table_set_col_cnt(gridDisplay, 4); lv_table_set_col_cnt(gridDisplay, nCols);
lv_table_set_row_cnt(gridDisplay, 4); lv_table_set_row_cnt(gridDisplay, nRows);
lv_table_set_col_width(gridDisplay, 0, LV_HOR_RES / 4); for (int col = 0; col < nCols; col++) {
lv_table_set_col_width(gridDisplay, 1, LV_HOR_RES / 4); static constexpr int colWidth = LV_HOR_RES_MAX / nCols;
lv_table_set_col_width(gridDisplay, 2, LV_HOR_RES / 4); lv_table_set_col_width(gridDisplay, col, colWidth);
lv_table_set_col_width(gridDisplay, 3, LV_HOR_RES / 4); for (int row = 0; row < nRows; row++) {
lv_obj_align(gridDisplay, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_obj_clean_style_list(gridDisplay, LV_TABLE_PART_BG);
// initialize grid
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
grid[row][col].value = 0; grid[row][col].value = 0;
lv_table_set_cell_type(gridDisplay, row, col, 1); lv_table_set_cell_type(gridDisplay, row, col, 1);
lv_table_set_cell_align(gridDisplay, row, col, LV_LABEL_ALIGN_CENTER); lv_table_set_cell_align(gridDisplay, row, col, LV_LABEL_ALIGN_CENTER);
} }
} }
// Move one pixel down to remove a gap
lv_obj_align(gridDisplay, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 1);
lv_obj_clean_style_list(gridDisplay, LV_TABLE_PART_BG);
placeNewTile(); placeNewTile();
placeNewTile(); placeNewTile();
@ -82,7 +77,7 @@ Twos::Twos(Pinetime::Applications::DisplayApp* app) : Screen(app) {
scoreText = lv_label_create(lv_scr_act(), nullptr); scoreText = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_width(scoreText, LV_HOR_RES); lv_obj_set_width(scoreText, LV_HOR_RES);
lv_label_set_align(scoreText, LV_ALIGN_IN_LEFT_MID); lv_label_set_align(scoreText, LV_ALIGN_IN_LEFT_MID);
lv_obj_align(scoreText, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 10); lv_obj_align(scoreText, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
lv_label_set_recolor(scoreText, true); lv_label_set_recolor(scoreText, true);
lv_label_set_text_fmt(scoreText, "Score #FFFF00 %i#", score); lv_label_set_text_fmt(scoreText, "Score #FFFF00 %i#", score);
} }
@ -97,39 +92,38 @@ Twos::~Twos() {
} }
bool Twos::placeNewTile() { bool Twos::placeNewTile() {
std::vector<std::pair<int, int>> availableCells; unsigned int emptyCells[nCells];
for (int row = 0; row < 4; row++) { unsigned int nEmpty = 0;
for (int col = 0; col < 4; col++) { for (unsigned int i = 0; i < nCells; i++) {
if (!grid[row][col].value) { const unsigned int row = i / nCols;
availableCells.push_back(std::make_pair(row, col)); const unsigned int col = i % nCols;
} if (grid[row][col].value == 0) {
emptyCells[nEmpty] = i;
nEmpty++;
} }
} }
if (availableCells.size() == 0) { if (nEmpty == 0) {
return false; // game lost return false; // game lost
} }
auto it = availableCells.cbegin(); int random = rand() % nEmpty;
int random = rand() % availableCells.size();
std::advance(it, random);
std::pair<int, int> newCell = *it;
if ((rand() % 100) < 90) if ((rand() % 100) < 90) {
grid[newCell.first][newCell.second].value = 2; grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 2;
else } else {
grid[newCell.first][newCell.second].value = 4; grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 4;
updateGridDisplay(grid); }
updateGridDisplay();
return true; return true;
} }
bool Twos::tryMerge(TwosTile grid[][4], int& newRow, int& newCol, int oldRow, int oldCol) { bool Twos::tryMerge(int newRow, int newCol, int oldRow, int oldCol) {
if (grid[newRow][newCol].value == grid[oldRow][oldCol].value) { if (grid[newRow][newCol].value == grid[oldRow][oldCol].value) {
if ((newCol != oldCol) || (newRow != oldRow)) { if ((newCol != oldCol) || (newRow != oldRow)) {
if (!grid[newRow][newCol].merged) { if (!grid[newRow][newCol].merged) {
unsigned int newVal = grid[oldRow][oldCol].value *= 2; grid[newRow][newCol].value *= 2;
grid[newRow][newCol].value = newVal; score += grid[newRow][newCol].value;
score += newVal;
lv_label_set_text_fmt(scoreText, "Score #FFFF00 %i#", score); lv_label_set_text_fmt(scoreText, "Score #FFFF00 %i#", score);
grid[oldRow][oldCol].value = 0; grid[oldRow][oldCol].value = 0;
grid[newRow][newCol].merged = true; grid[newRow][newCol].merged = true;
@ -140,7 +134,7 @@ bool Twos::tryMerge(TwosTile grid[][4], int& newRow, int& newCol, int oldRow, in
return false; return false;
} }
bool Twos::tryMove(TwosTile grid[][4], int newRow, int newCol, int oldRow, int oldCol) { bool Twos::tryMove(int newRow, int newCol, int oldRow, int oldCol) {
if (((newCol >= 0) && (newCol != oldCol)) || ((newRow >= 0) && (newRow != oldRow))) { if (((newCol >= 0) && (newCol != oldCol)) || ((newRow >= 0) && (newRow != oldRow))) {
grid[newRow][newCol].value = grid[oldRow][oldCol].value; grid[newRow][newCol].value = grid[oldRow][oldCol].value;
grid[oldRow][oldCol].value = 0; grid[oldRow][oldCol].value = 0;
@ -151,100 +145,108 @@ bool Twos::tryMove(TwosTile grid[][4], int newRow, int newCol, int oldRow, int o
bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
bool validMove = false; bool validMove = false;
for (int row = 0; row < 4; row++) { for (unsigned int i = 0; i < nCells; i++) {
for (int col = 0; col < 4; col++) { const unsigned int row = i / nCols;
const unsigned int col = i % nCols;
grid[row][col].merged = false; // reinitialize merge state grid[row][col].merged = false; // reinitialize merge state
} }
}
switch (event) { switch (event) {
case TouchEvents::SwipeLeft: case TouchEvents::SwipeLeft:
for (int col = 1; col < 4; col++) { // ignore tiles already on far left for (int col = 1; col < nCols; col++) { // ignore tiles already on far left
for (int row = 0; row < 4; row++) { for (int row = 0; row < nRows; row++) {
if (grid[row][col].value) { if (grid[row][col].value > 0) {
int newCol = -1; int newCol = -1;
for (int potentialNewCol = col - 1; potentialNewCol >= 0; potentialNewCol--) { for (int potentialNewCol = col - 1; potentialNewCol >= 0; potentialNewCol--) {
if (!grid[row][potentialNewCol].value) { if (grid[row][potentialNewCol].value == 0) {
newCol = potentialNewCol; newCol = potentialNewCol;
} else { // blocked by another tile } else { // blocked by another tile
if (tryMerge(grid, row, potentialNewCol, row, col)) if (tryMerge(row, potentialNewCol, row, col)) {
validMove = true; validMove = true;
}
break; break;
} }
} }
if (tryMove(grid, row, newCol, row, col)) if (tryMove(row, newCol, row, col)) {
validMove = true; validMove = true;
} }
} }
} }
}
if (validMove) { if (validMove) {
placeNewTile(); placeNewTile();
} }
return true; return true;
case TouchEvents::SwipeRight: case TouchEvents::SwipeRight:
for (int col = 2; col >= 0; col--) { // ignore tiles already on far right for (int col = nCols - 2; col >= 0; col--) { // ignore tiles already on far right
for (int row = 0; row < 4; row++) { for (int row = 0; row < nRows; row++) {
if (grid[row][col].value) { if (grid[row][col].value > 0) {
int newCol = -1; int newCol = -1;
for (int potentialNewCol = col + 1; potentialNewCol < 4; potentialNewCol++) { for (int potentialNewCol = col + 1; potentialNewCol < nCols; potentialNewCol++) {
if (!grid[row][potentialNewCol].value) { if (grid[row][potentialNewCol].value == 0) {
newCol = potentialNewCol; newCol = potentialNewCol;
} else { // blocked by another tile } else { // blocked by another tile
if (tryMerge(grid, row, potentialNewCol, row, col)) if (tryMerge(row, potentialNewCol, row, col)) {
validMove = true; validMove = true;
}
break; break;
} }
} }
if (tryMove(grid, row, newCol, row, col)) if (tryMove(row, newCol, row, col)) {
validMove = true; validMove = true;
} }
} }
} }
}
if (validMove) { if (validMove) {
placeNewTile(); placeNewTile();
} }
return true; return true;
case TouchEvents::SwipeUp: case TouchEvents::SwipeUp:
for (int row = 1; row < 4; row++) { // ignore tiles already on top for (int row = 1; row < nRows; row++) { // ignore tiles already on top
for (int col = 0; col < 4; col++) { for (int col = 0; col < nCols; col++) {
if (grid[row][col].value) { if (grid[row][col].value > 0) {
int newRow = -1; int newRow = -1;
for (int potentialNewRow = row - 1; potentialNewRow >= 0; potentialNewRow--) { for (int potentialNewRow = row - 1; potentialNewRow >= 0; potentialNewRow--) {
if (!grid[potentialNewRow][col].value) { if (grid[potentialNewRow][col].value == 0) {
newRow = potentialNewRow; newRow = potentialNewRow;
} else { // blocked by another tile } else { // blocked by another tile
if (tryMerge(grid, potentialNewRow, col, row, col)) if (tryMerge(potentialNewRow, col, row, col)) {
validMove = true; validMove = true;
}
break; break;
} }
} }
if (tryMove(grid, newRow, col, row, col)) if (tryMove(newRow, col, row, col)) {
validMove = true; validMove = true;
} }
} }
} }
}
if (validMove) { if (validMove) {
placeNewTile(); placeNewTile();
} }
return true; return true;
case TouchEvents::SwipeDown: case TouchEvents::SwipeDown:
for (int row = 2; row >= 0; row--) { // ignore tiles already on bottom for (int row = nRows - 2; row >= 0; row--) { // ignore tiles already on bottom
for (int col = 0; col < 4; col++) { for (int col = 0; col < nCols; col++) {
if (grid[row][col].value) { if (grid[row][col].value > 0) {
int newRow = -1; int newRow = -1;
for (int potentialNewRow = row + 1; potentialNewRow < 4; potentialNewRow++) { for (int potentialNewRow = row + 1; potentialNewRow < nRows; potentialNewRow++) {
if (!grid[potentialNewRow][col].value) { if (grid[potentialNewRow][col].value == 0) {
newRow = potentialNewRow; newRow = potentialNewRow;
} else { // blocked by another tile } else { // blocked by another tile
if (tryMerge(grid, potentialNewRow, col, row, col)) if (tryMerge(potentialNewRow, col, row, col)) {
validMove = true; validMove = true;
}
break; break;
} }
} }
if (tryMove(grid, newRow, col, row, col)) if (tryMove(newRow, col, row, col)) {
validMove = true; validMove = true;
} }
} }
} }
}
if (validMove) { if (validMove) {
placeNewTile(); placeNewTile();
} }
@ -255,10 +257,11 @@ bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
return false; return false;
} }
void Twos::updateGridDisplay(TwosTile grid[][4]) { void Twos::updateGridDisplay() {
for (int row = 0; row < 4; row++) { for (unsigned int i = 0; i < nCells; i++) {
for (int col = 0; col < 4; col++) { const unsigned int row = i / nCols;
if (grid[row][col].value) { const unsigned int col = i % nCols;
if (grid[row][col].value > 0) {
char buffer[7]; char buffer[7];
sprintf(buffer, "%d", grid[row][col].value); sprintf(buffer, "%d", grid[row][col].value);
lv_table_set_cell_value(gridDisplay, row, col, buffer); lv_table_set_cell_value(gridDisplay, row, col, buffer);
@ -286,5 +289,4 @@ void Twos::updateGridDisplay(TwosTile grid[][4]) {
break; break;
} }
} }
}
} }

View file

@ -26,11 +26,14 @@ namespace Pinetime {
lv_obj_t* scoreText; lv_obj_t* scoreText;
lv_obj_t* gridDisplay; lv_obj_t* gridDisplay;
TwosTile grid[4][4]; static constexpr int nCols = 4;
static constexpr int nRows = 4;
static constexpr int nCells = nCols * nRows;
TwosTile grid[nRows][nCols];
unsigned int score = 0; unsigned int score = 0;
void updateGridDisplay(TwosTile grid[][4]); void updateGridDisplay();
bool tryMerge(TwosTile grid[][4], int& newRow, int& newCol, int oldRow, int oldCol); bool tryMerge(int newRow, int newCol, int oldRow, int oldCol);
bool tryMove(TwosTile grid[][4], int newRow, int newCol, int oldRow, int oldCol); bool tryMove(int newRow, int newCol, int oldRow, int oldCol);
bool placeNewTile(); bool placeNewTile();
}; };
} }