#include #include #include // We assume the size of the screen 120x160 #define GRID_WIDTH 12 #define GRID_HEIGHT 16 #define BLOCK_SIZE 10 #define BLOCK_MARGIN 2 const int BTN1_PIN = 25; volatile bool btn1_pressed = false; const int BTN2_PIN = 33; volatile bool btn2_pressed = false; // I am using ST7735S, a very cheap display from aliexpress TFT_eSPI tft = TFT_eSPI(); // pins are defined un User_Setup.h // forward declarations void IRAM_ATTR isr1(); void IRAM_ATTR isr2(); // the main board and the offboard, to check for dirty squares uint32_t main_board[GRID_WIDTH][GRID_HEIGHT], offboard[GRID_WIDTH][GRID_HEIGHT]; /* The value (i,j) on the board is given by main_board[i][j]. if board(i,j) = 0, then the space there is empty otherwise board(i,j)'s first bit determines whether the block is "old" or not, and the rest denotes a color */ /* Blocks are defined in grids of 4x4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ #define BLOCKS_COUNT 2 uint8_t blocks[BLOCKS_COUNT][4] = { {0,1,2,3},{0,1,4,5} }; #define COLORS_SIZE 6 uint32_t block_colors[COLORS_SIZE] = { TFT_NAVY, TFT_DARKGREEN, TFT_RED, TFT_YELLOW, TFT_GREEN, TFT_PINK }; // Graphics-related functions void init_board() { tft.fillScreen(TFT_BLACK); for(int i = 0; i < GRID_WIDTH; i++) for(int j = 0; j < GRID_HEIGHT; j++) { main_board[i][j] = 0; offboard[i][j] = 0; } } void set_board(int i, int j, int color, bool old) { int v = color<<1; if (old) { v = v|1; } main_board[i][j] = v; } void draw_board() { for(int i = 0; i < GRID_WIDTH; i++) for(int j = 0; j < GRID_HEIGHT; j++) { if (main_board[i][j] == offboard[i][j]) continue; // no need to redraw offboard[i][j] = main_board[i][j]; int color = main_board[i][j]>>1; tft.fillRect(i*BLOCK_SIZE+BLOCK_MARGIN,j*BLOCK_SIZE+BLOCK_MARGIN, BLOCK_SIZE-BLOCK_MARGIN,BLOCK_SIZE-BLOCK_MARGIN, color); } } // Draw a block of type TYPE with color COLOR, starting at coordinates X, Y. // Updates the main_board directly void draw_block(int x, int y, int type, int color, bool old) { for (int i = 0; i < 4; i++) { int v = blocks[type][i]; int dx = v%4; int dy = v/4; set_board(x+dx, y+dy, color, old); } } // Game logic // Current and previous positions of the active block int px = 0, cx = 0; int py = 0, cy = 0; // Type of the active block. Should be 0 <= cblokc_ty < BLOCKS_COUNT int cblock_ty = 0; // Color of the current block int cblock_color = TFT_RED; void new_block() { px = 0; cx = 0; py = 0; cy = 0; cblock_ty = 0; cblock_color = TFT_RED; } bool check_collision() { for (int i = 0; i < 4; i++) { int v = blocks[cblock_ty][i]; int dx = v%4; int dy = v/4; if (cy+dy >= GRID_HEIGHT) { return true; } int is_old = main_board[cx+dx][cy+dy]&1; if (is_old) { return true; } } return false; } // Setup void setup() { tft.init(); // initialize a ST7735S chip Serial.begin(115200); // set up the buttons and the interrupts pinMode(BTN1_PIN, INPUT_PULLUP); attachInterrupt(BTN1_PIN, isr1, FALLING); pinMode(BTN2_PIN, INPUT_PULLUP); attachInterrupt(BTN2_PIN, isr2, FALLING); init_board(); new_block(); delay(100); } void loop() { if (check_collision ()) { // designate the block as "old" and leave it there draw_block(px, py, cblock_ty, cblock_color, true); new_block(); return; } // clear the previous block if (cy != py || cx != px) { draw_block(px, py, cblock_ty, TFT_BLACK, false); } draw_block(cx, cy, cblock_ty, cblock_color, false); draw_board(); px = cx; py = cy; if (btn1_pressed) { btn1_pressed = false; cx--; } if (btn2_pressed) { btn2_pressed = false; cx++; } cy++; delay(350); } void IRAM_ATTR isr1() { btn1_pressed = true; } void IRAM_ATTR isr2() { btn2_pressed = true; }