diff --git a/src/displayapp/screens/Twos.cpp b/src/displayapp/screens/Twos.cpp index 7e465fcd..5d1f4980 100644 --- a/src/displayapp/screens/Twos.cpp +++ b/src/displayapp/screens/Twos.cpp @@ -1,10 +1,7 @@ #include "displayapp/screens/Twos.h" -#include #include #include #include -#include -#include 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_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_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_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_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_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_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_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_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_width(&style_cell4, LV_STATE_DEFAULT, 3); 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_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_width(&style_cell5, LV_STATE_DEFAULT, 3); 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_pad_top(&style_cell5, LV_STATE_DEFAULT, 25); + lv_style_set_pad_top(&style_cell5, LV_STATE_DEFAULT, 29); // 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_CELL4, &style_cell4); lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL4 + 1, &style_cell5); - 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); - - 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++) { + lv_table_set_col_cnt(gridDisplay, nCols); + lv_table_set_row_cnt(gridDisplay, nRows); + for (int col = 0; col < nCols; col++) { + static constexpr int colWidth = LV_HOR_RES_MAX / nCols; + lv_table_set_col_width(gridDisplay, col, colWidth); + for (int row = 0; row < nRows; row++) { grid[row][col].value = 0; lv_table_set_cell_type(gridDisplay, row, col, 1); 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(); @@ -82,7 +77,7 @@ Twos::Twos(Pinetime::Applications::DisplayApp* app) : Screen(app) { 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, 10); + lv_obj_align(scoreText, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0); lv_label_set_recolor(scoreText, true); lv_label_set_text_fmt(scoreText, "Score #FFFF00 %i#", score); } @@ -97,39 +92,38 @@ Twos::~Twos() { } bool Twos::placeNewTile() { - std::vector> 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)); - } + unsigned int emptyCells[nCells]; + unsigned int nEmpty = 0; + for (unsigned int i = 0; i < nCells; i++) { + const unsigned int row = i / nCols; + 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 } - auto it = availableCells.cbegin(); - int random = rand() % availableCells.size(); - std::advance(it, random); - std::pair newCell = *it; + int random = rand() % nEmpty; - if ((rand() % 100) < 90) - grid[newCell.first][newCell.second].value = 2; - else - grid[newCell.first][newCell.second].value = 4; - updateGridDisplay(grid); + if ((rand() % 100) < 90) { + grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 2; + } else { + grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 4; + } + updateGridDisplay(); 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 ((newCol != oldCol) || (newRow != oldRow)) { if (!grid[newRow][newCol].merged) { - unsigned int newVal = grid[oldRow][oldCol].value *= 2; - grid[newRow][newCol].value = newVal; - score += newVal; + grid[newRow][newCol].value *= 2; + score += grid[newRow][newCol].value; lv_label_set_text_fmt(scoreText, "Score #FFFF00 %i#", score); grid[oldRow][oldCol].value = 0; grid[newRow][newCol].merged = true; @@ -140,7 +134,7 @@ bool Twos::tryMerge(TwosTile grid[][4], int& newRow, int& newCol, int oldRow, in 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))) { grid[newRow][newCol].value = grid[oldRow][oldCol].value; grid[oldRow][oldCol].value = 0; @@ -151,28 +145,30 @@ bool Twos::tryMove(TwosTile grid[][4], int newRow, int newCol, int oldRow, int o bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool validMove = false; - for (int row = 0; row < 4; row++) { - for (int col = 0; col < 4; col++) { - grid[row][col].merged = false; // reinitialize merge state - } + for (unsigned int i = 0; i < nCells; i++) { + const unsigned int row = i / nCols; + const unsigned int col = i % nCols; + 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) { + for (int col = 1; col < nCols; col++) { // ignore tiles already on far left + for (int row = 0; row < nRows; row++) { + if (grid[row][col].value > 0) { int newCol = -1; for (int potentialNewCol = col - 1; potentialNewCol >= 0; potentialNewCol--) { - if (!grid[row][potentialNewCol].value) { + if (grid[row][potentialNewCol].value == 0) { newCol = potentialNewCol; } else { // blocked by another tile - if (tryMerge(grid, row, potentialNewCol, row, col)) + if (tryMerge(row, potentialNewCol, row, col)) { validMove = true; + } break; } } - if (tryMove(grid, row, newCol, row, col)) + if (tryMove(row, newCol, row, col)) { validMove = true; + } } } } @@ -181,21 +177,23 @@ bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) { } 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) { + for (int col = nCols - 2; col >= 0; col--) { // ignore tiles already on far right + for (int row = 0; row < nRows; row++) { + if (grid[row][col].value > 0) { int newCol = -1; - for (int potentialNewCol = col + 1; potentialNewCol < 4; potentialNewCol++) { - if (!grid[row][potentialNewCol].value) { + for (int potentialNewCol = col + 1; potentialNewCol < nCols; potentialNewCol++) { + if (grid[row][potentialNewCol].value == 0) { newCol = potentialNewCol; } else { // blocked by another tile - if (tryMerge(grid, row, potentialNewCol, row, col)) + if (tryMerge(row, potentialNewCol, row, col)) { validMove = true; + } break; } } - if (tryMove(grid, row, newCol, row, col)) + if (tryMove(row, newCol, row, col)) { validMove = true; + } } } } @@ -204,21 +202,23 @@ bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) { } 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) { + for (int row = 1; row < nRows; row++) { // ignore tiles already on top + for (int col = 0; col < nCols; col++) { + if (grid[row][col].value > 0) { int newRow = -1; for (int potentialNewRow = row - 1; potentialNewRow >= 0; potentialNewRow--) { - if (!grid[potentialNewRow][col].value) { + if (grid[potentialNewRow][col].value == 0) { newRow = potentialNewRow; } else { // blocked by another tile - if (tryMerge(grid, potentialNewRow, col, row, col)) + if (tryMerge(potentialNewRow, col, row, col)) { validMove = true; + } break; } } - if (tryMove(grid, newRow, col, row, col)) + if (tryMove(newRow, col, row, col)) { validMove = true; + } } } } @@ -227,21 +227,23 @@ bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) { } 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) { + for (int row = nRows - 2; row >= 0; row--) { // ignore tiles already on bottom + for (int col = 0; col < nCols; col++) { + if (grid[row][col].value > 0) { int newRow = -1; - for (int potentialNewRow = row + 1; potentialNewRow < 4; potentialNewRow++) { - if (!grid[potentialNewRow][col].value) { + for (int potentialNewRow = row + 1; potentialNewRow < nRows; potentialNewRow++) { + if (grid[potentialNewRow][col].value == 0) { newRow = potentialNewRow; } else { // blocked by another tile - if (tryMerge(grid, potentialNewRow, col, row, col)) + if (tryMerge(potentialNewRow, col, row, col)) { validMove = true; + } break; } } - if (tryMove(grid, newRow, col, row, col)) + if (tryMove(newRow, col, row, col)) { validMove = true; + } } } } @@ -255,36 +257,36 @@ bool Twos::OnTouchEvent(Pinetime::Applications::TouchEvents event) { return false; } -void Twos::updateGridDisplay(TwosTile grid[][4]) { - for (int row = 0; row < 4; row++) { - for (int col = 0; col < 4; col++) { - if (grid[row][col].value) { - char buffer[7]; - sprintf(buffer, "%d", grid[row][col].value); - lv_table_set_cell_value(gridDisplay, row, col, buffer); - } else { - lv_table_set_cell_value(gridDisplay, row, col, ""); - } - switch (grid[row][col].value) { - case 0: - lv_table_set_cell_type(gridDisplay, row, col, 1); - break; - case 2: - case 4: - lv_table_set_cell_type(gridDisplay, row, col, 2); - break; - case 8: - case 16: - lv_table_set_cell_type(gridDisplay, row, col, 3); - break; - case 32: - case 64: - lv_table_set_cell_type(gridDisplay, row, col, 4); - break; - default: - lv_table_set_cell_type(gridDisplay, row, col, 5); - break; - } +void Twos::updateGridDisplay() { + for (unsigned int i = 0; i < nCells; i++) { + const unsigned int row = i / nCols; + const unsigned int col = i % nCols; + if (grid[row][col].value > 0) { + char buffer[7]; + sprintf(buffer, "%d", grid[row][col].value); + lv_table_set_cell_value(gridDisplay, row, col, buffer); + } else { + lv_table_set_cell_value(gridDisplay, row, col, ""); + } + switch (grid[row][col].value) { + case 0: + lv_table_set_cell_type(gridDisplay, row, col, 1); + break; + case 2: + case 4: + lv_table_set_cell_type(gridDisplay, row, col, 2); + break; + case 8: + case 16: + lv_table_set_cell_type(gridDisplay, row, col, 3); + break; + case 32: + case 64: + lv_table_set_cell_type(gridDisplay, row, col, 4); + break; + default: + lv_table_set_cell_type(gridDisplay, row, col, 5); + break; } } } diff --git a/src/displayapp/screens/Twos.h b/src/displayapp/screens/Twos.h index 5a0c4350..4a6ada0b 100644 --- a/src/displayapp/screens/Twos.h +++ b/src/displayapp/screens/Twos.h @@ -26,11 +26,14 @@ namespace Pinetime { lv_obj_t* scoreText; 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; - void updateGridDisplay(TwosTile grid[][4]); - bool tryMerge(TwosTile grid[][4], int& newRow, int& newCol, int oldRow, int oldCol); - bool tryMove(TwosTile grid[][4], int newRow, int newCol, int oldRow, int oldCol); + void updateGridDisplay(); + bool tryMerge(int newRow, int newCol, int oldRow, int oldCol); + bool tryMove(int newRow, int newCol, int oldRow, int oldCol); bool placeNewTile(); }; }